diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..94f896dbe4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +.project +.settings +*.csproj +*.csproj.user +*.build +*.mdb +*.mdp +*.mds +*.pdb +*.pidb +*.dll.build +*.dll + +# 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 +*/*/*/*/obj +*/*/*/*/*/obj +*/*/*/*/*/*/obj +*/*/*/*/*/*/*/obj +*/*/bin +*/*/*/bin +*/*/*/*/bin +*/*/*/*/*/bin +*/*/*/*/*/*/bin +*/*/*/*/*/*/*/bin +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 +bin/ScriptEngines/*/*.state +bin/*.maddin +bin/*.exe +bin/*.ini +bin/j2kDecodeCache +bin/Physics* +bin/Terrain* +bin/Regions/* +bin/UserAssets +bin/assetcache +bin/maptiles +bin/bakes +bin/estate_settings.xml +bin/config-include/CenomeCache.ini +bin/config-include/FlotsamCache.ini +bin/config-include/GridCommon.ini +bin/config-include/StandaloneCommon.ini +bin/OpenSim.Grid.AssetInventoryServer.log +bin/OpenSim.Grid.AssetServer.log +bin/OpenSim.Grid.GridServer.log +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 +Prebuild/Prebuild.build +Prebuild/Prebuild.sln +TestResult.xml +cov/* +OpenSim/OpenSim.userprefs +OpenSim/OpenSim.usertasks +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/ +OpenSim/Framework/Tests/test-results/ +OpenSim/Region/ClientStack/Linden/Caps/test-results/ +OpenSim/Region/ClientStack/Linden/UDP/Tests/test-results/ +OpenSim/Region/CoreModules/test-results/ +OpenSim/Region/Framework/test-results/ +OpenSim/Region/OptionalModules/test-results/ +OpenSim/Region/Physics/BulletDotNETPlugin/ +OpenSim/Region/Physics/Manager/test-results/ +OpenSim/Region/Physics/OdePlugin/Tests/test-results/ +OpenSim/Region/ScriptEngine/test-results/ +OpenSim/Tests/Common/test-results/ +OpenSim/Tests/test-results/ +test-results/ +doc/html +doc/doxygen.error.log + +*.patch diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000000..29bba21730 --- /dev/null +++ b/.hgignore @@ -0,0 +1,23 @@ +^tailor.state.old$ +^tailor.state.journal$ +\.csproj$ +\.csproj\.user$ +\.mdp$ +\.mds$ +\.dll\.build$ +^bin/Debug/.+\.dll$ +^bin/.+\.db$ +^bin/OpenSim\.ini$ +^bin/OpenSim\.log$ +^bin/estate_settings\.xml$ +^bin/Regions +bin/.+\.dll.mdb$ +bin/addin-db- +bin/.+\.dll$ +bin/.+\.maddin$ +Examples/.+\.dll$ +^bin/.+\.exe +^(OpenSim|Prebuild)/.+\.(exe|exe\.build|exe\.mdb)$ +^OpenSim\.(build|sln)$ +^Prebuild/Prebuild\.(build|sln)$ +.+~$ diff --git a/.nant/local.include b/.nant/local.include new file mode 100644 index 0000000000..c20794499f --- /dev/null +++ b/.nant/local.include @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BUILDING.md b/BUILDING.md new file mode 100644 index 0000000000..d8deeeb237 --- /dev/null +++ b/BUILDING.md @@ -0,0 +1,40 @@ +# 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 + !* xbuild option switches + !* clean: xbuild /target:clean + !* debug: (default) xbuild /property:Configuration=Debug + !* release: xbuild /property:Configuration=Release + +# Using Monodevelop + +From the distribution type: + * ./runprebuild.sh + * type monodevelop OpenSim.sln + +# References + +Helpful resources: +* http://opensimulator.org/wiki/Build_Instructions diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt new file mode 100644 index 0000000000..e14e5efcc1 --- /dev/null +++ b/CONTRIBUTORS.txt @@ -0,0 +1,239 @@ +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) +* dahlia +* Melanie Thielker +* Diva (Crista Lopes, University of California, Irvine) +* BlueWall (James Hughes) +* Nebadon Izumi (Michael Cerquoni, OSgrid) +* Snoopy Pfeffer +* Robert Adams (MisterBlue) +* Oren Hurvitz (Kitely) +* Kevin Cozens + += 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. + +* Teravus (w3z) +* Arthur Rodrigo S Valadares (IBM) +* Dan Lake +* Marck +* Mic Bowman + += Past Open Sim Developers = +These folks are alumns of the OpenSim core group, but are now +currently not active. Their great contributions helped get us to +where we are today. + +* Gareth +* Andy- +* MorphW +* CW +* Babblefrog +* Danx0r +* Dalien +* Darok +* Alondria +* Sean Dague / sdague (IBM) +* Tedd +* MingChen (DeepThink Pty Ltd) +* 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) + + += Additional OpenSim Contributors = +These folks have contributed code patches or content to OpenSimulator to help make it +what it is today. + +* A_Biondi +* aduffy70 +* Ai Austin +* alex_carnell +* Alan Webb (IBM) +* Aleric +* Alicia Raven +* Allen Kerensky +* BigFootAg +* 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 +* dr0b3rts +* dslake +* eeyore +* FredoChaplin +* FreakyTech +* Garmin Kawaguichi +* Gavin Hird +* Gerhard +* Godfrey +* Greg C. +* Grumly57 +* GuduleLapointe +* Ewe Loon +* Fernando Oliveira +* Fly-Man +* Flyte Xevious +* Freaky Tech +* Garmin Kawaguichi +* Glenn Martin (MOSES) +* Gryc Ueusp +* H-H-H (ginge264) +* Hiro Lecker +* Iain Oliver +* Imaze Rhiano +* Intimidated +* Jak Daniels +* Jeremy Bongio (IBM) +* jhurliman +* John R Sohn (XenReborn) +* jonc +* Jon Cundill +* Junta Kohime +* Kayne +* kinoc (Daxtron Labs) +* Kira +* Kitto Flora +* KittyLiu +* Kurt Taylor (IBM) +* Lani Global +* lillith_xue +* lkalif +* LuciusSirnah +* lulurun +* M.Igarashi +* Magnuz Binder +* maimedleech +* Mana Janus +* MarcelEdward +* Matt Lehmann +* Mic Bowman +* Michelle Argus +* Michael Cortez (The Flotsam Project, http://osflotsam.org/) +* Michael Heilmann (MOSES) +* Micheil Merlin +* Mike Osias (IBM) +* Mike Pitman (IBM) +* mikemig +* mikkopa/_someone - RealXtend +* Misterblue +* Mircea Kitsune +* mpallari +* MrMonkE +* Nebadon Izumi (Michael Cerquoni - http://OSgrid.org) +* Neil Canham +* nornalbion +* Omar Vera Ustariz (IBM) +* openlifegrid.com +* otakup0pe +* Pixel Tomsen +* 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) +* Strawberry Fride +* Talun +* TechplexEngineer (Blake Bourque) +* TBG Renfold +* tglion +* tlaukkan/Tommil (Tommi S. E. Laukkanen, Bubble Cloud) +* tyre +* Vegaslon +* 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) +* Prototype JavaScript Framework ajax (http://www.prototypejs.org/) +* C5 GENERIC COLLECTION LIBRARY FOR C#/CLI +* Nini (http://nini.sourceforge.net/) +* log4net (http://logging.apache.org/log4net/) +* GlynnTucker.Cache (http://gtcache.sourceforge.net/) +* NDesk.Options 0.2.1 (http://www.ndesk.org/Options) +* Json.NET 3.5 Release 6. The binary used is actually Newtonsoft.Json.Net20.dll for Mono 2.4 compatability (http://james.newtonking.com/projects/json-net.aspx) +* zlib.net for C# 1.0.4 (http://www.componentace.com/zlib_.NET.htm) + +Some plugins are based on Cable Beach +Cable Beach is Copyright (c) 2008 Intel Corporation +see http://forge.opensimulator.org/gf/project/assetserver/ + +In addition, we would like to thank: +* The Mono Project +* The NANT Developers +* Microsoft (.NET, MSSQL-Adapters) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000000..570f7328af --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,25 @@ +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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..493cdca619 --- /dev/null +++ b/Makefile @@ -0,0 +1,43 @@ +# hey, emacs! this is a -*- makefile -*- +# +# OpenSim makefile +# + +RUBY = $(strip $(shell which ruby 2>/dev/null)) +ifeq ($(RUBY),) +NANT = nant +else +NANT = $(shell if test "$$EMACS" = "t" ; then echo "nant"; else echo "./nant-color"; fi) +endif + +all: prebuild + # @export PATH=/usr/local/bin:$(PATH) + ${NANT} + find OpenSim -name \*.mdb -exec cp {} bin \; + +release: prebuild + ${NANT} -D:project.config=Release + find OpenSim -name \*.mdb -exec cp {} bin \; + +prebuild: + ./runprebuild.sh + +clean: + # @export PATH=/usr/local/bin:$(PATH) + -${NANT} clean + +test: prebuild + ${NANT} test + +test-xml: prebuild + ${NANT} test-xml + +tags: + find OpenSim -name \*\.cs | xargs etags + +cscope-tags: + find OpenSim -name \*\.cs -fprint cscope.files + cscope -b + +include $(wildcard Makefile.local) + diff --git a/OpenSim.FxCop b/OpenSim.FxCop new file mode 100644 index 0000000000..d07c26efbf --- /dev/null +++ b/OpenSim.FxCop @@ -0,0 +1,7241 @@ + + + + True + http://www.gotdotnet.com/team/fxcop//xsl/1.35/FxCopReport.xsl + + + + + + True + True + True + 10 + 1 + + False + False + + False + 120 + + + + $(ProjectDir)/lib/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sim + OpenSim + + + + + + + + + Sim + OpenSim.Assets + + + + + + + + + OpenSim.CAPS + + + + + Sim + OpenSim.CAPS + + + + + OpenSim.CAPS + + + + + + + + + Sim + OpenSim.Config.SimConfigDb4o + + + Sim + OpenSim.Config.SimConfigDb4o + + + + + + + + + OpenSim.Framework.Assets + + + + + Sim + OpenSim.Framework.Assets + + + + + + + + + Sim + OpenSim.Framework.Console + + + + + + + + + OpenSim.Framework.Grid + + + + + Sim + OpenSim.Framework.Grid + + + + + + + + + Sim + OpenSim.Framework.Interfaces + + + + + + + + + OpenSim.Framework.Inventory + + + + + Sim + OpenSim.Framework.Inventory + + + + + + + + + OpenSim.Framework.Sims + + + + + Sim + OpenSim.Framework.Sims + + + + + + + + + OpenSim.Framework.Terrain + + + + + Sim + OpenSim.Framework.Terrain + + + + + + + + + OpenSim.Framework.User + + + + + Sim + OpenSim.Framework.User + + + + + + + + + OpenSim.Framework.Utilities + + + + + Sim + OpenSim.Framework.Utilities + + + + + + + + + Sim + OpenSim.GridInterfaces.Local + + + + + + + + + OpenSim.GridInterfaces.Remote + + + + + Sim + OpenSim.GridInterfaces.Remote + + + + + + + + + Plugin + OpenSim.Physics.BasicPhysicsPlugin + + + + + Sim + OpenSim.Physics.BasicPhysicsPlugin + + + + + + + + + Sim + OpenSim.Physics.Manager + + + + + + + + + OpenSim.Physics.OdePlugin + + + + + Plugin + OpenSim.Physics.OdePlugin + + + + + Sim + OpenSim.Physics.OdePlugin + + + + + + + + + OpenSim.Physics.PhysXPlugin + + + + + Plugin + OpenSim.Physics.PhysXPlugin + + + + + Sim + OpenSim.Physics.PhysXPlugin + + + + + + + + + Sim + OpenSim.Storage.LocalStorageDb4o + + + + + + + + + OpenSim.types + + + + + OpenSim.types + + + + + Sim + OpenSim.types + + + + + + + + + OpenSim.UserServer + + + + + Sim + OpenSim.UserServer + + + + + + + + + OpenSim.world + + + + + Sim + OpenSim.world + + + + + + + + + OpenSim.world.scripting + + + + + OpenSim.world.scripting + + + OpenSim.world.scripting + + + + + Sim + OpenSim.world.scripting + + + + + + + + + + + + + OpenGridServices.ServerConsole + + + + + OpenGridServices.ServerConsole + + + + + OpenGridServices.ServerConsole + + + + + + + + + + + conscmd_callback + + + + + conscmd + ServerConsole.conscmd_callback + + + + + conscmd_callback + + + + + + + + + conscmd_callback.RunCmd(String, String[]):Void + cmdparams + cmdparams + + + + + + + + + ShowWhat + + + + + + + + + + + + + ConsoleBase.CmdPrompt(String, String):String + defaultresponse + defaultresponse + + + + + + + + + OptionA + + + + + OptionB + + + + + ConsoleBase.CmdPrompt(String, String, String, String):String + defaultresponse + defaultresponse + + + + + + + + + Passwd + ConsoleBase.PasswdPrompt(String):String + + + + + + + + + Cmd + + + + + ConsoleBase.RunCmd(String, String[]):Object + cmdparams + cmdparams + + + + + + + + + ShowWhat + + + + + + + + + Line + + + + + + + + + Line + + + + + + + + + + + + + Sim + ConsoleType.SimChat + + + + + + + + + ConsoleType.TCP + + + + + + + + + + + MainConsole + + + + + + + + + + + + + + + + + OpenSim.Config.SimConfigDb4o + + + + + OpenSim.Config.SimConfigDb4o + + + + + OpenSim.Config.SimConfigDb4o + + + + + + + + + + + Plugin + OpenSim.Config.SimConfigDb4o.Db40ConfigPlugin + + + + + + + + + Sim + OpenSim.Config.SimConfigDb4o.DbSimConfig + + + + + Db + OpenSim.Config.SimConfigDb4o.DbSimConfig + + + + + + + + + DbSimConfig.InitConfig(Boolean):Void + System.Exception + + + + + DbSimConfig.InitConfig(Boolean):Void + System.UInt32.ToString + System.UInt32.ToString(System.IFormatProvider) + + + + + DbSimConfig.InitConfig(Boolean):Void + System.UInt64.ToString + System.UInt64.ToString(System.IFormatProvider) + + + + + + + + + DbSimConfig.LoadDefaults():Void + System.Convert.ToInt32(System.String) + System.Convert.ToInt32(System.String,System.IFormatProvider) + + + DbSimConfig.LoadDefaults():Void + System.Convert.ToInt32(System.String) + System.Convert.ToInt32(System.String,System.IFormatProvider) + + + DbSimConfig.LoadDefaults():Void + System.Convert.ToInt32(System.String) + System.Convert.ToInt32(System.String,System.IFormatProvider) + + + + + + + + + + + + + Map + + + + + + + + + + + + + + + + + + + OpenSim + + + + + OpenSim + + + + + OpenSim + + + + + OpenSim + + + + + OpenSim + + + + + + + + + + + + + 'args' + RegionServer.Main(String[]):Void + + + 'args' + RegionServer.Main(String[]):Void + + + + + + + + + + + + + + + + + + + OpenSim.Framework.Console + + + + + OpenSim.Framework.Console + + + + + OpenSim.Framework.Console + + + + + + + + + + + + + ConsoleBase.CmdPrompt(String, String):String + defaultresponse + defaultresponse + + + + + + + + + OptionA + + + + + OptionB + + + + + ConsoleBase.CmdPrompt(String, String, String, String):String + defaultresponse + defaultresponse + + + + + + + + + Cmd + + + + + ConsoleBase.RunCmd(String, String[]):Object + cmdparams + cmdparams + + + + + + + + + ShowWhat + + + + + + + + + + + + + Sim + ConsoleType.SimChat + + + + + + + + + ConsoleType.TCP + + + + + + + + + + + MainConsole + + + + + + + + + + + + + + + + + OpenSim.Framework + + + + + OpenSim.Framework + + + + + OpenSim.Framework + + + + + + + + + + + + + Data + + + + + + + + + Description + + + + + + + + + FullID + + + + + + + + + InvType + + + + + + + + + Name + + + + + + + + + Type + + + + + + + + + + + + + PrimData.PrimData() + ParentID + System.UInt32 + 0 + + + + + + + + + FullID + + + + + + + + + LocalID + + + + + + + + + OwnerID + + + + + + + + + ParentID + + + + + + + + + PathBegin + + + + + + + + + PathCurve + + + + + + + + + PathEnd + + + + + + + + + PathRadiusOffset + + + + + + + + + PathRevolutions + + + + + + + + + PathScaleX + + + + + + + + + PathScaleY + + + + + + + + + PathShearX + + + + + + + + + PathShearY + + + + + + + + + PathSkew + + + + + + + + + PathTaperX + + + + + + + + + PathTaperY + + + + + + + + + PathTwist + + + + + + + + + PathTwistBegin + + + + + + + + + PCode + + + + + + + + + Position + + + + + + + + + ProfileBegin + + + + + + + + + ProfileCurve + + + + + + + + + ProfileEnd + + + + + + + + + ProfileHollow + + + + + + + + + Rotation + + + + + + + + + Scale + + + + + + + + + Texture + + + + + + + + + + + + + + + Login + LoginService + LogOn + + + + + + + + + + + + + + + AgentID + + + + + + + + + circuitcode + + + + + circuitcode + AgentCircuitData.circuitcode + + + + + + + + + firstname + + + + + firstname + AgentCircuitData.firstname + + + + + + + + + lastname + + + + + lastname + AgentCircuitData.lastname + + + + + + + + + SecureSessionID + + + + + + + + + SessionID + + + + + + + + + + + OpenSim.Framework.Interfaces.ARequest + + + OpenSim.Framework.Interfaces.ARequest + + + + + + + + + AssetID + + + + + + + + + IsTexture + + + + + + + + + + + + + Authorised + + + + + Authorised + AuthenticateResponse.Authorised + + + + + + + + + LoginInfo + + + + + Login + LoginInfo + LogOn + + + + + + + + + + + Plugin + OpenSim.Framework.Interfaces.IAssetPlugin + + + + + + + + + GetAssetServer + + + + + + + + + + + + + IsTexture + + + + + + + + + + + + + ID + assetID + Id + + + + + + + + + ServerUrl + + + + + ServerKey + + + + + ServerUrl + IAssetServer.SetServerInfo(String, String):Void + + + + + + + + + + + Plugin + OpenSim.Framework.Interfaces.IGridPlugin + + + + + + + + + GetGridServer + + + + + + + + + + + + + ID + sessionID + Id + + + + + ID + agentID + Id + + + + + + + + + GetName + + + + + + + + + ID + sessionID + Id + + + + + ID + agentID + Id + + + + + Logout + LogoutSession + LogOff + + + + + + + + + Neighbours + IGridServer.RequestNeighbours():NeighbourInfo[] + + + + + + + + + IGridServer.RequestUUIDBlock():UUIDBlock + + + + + + + + + ServerUrl + + + + + SendKey + + + + + RecvKey + + + + + IGridServer.SetServerInfo(String, String, String):Void + Recv + RecvKey + + + + + ServerUrl + IGridServer.SetServerInfo(String, String, String):Void + + + + + + + + + + + + + ID + primID + Id + + + + + + + + + ShutDown + method + ShutDown + Shutdown + + + + + + + + + + + Sim + OpenSim.Framework.Interfaces.ISimConfig + + + + + + + + + GetConfigObject + + + + + + + + + + + + + ID + agentID + Id + + + + + + + + + ServerUrl + + + + + SendKey + + + + + RecvKey + + + + + IUserServer.SetServerInfo(String, String, String):Void + Recv + RecvKey + + + + + ServerUrl + IUserServer.SetServerInfo(String, String, String):Void + + + + + + + + + + + + + Logout + LogoutSession + LogOff + + + + + + + + + + + Login + Login + LogOn + + + + + + + + + Agent + + + + + + + + + BaseFolder + + + + + + + + + First + + + + + + + + + InventoryFolder + + + + + + + + + Last + + + + + + + + + SecureSession + + + + + + + + + Session + + + + + + + + + + + Neighbour + OpenSim.Framework.Interfaces.NeighbourInfo + + + + + + + + + regionhandle + + + + + regionhandle + NeighbourInfo.regionhandle + + + + + + + + + RegionLocX + + + + + + + + + RegionLocY + + + + + + + + + sim_ip + + + + + sim + NeighbourInfo.sim_ip + + + + + sim_ip + + + + + + + + + sim_port + + + + + sim + NeighbourInfo.sim_port + + + + + sim_port + + + + + + + + + + + + + agentcircuits + + + + + agentcircuits + + + + + agentcircuits + RemoteGridBase.agentcircuits:Dictionary`2<System.UInt32,OpenSim.Framework.Interfaces.AgentCircuitData> + + + + + + + + + Logout + LogoutSession + LogOff + + + + + + + + + + + Sim + OpenSim.Framework.Interfaces.SimConfig + + + + + + + + + AssetSendKey + + + + + + + + + AssetURL + + + + + + + + + GridRecvKey + + + + + Recv + SimConfig.GridRecvKey + + + + + + + + + GridSendKey + + + + + + + + + GridURL + + + + + + + + + IPListenAddr + + + + + Addr + SimConfig.IPListenAddr + + + + + + + + + IPListenPort + + + + + + + + + RegionHandle + + + + + + + + + RegionLocX + + + + + + + + + RegionLocY + + + + + + + + + RegionName + + + + + + + + + SimConfig.SaveMap(Single[]):Void + heightmap + heightmap + + + + + + + + + UserRecvKey + + + + + Recv + SimConfig.UserRecvKey + + + + + + + + + UserSendKey + + + + + + + + + UserURL + + + + + + + + + + + UUIDBlock + + + + + OpenSim.Framework.Interfaces.UUIDBlock + + + OpenSim.Framework.Interfaces.UUIDBlock + + + + + + + + + BlockEnd + + + + + + + + + BlockStart + + + + + + + + + + + + + + + + + AgentInventory.AgentInventory() + AgentInventory.AgentInventory() AgentInventory.Initialise():Void + + + + + + + + + ID + folderID + Id + + + + + + + + + AgentID + + + + + + + + + ID + folderID + Id + + + + + + + + + Initialise + AgentInventory.Initialise():Void + + + + + + + + + InventoryFolders + + + + + + + + + InventoryItems + + + + + + + + + InventoryRoot + + + + + + + + + LastCached + + + + + + + + + ID + itemID + Id + + + + + + + + + Wearables + + + + + Wearables + AgentInventory.Wearables + + + + + + + + + + + + + AssetID + + + + + + + + + ItemID + + + + + + + + + + + + + DefaultType + + + + + + + + + FolderID + + + + + + + + + FolderName + + + + + + + + + Items + + + + + System.Collections.Generic.List`1<OpenSim.Framework.Inventory.InventoryItem> + InventoryFolder.Items + + + + + + + + + OwnerID + + + + + + + + + ParentID + + + + + + + + + Version + + + + + + + + + + + + + AssetID + + + + + + + + + CreatorID + + + + + + + + + Description + + + + + + + + + FolderID + + + + + + + + + InvType + + + + + + + + + ItemID + + + + + + + + + Name + + + + + + + + + OwnerID + + + + + + + + + Type + + + + + + + + + + + + + + + Sim + OpenSim.Framework.Sims.SimProfile + + + + + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.Exception + + + + + GridURL + + + + + SendKey + + + + + RecvKey + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + Recv + RecvKey + + + + + region_handle + + + + + GridURL + + + + + RecvKey + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.Convert.ToUInt16(System.Object) + System.Convert.ToUInt16(System.Object,System.IFormatProvider) + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.Convert.ToUInt32(System.Object) + System.Convert.ToUInt32(System.Object,System.IFormatProvider) + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.Convert.ToUInt32(System.Object) + System.Convert.ToUInt32(System.Object,System.IFormatProvider) + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.Convert.ToUInt64(System.Object) + System.Convert.ToUInt64(System.Object,System.IFormatProvider) + + + + + SimProfile.LoadFromGrid(UInt64, String, String, String):SimProfile + System.UInt64.ToString + System.UInt64.ToString(System.IFormatProvider) + + + + + + + + + + + Sim + OpenSim.Framework.Sims.SimProfileBase + + + + + + + + + caps_url + + + + + caps_url + + + + + + + + + recvkey + + + + + recvkey + SimProfileBase.recvkey + + + + + + + + + regionhandle + + + + + regionhandle + SimProfileBase.regionhandle + + + + + + + + + RegionLocX + + + + + + + + + RegionLocY + + + + + + + + + regionname + + + + + regionname + SimProfileBase.regionname + + + + + + + + + sendkey + + + + + sendkey + SimProfileBase.sendkey + + + + + + + + + sim_ip + + + + + sim + SimProfileBase.sim_ip + + + + + sim_ip + + + + + + + + + sim_port + + + + + sim + SimProfileBase.sim_port + + + + + sim_port + + + + + + + + + UUID + + + + + + + + + + + + + + + Heightmap + OpenSim.Framework.Terrain.HeightmapGenHills + + + + + + + + + HeightmapGenHills.GenerateHeightmap(Int32, Single, Single, Boolean):Single[] + num + numHills + + + + + Heightmap + HeightmapGenHills.GenerateHeightmap(Int32, Single, Single, Boolean):Single[] + + + + + + + + + HeightmapGenHills.NumHills + + + + + + + + + + + + + + + + + UserProfile.UserProfile() + IsGridGod + System.Boolean + false + + + + + + + + + Sim + UserProfile.AddSimCircuit(UInt32, LLUUID):Void + + + + + regionUUID + + + + + + + + + AssetURL + + + + + + + + + Circuits + + + + + + + + + CurrentSecureSessionID + + + + + + + + + CurrentSessionID + + + + + + + + + firstname + + + + + firstname + UserProfile.firstname + + + + + + + + + homelookat + + + + + homelookat + UserProfile.homelookat + + + + + + + + + homepos + + + + + homepos + UserProfile.homepos + + + + + + + + + homeregionhandle + + + + + homeregionhandle + UserProfile.homeregionhandle + + + + + + + + + Inventory + + + + + + + + + IsGridGod + + + + + + + + + IsLocal + + + + + + + + + lastname + + + + + lastname + UserProfile.lastname + + + + + + + + + MD5passwd + + + + + + + + + UUID + + + + + + + + + + + + + response + + + + + Customise + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + + + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + GridResp + Nwc.XmlRpc.XmlRpcResponse + + + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.UInt32.ToString + System.UInt32.ToString(System.IFormatProvider) + + + UserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.UInt32.ToString + System.UInt32.ToString(System.IFormatProvider) + + + + + + + + + DefaultStartupMsg + + + + + + + + + GridRecvKey + + + + + Recv + UserProfileManager.GridRecvKey + + + + + + + + + GridSendKey + + + + + + + + + GridURL + + + + + + + + + UserProfileManager.ParseXMLRPC(String):String + System.Exception + + + + + UserProfileManager.ParseXMLRPC(String):String + + + + + UserProfileManager.ParseXMLRPC(String):String + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + UserProfileManager.ParseXMLRPC(String):String + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + + + + + + + UserProfileManager.SetKeys(String, String, String, String):Void + recv + recvKey + + + + + url + UserProfileManager.SetKeys(String, String, String, String):Void + + + + + + + + + + + + + UserProfileManagerBase.AuthenticateUser(String, String, String):Boolean + firstname + firstname + + + + + UserProfileManagerBase.AuthenticateUser(String, String, String):Boolean + lastname + lastname + + + + + UserProfileManagerBase.AuthenticateUser(String, String, String):Boolean + passwd + passwd + + + + + + + + + MD5passwd + + + + + UserProfileManagerBase.CreateNewProfile(String, String, String):UserProfile + firstname + firstname + + + + + UserProfileManagerBase.CreateNewProfile(String, String, String):UserProfile + lastname + lastname + + + + + UserProfileManagerBase.CreateNewProfile(String, String, String):UserProfile + M + MD5passwd + + + + + + + + + ProfileLLUUID + + + + + ProfileLLUUID + + + + + UserProfileManagerBase.GetProfileByLLUUID(LLUUID):UserProfile + + + + + + + + + UserProfileManagerBase.GetProfileByName(String, String):UserProfile + firstname + firstname + + + + + UserProfileManagerBase.GetProfileByName(String, String):UserProfile + lastname + lastname + + + + + + + + + ID + agentID + Id + + + + + + + + + GodID + + + + + ID + GodID + Id + + + + + + + + + UserProfiles + + + + + + + + + + + + + + + OpenSim.Framework.Utilities.BlockingQueue`1 + Queue + + + + + + + + + Util + OpenSim.Framework.Utilities.Util + + + + + Util + + + + + Util + System.Web.Util + + + + + + + + + Xfer + Util.GetNextXferID():UInt32 + + + + + Util.GetNextXferID():UInt32 + + + + + GetNextXferID + + + + + + + + + X + + + + + Y + + + + + Util.UIntsToLong(UInt32, UInt32):UInt64 + X + + + + + Util.UIntsToLong(UInt32, UInt32):UInt64 + Y + + + + + Ints + Util.UIntsToLong(UInt32, UInt32):UInt64 + + + + + + + + + + + + + + + + + + + OpenSim.GridInterfaces.Local + + + + + OpenSim.GridInterfaces.Local + + + + + OpenSim.GridInterfaces.Local + + + + + + + + + + + + + Data + + + + + + + + + Name + + + + + + + + + Type + + + + + + + + + UUID + + + + + + + + + + + AssetUUIDQuery + + + + + + + + + 'asset' + AssetUUIDQuery.Match(AssetStorage):Boolean + + + + + + + + + + + Plugin + OpenSim.GridInterfaces.Local.LocalAssetPlugin + + + + + + + + + + + LocalAssetServer.LocalAssetServer() + System.Exception + + + + + + + + + LocalAssetServer.LoadAsset(AssetBase, Boolean, String):Void + + + + + image + LocalAssetServer.LoadAsset(AssetBase, Boolean, String):Void + + + + + + + + + 'asset' + LocalAssetServer.UploadNewAsset(AssetBase):Void + + + + + + + + + + + Plugin + OpenSim.GridInterfaces.Local.LocalGridPlugin + + + + + + + + + + + Logout + LogoutSession + LogOff + + + + + + + + + Sessions + + + + + System.Collections.Generic.List`1<OpenSim.Framework.Interfaces.Login> + LocalGridServer.Sessions + + + + + + + + + + + + + + + + + + + OpenSim.GridInterfaces.Remote + + + + + OpenSim.GridInterfaces.Remote + + + + + OpenSim.GridInterfaces.Remote + + + + + + + + + + + Plugin + OpenSim.GridInterfaces.Remote.RemoteAssetPlugin + + + + + + + + + Plugin + OpenSim.GridInterfaces.Remote.RemoteGridPlugin + + + + + + + + + + + agentcircuits + + + + + + + + + circuitcode + RemoteGridServer.AuthenticateSession(LLUUID, LLUUID, UInt32):AuthenticateResponse + circuitCode + IGridServer.AuthenticateSession(LLUUID, LLUUID, UInt32):AuthenticateResponse + + + + + + + + + RemoteGridServer.GridRecvKey + + + + + + + + + RemoteGridServer.GridSendKey + + + + + + + + + RemoteGridServer.LogoutSession(LLUUID, LLUUID, UInt32):Boolean + WebRequest.Create(Uri):WebRequest + WebRequest.Create(String):WebRequest + + + + + RemoteGridServer.LogoutSession(LLUUID, LLUUID, UInt32):Boolean + GridResponse + System.String + + + + + Logout + LogoutSession + LogOff + + + + + 'sessionID' + RemoteGridServer.LogoutSession(LLUUID, LLUUID, UInt32):Boolean + + + + + + + + + + + + + + + + + + + OpenSim.Physics.Manager + + + + + OpenSim.Physics.Manager + + + + + OpenSim.Physics.Manager + + + + + + + + + + + Plugin + OpenSim.Physics.Manager.IPhysicsPlugin + + + + + + + + + GetName + + + + + + + + + GetScene + + + + + + + + + + + + + 'heightMap' + NullPhysicsScene.SetTerrain(Single[]):Void + + + + + + + + + NullPhysicsScene.Simulate(Single):Void + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + + + + + + + + + + + Kinematic + PhysicsActor.Kinematic:Boolean + + + + + + + + + + + + + PhysicsManager.GetPhysicsScene(String):PhysicsScene + System.String.Format(System.String,System.Object) + System.String.Format(System.IFormatProvider,System.String,System.Object[]) + + + + + + + + + Plugins + PhysicsManager.LoadPlugins():Void + + + + + + + + + + + + + PhysicsVector.PhysicsVector(Single, Single, Single) + x + + + + + PhysicsVector.PhysicsVector(Single, Single, Single) + y + + + + + PhysicsVector.PhysicsVector(Single, Single, Single) + z + + + + + + + + + X + + + + + X + PhysicsVector.X + + + + + + + + + Y + + + + + Y + PhysicsVector.Y + + + + + + + + + Z + + + + + Z + PhysicsVector.Z + + + + + + + + + PhysicsVector.Zero + OpenSim.Physics.Manager.PhysicsVector + + + + + + + + + + + + + + + + + + + OpenSim.RegionServer + + + + + OpenSim.RegionServer + + + + + OpenSim.RegionServer + + + + + OpenSim.RegionServer + + + + + OpenSim.RegionServer + + + + + + + + + + + + + ID + transactionID + Id + + + + + + + + + ID + transactionID + Id + + + + + + + + + ID + transactionID + Id + + + + + + + + + ID + assetID + Id + + + + + AgentAssetUpload.HandleUploadPacket(AssetUploadRequestPacket, LLUUID):Void + System.Int32.ToString(System.String) + System.Int32.ToString(System.String,System.IFormatProvider) + + + AgentAssetUpload.HandleUploadPacket(AssetUploadRequestPacket, LLUUID):Void + System.Int32.ToString(System.String) + System.Int32.ToString(System.String,System.IFormatProvider) + + + + + 'assetID' + AgentAssetUpload.HandleUploadPacket(AssetUploadRequestPacket, LLUUID):Void + + + 'pack' + AgentAssetUpload.HandleUploadPacket(AssetUploadRequestPacket, LLUUID):Void + + + + + + + + + AgentAssetUpload.HandleXferPacket(SendXferPacketPacket):Void + xfer + xferPacket + + + + + Xfer + AgentAssetUpload.HandleXferPacket(SendXferPacketPacket):Void + + + + + + + + + + + + + AssetTransaction.AssetTransaction() + UploadComplete + System.Boolean + false + + + + + + + + + AddToInventory + + + + + + + + + Asset + + + + + + + + + InventFolder + + + + + + + + + TransactionID + + + + + + + + + UploadComplete + + + + + + + + + XferID + + + + + Xfer + AssetTransaction.XferID + + + + + + + + + + + Grid + OpenSim.Framework.Grid + + + + + + + + + AssetDll + + + + + + + + + AssetServer + + + + + + + + + GridDll + + + + + + + + + GridServer + + + + + + + + + Initialise + Grid.Initialise():Void + + + + + + + + + Grid.LoadAssetDll(String):IAssetServer + + + + + + + + + Grid.LoadGridDll(String):IGridServer + + + + + + + + + + + Sim + OpenSim.OpenSimApplication + + + + + + + + + OpenSimApplication.RemoveClientCircuit(UInt32):Void + circuitcode + circuitcode + + + + + + + + + OpenSimApplication.SendPacketTo(Byte[], Int32, SocketFlags, UInt32):Void + circuitcode + circuitcode + + + + + + + + + StartUp + method + StartUp + Startup + + + + + + + + + + + Sim + OpenSim.OpenSimMain + + + + + OpenSim.OpenSimMain + System.Timers.Timer, System.Net.Sockets.Socket + + + + + + + + + OpenSimMain.OpenSimMain() + loginserver + System.Boolean + false + + + OpenSimMain.OpenSimMain() + sandbox + System.Boolean + false + + + + + + + + + _physicsEngine + + + + + _physicsEngine + + + + + + + + + OpenSimMain.LoadConfigDll(String):SimConfig + + + + + + + + + loginserver + + + + + loginserver + OpenSimMain.loginserver + + + + + + + + + sandbox + + + + + + + + + Server + + + + + + + + + Timer.set_Interval(Double):Void + OpenSimMain.StartUp():Void + + + + + OpenSimMain.StartUp():Void + System.UInt32.ToString + System.UInt32.ToString(System.IFormatProvider) + + + OpenSimMain.StartUp():Void + System.UInt32.ToString + System.UInt32.ToString(System.IFormatProvider) + + + + + + + + + + + Sim + OpenSim.OpenSimRoot + + + + + + + + + OpenSimRoot.OpenSimRoot() + Sandbox + System.Boolean + false + + + + + + + + + Application + + + + + + + + + AssetCache + + + + + + + + + Cfg + + + + + Cfg + OpenSimRoot.Cfg + + + + + + + + + ClientThreads + + + + + + + + + GridServers + + + + + + + + + HttpServer + + + + + + + + + InventoryCache + + + + + + + + + LocalWorld + + + + + + + + + Sandbox + + + + + + + + + StartUp + method + StartUp + Startup + + + + + + + + + startuptime + + + + + startuptime + OpenSimRoot.startuptime + + + + + + + + + + + Que + OpenSim.QueItem + + + + + + + + + Incoming + + + + + + + + + Packet + + + + + + + + + + + Sim + OpenSim.SimClient + + + + + OpenSim.SimClient + System.Timers.Timer + + + + + + + + + SimClient.SimClient(EndPoint, UseCircuitCodePacket) + Sequence + System.UInt32 + 0 + + + SimClient.SimClient(EndPoint, UseCircuitCodePacket) + debug + System.Boolean + false + + + + + Timer.Timer(Double) + SimClient.SimClient(EndPoint, UseCircuitCodePacket) + + + + + SimClient.SimClient(EndPoint, UseCircuitCodePacket) + initialcirpack + initialcirpack + + + + + + + + + AgentID + + + + + + + + + CircuitCode + + + + + + + + + ClientAvatar + + + + + + + + + NewPack + + + + + + + + + SimClient.newAssetFolder + + + + + + + + + NewPack + + + + + + + + + Pack + + + + + SimClient.ProcessInPacket(Packet):Void + wear + libsecondlife.Packets.AgentIsNowWearingPacket + + + + + op_Equality + "" + SimClient.ProcessInPacket(Packet):Void + + + + + + + + + SimClient.ProcessOutPacket(Packet):Void + System.Exception + + + + + Pack + + + + + + + + + SecureSessionID + + + + + + + + + SessionID + + + + + + + + + userEP + + + + + + + + + + + OpenSim.SimConsole + OpenSim.Framework.Console.ConsoleBase + + + + + Sim + OpenSim.SimConsole + + + + + + + + + SimConsole.SimConsole(ConsoleType, String, Int32) + constype + constype + + + + + SimConsole.SimConsole(ConsoleType, String, Int32) + sparam + sparam + + + + + SimConsole.SimConsole(ConsoleType, String, Int32) + iparam + iparam + + + + + iparam + SimConsole.SimConsole(ConsoleType, String, Int32) + + + + + sparam + SimConsole.SimConsole(ConsoleType, String, Int32) + + + + + + + + + op_Equality + "" + SimConsole.CmdPrompt(String, String):String + + + + + + + + + SimConsole.ConsType + + + + + + + + + SimConsole.MainConsolePrompt():Void + System.UInt64.ToString + System.UInt64.ToString(System.IFormatProvider) + + + + + + + + + 'cmdparams' + SimConsole.RunCmd(String, String[]):Object + + + + + + + + + SimConsole.ShowCommands(String):Void + System.String.Format(System.String,System.Object[]) + System.String.Format(System.IFormatProvider,System.String,System.Object[]) + + + SimConsole.ShowCommands(String):Void + System.String.Format(System.String,System.Object[]) + System.String.Format(System.IFormatProvider,System.String,System.Object[]) + + + + + + + + + + + + + Version + + + + + + + + + + + + + + + + + ID + imageID + Id + + + + + + + + + AssetRequests + + + + + System.Collections.Generic.List`1<OpenSim.Assets.AssetRequest> + AssetCache.AssetRequests + + + + + + + + + Assets + + + + + + + + + sourceAsset + AssetCache.CloneAsset(LLUUID, AssetInfo):AssetInfo + OpenSim.Assets.AssetInfo + OpenSim.Framework.Assets.AssetBase + + + + + AssetCache.CloneAsset(LLUUID, AssetInfo):AssetInfo + + + + + newOwner + AssetCache.CloneAsset(LLUUID, AssetInfo):AssetInfo + + + + + 'sourceAsset' + AssetCache.CloneAsset(LLUUID, AssetInfo):AssetInfo + + + + + + + + + source + AssetCache.CloneImage(LLUUID, TextureImage):TextureImage + OpenSim.Assets.TextureImage + OpenSim.Framework.Assets.AssetBase + + + + + AssetCache.CloneImage(LLUUID, TextureImage):TextureImage + + + + + newOwner + AssetCache.CloneImage(LLUUID, TextureImage):TextureImage + + + + + 'source' + AssetCache.CloneImage(LLUUID, TextureImage):TextureImage + + + + + + + + + ID + agentID + Id + + + + + + + + + ID + assetID + Id + + + + + + + + + RequestedAssets + + + + + + + + + RequestedTextures + + + + + + + + + AssetCache.RunAssetManager():Void + System.Exception + + + + + + + + + TextureRequests + + + + + System.Collections.Generic.List`1<OpenSim.Assets.AssetRequest> + AssetCache.TextureRequests + + + + + + + + + Textures + + + + + + + + + + + OpenSim.Assets.AssetInfo + OpenSim.Framework.Assets.AssetBase + + + + + + + + + AssetInfo.AssetInfo(AssetBase) + a + aBase + + + + + 'aBase' + AssetInfo.AssetInfo(AssetBase) + + + + + + + + + + + + + AssetRequest.AssetRequest() + DataPointer + System.Int64 + 0 + + + AssetRequest.AssetRequest() + NumPackets + System.Int32 + 0 + + + AssetRequest.AssetRequest() + PacketCounter + System.Int32 + 0 + + + + + + + + + AssetInf + + + + + + + + + DataPointer + + + + + + + + + ImageInfo + + + + + + + + + IsTextureRequest + + + + + + + + + NumPackets + + + + + Num + AssetRequest.NumPackets + + + + + + + + + PacketCounter + + + + + + + + + RequestAssetID + + + + + + + + + RequestUser + + + + + + + + + TransferRequestID + + + + + + + + + + + + + ID + folderID + Id + + + + + + + + + ID + clientID + Id + + + + + + + + + ID + folderID + Id + + + + + + + + + ID + folderID + Id + + + + + + + + + FetchItems + + + + + + + + + FetchDescend + + + + + + + + + ID + agentID + Id + + + + + + + + + ID + itemID + Id + + + + + + + + + + + OpenSim.Assets.TextureImage + OpenSim.Framework.Assets.AssetBase + + + + + + + + + TextureImage.TextureImage(AssetBase) + a + aBase + + + + + 'aBase' + TextureImage.TextureImage(AssetBase) + + + + + + + + + + + + + + + Sim + OpenSim.CAPS.SimCAPSHTTPServer + + + + + SimCAPSHTTPServer + + + + + OpenSim.CAPS.SimCAPSHTTPServer + System.Net.HttpListener + + + + + + + + + SimCAPSHTTPServer.HandleRequest(Object):Void + stateinfo + stateinfo + + + + + + + + + HTTPD + + + + + + + + + Listener + + + + + + + + + SimCAPSHTTPServer.LoadAdminPage():Void + System.Exception + + + + + + + + + SimCAPSHTTPServer.ParseLLSDXML(String):String + + + + + requestBody + SimCAPSHTTPServer.ParseLLSDXML(String):String + + + + + + + + + SimCAPSHTTPServer.ParseREST(String, String, String):String + System.Exception + + + + + SimCAPSHTTPServer.ParseREST(String, String, String):String + System.String.Format(System.String,System.Object[]) + System.String.Format(System.IFormatProvider,System.String,System.Object[]) + + + + + + + + + SimCAPSHTTPServer.ParseXMLRPC(String):String + System.Exception + + + + + SimCAPSHTTPServer.ParseXMLRPC(String):String + + + + + SimCAPSHTTPServer.ParseXMLRPC(String):String + System.Convert.ToUInt32(System.Object) + System.Convert.ToUInt32(System.Object,System.IFormatProvider) + + + + + + + + + SimCAPSHTTPServer.StartHTTP():Void + System.Exception + + + + + SimCAPSHTTPServer.StartHTTP():Void + + + + + + + + + + + + + + + + + mesh + + + + + System.Collections.Generic.List`1<OpenSim.types.Triangle> + Mesh.mesh + + + + + + + + + Mesh.op_Addition(Mesh, Mesh):Mesh + a + + + + + Mesh.op_Addition(Mesh, Mesh):Mesh + b + + + + + Add + Mesh.op_Addition(Mesh, Mesh):Mesh + + + + + Mesh + Mesh.op_Addition(Mesh, Mesh):Mesh + + + + + + + + + + + + + A + + + + + B + + + + + C + + + + + Triangle.Triangle(Vector3, Vector3, Vector3) + A + + + + + Triangle.Triangle(Vector3, Vector3, Vector3) + B + + + + + Triangle.Triangle(Vector3, Vector3, Vector3) + C + + + + + + + + + + + + + + + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + LocalUserProfileManager.CustomiseResponse(Hashtable&, UserProfile):Void + System.Single.ToString + System.Single.ToString(System.IFormatProvider) + + + + + + + + + + + OpenSim.UserServer.LoginServer + OpenSim.Framework.Grid.LoginService + + + + + OpenSim.UserServer.LoginServer + System.Net.Sockets.Socket + + + + + Login + LoginServer + LogOn + + + + + + + + + LoginServer.LoginServer(IGridServer) + _needPasswd + System.Boolean + false + + + LoginServer.LoginServer(IGridServer) + userAccounts + System.Boolean + false + + + + + + + + + LoginServer.Authenticate(String, String, String):Boolean + passwd + passwd + + + + + + + + + clientAddress + + + + + + + + + Customise + LoginServer.CustomiseLoginResponse(Hashtable, String, String):Void + + + + + Login + CustomiseLoginResponse + LogOn + + + + + + + + + LoginServer.EncodePassword(String):String + System.String.ToLower + System.String.ToLower(System.Globalization.CultureInfo) + + + + + + + + + LoginServer.GetAgentId(String, String):LLUUID + System.Int32.ToString(System.String) + System.Int32.ToString(System.String,System.IFormatProvider) + + + + + + + + + LoginServer.InitializeLogin():Void + 4 + UserProfileManager.SetKeys(String, String, String, String):Void + Welcome to OpenSim + + + + + Sim + OpenSim + + + + + + + + + LoginServer.LoginRequest(StreamReader, StreamWriter):Void + System.Exception + + + + + LoginServer.LoginRequest(StreamReader, StreamWriter):Void + System.Convert.ToInt32(System.String) + System.Convert.ToInt32(System.String,System.IFormatProvider) + + + + + op_Inequality + "" + LoginServer.LoginRequest(StreamReader, StreamWriter):Void + + + + + + + + + writer + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + System.IO.StreamWriter + System.IO.TextWriter + + + + + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + System.Int32.ToString + System.Int32.ToString(System.IFormatProvider) + + + + + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + System.Int32.ToString(System.String) + System.Int32.ToString(System.String,System.IFormatProvider) + + + + + 'request' + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + + + 'writer' + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + + + 'writer' + LoginServer.ProcessXmlRequest(XmlRpcRequest, StreamWriter):Boolean + + + + + + + + + remoteAddress + + + + + + + + + LoginServer.RunLogin():Void + System.Exception + + + LoginServer.RunLogin():Void + System.Exception + + + + + LoginServer.RunLogin():Void + clientEndPoint + System.Net.IPEndPoint + + + + + + + + + + + + + + + + + Avatar.Avatar() + PhysicsEngineFlying + System.Boolean + false + + + + + + + + + Avatar.Avatar(SimClient) + _updateCount + System.Int16 + 0 + + + Avatar.Avatar(SimClient) + avatarAppearanceTexture + libsecondlife.LLObject+TextureEntry + null + + + Avatar.Avatar(SimClient) + movementflag + System.Byte + 0 + + + Avatar.Avatar(SimClient) + updateflag + System.Boolean + false + + + + + TheClient + + + + + + + + + anim_seq + + + + + anim + Avatar.anim_seq + + + + + anim_seq + + + + + + + + + Animations + + + + + + + + + RegionInfo + + + + + RegionInfo + Avatar.CompleteMovement(World):Void + + + + + + + + + ControllingClient + + + + + + + + + current_anim + + + + + anim + Avatar.current_anim + + + + + current_anim + + + + + + + + + firstname + + + + + firstname + Avatar.firstname + + + + + + + + + lastname + + + + + lastname + Avatar.lastname + + + + + + + + + Anims + Avatar.LoadAnims():Void + + + + + + + + + PhysActor + + + + + + + + + PhysicsEngineFlying + + + + + + + + + Anim + Avatar.SendAnimPack():Void + + + + + + + + + 'userInfo' + Avatar.SendAppearanceToOtherAgent(SimClient):Void + + + + + + + + + RegionInfo + + + + + RegionInfo + Avatar.SendRegionHandshake(World):Void + + + + + + + + + + + + + AnimsLLUUID + + + + + Anims + AvatarAnimations.AnimsLLUUID + + + + + + + + + AnimsNames + + + + + Anims + AvatarAnimations.AnimsNames + + + + + + + + + Anims + AvatarAnimations.LoadAnims():Void + + + + + + + + + + + + + Entity.Entity() + localid + System.UInt32 + 0 + + + + + + + + + addForces + + + + + + + + + BackUp + method + BackUp + Backup + + + + + + + + + children + + + + + System.Collections.Generic.List`1<OpenSim.world.Entity> + Entity.children + + + + + + + + + getMesh + + + + + + + + + getName + + + + + + + + + localid + + + + + localid + Entity.localid + + + + + + + + + name + + + + + + + + + position + + + + + + + + + rotation + + + + + + + + + update + + + + + + + + + uuid + + + + + uuid + Entity.uuid + + + + + + + + + velocity + + + + + + + + + + + + + X + + + + + X + NewForce.X + + + + + + + + + Y + + + + + Y + NewForce.Y + + + + + + + + + Z + + + + + Z + NewForce.Z + + + + + + + + + + + 'UpdateFlag' + updateFlag + + + + + + + + + Primitive.Primitive() + dirtyFlag + System.Boolean + false + + + Primitive.Primitive() + mesh_cutbegin + System.Single + 0.0 + + + Primitive.Primitive() + newPrimFlag + System.Boolean + false + + + Primitive.Primitive() + physicsEnabled + System.Boolean + false + + + Primitive.Primitive() + physicstest + System.Boolean + false + + + Primitive.Primitive() + updateFlag + System.Boolean + false + + + + + + + + + localID-702000 + Primitive.CreateFromPacket(ObjectAddPacket, LLUUID, UInt32):Void + + + + + ID + agentID + Id + + + + + ID + localID + Id + + + + + Primitive.CreateFromPacket(ObjectAddPacket, LLUUID, UInt32):Void + System.UInt32.ToString(System.String) + System.UInt32.ToString(System.String,System.IFormatProvider) + + + + + 'addPacket' + Primitive.CreateFromPacket(ObjectAddPacket, LLUUID, UInt32):Void + + + + + + + + + 'store' + Primitive.CreateFromStorage(PrimData):Void + + + + + + + + + dirtyFlag + + + + + + + + + mesh_cutbegin + + + + + cutbegin + Primitive.mesh_cutbegin + + + + + mesh_cutbegin + + + + + + + + + mesh_cutend + + + + + cutend + Primitive.mesh_cutend + + + + + mesh_cutend + + + + + + + + + newPrimFlag + + + + + + + + + PhysActor + + + + + + + + + primData + + + + + + + + + RemoteClient + + + + + 'RemoteClient' + Primitive.UpdateClient(SimClient):Void + + + + + + + + + updateFlag + + + + + + + + + 'pack' + Primitive.UpdateObjectFlags(ObjectFlagUpdatePacket):Void + + + + + + + + + 'addPacket' + Primitive.UpdateShape(ObjectDataBlock):Void + + + + + + + + + + + + + ScriptEngine.ScriptEngine(World) + env + env + + + + + env + ScriptEngine.ScriptEngine(World) + + + + + + + + + ScriptEngine.LoadScript():Void + + + + + + + + + + + + + HeightMap + + + + + + + + + + + World + OpenSim.world + + + + + + + + + World.World() + _localNumber + System.UInt32 + 0 + + + + + + + + + _localNumber + + + + + _localNumber + + + + + + + + + AgentClient + + + + + + + + + AgentClient + + + + + + + + + DeRezPacket + + + + + AgentClient + + + + + World.DeRezObject(DeRezObjectPacket, SimClient):Void + Rez + DeRezPacket + + + + + Rez + World.DeRezObject(DeRezObjectPacket, SimClient):Void + + + + + AgentClient + World.DeRezObject(DeRezObjectPacket, SimClient):Void + + + + + De + World.DeRezObject(DeRezObjectPacket, SimClient):Void + + + + + + + + + Entities + + + + + + + + + RemoteClient + + + + + Prims + World.GetInitialPrims(SimClient):Void + + + + + + + + + LandMap + + + + + + + + + Prims + World.LoadPrimsFromStorage():Void + + + + + + + + + World.LoadStorageDLL(String):Boolean + + + + + + + + + localStorage + + + + + + + + + World.Rand + + + + + + + + + Scripts + + + + + + + + + RemoteClient + + + + + 'RemoteClient' + World.SendLayerData(SimClient):Void + + + 'RemoteClient' + World.SendLayerData(SimClient):Void + + + 'RemoteClient' + World.SendLayerData(SimClient):Void + + + + + + + + + + + + + + + + + IScriptHost.Register(IScript):Boolean + iscript + iscript + + + + + + + + + + + + + + + + + + + OpenSim.Storage.LocalStorageDb4o + + + + + OpenSim.Storage.LocalStorageDb4o + + + + + OpenSim.Storage.LocalStorageDb4o + + + + + + + + + + + + + Db4LocalStorage.Db4LocalStorage() + System.Exception + + + + + + + + + 'receiver' + Db4LocalStorage.LoadPrimitives(ILocalStorageReceiver):Void + + + + + + + + + 'prim' + Db4LocalStorage.StorePrim(PrimData):Void + + + + + + + + + + + UUIDQuery + + + + + + + + + 'prim' + UUIDQuery.Match(PrimData):Boolean + + + + + + + + + + + + + + + + + + + OpenSim.Physics.BasicPhysicsPlugin + + + + + + + + + + OpenSim.Physics.BasicPhysicsPlugin + + + + + + + + + + OpenSim.Physics.BasicPhysicsPlugin + + + + + + + + + + + + + + + + + + BasicActor.flying + + + + + + + + + BasicActor.SetAcceleration(PhysicsVector):Void + accel + accel + + + + + + + + + + + Plugin + OpenSim.Physics.BasicPhysicsPlugin.BasicPhysicsPlugin + + + + + BasicPhysicsPlugin + OpenSim.Physics.BasicPhysicsPlugin + + + + + + + + + + + + + + + + + OpenSim.Physics.OdePlugin + + + + + OpenSim.Physics.OdePlugin + + + + + OpenSim.Physics.OdePlugin + + + + + + + + + + + OdeCharacter + + + + + + + + + parent_scene + OdeCharacter.OdeCharacter(OdeScene, PhysicsVector) + + + + + 'pos' + OdeCharacter.OdeCharacter(OdeScene, PhysicsVector) + + + + + + + + + OdeCharacter.capsule_geom + + + + + + + + + OdeCharacter.gravityAccel + + + + + + + + + OdeCharacter.SetAcceleration(PhysicsVector):Void + accel + accel + + + + + + + + + + + Plugin + OpenSim.Physics.OdePlugin.OdePlugin + + + + + OdePlugin + OpenSim.Physics.OdePlugin + + + + + + + + + + + OdePrim._position + + + + + + + + + OdePrim.SetAcceleration(PhysicsVector):Void + accel + accel + + + + + + + + + + + + + 'position' + OdeScene.AddPrim(PhysicsVector, PhysicsVector):PhysicsActor + + + 'size' + OdeScene.AddPrim(PhysicsVector, PhysicsVector):PhysicsActor + + + + + + + + + OdeScene.Land + + + + + + + + + OdeScene.LandGeom + + + + + + + + + 'heightMap' + OdeScene.SetTerrain(Single[]):Void + + + 'heightMap' + OdeScene.SetTerrain(Single[]):Void + + + + + + + + + space + + + + + space + + + + + + + + + world + + + + + world + + + + + + + + + + + + + + + + + + + OpenSim.Physics.PhysXPlugin + + + + + OpenSim.Physics.PhysXPlugin + + + + + OpenSim.Physics.PhysXPlugin + + + + + + + + + + + + + PhysXCharacter.SetAcceleration(PhysicsVector):Void + accel + accel + + + + + + + + + + + Plugin + OpenSim.Physics.PhysXPlugin.PhysXPlugin + + + + + PhysXPlugin + OpenSim.Physics.PhysXPlugin + + + + + + + + + + + PhysXPrim._position + + + + + + + + + PhysXPrim.SetAcceleration(PhysicsVector):Void + accel + accel + + + + + + + + + + + + + + + + Save it for a rainy day. + Save it for a rainy day. + Save it for a rainy day. + + + + + No valid permission requests were found for assembly '{0}'. You should always specify the minimum security permissions using SecurityAction.RequestMinimum. + + + Sign '{0}' with a strong name key. + + + Consider merging the types defined in '{0}' with another namespace. + + + It appears that field '{0}' is never used or is only ever assigned to. Use this field or remove it. + + + Change '{0}' to be read-only by removing the property setter. + + + The compound word '{0}' in {1} '{2}' exists as a discrete term. If your usage is intended to be single word, case it as '{3}'. + + + '{0}' is marked ComVisible(true) but has the following ComVisible(false) types in its object hierarchy: {1} + + + Consider changing the type of parameter '{0}' in {1} from {2} to its base type {3}. This method appears to only require base class members in its implementation. Suppress this violation if there is a compelling reason to require the more derived type in the method signature. + + + '{0}' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: {1} + + + Modify '{0}' to catch a more specific exception than '{1}' or rethrow the exception. + + + Remove the readonly declaration from '{0}' or change the field to one that is an immutable reference type. If the reference type '{1}' is, in fact, immutable, exclude this message. + + + Make '{0}' private or internal (Friend in VB, public private in C++) and provide a public or protected property to access it. + + + Change '{0}' in {1} to use Collection<T>, ReadOnlyCollection<T> or KeyedCollection<K,V> + + + {0} initializes field {1} of type {2} to {3}. Remove this initialization as it will be done automatically by the runtime. + + + {0} passes a literal as parameter {1} of a call to {2}. Retrieve the following string argument from a resource table instead: '{3}' + + + Consider a design that does not require that '{0}' be a reference parameter. + + + {0} creates an exception of type '{1}', an exception type that is not sufficiently specific and should never be raised by user code. If this exception instance might be thrown, use a different exception type. + + + Modify the call to {0} in method {1} to set the timer interval to a value that's greater than or equal to one second. + + + Correct the casing of member name '{0}'. + Correct the casing of namespace name '{0}'. + Correct the casing of parameter name '{0}'. + Correct the casing of type name '{0}'. + + + Correct the spelling of the unrecognized token '{0}' in member name '{1}'. + Consider providing a more meaningful name than the one-letter token '{0}' in member name '{1}'. + Correct the spelling of the unrecognized token '{0}' in namespace '{1}'. + In method {0}, correct the spelling of the unrecognized token '{1}' in parameter name '{2}' or strip it entirely if it represents any sort of hungarian notation. + In method {0}, consider providing a more meaningful name than the one-letter parameter name '{1}'. + Correct the spelling of the unrecognized token '{0}' in type name '{1}'. + + + Change member names {0} and '{1}' so that they differ by more than case. + + + Remove all underscores from member '{0}'. + Remove all underscores from parameter '{0}'. + Remove all underscores from type '{0}'. + + + Rename '{0}' so that it does not end in '{1}'. + + + Correct the spelling of the unrecognized token '{0}' in the literal '{1}'. + + + Correct the capitalization of member name '{0}'. + Correct the capitalization of namespace name '{0}'. + Correct the capitalization of parameter name '{0}'. + Correct the capitalization of type name '{0}'. + + + Add an AssemblyVersion attribute to '{0}'. + + + '{0}' should be marked with CLSCompliantAttribute and its value should be true. + + + Mark '{0}' as ComVisible(false) at the assembly level, then mark all types within the assembly that should be exposed to Com clients as ComVisible(true). + + + The 'this' parameter (or 'Me' in VB) of {0} is never used. Mark the member as static (or Shared in VB) or use 'this'/'Me' in the method body or at least one property accessor, if appropriate. + + + Consider making '{0}' non-public or a constant. + + + Correct the potential overflow in the operation '{0}' in '{1}'. + + + Provide a method named '{0}' as a friendly alternate for operator {1}. + + + Consider adding an overload of the equality operator for '{0}' that takes the same parameters as {1}. + + + '{0}' should override Equals. + '{0}' should override the equality (==) and inequality (!=) operators. + + + Change parameter name '{0}' of method {1} to '{2}' in order to match the identifier as it has been declared in {3}. + + + Modify {0} to call {1} instead of {2}. + + + Make '{0}' private. + + + Add a property getter to '{0}'. + + + {0} declares a local, '{1}', of type {2}, which is never used or is only assigned to. Use this local or remove it. + + + Parameter '{0}' of {1} is never used. Remove the parameter or use it in the method body. + + + Correct the capitalization of '{0}' in member name '{1}'. + 'Id' is an abbreviation and therefore is not subject to acronym casing guidelines. Correct the capitalization of 'ID' in member name '{0}' by changing it to 'Id'. + 'Id' is an abbreviation and therefore is not subject to acronym casing guidelines. Correct the capitalization of '{0}' in parameter name '{1}' by changing it to '{2}'. + Correct the capitalization of '{0}' in type name '{1}'. + + + {0} makes a call to {1} that does not explicitly provide a CultureInfo. This should be replaced with a call to {2}. + + + {0} makes a call to {1} that does not explicitly provide an IFormatProvider. This should be replaced with a call to {2}. + + + Remove the public constructors from '{0}'. + + + Replace the call to String.{0}({1}) in '{2}' with a call to String.IsNullOrEmpty. + + + The type name '{0}' conflicts in whole or in part with the namespace name '{1}'. Change either name to eliminate the conflict. + + + Implement IDisposable on '{0}' as it instantiates members of the following IDisposable types: {1} + + + Implement IDisposable on '{0}'. + + + Change the type of parameter '{0}' of method {1} from string to System.Uri, or provide an overload of {1}, that allows '{0}' to be passed as a System.Uri object. + + + Replace the term '{0}' in member name '{1}' with the preferred alternate '{2}'. + Replace the term '{0}' in type name '{1}' with the preferred alternate '{2}'. + + + Change '{0}' to a property if appropriate. + + + Validate parameter {0} passed to externally visible method {1}. + + + + diff --git a/OpenSim/Addons/Groups/ForeignImporter.cs b/OpenSim/Addons/Groups/ForeignImporter.cs new file mode 100644 index 0000000000..055f76c60b --- /dev/null +++ b/OpenSim/Addons/Groups/ForeignImporter.cs @@ -0,0 +1,78 @@ +/* + * 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; + } + + } +} diff --git a/OpenSim/Addons/Groups/GroupsExtendedData.cs b/OpenSim/Addons/Groups/GroupsExtendedData.cs new file mode 100644 index 0000000000..c783b9eeda --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsExtendedData.cs @@ -0,0 +1,533 @@ +/* + * 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 GroupRecord(ExtendedGroupRecord grec) + { + Dictionary dict = new Dictionary(); + 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 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 GroupMembershipData(ExtendedGroupMembershipData membership) + { + Dictionary dict = new Dictionary(); + 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 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 GroupMembersData(ExtendedGroupMembersData member) + { + Dictionary dict = new Dictionary(); + + 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 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 GroupRolesData(GroupRolesData role) + { + Dictionary dict = new Dictionary(); + + 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 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 GroupRoleMembersData(ExtendedGroupRoleMembersData rmember) + { + Dictionary dict = new Dictionary(); + + dict["RoleID"] = rmember.RoleID.ToString(); + dict["MemberID"] = rmember.MemberID; + return dict; + } + + public static ExtendedGroupRoleMembersData GroupRoleMembersData(Dictionary 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 GroupInviteInfo(GroupInviteInfo invite) + { + Dictionary dict = new Dictionary(); + + 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 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 GroupNoticeData(ExtendedGroupNoticeData notice) + { + Dictionary dict = new Dictionary(); + + 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 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 GroupNoticeInfo(GroupNoticeInfo notice) + { + Dictionary dict = GroupNoticeData(notice.noticeData); + + dict["GroupID"] = notice.GroupID.ToString(); + dict["Message"] = Sanitize(notice.Message); + + return dict; + } + + public static GroupNoticeInfo GroupNoticeInfo(Dictionary 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 DirGroupsReplyData(DirGroupsReplyData g) + { + Dictionary dict = new Dictionary(); + + dict["GroupID"] = g.groupID; + dict["Name"] = g.groupName; + dict["NMembers"] = g.members; + dict["SearchOrder"] = g.searchOrder; + + return dict; + } + + public static DirGroupsReplyData DirGroupsReplyData(Dictionary 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; + } + } + +} diff --git a/OpenSim/Addons/Groups/GroupsMessagingModule.cs b/OpenSim/Addons/Groups/GroupsMessagingModule.cs new file mode 100644 index 0000000000..e95db4120d --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsMessagingModule.cs @@ -0,0 +1,848 @@ +/* + * 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 m_sceneList = new List(); + 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; + + /// + /// If enabled, module only tries to send group IMs to online users by querying cached presence information. + /// + private bool m_messageOnlineAgentsOnly; + + /// + /// Cache for online users. + /// + /// + /// 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. + /// + private ExpiringCache m_usersOnlineCache; + + private int m_usersOnlineCacheExpirySeconds = 20; + + private Dictionary> m_groupsAgentsDroppedFromChatSession = new Dictionary>(); + private Dictionary> m_groupsAgentsInvitedToChatSession = new Dictionary>(); + + #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(); + } + 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(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 ", + "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(); + + // 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(); + + // 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(); + + // 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(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 "); + return; + } + + bool verbose = false; + if (!bool.TryParse(args[4], out verbose)) + { + MainConsole.Instance.Output("Usage: debug groups messaging verbose "); + return; + } + + m_debugEnabled = verbose; + + MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled); + } + + /// + /// Not really needed, but does confirm that the group exists. + /// + 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 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 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(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 onlineAgentsUuidSet = new HashSet(); + Array.ForEach(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 regions = new List(); + List clientsAlreadySent = new List(); + + // 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 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"); + + // 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(); + eq.ChatterboxInvitation( + GroupID + , groupInfo.GroupName + , new UUID(msg.fromAgentID) + , 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) + ); + + eq.ChatterBoxSessionAgentListUpdates( + new UUID(GroupID) + , AgentID + , new UUID(msg.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + } + + #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(); + queue.ChatterBoxSessionAgentListUpdates( + GroupID + , AgentID + , new UUID(im.toAgentID) + , false //canVoiceChat + , false //isModerator + , false //text mute + ); + } + } + + // 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(); + + if (queue != null) + { + 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 + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + 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 agentList in m_groupsAgentsDroppedFromChatSession.Values) + agentList.Remove(agentID); + + foreach (List 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()); + m_groupsAgentsInvitedToChatSession.Add(groupID, new List()); + } + + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/GroupsModule.cs b/OpenSim/Addons/Groups/GroupsModule.cs new file mode 100644 index 0000000000..d121d1a8ce --- /dev/null +++ b/OpenSim/Addons/Groups/GroupsModule.cs @@ -0,0 +1,1467 @@ +/* + * 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 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 DirFindFlags = OpenMetaverse.DirectoryManager.DirFindFlags; + +namespace OpenSim.Groups +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GroupsModule")] + public class GroupsModule : ISharedRegionModule, IGroupsModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private List m_sceneList = new List(); + + private IMessageTransferModule m_msgTransferModule = null; + + private IGroupsServicesConnector m_groupData = null; + private IUserManagement m_UserManagement; + + // Configuration settings + private bool m_groupsEnabled = false; + private bool m_groupNoticesEnabled = true; + private bool m_debugEnabled = false; + private int m_levelGroupCreate = 0; + + #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; + } + else + { + m_groupsEnabled = groupsConfig.GetBoolean("Enabled", false); + if (!m_groupsEnabled) + { + return; + } + + if (groupsConfig.GetString("Module", "Default") != Name) + { + m_groupsEnabled = false; + + return; + } + + m_log.InfoFormat("[Groups]: Initializing {0}", this.Name); + + m_groupNoticesEnabled = groupsConfig.GetBoolean("NoticesEnabled", true); + m_debugEnabled = groupsConfig.GetBoolean("DebugEnabled", false); + m_levelGroupCreate = groupsConfig.GetInt("LevelGroupCreate", 0); + } + } + + public void AddRegion(Scene scene) + { + if (m_groupsEnabled) + { + scene.RegisterModuleInterface(this); + scene.AddCommand( + "Debug", + this, + "debug groups verbose", + "debug groups verbose ", + "This setting turns on very verbose groups debugging", + HandleDebugGroupsVerbose); + } + } + + private void HandleDebugGroupsVerbose(object modules, string[] args) + { + if (args.Length < 4) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + bool verbose = false; + if (!bool.TryParse(args[3], out verbose)) + { + MainConsole.Instance.Output("Usage: debug groups verbose "); + return; + } + + m_debugEnabled = verbose; + + MainConsole.Instance.OutputFormat("{0} verbose logging set to {1}", Name, m_debugEnabled); + } + + public void RegionLoaded(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnMakeRootAgent += OnMakeRoot; + scene.EventManager.OnMakeChildAgent += OnMakeChild; + scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; + // The InstantMessageModule itself doesn't do this, + // so lets see if things explode if we don't do it + // scene.EventManager.OnClientClosed += OnClientClosed; + + if (m_groupData == null) + { + m_groupData = scene.RequestModuleInterface(); + + // No Groups Service Connector, then nothing works... + if (m_groupData == null) + { + m_groupsEnabled = false; + m_log.Error("[Groups]: Could not get IGroupsServicesConnector"); + RemoveRegion(scene); + return; + } + } + + if (m_msgTransferModule == null) + { + m_msgTransferModule = scene.RequestModuleInterface(); + + // No message transfer module, no notices, group invites, rejects, ejects, etc + if (m_msgTransferModule == null) + { + m_log.Warn("[Groups]: Could not get MessageTransferModule"); + } + } + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + if (m_UserManagement == null) + m_log.Warn("[Groups]: Could not get UserManagementModule"); + } + + lock (m_sceneList) + { + m_sceneList.Add(scene); + } + + + } + + public void RemoveRegion(Scene scene) + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnMakeRootAgent -= OnMakeRoot; + scene.EventManager.OnMakeChildAgent -= OnMakeChild; + scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; + + lock (m_sceneList) + { + m_sceneList.Remove(scene); + } + } + + public void Close() + { + if (!m_groupsEnabled) + return; + + if (m_debugEnabled) m_log.Debug("[Groups]: Shutting down Groups module."); + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public string Name + { + get { return "Groups Module V2"; } + } + + public void PostInitialise() + { + // NoOp + } + + #endregion + + #region EventHandlers + private void OnNewClient(IClientAPI client) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; + client.OnRequestAvatarProperties += OnRequestAvatarProperties; + } + + private void OnMakeRoot(ScenePresence sp) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + sp.ControllingClient.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; + // Used for Notices and Group Invites/Accept/Reject + sp.ControllingClient.OnInstantMessage += OnInstantMessage; + + // Send client their groups information. + SendAgentGroupDataUpdate(sp.ControllingClient, sp.UUID); + } + + private void OnMakeChild(ScenePresence sp) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + sp.ControllingClient.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; + // Used for Notices and Group Invites/Accept/Reject + sp.ControllingClient.OnInstantMessage -= OnInstantMessage; + } + + private void OnRequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + //GroupMembershipData[] avatarGroups = m_groupData.GetAgentGroupMemberships(GetRequestingAgentID(remoteClient), avatarID).ToArray(); + GroupMembershipData[] avatarGroups = GetProfileListedGroupMemberships(remoteClient, avatarID); + remoteClient.SendAvatarGroupsReply(avatarID, avatarGroups); + } + + /* + * This becomes very problematic in a shared module. In a shared module you may have more then one + * reference to IClientAPI's, one for 0 or 1 root connections, and 0 or more child connections. + * The OnClientClosed event does not provide anything to indicate which one of those should be closed + * nor does it provide what scene it was from so that the specific reference can be looked up. + * The InstantMessageModule.cs does not currently worry about unregistering the handles, + * and it should be an issue, since it's the client that references us not the other way around + * , so as long as we don't keep a reference to the client laying around, the client can still be GC'ed + private void OnClientClosed(UUID AgentId) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + lock (m_ActiveClients) + { + if (m_ActiveClients.ContainsKey(AgentId)) + { + IClientAPI client = m_ActiveClients[AgentId]; + client.OnUUIDGroupNameRequest -= HandleUUIDGroupNameRequest; + client.OnAgentDataUpdateRequest -= OnAgentDataUpdateRequest; + client.OnDirFindQuery -= OnDirFindQuery; + client.OnInstantMessage -= OnInstantMessage; + + m_ActiveClients.Remove(AgentId); + } + else + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Client closed that wasn't registered here."); + } + + + } + } + */ + + private void OnAgentDataUpdateRequest(IClientAPI remoteClient, UUID dataForAgentID, UUID sessionID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + UUID activeGroupID = UUID.Zero; + string activeGroupTitle = string.Empty; + string activeGroupName = string.Empty; + ulong activeGroupPowers = (ulong)GroupPowers.None; + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(GetRequestingAgentIDStr(remoteClient), dataForAgentID.ToString()); + if (membership != null) + { + activeGroupID = membership.GroupID; + activeGroupTitle = membership.GroupTitle; + activeGroupPowers = membership.GroupPowers; + } + + SendAgentDataUpdate(remoteClient, dataForAgentID, activeGroupID, activeGroupName, activeGroupPowers, activeGroupTitle); + + SendScenePresenceUpdate(dataForAgentID, activeGroupTitle); + } + + private void HandleUUIDGroupNameRequest(UUID GroupID, IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string GroupName; + + GroupRecord group = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null); + if (group != null) + { + GroupName = group.GroupName; + } + else + { + GroupName = "Unknown"; + } + + remoteClient.SendGroupNameReply(GroupID, GroupName); + } + + private void OnInstantMessage(IClientAPI remoteClient, GridInstantMessage im) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + //m_log.DebugFormat("[Groups]: IM From {0} to {1} msg {2} type {3}", im.fromAgentID, im.toAgentID, im.message, (InstantMessageDialog)im.dialog); + // Group invitations + if ((im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) || (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline)) + { + UUID inviteID = new UUID(im.imSessionID); + GroupInviteInfo inviteInfo = m_groupData.GetAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + if (inviteInfo == null) + { + if (m_debugEnabled) m_log.WarnFormat("[Groups]: Received an Invite IM for an invite that does not exist {0}.", inviteID); + return; + } + + //m_log.DebugFormat("[XXX]: Invite is for Agent {0} to Group {1}.", inviteInfo.AgentID, inviteInfo.GroupID); + + UUID fromAgentID = new UUID(im.fromAgentID); + UUID invitee = UUID.Zero; + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(inviteInfo.AgentID, out invitee, out tmp, out tmp, out tmp, out tmp); + if ((inviteInfo != null) && (fromAgentID == invitee)) + { + // Accept + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationAccept) + { + //m_log.DebugFormat("[XXX]: Received an accept invite notice."); + + // and the sessionid is the role + string reason = string.Empty; + if (!m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), invitee.ToString(), inviteInfo.GroupID, inviteInfo.RoleID, string.Empty, out reason)) + remoteClient.SendAgentAlertMessage("Unable to add you to the group: " + reason, false); + else + { + GridInstantMessage msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = UUID.Zero.Guid; + msg.toAgentID = invitee.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.fromAgentName = "Groups"; + msg.message = string.Format("You have been added to the group."); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageBox; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + msg.binaryBucket = new byte[0]; + + OutgoingInstantMessage(msg, invitee); + + UpdateAllClientsWithGroupInfo(invitee); + } + + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + } + + // Reject + if (im.dialog == (byte)InstantMessageDialog.GroupInvitationDecline) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Received a reject invite notice."); + m_groupData.RemoveAgentToGroupInvite(GetRequestingAgentIDStr(remoteClient), inviteID); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), inviteInfo.AgentID, inviteInfo.GroupID); + } + } + } + + // Group notices + if ((im.dialog == (byte)InstantMessageDialog.GroupNotice)) + { + if (!m_groupNoticesEnabled) + { + return; + } + + UUID GroupID = new UUID(im.toAgentID); + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), GroupID, null) != null) + { + UUID NoticeID = UUID.Random(); + string Subject = im.message.Substring(0, im.message.IndexOf('|')); + string Message = im.message.Substring(Subject.Length + 1); + + InventoryItemBase item = null; + bool hasAttachment = false; + + if (im.binaryBucket.Length >= 1 && im.binaryBucket[0] > 0) + { + hasAttachment = true; + string binBucket = OpenMetaverse.Utils.BytesToString(im.binaryBucket); + binBucket = binBucket.Remove(0, 14).Trim(); + + OSD binBucketOSD = OSDParser.DeserializeLLSDXml(binBucket); + if (binBucketOSD is OSDMap) + { + OSDMap binBucketMap = (OSDMap)binBucketOSD; + + UUID itemID = binBucketMap["item_id"].AsUUID(); + UUID ownerID = binBucketMap["owner_id"].AsUUID(); + item = new InventoryItemBase(itemID, ownerID); + item = m_sceneList[0].InventoryService.GetItem(item); + } + else + m_log.DebugFormat("[Groups]: Received OSD with unexpected type: {0}", binBucketOSD.GetType()); + } + + if (m_groupData.AddGroupNotice(GetRequestingAgentIDStr(remoteClient), GroupID, NoticeID, im.fromAgentName, Subject, Message, + hasAttachment, + (byte)(item == null ? 0 : item.AssetType), + item == null ? null : item.Name, + item == null ? UUID.Zero : item.ID, + item == null ? UUID.Zero.ToString() : item.Owner.ToString())) + { + if (OnNewGroupNotice != null) + { + OnNewGroupNotice(GroupID, NoticeID); + } + + + // Send notice out to everyone that wants notices + foreach (GroupMembersData member in m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), GroupID)) + { + if (member.AcceptNotices) + { + // Build notice IIM, one of reach, because the sending may be async + GridInstantMessage msg = CreateGroupNoticeIM(UUID.Zero, NoticeID, (byte)OpenMetaverse.InstantMessageDialog.GroupNotice); + msg.toAgentID = member.AgentID.Guid; + OutgoingInstantMessage(msg, member.AgentID); + } + } + } + } + } + + if (im.dialog == (byte)InstantMessageDialog.GroupNoticeInventoryAccepted) + { + if (im.binaryBucket.Length < 16) // Invalid + return; + + //// 16 bytes are the UUID. Maybe. +// UUID folderID = new UUID(im.binaryBucket, 0); + UUID noticeID = new UUID(im.imSessionID); + + GroupNoticeInfo notice = m_groupData.GetGroupNotice(remoteClient.AgentId.ToString(), noticeID); + if (notice != null) + { + UUID giver = new UUID(im.toAgentID); + string tmp = string.Empty; + Util.ParseUniversalUserIdentifier(notice.noticeData.AttachmentOwnerID, out giver, out tmp, out tmp, out tmp, out tmp); + + m_log.DebugFormat("[Groups]: Giving inventory from {0} to {1}", giver, remoteClient.AgentId); + string message; + InventoryItemBase itemCopy = ((Scene)(remoteClient.Scene)).GiveInventoryItem(remoteClient.AgentId, + giver, notice.noticeData.AttachmentItemID, out message); + + if (itemCopy == null) + { + remoteClient.SendAgentAlertMessage(message, false); + return; + } + + remoteClient.SendInventoryItemCreateUpdate(itemCopy, 0); + } + + } + + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + if ((im.dialog == 210)) + { + // This is sent from the region that the ejectee was ejected from + // if it's being delivered here, then the ejectee is here + // so we need to send local updates to the agent. + + UUID ejecteeID = new UUID(im.toAgentID); + + im.dialog = (byte)InstantMessageDialog.MessageFromAgent; + OutgoingInstantMessage(im, ejecteeID); + + IClientAPI ejectee = GetActiveClient(ejecteeID); + if (ejectee != null) + { + UUID groupID = new UUID(im.imSessionID); + ejectee.SendAgentDropGroup(groupID); + } + } + } + + private void OnGridInstantMessage(GridInstantMessage msg) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Trigger the above event handler + OnInstantMessage(null, msg); + + // If a message from a group arrives here, it may need to be forwarded to a local client + if (msg.fromGroup == true) + { + switch (msg.dialog) + { + case (byte)InstantMessageDialog.GroupInvitation: + case (byte)InstantMessageDialog.GroupNotice: + UUID toAgentID = new UUID(msg.toAgentID); + IClientAPI localClient = GetActiveClient(toAgentID); + if (localClient != null) + { + localClient.SendInstantMessage(msg); + } + break; + } + } + } + + #endregion + + #region IGroupsModule Members + + public event NewGroupNotice OnNewGroupNotice; + + public GroupRecord GetGroupRecord(UUID GroupID) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), GroupID, null); + } + + public GroupRecord GetGroupRecord(string name) + { + return m_groupData.GetGroupRecord(UUID.Zero.ToString(), UUID.Zero, name); + } + + public void ActivateGroup(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + // Changing active group changes title, active powers, all kinds of things + // anyone who is in any region that can see this client, should probably be + // updated with new group info. At a minimum, they should get ScenePresence + // updated with new title. + UpdateAllClientsWithGroupInfo(remoteClient.AgentId); + } + + /// + /// Get the Role Titles for an Agent, for a specific group + /// + public List GroupTitlesRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List agentRoles = m_groupData.GetAgentGroupRoles(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + GroupMembershipData agentMembership = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + List titles = new List(); + foreach (GroupRolesData role in agentRoles) + { + GroupTitlesData title = new GroupTitlesData(); + title.Name = role.Name; + if (agentMembership != null) + { + title.Selected = agentMembership.ActiveRole == role.RoleID; + } + title.UUID = role.RoleID; + + titles.Add(title); + } + + return titles; + } + + public List GroupMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: GroupMembersRequest called for {0} from client {1}", groupID, remoteClient.Name); + + List data = m_groupData.GetGroupMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - IsOwner({1})", member.AgentID, member.IsOwner); + } + } + + return data; + + } + + public List GroupRoleDataRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoles(GetRequestingAgentIDStr(remoteClient), groupID); + + return data; + } + + public List GroupRoleMembersRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + List data = m_groupData.GetGroupRoleMembers(GetRequestingAgentIDStr(remoteClient), groupID); + + if (m_debugEnabled) + { + foreach (GroupRoleMembersData member in data) + { + m_log.DebugFormat("[Groups]: Member({0}) - Role({1})", member.MemberID, member.RoleID); + } + } + return data; + } + + public GroupProfileData GroupProfileRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupProfileData profile = new GroupProfileData(); + + // just to get the OwnerRole... + ExtendedGroupRecord groupInfo = m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), groupID, string.Empty); + GroupMembershipData memberInfo = m_groupData.GetAgentGroupMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + if (groupInfo != null) + { + profile.AllowPublish = groupInfo.AllowPublish; + profile.Charter = groupInfo.Charter; + profile.FounderID = groupInfo.FounderID; + profile.GroupID = groupID; + profile.GroupMembershipCount = groupInfo.MemberCount; + profile.GroupRolesCount = groupInfo.RoleCount; + profile.InsigniaID = groupInfo.GroupPicture; + profile.MaturePublish = groupInfo.MaturePublish; + profile.MembershipFee = groupInfo.MembershipFee; + profile.Money = 0; + profile.Name = groupInfo.GroupName; + profile.OpenEnrollment = groupInfo.OpenEnrollment; + profile.OwnerRole = groupInfo.OwnerRoleID; + profile.ShowInList = groupInfo.ShowInList; + } + if (memberInfo != null) + { + profile.MemberTitle = memberInfo.GroupTitle; + profile.PowersMask = memberInfo.GroupPowers; + } + + return profile; + } + + public GroupMembershipData[] GetMembershipData(UUID agentID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + return m_groupData.GetAgentGroupMemberships(UUID.Zero.ToString(), agentID.ToString()).ToArray(); + } + + public GroupMembershipData GetMembershipData(UUID groupID, UUID agentID) + { + if (m_debugEnabled) + m_log.DebugFormat( + "[Groups]: {0} called with groupID={1}, agentID={2}", + System.Reflection.MethodBase.GetCurrentMethod().Name, groupID, agentID); + + return m_groupData.GetAgentGroupMembership(UUID.Zero.ToString(), agentID.ToString(), groupID); + } + + public void UpdateGroupInfo(IClientAPI remoteClient, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + string reason = string.Empty; + if (!m_groupData.UpdateGroup(GetRequestingAgentIDStr(remoteClient), groupID, charter, showInList, insigniaID, membershipFee, + openEnrollment, allowPublish, maturePublish, out reason)) + remoteClient.SendAgentAlertMessage(reason, false); + } + + public void SetGroupAcceptNotices(IClientAPI remoteClient, UUID groupID, bool acceptNotices, bool listInProfile) + { + // Note: Permissions checking for modification rights is handled by the Groups Server/Service + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.UpdateMembership(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, acceptNotices, listInProfile); + } + + public UUID CreateGroup(IClientAPI remoteClient, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called in {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Scene.RegionInfo.RegionName); + + if (m_groupData.GetGroupRecord(GetRequestingAgentIDStr(remoteClient), UUID.Zero, name) != null) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "A group with the same name already exists."); + return UUID.Zero; + } + + // check user level + ScenePresence avatar = null; + Scene scene = (Scene)remoteClient.Scene; + scene.TryGetScenePresence(remoteClient.AgentId, out avatar); + + if (avatar != null) + { + if (avatar.UserLevel < m_levelGroupCreate) + { + remoteClient.SendCreateGroupReply(UUID.Zero, false, String.Format("Insufficient permissions to create a group. Requires level {0}", m_levelGroupCreate)); + return UUID.Zero; + } + } + + // check funds + // is there is a money module present ? + IMoneyModule money = scene.RequestModuleInterface(); + if (money != null) + { + // do the transaction, that is if the agent has got sufficient funds + if (!money.AmountCovered(remoteClient.AgentId, money.GroupCreationCharge)) { + remoteClient.SendCreateGroupReply(UUID.Zero, false, "Insufficient funds to create a group."); + return UUID.Zero; + } + } + + string reason = string.Empty; + UUID groupID = m_groupData.CreateGroup(remoteClient.AgentId, name, charter, showInList, insigniaID, membershipFee, openEnrollment, + allowPublish, maturePublish, remoteClient.AgentId, out reason); + + if (groupID != UUID.Zero) + { + if (money != null) + money.ApplyCharge(remoteClient.AgentId, money.GroupCreationCharge, MoneyTransactionType.GroupCreate); + + remoteClient.SendCreateGroupReply(groupID, true, "Group created successfullly"); + + // Update the founder with new group information. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + else + remoteClient.SendCreateGroupReply(groupID, false, reason); + + return groupID; + } + + public GroupNoticeData[] GroupNoticesListRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // ToDo: check if agent is a member of group and is allowed to see notices? + + List notices = m_groupData.GetGroupNotices(GetRequestingAgentIDStr(remoteClient), groupID); + List os_notices = new List(); + foreach (ExtendedGroupNoticeData n in notices) + { + GroupNoticeData osn = n.ToGroupNoticeData(); + os_notices.Add(osn); + } + + return os_notices.ToArray(); + } + + /// + /// Get the title of the agent's current role. + /// + public string GetGroupTitle(UUID avatarID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GroupMembershipData membership = m_groupData.GetAgentActiveMembership(UUID.Zero.ToString(), avatarID.ToString()); + if (membership != null) + { + return membership.GroupTitle; + } + return string.Empty; + } + + /// + /// Change the current Active Group Role for Agent + /// + public void GroupTitleUpdate(IClientAPI remoteClient, UUID groupID, UUID titleRoleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.SetAgentActiveGroupRole(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, titleRoleID); + + // TODO: Not sure what all is needed here, but if the active group role change is for the group + // the client currently has set active, then we need to do a scene presence update too + // if (m_groupData.GetAgentActiveMembership(GetRequestingAgentID(remoteClient)).GroupID == GroupID) + + UpdateAllClientsWithGroupInfo(GetRequestingAgentID(remoteClient)); + } + + + public void GroupRoleUpdate(IClientAPI remoteClient, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, byte updateType) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Security Checks are handled in the Groups Service. + + switch ((OpenMetaverse.GroupRoleUpdate)updateType) + { + case OpenMetaverse.GroupRoleUpdate.Create: + string reason = string.Empty; + if (!m_groupData.AddGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, UUID.Random(), name, description, title, powers, out reason)) + remoteClient.SendAgentAlertMessage("Unable to create role: " + reason, false); + break; + + case OpenMetaverse.GroupRoleUpdate.Delete: + m_groupData.RemoveGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID); + break; + + case OpenMetaverse.GroupRoleUpdate.UpdateAll: + case OpenMetaverse.GroupRoleUpdate.UpdateData: + case OpenMetaverse.GroupRoleUpdate.UpdatePowers: + if (m_debugEnabled) + { + GroupPowers gp = (GroupPowers)powers; + m_log.DebugFormat("[Groups]: Role ({0}) updated with Powers ({1}) ({2})", name, powers.ToString(), gp.ToString()); + } + m_groupData.UpdateGroupRole(GetRequestingAgentIDStr(remoteClient), groupID, roleID, name, description, title, powers); + break; + + case OpenMetaverse.GroupRoleUpdate.NoUpdate: + default: + // No Op + break; + + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupRoleChanges(IClientAPI remoteClient, UUID groupID, UUID roleID, UUID memberID, uint changes) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + // Todo: Security check + + switch (changes) + { + case 0: + // Add + m_groupData.AddAgentToGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + case 1: + // Remove + m_groupData.RemoveAgentFromGroupRole(GetRequestingAgentIDStr(remoteClient), memberID.ToString(), groupID, roleID); + + break; + default: + m_log.ErrorFormat("[Groups]: {0} does not understand changes == {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, changes); + break; + } + + // TODO: This update really should send out updates for everyone in the role that just got changed. + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void GroupNoticeRequest(IClientAPI remoteClient, UUID groupNoticeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called for notice {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, groupNoticeID); + + GridInstantMessage msg = CreateGroupNoticeIM(remoteClient.AgentId, groupNoticeID, (byte)InstantMessageDialog.GroupNoticeRequested); + + OutgoingInstantMessage(msg, GetRequestingAgentID(remoteClient)); + } + + public GridInstantMessage CreateGroupNoticeIM(UUID agentID, UUID groupNoticeID, byte dialog) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + GridInstantMessage msg = new GridInstantMessage(); + byte[] bucket; + + msg.imSessionID = groupNoticeID.Guid; + msg.toAgentID = agentID.Guid; + msg.dialog = dialog; + // msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupNotice; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = UUID.Zero.Guid; + + GroupNoticeInfo info = m_groupData.GetGroupNotice(agentID.ToString(), groupNoticeID); + if (info != null) + { + msg.fromAgentID = info.GroupID.Guid; + msg.timestamp = info.noticeData.Timestamp; + msg.fromAgentName = info.noticeData.FromName; + msg.message = info.noticeData.Subject + "|" + info.Message; + if (info.noticeData.HasAttachment) + { + byte[] name = System.Text.Encoding.UTF8.GetBytes(info.noticeData.AttachmentName); + bucket = new byte[19 + name.Length]; + bucket[0] = 1; // has attachment? + bucket[1] = info.noticeData.AttachmentType; // attachment type + name.CopyTo(bucket, 18); + } + else + { + bucket = new byte[19]; + bucket[0] = 0; // Has att? + bucket[1] = 0; // type + bucket[18] = 0; // null terminated + } + + info.GroupID.ToBytes(bucket, 2); + msg.binaryBucket = bucket; + } + else + { + m_log.DebugFormat("[Groups]: Group Notice {0} not found, composing empty message.", groupNoticeID); + msg.fromAgentID = UUID.Zero.Guid; + msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); ; + msg.fromAgentName = string.Empty; + msg.message = string.Empty; + msg.binaryBucket = new byte[0]; + } + + return msg; + } + + public void SendAgentGroupDataUpdate(IClientAPI remoteClient) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Send agent information about his groups + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void JoinGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string reason = string.Empty; + // Should check to see if OpenEnrollment, or if there's an outstanding invitation + if (m_groupData.AddAgentToGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID, UUID.Zero, string.Empty, out reason)) + { + + remoteClient.SendJoinGroupReply(groupID, true); + + // Should this send updates to everyone in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + + if (reason != string.Empty) + // A warning + remoteClient.SendAlertMessage("Warning: " + reason); + } + else + remoteClient.SendJoinGroupReply(groupID, false); + } + + public void LeaveGroupRequest(IClientAPI remoteClient, UUID groupID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + m_groupData.RemoveAgentFromGroup(GetRequestingAgentIDStr(remoteClient), GetRequestingAgentIDStr(remoteClient), groupID); + + remoteClient.SendLeaveGroupReply(groupID, true); + + remoteClient.SendAgentDropGroup(groupID); + + // SL sends out notifcations to the group messaging session that the person has left + // Should this also update everyone who is in the group? + SendAgentGroupDataUpdate(remoteClient, GetRequestingAgentID(remoteClient)); + } + + public void EjectGroupMemberRequest(IClientAPI remoteClient, UUID groupID, UUID ejecteeID) + { + EjectGroupMember(remoteClient, GetRequestingAgentID(remoteClient), groupID, ejecteeID); + } + + public void EjectGroupMember(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID ejecteeID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // Todo: Security check? + m_groupData.RemoveAgentFromGroup(agentID.ToString(), ejecteeID.ToString(), groupID); + + string agentName; + RegionInfo regionInfo; + + // remoteClient provided or just agentID? + if (remoteClient != null) + { + agentName = remoteClient.Name; + regionInfo = remoteClient.Scene.RegionInfo; + remoteClient.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + IClientAPI client = GetActiveClient(agentID); + + if (client != null) + { + agentName = client.Name; + regionInfo = client.Scene.RegionInfo; + client.SendEjectGroupMemberReply(agentID, groupID, true); + } + else + { + regionInfo = m_sceneList[0].RegionInfo; + UserAccount acc = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, agentID); + + if (acc != null) + { + agentName = acc.FirstName + " " + acc.LastName; + } + else + { + agentName = "Unknown member"; + } + } + } + + GroupRecord groupInfo = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + + UserAccount account = m_sceneList[0].UserAccountService.GetUserAccount(regionInfo.ScopeID, ejecteeID); + if ((groupInfo == null) || (account == null)) + { + return; + } + + // Send Message to Ejectee + GridInstantMessage msg = new GridInstantMessage(); + + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + // msg.fromAgentID = info.GroupID; + msg.toAgentID = ejecteeID.Guid; + //msg.timestamp = (uint)Util.UnixTimeSinceEpoch(); + msg.timestamp = 0; + msg.fromAgentName = agentName; + msg.message = string.Format("You have been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.MessageFromAgent; + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, ejecteeID); + + // Message to ejector + // Interop, received special 210 code for ejecting a group member + // this only works within the comms servers domain, and won't work hypergrid + // TODO:FIXME: Use a presense server of some kind to find out where the + // client actually is, and try contacting that region directly to notify them, + // or provide the notification via xmlrpc update queue + + msg = new GridInstantMessage(); + msg.imSessionID = UUID.Zero.Guid; + msg.fromAgentID = agentID.Guid; + msg.toAgentID = agentID.Guid; + msg.timestamp = 0; + msg.fromAgentName = agentName; + if (account != null) + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, account.FirstName + " " + account.LastName); + } + else + { + msg.message = string.Format("{2} has been ejected from '{1}' by {0}.", agentName, groupInfo.GroupName, "Unknown member"); + } + msg.dialog = (byte)210; //interop + msg.fromGroup = false; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[0]; + OutgoingInstantMessage(msg, agentID); + + + // SL sends out messages to everyone in the group + // Who all should receive updates and what should they be updated with? + UpdateAllClientsWithGroupInfo(ejecteeID); + } + + public void InviteGroupRequest(IClientAPI remoteClient, UUID groupID, UUID invitedAgentID, UUID roleID) + { + InviteGroup(remoteClient, GetRequestingAgentID(remoteClient), groupID, invitedAgentID, roleID); + } + + public void InviteGroup(IClientAPI remoteClient, UUID agentID, UUID groupID, UUID invitedAgentID, UUID roleID) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + string agentName = m_UserManagement.GetUserName(agentID); + RegionInfo regionInfo = m_sceneList[0].RegionInfo; + + GroupRecord group = m_groupData.GetGroupRecord(agentID.ToString(), groupID, null); + if (group == null) + { + m_log.DebugFormat("[Groups]: No such group {0}", groupID); + return; + } + + // Todo: Security check, probably also want to send some kind of notification + UUID InviteID = UUID.Random(); + + if (m_groupData.AddAgentToGroupInvite(agentID.ToString(), InviteID, groupID, roleID, invitedAgentID.ToString())) + { + if (m_msgTransferModule != null) + { + 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 = agentName; + msg.message = string.Format("{0} has invited you to join a group called {1}. There is no cost to join this group.", agentName, group.GroupName); + msg.dialog = (byte)OpenMetaverse.InstantMessageDialog.GroupInvitation; + msg.fromGroup = true; + msg.offline = (byte)0; + msg.ParentEstateID = 0; + msg.Position = Vector3.Zero; + msg.RegionID = regionInfo.RegionID.Guid; + msg.binaryBucket = new byte[20]; + + OutgoingInstantMessage(msg, invitedAgentID); + } + } + } + + public List FindGroups(IClientAPI remoteClient, string query) + { + return m_groupData.FindGroups(GetRequestingAgentIDStr(remoteClient), query); + } + + #endregion + + #region Client/Update Tools + + /// + /// Try to find an active IClientAPI reference for agentID giving preference to root connections + /// + private IClientAPI GetActiveClient(UUID 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) + { + return sp.ControllingClient; + } + else + { + child = sp.ControllingClient; + } + } + } + + // If we didn't find a root, then just return whichever child we found, or null if none + return child; + } + + /// + /// Send 'remoteClient' the group membership 'data' for agent 'dataForAgentID'. + /// + private void SendGroupMembershipInfoViaCaps(IClientAPI remoteClient, UUID dataForAgentID, GroupMembershipData[] data) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // NPCs currently don't have a CAPs structure or event queues. There is a strong argument for conveying this information + // to them anyway since it makes writing server-side bots a lot easier, but for now we don't do anything. + if (remoteClient.SceneAgent.PresenceType == PresenceType.Npc) + return; + + OSDArray AgentData = new OSDArray(1); + OSDMap AgentDataMap = new OSDMap(1); + AgentDataMap.Add("AgentID", OSD.FromUUID(dataForAgentID)); + AgentData.Add(AgentDataMap); + + OSDArray GroupData = new OSDArray(data.Length); + OSDArray NewGroupData = new OSDArray(data.Length); + + foreach (GroupMembershipData membership in data) + { + if (GetRequestingAgentID(remoteClient) != dataForAgentID) + { + if (!membership.ListInProfile) + { + // If we're sending group info to remoteclient about another agent, + // filter out groups the other agent doesn't want to share. + continue; + } + } + + OSDMap GroupDataMap = new OSDMap(6); + OSDMap NewGroupDataMap = new OSDMap(1); + + GroupDataMap.Add("GroupID", OSD.FromUUID(membership.GroupID)); + GroupDataMap.Add("GroupPowers", OSD.FromULong(membership.GroupPowers)); + GroupDataMap.Add("AcceptNotices", OSD.FromBoolean(membership.AcceptNotices)); + GroupDataMap.Add("GroupInsigniaID", OSD.FromUUID(membership.GroupPicture)); + GroupDataMap.Add("Contribution", OSD.FromInteger(membership.Contribution)); + GroupDataMap.Add("GroupName", OSD.FromString(membership.GroupName)); + NewGroupDataMap.Add("ListInProfile", OSD.FromBoolean(membership.ListInProfile)); + + GroupData.Add(GroupDataMap); + NewGroupData.Add(NewGroupDataMap); + } + + OSDMap llDataStruct = new OSDMap(3); + llDataStruct.Add("AgentData", AgentData); + llDataStruct.Add("GroupData", GroupData); + llDataStruct.Add("NewGroupData", NewGroupData); + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: {0}", OSDParser.SerializeJsonString(llDataStruct)); + } + + IEventQueue queue = remoteClient.Scene.RequestModuleInterface(); + + if (queue != null) + { + queue.Enqueue(queue.BuildEvent("AgentGroupDataUpdate", llDataStruct), GetRequestingAgentID(remoteClient)); + } + } + + private void SendScenePresenceUpdate(UUID AgentID, string Title) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: Updating scene title for {0} with title: {1}", AgentID, Title); + + ScenePresence presence = null; + + foreach (Scene scene in m_sceneList) + { + presence = scene.GetScenePresence(AgentID); + if (presence != null) + { + if (presence.Grouptitle != Title) + { + presence.Grouptitle = Title; + + if (! presence.IsChildAgent) + presence.SendAvatarDataToAllClients(); + } + } + } + } + + /// + /// Send updates to all clients who might be interested in groups data for dataForClientID + /// + private void UpdateAllClientsWithGroupInfo(UUID dataForClientID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: Probably isn't nessesary to update every client in every scene. + // Need to examine client updates and do only what's nessesary. + lock (m_sceneList) + { + foreach (Scene scene in m_sceneList) + { + scene.ForEachClient(delegate(IClientAPI client) { SendAgentGroupDataUpdate(client, dataForClientID); }); + } + } + } + + /// + /// Update remoteClient with group information about dataForAgentID + /// + private void SendAgentGroupDataUpdate(IClientAPI remoteClient, UUID dataForAgentID) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called for {1}", System.Reflection.MethodBase.GetCurrentMethod().Name, remoteClient.Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + + OnAgentDataUpdateRequest(remoteClient, dataForAgentID, UUID.Zero); + + // Need to send a group membership update to the client + // UDP version doesn't seem to behave nicely. But we're going to send it out here + // with an empty group membership to hopefully remove groups being displayed due + // to the core Groups Stub + //remoteClient.SendGroupMembership(new GroupMembershipData[0]); + + GroupMembershipData[] membershipArray = GetProfileListedGroupMemberships(remoteClient, dataForAgentID); + SendGroupMembershipInfoViaCaps(remoteClient, dataForAgentID, membershipArray); + + //remoteClient.SendAvatarGroupsReply(dataForAgentID, membershipArray); + if (remoteClient.AgentId == dataForAgentID) + remoteClient.RefreshGroupMembership(); + } + + /// + /// Get a list of groups memberships for the agent that are marked "ListInProfile" + /// (unless that agent has a godLike aspect, in which case get all groups) + /// + /// + /// + private GroupMembershipData[] GetProfileListedGroupMemberships(IClientAPI requestingClient, UUID dataForAgentID) + { + List membershipData = m_groupData.GetAgentGroupMemberships(requestingClient.AgentId.ToString(), dataForAgentID.ToString()); + GroupMembershipData[] membershipArray; + + // cScene and property accessor 'isGod' are in support of the opertions to bypass 'hidden' group attributes for + // those with a GodLike aspect. + Scene cScene = (Scene)requestingClient.Scene; + bool isGod = cScene.Permissions.IsGod(requestingClient.AgentId); + + if (isGod) + { + membershipArray = membershipData.ToArray(); + } + else + { + if (requestingClient.AgentId != dataForAgentID) + { + Predicate showInProfile = delegate(GroupMembershipData membership) + { + return membership.ListInProfile; + }; + + membershipArray = membershipData.FindAll(showInProfile).ToArray(); + } + else + { + membershipArray = membershipData.ToArray(); + } + } + + if (m_debugEnabled) + { + m_log.InfoFormat("[Groups]: Get group membership information for {0} requested by {1}", dataForAgentID, requestingClient.AgentId); + foreach (GroupMembershipData membership in membershipArray) + { + m_log.InfoFormat("[Groups]: {0} :: {1} - {2} - {3}", dataForAgentID, membership.GroupName, membership.GroupTitle, membership.GroupPowers); + } + } + + return membershipArray; + } + + + private void SendAgentDataUpdate(IClientAPI remoteClient, UUID dataForAgentID, UUID activeGroupID, string activeGroupName, ulong activeGroupPowers, string activeGroupTitle) + { + if (m_debugEnabled) m_log.DebugFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + // TODO: All the client update functions need to be reexamined because most do too much and send too much stuff + string firstname = "Unknown", lastname = "Unknown"; + string name = m_UserManagement.GetUserName(dataForAgentID); + if (!string.IsNullOrEmpty(name)) + { + string[] parts = name.Split(new char[] { ' ' }); + if (parts.Length >= 2) + { + firstname = parts[0]; + lastname = parts[1]; + } + } + + remoteClient.SendAgentDataUpdate(dataForAgentID, activeGroupID, firstname, + lastname, activeGroupPowers, activeGroupName, + activeGroupTitle); + } + + #endregion + + #region IM Backed Processes + + private void OutgoingInstantMessage(GridInstantMessage msg, UUID msgTo) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: {0} called", System.Reflection.MethodBase.GetCurrentMethod().Name); + + IClientAPI localClient = GetActiveClient(msgTo); + if (localClient != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is local, delivering directly", localClient.Name); + localClient.SendInstantMessage(msg); + } + else if (m_msgTransferModule != null) + { + if (m_debugEnabled) m_log.InfoFormat("[Groups]: MsgTo ({0}) is not local, delivering via TransferModule", msgTo); + m_msgTransferModule.SendInstantMessage(msg, delegate(bool success) { if (m_debugEnabled) m_log.DebugFormat("[Groups]: Message Sent: {0}", success?"Succeeded":"Failed"); }); + } + } + + public void NotifyChange(UUID groupID) + { + // Notify all group members of a chnge in group roles and/or + // permissions + // + } + + #endregion + + private string GetRequestingAgentIDStr(IClientAPI client) + { + return GetRequestingAgentID(client).ToString(); + } + + private UUID GetRequestingAgentID(IClientAPI client) + { + UUID requestingAgentID = UUID.Zero; + if (client != null) + { + requestingAgentID = client.AgentId; + } + return requestingAgentID; + } + + } + +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs new file mode 100644 index 0000000000..653dbac8bd --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnector.cs @@ -0,0 +1,289 @@ +/* + * 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 sendData = new Dictionary(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AgentID"] = AgentID.ToString(); + sendData["AccessToken"] = accessToken; + sendData["GroupID"] = groupID.ToString(); + sendData["Location"] = url; + sendData["Name"] = name; + Dictionary 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 sendData = new Dictionary(); + 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 sendData = new Dictionary(); + 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 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)ret["RESULT"]); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary 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)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)v); + members.Add(m); + } + + return members; + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary 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)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = GroupsDataUtils.Sanitize(token); + Dictionary 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)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)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 sendData = new Dictionary(); + 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 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 sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["GroupID"] = groupID.ToString(); + Dictionary 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 MakeRequest(string method, Dictionary 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 replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs new file mode 100644 index 0000000000..7d57de190b --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/GroupsServiceHGConnectorModule.cs @@ -0,0 +1,707 @@ +/* + * 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 m_Scenes; + private ForeignImporter m_ForeignImporter; + private string m_ServiceLocation; + private IConfigSource m_Config; + + private Dictionary m_NetworkConnectors = new Dictionary(); + 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(); + + 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(this); + m_Scenes.Add(scene); + + scene.EventManager.OnNewClient += OnNewClient; + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + m_OfflineIM = scene.RequestModuleInterface(); + m_Messaging = scene.RequestModuleInterface(); + 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(), scene.RequestModuleInterface()); + + } + 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 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 FindGroups(string RequestingAgentID, string search) + { + return m_LocalGroupsConnector.FindGroups(AgentUUI(RequestingAgentID), search); + } + + public List 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(); + } + + 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 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(); + } + + public List 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(); + } + + 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 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(); + } + + 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 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 members = m_LocalGroupsConnector.GetGroupMembers(AgentUUI(RequestingAgentID), groupID); + List urls = new List(); + 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 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; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.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; + try + { + AgentID = new UUID(AgentIDStr); + } + catch (FormatException) + { + return AgentID.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 + } +} diff --git a/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs new file mode 100644 index 0000000000..f60c1a5f68 --- /dev/null +++ b/OpenSim/Addons/Groups/Hypergrid/HGGroupsServiceRobustConnector.cs @@ -0,0 +1,444 @@ +/* + * 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(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(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(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) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID, token); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID, token); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID, token); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/IGroupsServicesConnector.cs b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs new file mode 100644 index 0000000000..a09b59e238 --- /dev/null +++ b/OpenSim/Addons/Groups/IGroupsServicesConnector.cs @@ -0,0 +1,112 @@ +/* + * 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 FindGroups(string RequestingAgentID, string search); + List 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 GetGroupRoles(string RequestingAgentID, UUID GroupID); + List 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 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); + + /// + /// Get information about a specific group to which the user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// The group requested. + /// + /// If the user is a member of the group then the data structure is returned. If not, then null is returned. + /// + ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID); + + /// + /// Get information about the groups to which a user belongs. + /// + /// The agent requesting the information. + /// The agent requested. + /// + /// Information about the groups to which the user belongs. If the user belongs to no groups then an empty + /// list is returned. + /// + List 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 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; + } + +} diff --git a/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs new file mode 100644 index 0000000000..8e30df5755 --- /dev/null +++ b/OpenSim/Addons/Groups/Local/GroupsServiceLocalConnectorModule.cs @@ -0,0 +1,326 @@ +/* + * 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 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(); + } + + #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(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + 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 FindGroups(string RequestingAgentID, string search) + { + return m_GroupsService.FindGroups(RequestingAgentID, search); + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List _members = m_GroupsService.GetGroupMembers(RequestingAgentID, GroupID); + if (_members != null && _members.Count > 0) + { + List members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + return members; + } + + return new List(); + } + + 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 GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List _rm = m_GroupsService.GetGroupRoleMembers(RequestingAgentID, GroupID); + if (_rm != null && _rm.Count > 0) + { + List rm = _rm.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + return rm; + } + + return new List(); + + } + + 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 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 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 GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + } + + #endregion + } +} diff --git a/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0a7fb5f754 --- /dev/null +++ b/OpenSim/Addons/Groups/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("0.8.2.*")] + +[assembly: Addin("OpenSim.Groups", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs new file mode 100644 index 0000000000..7450c1442e --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnector.cs @@ -0,0 +1,696 @@ +/* + * 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(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 sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null) + return null; + + if (ret["RESULT"].ToString() == "NULL") + { + reason = ret["REASON"].ToString(); + return null; + } + + return GroupsDataUtils.GroupRecord((Dictionary)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 sendData = GroupsDataUtils.GroupRecord(rec); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "UPDATE"; + Dictionary ret = MakeRequest("PUTGROUP", sendData); + + if (ret == null || (ret != null && (!ret.ContainsKey("RESULT") || ret["RESULT"].ToString() == "NULL"))) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)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 sendData = new Dictionary(); + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + if (!string.IsNullOrEmpty(GroupName)) + sendData["Name"] = GroupsDataUtils.Sanitize(GroupName); + + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary ret = MakeRequest("GETGROUP", sendData); + + if (ret == null || (ret != null && (!ret.ContainsKey("RESULT") || ret["RESULT"].ToString() == "NULL"))) + return null; + + return GroupsDataUtils.GroupRecord((Dictionary)ret["RESULT"]); + } + + public List FindGroups(string RequestingAgentID, string query) + { + List hits = new List(); + if (string.IsNullOrEmpty(query)) + return hits; + + Dictionary sendData = new Dictionary(); + sendData["Query"] = query; + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary 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)ret["RESULT"]).Values) + { + DirGroupsReplyData m = GroupsDataUtils.DirGroupsReplyData((Dictionary)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 sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["AccessToken"] = token; + Dictionary 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)ret["RESULT"]); + + } + + public void RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEAGENTFROMGROUP", sendData); + } + + public ExtendedGroupMembershipData GetMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + if (GroupID != UUID.Zero) + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]); + } + + public List GetMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID; + sendData["ALL"] = "true"; + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]).Values) + { + GroupMembershipData m = GroupsDataUtils.GroupMembershipData((Dictionary)v); + memberships.Add(m); + } + + return memberships; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary 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)ret["RESULT"]).Values) + { + ExtendedGroupMembersData m = GroupsDataUtils.GroupMembersData((Dictionary)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 sendData = new Dictionary(); + 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 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 sendData = new Dictionary(); + 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 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 sendData = new Dictionary(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + MakeRequest("REMOVEROLE", sendData); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + List rmembers = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]).Values) + { + ExtendedGroupRoleMembersData m = GroupsDataUtils.GroupRoleMembersData((Dictionary)v); + rmembers.Add(m); + } + + return rmembers; + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary 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 sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RoleID"] = RoleID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "DELETE"; + + Dictionary 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 GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]).Values) + { + GroupRolesData m = GroupsDataUtils.GroupRolesData((Dictionary)v); + roles.Add(m); + } + + return roles; + } + + public GroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + Dictionary sendData = new Dictionary(); + sendData["AgentID"] = AgentID.ToString(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GROUP"; + + Dictionary 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)ret["RESULT"]); + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + Dictionary sendData = new Dictionary(); + 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 sendData = new Dictionary(); + 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 sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["GroupID"] = groupID.ToString(); + sendData["RoleID"] = roleID.ToString(); + sendData["AgentID"] = agentID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "ADD"; + + Dictionary 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 sendData = new Dictionary(); + sendData["InviteID"] = inviteID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + sendData["OP"] = "GET"; + + Dictionary 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)ret["RESULT"]); + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + Dictionary sendData = new Dictionary(); + 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 sendData = new Dictionary(); + 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 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 sendData = new Dictionary(); + sendData["NoticeID"] = noticeID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + + Dictionary 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)ret["RESULT"]); + } + + public List GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + List notices = new List(); + + Dictionary sendData = new Dictionary(); + sendData["GroupID"] = GroupID.ToString(); + sendData["RequestingAgentID"] = RequestingAgentID; + Dictionary 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)ret["RESULT"]).Values) + { + ExtendedGroupNoticeData m = GroupsDataUtils.GroupNoticeData((Dictionary)v); + notices.Add(m); + } + + return notices; + } + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary 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 replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs new file mode 100644 index 0000000000..d4739c6ac1 --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRemoteConnectorModule.cs @@ -0,0 +1,408 @@ +/* + * 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 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(); + + } + + #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(this); + m_Scenes.Add(scene); + } + + public void RemoveRegion(Scene scene) + { + if (!m_Enabled) + return; + + scene.UnregisterModuleInterface(this); + m_Scenes.Remove(scene); + } + + public void RegionLoaded(Scene scene) + { + if (!m_Enabled) + return; + + if (m_UserManagement == null) + { + m_UserManagement = scene.RequestModuleInterface(); + 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 FindGroups(string RequestingAgentID, string search) + { + // TODO! + return m_GroupsService.FindGroups(RequestingAgentID, 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 GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + return m_CacheWrapper.GetAgentGroupMemberships(AgentID, delegate + { + return m_GroupsService.GetMemberships(RequestingAgentID, AgentID); + }); + } + + + public List 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 GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupRoles(RequestingAgentID, GroupID, delegate + { + return m_GroupsService.GetGroupRoles(RequestingAgentID, GroupID); + }); + } + + public List 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 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 GetGroupNotices(string RequestingAgentID, UUID GroupID) + { + return m_CacheWrapper.GetGroupNotices(GroupID, delegate + { + return m_GroupsService.GetGroupNotices(RequestingAgentID, GroupID); + }); + } + + #endregion + } + +} diff --git a/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs new file mode 100644 index 0000000000..26e844eb57 --- /dev/null +++ b/OpenSim/Addons/Groups/Remote/GroupsServiceRobustConnector.cs @@ -0,0 +1,816 @@ +/* + * 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) + { + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + + try + { + Dictionary 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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.", agentID)); + else + result["RESULT"] = "true"; + } + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(ServerUtils.BuildXmlResponse(result)); + } + + byte[] HandleGetMembership(Dictionary request) + { + Dictionary result = new Dictionary(); + + 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 memberships = m_GroupsService.GetAgentGroupMemberships(requestingAgentID, agentID); + if (memberships == null || (memberships != null && memberships.Count == 0)) + { + NullResult(result, "No memberships"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 members = m_GroupsService.GetGroupMembers(requestingAgentID, groupID); + if (members == null || (members != null && members.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 roles = m_GroupsService.GetGroupRoles(requestingAgentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 rmembers = m_GroupsService.GetGroupRoleMembers(requestingAgentID, groupID); + if (rmembers == null || (rmembers != null && rmembers.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 roles = m_GroupsService.GetAgentGroupRoles(requestingAgentID, agentID, groupID); + if (roles == null || (roles != null && roles.Count == 0)) + { + NullResult(result, "No members"); + } + else + { + Dictionary dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + 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 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 dict = new Dictionary(); + 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 request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("RequestingAgentID") || !request.ContainsKey("Query")) + NullResult(result, "Bad network data"); + + List hits = m_GroupsService.FindGroups(request["RequestingAgentID"].ToString(), request["Query"].ToString()); + + if (hits == null || (hits != null && hits.Count == 0)) + NullResult(result, "No hits"); + else + { + Dictionary dict = new Dictionary(); + 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 result, string reason) + { + result["RESULT"] = "NULL"; + result["REASON"] = reason; + } + + private byte[] FailureResult() + { + Dictionary result = new Dictionary(); + NullResult(result, "Unknown method"); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + private byte[] FailureResult(string reason) + { + Dictionary result = new Dictionary(); + NullResult(result, reason); + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + #endregion + } +} diff --git a/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs new file mode 100644 index 0000000000..813f79658e --- /dev/null +++ b/OpenSim/Addons/Groups/RemoteConnectorCacheWrapper.cs @@ -0,0 +1,888 @@ +/* + * 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 GroupMembershipListDelegate(); + public delegate List GroupMembersListDelegate(); + public delegate List GroupRolesListDelegate(); + public delegate List RoleMembersListDelegate(); + public delegate GroupNoticeInfo NoticeDelegate(); + public delegate List NoticeListDelegate(); + public delegate void VoidDelegate(); + public delegate bool BooleanDelegate(); + + public class RemoteConnectorCacheWrapper + { + private ForeignImporter m_ForeignImporter; + + private Dictionary m_ActiveRequests = new Dictionary(); + private const int GROUPS_CACHE_TIMEOUT = 1 * 60; // 1 minutes + + // This all important cache cahces objects of different types: + // group- or group- => ExtendedGroupRecord + // active- => GroupMembershipData + // membership-- => GroupMembershipData + // memberships- => List + // members-- => List + // role- => GroupRolesData + // roles- => List ; all roles in the group + // roles-- => List ; roles that the agent has + // rolemembers-- => List + // notice- => GroupNoticeInfo + // notices- => List + private ExpiringCache m_Cache = new ExpiringCache(); + + 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 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)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)memberships; + } + } + finally + { + m_ActiveRequests.Remove(cacheKey); + } + } + else + Thread.Sleep(50); + } + } + + public List 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 xx = (List)members; + return xx.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + try + { + List _members = d(); + + if (_members != null && _members.Count > 0) + members = _members.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupMembersData)); + else + members = new List(); + + lock (m_Cache) + { + //m_Cache.AddOrUpdate(cacheKey, members, GROUPS_CACHE_TIMEOUT); + m_Cache.AddOrUpdate(cacheKey, _members, GROUPS_CACHE_TIMEOUT); + + return (List)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 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)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)roles; + } + } + } + finally + { + m_ActiveRequests.Remove(cacheKey); + } + } + else + Thread.Sleep(50); + } + } + + public List 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 xx = (List)rmembers; + return xx.ConvertAll(m_ForeignImporter.ConvertGroupRoleMembersData); + } + + // not cached + if (!m_ActiveRequests.ContainsKey(cacheKey)) + { + m_ActiveRequests.Add(cacheKey, true); + firstCall = true; + } + } + + if (firstCall) + { + try + { + List _rmembers = d(); + + if (_rmembers != null && _rmembers.Count > 0) + rmembers = _rmembers.ConvertAll(new Converter(m_ForeignImporter.ConvertGroupRoleMembersData)); + else + rmembers = new List(); + + 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)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 xx = (List)obj; + List rmlist = xx.ConvertAll(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 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)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)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 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)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)notices; + } + } + finally + { + m_ActiveRequests.Remove(cacheKey); + } + } + else + Thread.Sleep(50); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Addons/Groups/Service/GroupsService.cs b/OpenSim/Addons/Groups/Service/GroupsService.cs new file mode 100644 index 0000000000..07641ef980 --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsService.cs @@ -0,0 +1,1060 @@ +/* + * 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 GroupsService : GroupsServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const GroupPowers DefaultEveryonePowers = GroupPowers.AllowSetHome | + GroupPowers.Accountable | + GroupPowers.JoinChat | + GroupPowers.AllowVoiceChat | + GroupPowers.ReceiveNotices | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + public const GroupPowers OwnerPowers = GroupPowers.Accountable | + GroupPowers.AllowEditLand | + GroupPowers.AllowFly | + GroupPowers.AllowLandmark | + GroupPowers.AllowRez | + GroupPowers.AllowSetHome | + GroupPowers.AllowVoiceChat | + GroupPowers.AssignMember | + GroupPowers.AssignMemberLimited | + GroupPowers.ChangeActions | + GroupPowers.ChangeIdentity | + GroupPowers.ChangeMedia | + GroupPowers.ChangeOptions | + GroupPowers.CreateRole | + GroupPowers.DeedObject | + GroupPowers.DeleteRole | + GroupPowers.Eject | + GroupPowers.FindPlaces | + GroupPowers.HostEvent | + GroupPowers.Invite | + GroupPowers.JoinChat | + GroupPowers.LandChangeIdentity | + GroupPowers.LandDeed | + GroupPowers.LandDivideJoin | + GroupPowers.LandEdit | + GroupPowers.LandEjectAndFreeze | + GroupPowers.LandGardening | + GroupPowers.LandManageAllowed | + GroupPowers.LandManageBanned | + GroupPowers.LandManagePasses | + GroupPowers.LandOptions | + GroupPowers.LandRelease | + GroupPowers.LandSetSale | + GroupPowers.ModerateChat | + GroupPowers.ObjectManipulate | + GroupPowers.ObjectSetForSale | + GroupPowers.ReceiveNotices | + GroupPowers.RemoveMember | + GroupPowers.ReturnGroupOwned | + GroupPowers.ReturnGroupSet | + GroupPowers.ReturnNonGroup | + GroupPowers.RoleProperties | + GroupPowers.SendNotices | + GroupPowers.SetLandingPoint | + GroupPowers.StartProposal | + GroupPowers.VoteOnProposal; + + #region Daily Cleanup + + private Timer m_CleanupTimer; + + public GroupsService(IConfigSource config, string configName) + : base(config, configName) + { + } + + public GroupsService(IConfigSource config) + : this(config, string.Empty) + { + // Once a day + m_CleanupTimer = new Timer(24 * 60 * 60 * 1000); + m_CleanupTimer.AutoReset = true; + m_CleanupTimer.Elapsed += new ElapsedEventHandler(m_CleanupTimer_Elapsed); + m_CleanupTimer.Enabled = true; + m_CleanupTimer.Start(); + } + + private void m_CleanupTimer_Elapsed(object sender, ElapsedEventArgs e) + { + m_Database.DeleteOldNotices(); + m_Database.DeleteOldInvites(); + } + + #endregion + + public UUID 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; + + // Check if the group already exists + if (m_Database.RetrieveGroup(name) != null) + { + reason = "A group with that name already exists"; + return UUID.Zero; + } + + // Create the group + GroupData data = new GroupData(); + data.GroupID = UUID.Random(); + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Charter"] = charter; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["FounderID"] = founderID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + UUID roleID = UUID.Random(); + data.Data["OwnerRoleID"] = roleID.ToString(); + + if (!m_Database.StoreGroup(data)) + return UUID.Zero; + + // Create Everyone role + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, UUID.Zero, "Everyone", "Everyone in the group", "Member of " + name, (ulong)DefaultEveryonePowers, true); + + // Create Owner role + _AddOrUpdateGroupRole(RequestingAgentID, data.GroupID, roleID, "Owners", "Owners of the group", "Owner of " + name, (ulong)OwnerPowers, true); + + // Add founder to group + _AddAgentToGroup(RequestingAgentID, founderID.ToString(), data.GroupID, roleID); + + return data.GroupID; + } + + public void UpdateGroup(string RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish) + { + GroupData data = m_Database.RetrieveGroup(groupID); + if (data == null) + return; + + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at updating group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + data.GroupID = groupID; + data.Data["Charter"] = charter; + data.Data["ShowInList"] = showInList ? "1" : "0"; + data.Data["InsigniaID"] = insigniaID.ToString(); + data.Data["MembershipFee"] = membershipFee.ToString(); + data.Data["OpenEnrollment"] = openEnrollment ? "1" : "0"; + data.Data["AllowPublish"] = allowPublish ? "1" : "0"; + data.Data["MaturePublish"] = maturePublish ? "1" : "0"; + + m_Database.StoreGroup(data); + + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, UUID GroupID) + { + GroupData data = m_Database.RetrieveGroup(GroupID); + + return _GroupDataToRecord(data); + } + + public ExtendedGroupRecord GetGroupRecord(string RequestingAgentID, string GroupName) + { + GroupData data = m_Database.RetrieveGroup(GroupName); + + return _GroupDataToRecord(data); + } + + public List FindGroups(string RequestingAgentID, string search) + { + List groups = new List(); + + GroupData[] data = m_Database.RetrieveGroups(search); + + if (data != null && data.Length > 0) + { + foreach (GroupData d in data) + { + // Don't list group proxies + if (d.Data.ContainsKey("Location") && d.Data["Location"] != string.Empty) + continue; + + DirGroupsReplyData g = new DirGroupsReplyData(); + g.groupID = d.GroupID; + + if (d.Data.ContainsKey("Name")) + g.groupName = d.Data["Name"]; + else + m_log.DebugFormat("[Groups]: Key Name not found"); + + g.members = m_Database.MemberCount(d.GroupID); + + groups.Add(g); + } + } + + return groups; + } + + public List GetGroupMembers(string RequestingAgentID, UUID GroupID) + { + List members = new List(); + + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return members; + + // Unfortunately this doesn't quite work on legacy group data because of a bug + // that's also being fixed here on CreateGroup. The OwnerRoleID sent to the DB was wrong. + // See how to find the ownerRoleID a few lines below. + UUID ownerRoleID = new UUID(group.Data["OwnerRoleID"]); + + RoleData[] roles = m_Database.RetrieveRoles(GroupID); + if (roles == null) + // something wrong with this group + return members; + List rolesList = new List(roles); + + // Let's find the "real" ownerRoleID + RoleData ownerRole = rolesList.Find(r => r.Data["Powers"] == ((long)OwnerPowers).ToString()); + if (ownerRole != null) + ownerRoleID = ownerRole.RoleID; + + // Check visibility? + // When we don't want to check visibility, we pass it "all" as the requestingAgentID + bool checkVisibility = !RequestingAgentID.Equals(UUID.Zero.ToString()); + + if (checkVisibility) + { + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + if (!isInGroup) // reduce the roles to the visible ones + rolesList = rolesList.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + } + + MembershipData[] datas = m_Database.RetrieveMembers(GroupID); + if (datas == null || (datas != null && datas.Length == 0)) + return members; + + // OK, we have everything we need + + foreach (MembershipData d in datas) + { + RoleMembershipData[] rolememberships = m_Database.RetrieveMemberRoles(GroupID, d.PrincipalID); + List rolemembershipsList = new List(rolememberships); + + ExtendedGroupMembersData m = new ExtendedGroupMembersData(); + + // What's this person's current role in the group? + UUID selectedRole = new UUID(d.Data["SelectedRoleID"]); + RoleData selected = rolesList.Find(r => r.RoleID == selectedRole); + + if (selected != null) + { + m.Title = selected.Data["Title"]; + m.AgentPowers = UInt64.Parse(selected.Data["Powers"]); + } + + m.AgentID = d.PrincipalID; + m.AcceptNotices = d.Data["AcceptNotices"] == "1" ? true : false; + m.Contribution = Int32.Parse(d.Data["Contribution"]); + m.ListInProfile = d.Data["ListInProfile"] == "1" ? true : false; + + GridUserData gud = m_GridUserService.Get(d.PrincipalID); + if (gud != null) + { + if (bool.Parse(gud.Data["Online"])) + { + m.OnlineStatus = @"Online"; + } + else + { + int unixtime = int.Parse(gud.Data["Login"]); + // The viewer is very picky about how these strings are formed. Eg. it will crash on malformed dates! + m.OnlineStatus = (unixtime == 0) ? @"unknown" : Util.ToDateTime(unixtime).ToString("MM/dd/yyyy"); + } + } + + // Is this person an owner of the group? + m.IsOwner = (rolemembershipsList.Find(r => r.RoleID == ownerRoleID) != null) ? true : false; + + 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; + // check that the requesting agent has permissions to add role + if (!HasPower(RequestingAgentID, groupID, GroupPowers.CreateRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at creating role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + reason = "Insufficient permission to create role"; + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, true); + + } + + public bool UpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.ChangeActions)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at changing role in group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddOrUpdateGroupRole(RequestingAgentID, groupID, roleID, name, description, title, powers, false); + } + + public void RemoveGroupRole(string RequestingAgentID, UUID groupID, UUID roleID) + { + // check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.DeleteRole)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at deleting role from group {1} denied because of lack of permission", RequestingAgentID, groupID); + return; + } + + // Can't delete Everyone and Owners roles + if (roleID == UUID.Zero) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Everyone role from group {0} denied", groupID); + return; + } + + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + { + m_log.DebugFormat("[Groups]: Attempt at deleting role from non-existing group {0}", groupID); + return; + } + + if (roleID == new UUID(group.Data["OwnerRoleID"])) + { + m_log.DebugFormat("[Groups]: Attempt at deleting Owners role from group {0} denied", groupID); + return; + } + + _RemoveGroupRole(groupID, roleID); + } + + public List GetGroupRoles(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + return _GetGroupRoles(GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID) + { + // TODO: check perms + + // Is the requester a member of the group? + bool isInGroup = false; + if (m_Database.RetrieveMember(GroupID, RequestingAgentID) != null) + isInGroup = true; + + return _GetGroupRoleMembers(GroupID, isInGroup); + } + + public bool AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string token, out string reason) + { + reason = string.Empty; + + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, token); + + return true; + } + + public bool RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // check perms + if (RequestingAgentID != AgentID && !HasPower(RequestingAgentID, GroupID, GroupPowers.Eject)) + return false; + + _RemoveAgentFromGroup(RequestingAgentID, AgentID, GroupID); + + return true; + } + + public bool AddAgentToGroupInvite(string RequestingAgentID, UUID inviteID, UUID groupID, UUID roleID, 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 permission to invite + if (!HasPower(RequestingAgentID, groupID, GroupPowers.Invite)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at inviting to group {1} denied because of lack of permission", RequestingAgentID, groupID); + 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 = roleID; + invite.Data = new Dictionary(); + + return m_Database.StoreInvitation(invite); + } + + public GroupInviteInfo GetAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + InvitationData data = m_Database.RetrieveInvitation(inviteID); + + if (data == null) + return null; + + GroupInviteInfo inviteInfo = new GroupInviteInfo(); + inviteInfo.AgentID = data.PrincipalID; + inviteInfo.GroupID = data.GroupID; + inviteInfo.InviteID = data.InviteID; + inviteInfo.RoleID = data.RoleID; + + return inviteInfo; + } + + public void RemoveAgentToGroupInvite(string RequestingAgentID, UUID inviteID) + { + m_Database.DeleteInvite(inviteID); + } + + public bool AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + //if (!m_Database.CheckOwnerRole(RequestingAgentID, GroupID, RoleID)) + // return; + + // check permissions + bool limited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMemberLimited); + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) | IsOwner(RequestingAgentID, GroupID); + if (!limited || !unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + // AssignMemberLimited means that the person can assign another person to the same roles that she has in the group + if (!unlimited && limited) + { + // check whether person's has this role + RoleMembershipData rolemembership = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (rolemembership == null) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at assigning {1} to role {2} denied because of limited permission", RequestingAgentID, AgentID, RoleID); + return false; + } + } + + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + return true; + } + + public bool RemoveAgentFromGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + // Don't remove from Everyone role! + if (RoleID == UUID.Zero) + return false; + + // check permissions + bool unlimited = HasPower(RequestingAgentID, GroupID, GroupPowers.AssignMember) || IsOwner(RequestingAgentID, GroupID); + if (!unlimited) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at removing {1} from role {2} denied because of lack of permission", RequestingAgentID, AgentID, RoleID); + return false; + } + + RoleMembershipData rolemember = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + + if (rolemember == null) + return false; + + m_Database.DeleteRoleMember(rolemember); + + // Find another role for this person + UUID newRoleID = UUID.Zero; // Everyone + RoleMembershipData[] rdata = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (rdata != null) + foreach (RoleMembershipData r in rdata) + { + if (r.RoleID != UUID.Zero) + { + newRoleID = r.RoleID; + break; + } + } + + MembershipData member = m_Database.RetrieveMember(GroupID, AgentID); + if (member != null) + { + member.Data["SelectedRoleID"] = newRoleID.ToString(); + m_Database.StoreMember(member); + } + + return true; + } + + public List GetAgentGroupRoles(string RequestingAgentID, string AgentID, UUID GroupID) + { + List roles = new List(); + // TODO: check permissions + + RoleMembershipData[] data = m_Database.RetrieveMemberRoles(GroupID, AgentID); + if (data == null || (data != null && data.Length ==0)) + return roles; + + foreach (RoleMembershipData d in data) + { + RoleData rdata = m_Database.RetrieveRole(GroupID, d.RoleID); + if (rdata == null) // hippos + continue; + + GroupRolesData r = new GroupRolesData(); + r.Name = rdata.Data["Name"]; + r.Powers = UInt64.Parse(rdata.Data["Powers"]); + r.RoleID = rdata.RoleID; + r.Title = rdata.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + public ExtendedGroupMembershipData SetAgentActiveGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // TODO: check perms + PrincipalData principal = new PrincipalData(); + principal.PrincipalID = AgentID; + principal.ActiveGroupID = GroupID; + m_Database.StorePrincipal(principal); + + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID); + } + + public ExtendedGroupMembershipData GetAgentActiveMembership(string RequestingAgentID, string AgentID) + { + // 1. get the principal data for the active group + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal == null) + return null; + + return GetAgentGroupMembership(RequestingAgentID, AgentID, principal.ActiveGroupID); + } + + public ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID) + { + return GetAgentGroupMembership(RequestingAgentID, AgentID, GroupID, null); + } + + private ExtendedGroupMembershipData GetAgentGroupMembership(string RequestingAgentID, string AgentID, UUID GroupID, MembershipData membership) + { + // 2. get the active group + GroupData group = m_Database.RetrieveGroup(GroupID); + if (group == null) + return null; + + // 3. get the membership info if we don't have it already + if (membership == null) + { + membership = m_Database.RetrieveMember(group.GroupID, AgentID); + if (membership == null) + return null; + } + + // 4. get the active role + UUID activeRoleID = new UUID(membership.Data["SelectedRoleID"]); + RoleData role = m_Database.RetrieveRole(group.GroupID, activeRoleID); + + ExtendedGroupMembershipData data = new ExtendedGroupMembershipData(); + data.AcceptNotices = membership.Data["AcceptNotices"] == "1" ? true : false; + data.AccessToken = membership.Data["AccessToken"]; + data.Active = true; + data.ActiveRole = activeRoleID; + data.AllowPublish = group.Data["AllowPublish"] == "1" ? true : false; + data.Charter = group.Data["Charter"]; + data.Contribution = Int32.Parse(membership.Data["Contribution"]); + data.FounderID = new UUID(group.Data["FounderID"]); + data.GroupID = new UUID(group.GroupID); + data.GroupName = group.Data["Name"]; + data.GroupPicture = new UUID(group.Data["InsigniaID"]); + if (role != null) + { + data.GroupPowers = UInt64.Parse(role.Data["Powers"]); + data.GroupTitle = role.Data["Title"]; + } + data.ListInProfile = membership.Data["ListInProfile"] == "1" ? true : false; + data.MaturePublish = group.Data["MaturePublish"] == "1" ? true : false; + data.MembershipFee = Int32.Parse(group.Data["MembershipFee"]); + data.OpenEnrollment = group.Data["OpenEnrollment"] == "1" ? true : false; + data.ShowInList = group.Data["ShowInList"] == "1" ? true : false; + + return data; + } + + public List GetAgentGroupMemberships(string RequestingAgentID, string AgentID) + { + List memberships = new List(); + + // 1. Get all the groups that this person is a member of + MembershipData[] mdata = m_Database.RetrieveMemberships(AgentID); + + if (mdata == null || (mdata != null && mdata.Length == 0)) + return memberships; + + foreach (MembershipData d in mdata) + { + GroupMembershipData gmember = GetAgentGroupMembership(RequestingAgentID, AgentID, d.GroupID, d); + if (gmember != null) + { + memberships.Add(gmember); + //m_log.DebugFormat("[XXX]: Member of {0} as {1}", gmember.GroupName, gmember.GroupTitle); + //Util.PrintCallStack(); + } + } + + return memberships; + } + + public void SetAgentActiveGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data == null) + return; + + data.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(data); + } + + public void UpdateMembership(string RequestingAgentID, string AgentID, UUID GroupID, bool AcceptNotices, bool ListInProfile) + { + // TODO: check perms + + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + return; + + membership.Data["AcceptNotices"] = AcceptNotices ? "1" : "0"; + membership.Data["ListInProfile"] = ListInProfile ? "1" : "0"; + + m_Database.StoreMember(membership); + } + + 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) + { + // Check perms + if (!HasPower(RequestingAgentID, groupID, GroupPowers.SendNotices)) + { + m_log.DebugFormat("[Groups]: ({0}) Attempt at sending notice to group {1} denied because of lack of permission", RequestingAgentID, groupID); + return false; + } + + return _AddNotice(groupID, noticeID, fromName, subject, message, hasAttachment, attType, attName, attItemID, attOwnerID); + } + + public GroupNoticeInfo GetGroupNotice(string RequestingAgentID, UUID noticeID) + { + NoticeData data = m_Database.RetrieveNotice(noticeID); + + if (data == null) + return null; + + return _NoticeDataToInfo(data); + } + + public List GetGroupNotices(string RequestingAgentID, UUID groupID) + { + NoticeData[] data = m_Database.RetrieveNotices(groupID); + List infos = new List(); + + if (data == null || (data != null && data.Length == 0)) + return infos; + + foreach (NoticeData d in data) + { + ExtendedGroupNoticeData info = _NoticeDataToData(d); + infos.Add(info); + } + + return infos; + } + + public void ResetAgentGroupChatSessions(string agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(string agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(string agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(string agentID, UUID groupID) + { + } + + #region Actions without permission checks + + protected void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + _AddAgentToGroup(RequestingAgentID, AgentID, GroupID, RoleID, string.Empty); + } + + protected void _RemoveAgentFromGroup(string RequestingAgentID, string AgentID, UUID GroupID) + { + // 1. Delete membership + m_Database.DeleteMember(GroupID, AgentID); + + // 2. Remove from rolememberships + m_Database.DeleteMemberAllRoles(GroupID, AgentID); + + // 3. if it was active group, inactivate it + PrincipalData principal = m_Database.RetrievePrincipal(AgentID); + if (principal != null && principal.ActiveGroupID == GroupID) + { + principal.ActiveGroupID = UUID.Zero; + m_Database.StorePrincipal(principal); + } + } + + protected void _AddAgentToGroup(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID, string accessToken) + { + // Check if it's already there + MembershipData data = m_Database.RetrieveMember(GroupID, AgentID); + if (data != null) + return; + + // Add the membership + data = new MembershipData(); + data.PrincipalID = AgentID; + data.GroupID = GroupID; + data.Data = new Dictionary(); + data.Data["SelectedRoleID"] = RoleID.ToString(); + data.Data["Contribution"] = "0"; + data.Data["ListInProfile"] = "1"; + data.Data["AcceptNotices"] = "1"; + data.Data["AccessToken"] = accessToken; + + m_Database.StoreMember(data); + + // Add principal to everyone role + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, UUID.Zero); + + // Add principal to role, if different from everyone role + if (RoleID != UUID.Zero) + _AddAgentToGroupRole(RequestingAgentID, AgentID, GroupID, RoleID); + + // Make thit this active group + PrincipalData pdata = new PrincipalData(); + pdata.PrincipalID = AgentID; + pdata.ActiveGroupID = GroupID; + m_Database.StorePrincipal(pdata); + + } + + protected bool _AddOrUpdateGroupRole(string RequestingAgentID, UUID groupID, UUID roleID, string name, string description, string title, ulong powers, bool add) + { + RoleData data = m_Database.RetrieveRole(groupID, roleID); + + if (add && data != null) // it already exists, can't create + { + m_log.DebugFormat("[Groups]: Group {0} already exists. Can't create it again", groupID); + return false; + } + + if (!add && data == null) // it deosn't exist, can't update + { + m_log.DebugFormat("[Groups]: Group {0} doesn't exist. Can't update it", groupID); + return false; + } + + if (add) + data = new RoleData(); + + data.GroupID = groupID; + data.RoleID = roleID; + data.Data = new Dictionary(); + data.Data["Name"] = name; + data.Data["Description"] = description; + data.Data["Title"] = title; + data.Data["Powers"] = powers.ToString(); + + return m_Database.StoreRole(data); + } + + protected void _RemoveGroupRole(UUID groupID, UUID roleID) + { + m_Database.DeleteRole(groupID, roleID); + } + + protected void _AddAgentToGroupRole(string RequestingAgentID, string AgentID, UUID GroupID, UUID RoleID) + { + RoleMembershipData data = m_Database.RetrieveRoleMember(GroupID, RoleID, AgentID); + if (data != null) + return; + + data = new RoleMembershipData(); + data.GroupID = GroupID; + data.PrincipalID = AgentID; + data.RoleID = RoleID; + m_Database.StoreRoleMember(data); + + // Make it the SelectedRoleID + MembershipData membership = m_Database.RetrieveMember(GroupID, AgentID); + if (membership == null) + { + m_log.DebugFormat("[Groups]: ({0}) No such member {0} in group {1}", AgentID, GroupID); + return; + } + + membership.Data["SelectedRoleID"] = RoleID.ToString(); + m_Database.StoreMember(membership); + + } + + protected List _GetGroupRoles(UUID groupID) + { + List roles = new List(); + + RoleData[] data = m_Database.RetrieveRoles(groupID); + + if (data == null || (data != null && data.Length == 0)) + return roles; + + foreach (RoleData d in data) + { + GroupRolesData r = new GroupRolesData(); + r.Description = d.Data["Description"]; + r.Members = m_Database.RoleMemberCount(groupID, d.RoleID); + r.Name = d.Data["Name"]; + r.Powers = UInt64.Parse(d.Data["Powers"]); + r.RoleID = d.RoleID; + r.Title = d.Data["Title"]; + + roles.Add(r); + } + + return roles; + } + + protected List _GetGroupRoleMembers(UUID GroupID, bool isInGroup) + { + List rmembers = new List(); + + RoleData[] rdata = new RoleData[0]; + if (!isInGroup) + { + rdata = m_Database.RetrieveRoles(GroupID); + if (rdata == null || (rdata != null && rdata.Length == 0)) + return rmembers; + } + List rlist = new List(rdata); + if (!isInGroup) + rlist = rlist.FindAll(r => (UInt64.Parse(r.Data["Powers"]) & (ulong)GroupPowers.MemberVisible) != 0); + + RoleMembershipData[] data = m_Database.RetrieveRolesMembers(GroupID); + + if (data == null || (data != null && data.Length == 0)) + return rmembers; + + foreach (RoleMembershipData d in data) + { + if (!isInGroup) + { + RoleData rd = rlist.Find(_r => _r.RoleID == d.RoleID); // visible role + if (rd == null) + continue; + } + + ExtendedGroupRoleMembersData r = new ExtendedGroupRoleMembersData(); + r.MemberID = d.PrincipalID; + r.RoleID = d.RoleID; + + rmembers.Add(r); + } + + return rmembers; + } + + protected bool _AddNotice(UUID groupID, UUID noticeID, string fromName, string subject, string message, + bool hasAttachment, byte attType, string attName, UUID attItemID, string attOwnerID) + { + NoticeData data = new NoticeData(); + data.GroupID = groupID; + data.NoticeID = noticeID; + data.Data = new Dictionary(); + data.Data["FromName"] = fromName; + data.Data["Subject"] = subject; + data.Data["Message"] = message; + data.Data["HasAttachment"] = hasAttachment ? "1" : "0"; + if (hasAttachment) + { + data.Data["AttachmentType"] = attType.ToString(); + data.Data["AttachmentName"] = attName; + data.Data["AttachmentItemID"] = attItemID.ToString(); + data.Data["AttachmentOwnerID"] = attOwnerID; + } + data.Data["TMStamp"] = ((uint)Util.UnixTimeSinceEpoch()).ToString(); + + return m_Database.StoreNotice(data); + } + + #endregion + + #region structure translations + ExtendedGroupRecord _GroupDataToRecord(GroupData data) + { + if (data == null) + return null; + + ExtendedGroupRecord rec = new ExtendedGroupRecord(); + rec.AllowPublish = data.Data["AllowPublish"] == "1" ? true : false; + rec.Charter = data.Data["Charter"]; + rec.FounderID = new UUID(data.Data["FounderID"]); + rec.GroupID = data.GroupID; + rec.GroupName = data.Data["Name"]; + rec.GroupPicture = new UUID(data.Data["InsigniaID"]); + rec.MaturePublish = data.Data["MaturePublish"] == "1" ? true : false; + rec.MembershipFee = Int32.Parse(data.Data["MembershipFee"]); + rec.OpenEnrollment = data.Data["OpenEnrollment"] == "1" ? true : false; + rec.OwnerRoleID = new UUID(data.Data["OwnerRoleID"]); + rec.ShowInList = data.Data["ShowInList"] == "1" ? true : false; + rec.ServiceLocation = data.Data["Location"]; + rec.MemberCount = m_Database.MemberCount(data.GroupID); + rec.RoleCount = m_Database.RoleCount(data.GroupID); + + return rec; + } + + GroupNoticeInfo _NoticeDataToInfo(NoticeData data) + { + GroupNoticeInfo notice = new GroupNoticeInfo(); + notice.GroupID = data.GroupID; + notice.Message = data.Data["Message"]; + notice.noticeData = _NoticeDataToData(data); + + return notice; + } + + ExtendedGroupNoticeData _NoticeDataToData(NoticeData data) + { + ExtendedGroupNoticeData notice = new ExtendedGroupNoticeData(); + notice.FromName = data.Data["FromName"]; + notice.NoticeID = data.NoticeID; + notice.Subject = data.Data["Subject"]; + notice.Timestamp = uint.Parse((string)data.Data["TMStamp"]); + notice.HasAttachment = data.Data["HasAttachment"] == "1" ? true : false; + if (notice.HasAttachment) + { + notice.AttachmentName = data.Data["AttachmentName"]; + notice.AttachmentItemID = new UUID(data.Data["AttachmentItemID"].ToString()); + notice.AttachmentType = byte.Parse(data.Data["AttachmentType"].ToString()); + notice.AttachmentOwnerID = data.Data["AttachmentOwnerID"].ToString(); + } + + + return notice; + } + + #endregion + + #region permissions + private bool HasPower(string agentID, UUID groupID, GroupPowers power) + { + RoleMembershipData[] rmembership = m_Database.RetrieveMemberRoles(groupID, agentID); + if (rmembership == null || (rmembership != null && rmembership.Length == 0)) + return false; + + foreach (RoleMembershipData rdata in rmembership) + { + RoleData role = m_Database.RetrieveRole(groupID, rdata.RoleID); + if ( (UInt64.Parse(role.Data["Powers"]) & (ulong)power) != 0 ) + return true; + } + return false; + } + + private bool IsOwner(string agentID, UUID groupID) + { + GroupData group = m_Database.RetrieveGroup(groupID); + if (group == null) + return false; + + RoleMembershipData rmembership = m_Database.RetrieveRoleMember(groupID, new UUID(group.Data["OwnerRoleID"]), agentID); + if (rmembership == null) + return false; + + return true; + } + #endregion + + } +} diff --git a/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs new file mode 100644 index 0000000000..8e237aa44b --- /dev/null +++ b/OpenSim/Addons/Groups/Service/GroupsServiceBase.cs @@ -0,0 +1,101 @@ +/* + * 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(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(dllName, new Object[] { connString, usersRealm }); + if (m_GridUserService == null) + throw new Exception("Could not find a storage inferface for the given users module " + dllName); + } + } +} diff --git a/OpenSim/Addons/Groups/Service/HGGroupsService.cs b/OpenSim/Addons/Groups/Service/HGGroupsService.cs new file mode 100644 index 0000000000..56e999bde4 --- /dev/null +++ b/OpenSim/Addons/Groups/Service/HGGroupsService.cs @@ -0,0 +1,361 @@ +/* + * 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(); + 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(); + 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 GetGroupMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List 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 GetGroupRoles(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + return GetGroupRoles(RequestingAgentID, GroupID); + } + + public List GetGroupRoleMembers(string RequestingAgentID, UUID GroupID, string token) + { + if (!VerifyToken(GroupID, RequestingAgentID, token)) + return new List(); + + List 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(); + + 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; + } + } +} diff --git a/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs new file mode 100644 index 0000000000..5340bcd094 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/OfflineIMRegionModule.cs @@ -0,0 +1,268 @@ +/* + * 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 m_SceneList = new List(); + 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(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(); + 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; + client.OnMuteListRequest -= OnMuteListRequest; + }); + } + + 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; + client.OnMuteListRequest += OnMuteListRequest; + } + + private void RetrieveInstantMessages(IClientAPI client) + { + m_log.DebugFormat("[OfflineIM.V2]: Retrieving stored messages for {0}", client.AgentId); + + List 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); + } + } + } + + // Apparently this is needed in order for the viewer to request the IMs. + private void OnMuteListRequest(IClientAPI client, uint crc) + { + m_log.DebugFormat("[OfflineIM.V2] Got mute list request for crc {0}", crc); + string filename = "mutes" + client.AgentId.ToString(); + + IXfer xfer = client.Scene.RequestModuleInterface(); + if (xfer != null) + { + xfer.AddNewFile(filename, new Byte[0]); + client.SendMuteListUpdate(filename); + } + } + + 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 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 + } +} + diff --git a/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3e993b4d62 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("0.8.2.*")] + +[assembly: Addin("OpenSim.OfflineIM", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs new file mode 100644 index 0000000000..047b8becf3 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRemoteConnector.cs @@ -0,0 +1,171 @@ +/* + * 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(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 GetMessages(UUID principalID) + { + List ims = new List(); + + Dictionary sendData = new Dictionary(); + sendData["PrincipalID"] = principalID; + Dictionary 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)ret["RESULT"]).Values) + { + GridInstantMessage m = OfflineIMDataUtils.GridInstantMessage((Dictionary)v); + ims.Add(m); + } + + return ims; + } + + public bool StoreMessage(GridInstantMessage im, out string reason) + { + reason = string.Empty; + Dictionary sendData = OfflineIMDataUtils.GridInstantMessage(im); + + Dictionary 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 sendData = new Dictionary(); + sendData["UserID"] = userID; + + MakeRequest("DELETE", sendData); + } + + #endregion + + + #region Make Request + + private Dictionary MakeRequest(string method, Dictionary sendData) + { + sendData["METHOD"] = method; + + string reply = string.Empty; + lock (m_Lock) + reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/offlineim", + ServerUtils.BuildQueryString(sendData), + m_Auth); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + #endregion + + } +} diff --git a/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs new file mode 100644 index 0000000000..b3673dab18 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Remote/OfflineIMServiceRobustConnector.cs @@ -0,0 +1,223 @@ +/* + * 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 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 request) + { + Dictionary result = new Dictionary(); + + 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 request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("PrincipalID")) + NullResult(result, "Bad network data"); + else + { + UUID principalID = new UUID(request["PrincipalID"].ToString()); + List ims = m_OfflineIMService.GetMessages(principalID); + + Dictionary dict = new Dictionary(); + 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 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 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 + } +} diff --git a/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs new file mode 100644 index 0000000000..02084ffbfe --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Service/OfflineIMService.cs @@ -0,0 +1,135 @@ +/* + * 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 GetMessages(UUID principalID) + { + List ims = new List(); + + 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(); + 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()); + } + + } +} diff --git a/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs b/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs new file mode 100644 index 0000000000..3376be4737 --- /dev/null +++ b/OpenSim/Addons/OfflineIM/Service/OfflineIMServiceBase.cs @@ -0,0 +1,83 @@ +/* + * 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 Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.OfflineIM +{ + public class OfflineIMServiceBase : ServiceBase + { + protected IOfflineIMData m_Database = null; + + public OfflineIMServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "im_offline"; + + // + // 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); + } + + // + // [Messaging] section overrides [DatabaseService], if it exists + // + IConfig imConfig = config.Configs["Messaging"]; + if (imConfig != null) + { + dllName = imConfig.GetString("StorageProvider", dllName); + connString = imConfig.GetString("ConnectionString", connString); + realm = imConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + } + } +} diff --git a/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs new file mode 100644 index 0000000000..74d9ae4bbf --- /dev/null +++ b/OpenSim/ApplicationPlugins/LoadRegions/LoadRegionsPlugin.cs @@ -0,0 +1,210 @@ +/* + * 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 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); + + public event NewRegionCreated OnNewRegionCreated; + private NewRegionCreated m_newRegionCreatedHandler; + + #region IApplicationPlugin Members + + // TODO: required by IPlugin, but likely not at all right + private string m_name = "LoadRegionsPlugin"; + private string m_version = "0.0"; + + public string Version + { + get { return m_version; } + } + + public string Name + { + get { return m_name; } + } + + protected OpenSimBase m_openSim; + + public void Initialise() + { + m_log.Error("[LOAD REGIONS PLUGIN]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + public void Initialise(OpenSimBase openSim) + { + m_openSim = openSim; + m_openSim.ApplicationRegistry.RegisterInterface(this); + } + + public void PostInitialise() + { + //m_log.Info("[LOADREGIONS]: Load Regions addin being initialised"); + + IRegionLoader regionLoader; + if (m_openSim.ConfigSource.Source.Configs["Startup"].GetString("region_info_source", "filesystem") == "filesystem") + { + m_log.Info("[LOAD REGIONS PLUGIN]: Loading region configurations from filesystem"); + regionLoader = new RegionLoaderFileSystem(); + } + else + { + m_log.Info("[LOAD REGIONS PLUGIN]: Loading region configurations from web"); + regionLoader = new RegionLoaderWebServer(); + } + + regionLoader.SetIniConfigSource(m_openSim.ConfigSource.Source); + 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("[LOADREGIONSPLUGIN]: AssetTransactionModule..."); +// m_openSim.ModuleLoader.LoadDefaultSharedModule(new AssetTransactionModule()); + m_log.Info("[LOAD REGIONS PLUGIN]: Done."); + + if (!CheckRegionsForSanity(regionsToLoad)) + { + m_log.Error("[LOAD REGIONS PLUGIN]: Halting startup due to conflicts in region configurations"); + Environment.Exit(1); + } + + List createdScenes = new List(); + + 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.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) + { + m_newRegionCreatedHandler(scene); + } + } + } + + public void Dispose() + { + } + + #endregion + + /// + /// Check that region configuration information makes sense. + /// + /// + /// True if we're sane, false if we're insane + private bool CheckRegionsForSanity(RegionInfo[] regions) + { + if (regions.Length == 0) + return true; + + foreach (RegionInfo region in regions) + { + if (region.RegionID == UUID.Zero) + { + m_log.ErrorFormat( + "[LOAD REGIONS PLUGIN]: Region {0} has invalid UUID {1}", + region.RegionName, region.RegionID); + return false; + } + } + + for (int i = 0; i < regions.Length - 1; i++) + { + for (int j = i + 1; j < regions.Length; j++) + { + if (regions[i].RegionID == regions[j].RegionID) + { + m_log.ErrorFormat( + "[LOAD REGIONS PLUGIN]: Regions {0} and {1} have the same UUID {2}", + regions[i].RegionName, regions[j].RegionName, regions[i].RegionID); + return false; + } + else if ( + regions[i].RegionLocX == regions[j].RegionLocX && regions[i].RegionLocY == regions[j].RegionLocY) + { + m_log.ErrorFormat( + "[LOAD REGIONS PLUGIN]: Regions {0} and {1} have the same grid location ({2}, {3})", + regions[i].RegionName, regions[j].RegionName, regions[i].RegionLocX, regions[i].RegionLocY); + return false; + } + else if (regions[i].InternalEndPoint.Port == regions[j].InternalEndPoint.Port) + { + m_log.ErrorFormat( + "[LOAD REGIONS PLUGIN]: Regions {0} and {1} have the same internal IP port {2}", + regions[i].RegionName, regions[j].RegionName, regions[i].InternalEndPoint.Port); + return false; + } + } + } + + return true; + } + } +} diff --git a/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6c3c3e3246 --- /dev/null +++ b/OpenSim/ApplicationPlugins/LoadRegions/Properties/AssemblyInfo.cs @@ -0,0 +1,69 @@ +/* + * 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.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 : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim")] +[assembly : AssemblyCopyright("Copyright OpenSimulator.org Developers 2007-2009")] +[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("45b979d9-d8d4-42fd-9780-fe9ac7e86cb4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("0.7.6.*")] + +[assembly : AssemblyVersion("0.8.2.*")] + +[assembly: Addin("OpenSim.ApplicationPlugins.LoadRegions", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)] diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..021209f56c --- /dev/null +++ b/OpenSim/ApplicationPlugins/RegionModulesController/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("0.8.2.*")] + +[assembly: Addin("OpenSim.ApplicationPlugins.RegionModulesController", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)] diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs new file mode 100644 index 0000000000..17edb67d18 --- /dev/null +++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs @@ -0,0 +1,533 @@ +/* + * 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 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 + { + // Logger + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Controls whether we load modules from Mono.Addins. + /// + /// For debug purposes. Defaults to true. + public bool LoadModulesFromAddins { get; set; } + + // Config access + private OpenSimBase m_openSim; + + // Our name + private string m_name; + + // Internal lists to collect information about modules present + private List m_nonSharedModules = + new List(); + private List m_sharedModules = + new List(); + + // List of shared module instances, for adding to Scenes + private List m_sharedInstances = + new List(); + + public RegionModulesControllerPlugin() + { + LoadModulesFromAddins = true; + } + +#region IApplicationPlugin implementation + + public void Initialise (OpenSimBase openSim) + { + m_openSim = openSim; + m_openSim.ApplicationRegistry.RegisterInterface(this); + m_log.DebugFormat("[REGIONMODULES]: Initializing..."); + + if (!LoadModulesFromAddins) + return; + + // Who we are + string id = AddinManager.CurrentAddin.Id; + + // Make friendly name + int pos = id.LastIndexOf("."); + if (pos == -1) + m_name = id; + else + m_name = id.Substring(pos + 1); + + // The [Modules] section in the ini file + IConfig modulesConfig = + m_openSim.ConfigSource.Source.Configs["Modules"]; + if (modulesConfig == null) + modulesConfig = m_openSim.ConfigSource.Source.AddConfig("Modules"); + + Dictionary> loadedModules = new Dictionary>(); + + // Scan modules and load all that aren't disabled + foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules")) + AddNode(node, modulesConfig, loadedModules); + + foreach (KeyValuePair> loadedModuleData in loadedModules) + { + 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]); + } + + // Load and init the module. We try a constructor with a port + // if a port was given, fall back to one without if there is + // no port or the more specific constructor fails. + // This will be removed, so that any module capable of using a port + // must provide a constructor with a port in the future. + // For now, we do this so migration is easy. + // + foreach (TypeExtensionNode node in m_sharedModules) + { + Object[] ctorArgs = new Object[] { (uint)0 }; + + // 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) + { + // Get the port number from the string + string[] moduleParts = moduleString.Split(new char[] { '/' }, + 2); + if (moduleParts.Length > 1) + ctorArgs[0] = Convert.ToUInt32(moduleParts[0]); + } + + // Try loading and initilaizing the module, using the + // port if appropriate + ISharedRegionModule module = null; + + try + { + module = (ISharedRegionModule)Activator.CreateInstance( + node.Type, ctorArgs); + } + catch + { + module = (ISharedRegionModule)Activator.CreateInstance( + node.Type); + } + + // OK, we're up and running + m_sharedInstances.Add(module); + module.Initialise(m_openSim.ConfigSource.Source); + } + } + + public void PostInitialise () + { + m_log.DebugFormat("[REGIONMODULES]: PostInitializing..."); + + // Immediately run PostInitialise on shared modules + foreach (ISharedRegionModule module in m_sharedInstances) + { + module.PostInitialise(); + } + } + +#endregion + +#region IPlugin implementation + + private void AddNode( + TypeExtensionNode node, IConfig modulesConfig, Dictionary> loadedModules) + { + IList loadedModuleData; + + if (!loadedModules.ContainsKey(node.Addin)) + loadedModules.Add(node.Addin, new List { 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 () + { + throw new System.NotImplementedException(); + } + +#endregion + +#region IDisposable implementation + + // Cleanup + // + public void Dispose () + { + // We expect that all regions have been removed already + while (m_sharedInstances.Count > 0) + { + m_sharedInstances[0].Close(); + m_sharedInstances.RemoveAt(0); + } + + m_sharedModules.Clear(); + m_nonSharedModules.Clear(); + } + +#endregion + + public string Version + { + get + { + return AddinManager.CurrentAddin.Version; + } + } + + public string Name + { + get + { + return m_name; + } + } + +#region Region Module interfacesController implementation + + /// + /// Check that the given module is no disabled in the [Modules] section of the config files. + /// + /// + /// The config section + /// true if the module is enabled, false if it is disabled + protected bool CheckModuleEnabled(TypeExtensionNode node, IConfig modulesConfig) + { + // Get the config string + string moduleString = + modulesConfig.GetString("Setup_" + node.Id, String.Empty); + + // We have a selector + if (moduleString != String.Empty) + { + // Allow disabling modules even if they don't have + // support for it + if (moduleString == "disabled") + return false; + + // Split off port, if present + string[] moduleParts = moduleString.Split(new char[] { '/' }, 2); + // Format is [port/][class] + string className = moduleParts[0]; + if (moduleParts.Length > 1) + className = moduleParts[1]; + + // Match the class name if given + 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 + // load. This means that here we deal with replaceable interfaces, + // nonshared modules, etc. + // + public void AddRegionToModules (Scene scene) + { + Dictionary deferredSharedModules = + new Dictionary(); + Dictionary deferredNonSharedModules = + new Dictionary(); + + // We need this to see if a module has already been loaded and + // has defined a replaceable interface. It's a generic call, + // so this can't be used directly. It will be used later + Type s = scene.GetType(); + MethodInfo mi = s.GetMethod("RequestModuleInterface"); + + // This will hold the shared modules we actually load + List sharedlist = + new List(); + + // Iterate over the shared modules that have been loaded + // Add them to the new Scene + foreach (ISharedRegionModule module in m_sharedInstances) + { + // Here is where we check if a replaceable interface + // is defined. If it is, the module is checked against + // the interfaces already defined. If the interface is + // defined, we simply skip the module. Else, if the module + // defines a replaceable interface, we add it to the deferred + // list. + Type replaceableInterface = module.ReplaceableInterface; + if (replaceableInterface != null) + { + MethodInfo mii = mi.MakeGenericMethod(replaceableInterface); + + if (mii.Invoke(scene, new object[0]) != null) + { + m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString()); + continue; + } + + deferredSharedModules[replaceableInterface] = module; + m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name); + continue; + } + + m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1}", + scene.RegionInfo.RegionName, module.Name); + + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + + sharedlist.Add(module); + } + + IConfig modulesConfig = + m_openSim.ConfigSource.Source.Configs["Modules"]; + + // Scan for, and load, nonshared modules + List list = new List(); + foreach (TypeExtensionNode node in m_nonSharedModules) + { + Object[] ctorArgs = new Object[] {0}; + + // Read the config + 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) + { + // Get the port number from the string + string[] moduleParts = moduleString.Split(new char[] {'/'}, + 2); + if (moduleParts.Length > 1) + ctorArgs[0] = Convert.ToUInt32(moduleParts[0]); + } + + // Actually load it + INonSharedRegionModule module = null; + + Type[] ctorParamTypes = new Type[ctorArgs.Length]; + for (int i = 0; i < ctorParamTypes.Length; i++) + ctorParamTypes[i] = ctorArgs[i].GetType(); + + if (node.Type.GetConstructor(ctorParamTypes) != null) + module = (INonSharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs); + else + module = (INonSharedRegionModule)Activator.CreateInstance(node.Type); + + // Check for replaceable interfaces + Type replaceableInterface = module.ReplaceableInterface; + if (replaceableInterface != null) + { + MethodInfo mii = mi.MakeGenericMethod(replaceableInterface); + + if (mii.Invoke(scene, new object[0]) != null) + { + m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString()); + continue; + } + + deferredNonSharedModules[replaceableInterface] = module; + m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name); + continue; + } + + m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1}", + scene.RegionInfo.RegionName, module.Name); + + // Initialise the module + module.Initialise(m_openSim.ConfigSource.Source); + + list.Add(module); + } + + // Now add the modules that we found to the scene. If a module + // wishes to override a replaceable interface, it needs to + // register it in Initialise, so that the deferred module + // won't load. + foreach (INonSharedRegionModule module in list) + { + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + } + + // Now all modules without a replaceable base interface are loaded + // Replaceable modules have either been skipped, or omitted. + // Now scan the deferred modules here + foreach (ISharedRegionModule module in deferredSharedModules.Values) + { + // Determine if the interface has been replaced + Type replaceableInterface = module.ReplaceableInterface; + MethodInfo mii = mi.MakeGenericMethod(replaceableInterface); + + if (mii.Invoke(scene, new object[0]) != null) + { + m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString()); + continue; + } + + m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1} (deferred)", + scene.RegionInfo.RegionName, module.Name); + + // Not replaced, load the module + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + + sharedlist.Add(module); + } + + // Same thing for nonshared modules, load them unless overridden + List deferredlist = + new List(); + + foreach (INonSharedRegionModule module in deferredNonSharedModules.Values) + { + // Check interface override + Type replaceableInterface = module.ReplaceableInterface; + if (replaceableInterface != null) + { + MethodInfo mii = mi.MakeGenericMethod(replaceableInterface); + + if (mii.Invoke(scene, new object[0]) != null) + { + m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString()); + continue; + } + } + + m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1} (deferred)", + scene.RegionInfo.RegionName, module.Name); + + module.Initialise(m_openSim.ConfigSource.Source); + + list.Add(module); + deferredlist.Add(module); + } + + // Finally, load valid deferred modules + foreach (INonSharedRegionModule module in deferredlist) + { + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + } + + // This is needed for all module types. Modules will register + // Interfaces with scene in AddScene, and will also need a means + // to access interfaces registered by other modules. Without + // this extra method, a module attempting to use another modules's + // interface would be successful only depending on load order, + // which can't be depended upon, or modules would need to resort + // to ugly kludges to attempt to request interfaces when needed + // and unneccessary caching logic repeated in all modules. + // The extra function stub is just that much cleaner + // + foreach (ISharedRegionModule module in sharedlist) + { + module.RegionLoaded(scene); + } + + foreach (INonSharedRegionModule module in list) + { + module.RegionLoaded(scene); + } + } + + public void RemoveRegionFromModules (Scene scene) + { + foreach (IRegionModuleBase module in scene.RegionModules.Values) + { + m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}", + scene.RegionInfo.RegionName, module.Name); + module.RemoveRegion(scene); + if (module is INonSharedRegionModule) + { + // as we were the only user, this instance has to die + module.Close(); + } + } + scene.RegionModules.Clear(); + } + +#endregion + + } +} diff --git a/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..443e3237d5 --- /dev/null +++ b/OpenSim/ApplicationPlugins/RemoteController/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +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("0.8.2.*")] + +[assembly: Addin("OpenSim.ApplicationPlugins.RemoteController", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim", OpenSim.VersionInfo.VersionNumber)] diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs new file mode 100644 index 0000000000..0228c3c690 --- /dev/null +++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs @@ -0,0 +1,3162 @@ +/* + * 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.Globalization; +using System.IO; +using System.Xml; +using System.Net; +using System.Reflection; +using System.Timers; +using System.Threading; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenMetaverse; +using Mono.Addins; +using OpenSim; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.CoreModules.World.Terrain; +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; +using PermissionMask = OpenSim.Framework.PermissionMask; +using RegionInfo = OpenSim.Framework.RegionInfo; + +namespace OpenSim.ApplicationPlugins.RemoteController +{ + [Extension(Path = "/OpenSim/Startup", Id = "LoadRegions", NodeName = "Plugin")] + public class RemoteAdminPlugin : IApplicationPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static bool m_defaultAvatarsLoaded = false; + private static Object m_requestLock = new Object(); + private static Object m_saveOarLock = new Object(); + + private OpenSimBase m_application; + private IHttpServer m_httpServer; + private IConfig m_config; + private IConfigSource m_configSource; + private string m_requiredPassword = String.Empty; + private HashSet m_accessIP; + + private string m_name = "RemoteAdminPlugin"; + private string m_version = "0.0"; + + public string Version + { + get { return m_version; } + } + + public string Name + { + get { return m_name; } + } + + public void Initialise() + { + m_log.Error("[RADMIN]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + public void Initialise(OpenSimBase openSim) + { + m_configSource = openSim.ConfigSource.Source; + try + { + if (m_configSource.Configs["RemoteAdmin"] == null || + !m_configSource.Configs["RemoteAdmin"].GetBoolean("enabled", false)) + { + // No config or disabled + } + else + { + m_config = m_configSource.Configs["RemoteAdmin"]; + m_log.Debug("[RADMIN]: Remote Admin Plugin Enabled"); + m_requiredPassword = m_config.GetString("access_password", String.Empty); + int port = m_config.GetInt("port", 0); + + string accessIP = m_config.GetString("access_ip_addresses", String.Empty); + m_accessIP = new HashSet(); + if (accessIP != String.Empty) + { + string[] ips = accessIP.Split(new char[] { ',' }); + foreach (string ip in ips) + { + string current = ip.Trim(); + + if (current != String.Empty) + m_accessIP.Add(current); + } + } + + m_application = openSim; + string bind_ip_address = m_config.GetString("bind_ip_address", "0.0.0.0"); + IPAddress ipaddr = IPAddress.Parse(bind_ip_address); + m_httpServer = MainServer.GetHttpServer((uint)port,ipaddr); + + Dictionary availableMethods = new Dictionary(); + availableMethods["admin_create_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCreateRegionMethod); + availableMethods["admin_delete_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcDeleteRegionMethod); + availableMethods["admin_close_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCloseRegionMethod); + availableMethods["admin_modify_region"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcModifyRegionMethod); + availableMethods["admin_region_query"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcRegionQueryMethod); + availableMethods["admin_shutdown"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcShutdownMethod); + availableMethods["admin_broadcast"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAlertMethod); + availableMethods["admin_restart"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcRestartMethod); + availableMethods["admin_load_heightmap"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcLoadHeightmapMethod); + availableMethods["admin_save_heightmap"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcSaveHeightmapMethod); + + // Agent management + availableMethods["admin_get_agents"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcGetAgentsMethod); + availableMethods["admin_teleport_agent"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcTeleportAgentMethod); + + // User management + availableMethods["admin_create_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCreateUserMethod); + availableMethods["admin_create_user_email"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcCreateUserMethod); + availableMethods["admin_exists_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcUserExistsMethod); + availableMethods["admin_update_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcUpdateUserAccountMethod); + availableMethods["admin_authenticate_user"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAuthenticateUserMethod); + + // Region state management + availableMethods["admin_load_xml"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcLoadXMLMethod); + availableMethods["admin_save_xml"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcSaveXMLMethod); + availableMethods["admin_load_oar"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcLoadOARMethod); + availableMethods["admin_save_oar"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcSaveOARMethod); + + // Estate access list management + availableMethods["admin_acl_clear"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListClear); + availableMethods["admin_acl_add"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListAdd); + availableMethods["admin_acl_remove"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListRemove); + availableMethods["admin_acl_list"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcAccessListList); + availableMethods["admin_estate_reload"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcEstateReload); + + // Land management + availableMethods["admin_reset_land"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcResetLand); + + // Either enable full remote functionality or just selected features + string enabledMethods = m_config.GetString("enabled_methods", "all"); + + // To get this, you must explicitly specify "all" or + // mention it in a whitelist. It won't be available + // If you just leave the option out! + // + if (!String.IsNullOrEmpty(enabledMethods)) + availableMethods["admin_console_command"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcConsoleCommandMethod); + + // The assumption here is that simply enabling Remote Admin as before will produce the same + // behavior - enable all methods unless the whitelist is in place for backward-compatibility. + if (enabledMethods.ToLower() == "all" || String.IsNullOrEmpty(enabledMethods)) + { + foreach (string method in availableMethods.Keys) + { + m_httpServer.AddXmlRPCHandler(method, availableMethods[method], false); + } + } + else + { + foreach (string enabledMethod in enabledMethods.Split('|')) + { + m_httpServer.AddXmlRPCHandler(enabledMethod, availableMethods[enabledMethod], false); + } + } + } + } + catch (NullReferenceException) + { + // Ignore. + } + } + + public void PostInitialise() + { + if (!CreateDefaultAvatars()) + { + m_log.Info("[RADMIN]: Default avatars not loaded"); + } + } + + /// + /// Invoke an XmlRpc method with the standard actions (password check, etc.) + /// + /// + private XmlRpcResponse InvokeXmlRpcMethod( + XmlRpcRequest request, IPEndPoint remoteClient, Action method) + { + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable responseData = new Hashtable(); + response.Value = responseData; + + try + { + Hashtable requestData = (Hashtable) request.Params[0]; + + CheckStringParameters(requestData, responseData, new string[] {"password"}); + + FailIfRemoteAdminNotAllowed((string)requestData["password"], responseData, remoteClient.Address.ToString()); + + method(request, response, remoteClient); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[RADMIN]: Method {0} failed. Exception {1}{2}", request.MethodName, e.Message, e.StackTrace); + + responseData["success"] = false; + responseData["error"] = e.Message; + } + + return response; + } + + private void FailIfRemoteAdminNotAllowed(string password, Hashtable responseData, string check_ip_address) + { + if (m_accessIP.Count > 0 && !m_accessIP.Contains(check_ip_address)) + { + m_log.WarnFormat("[RADMIN]: Unauthorized access blocked from IP {0}", check_ip_address); + responseData["accepted"] = false; + throw new Exception("not authorized"); + } + + if (m_requiredPassword != String.Empty && password != m_requiredPassword) + { + m_log.WarnFormat("[RADMIN]: Wrong password, blocked access from IP {0}", check_ip_address); + responseData["accepted"] = false; + throw new Exception("wrong password"); + } + } + + private void XmlRpcRestartMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + try + { + m_log.Info("[RADMIN]: Request to restart Region."); + + CheckRegionParams(requestData, responseData); + + Scene rebootedScene = null; + GetSceneFromRegionParams(requestData, responseData, out rebootedScene); + + responseData["success"] = false; + responseData["accepted"] = true; + responseData["rebooting"] = true; + + IRestartModule restartModule = rebootedScene.RequestModuleInterface(); + if (restartModule != null) + { + List times = new List { 30, 15 }; + + restartModule.ScheduleRestart(UUID.Zero, "Region will restart in {0}", times.ToArray(), true); + responseData["success"] = true; + } + } + catch (Exception e) + { +// m_log.ErrorFormat("[RADMIN]: Restart region: failed: {0} {1}", e.Message, e.StackTrace); + responseData["rebooting"] = false; + + throw e; + } + + m_log.Info("[RADMIN]: Restart Region request complete"); + } + + private void XmlRpcAlertMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Alert request started"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + string message = (string) requestData["message"]; + m_log.InfoFormat("[RADMIN]: Broadcasting: {0}", message); + + responseData["accepted"] = true; + responseData["success"] = true; + + m_application.SceneManager.ForEachScene( + delegate(Scene scene) + { + IDialogModule dialogModule = scene.RequestModuleInterface(); + if (dialogModule != null) + dialogModule.SendGeneralAlert(message); + }); + + m_log.Info("[RADMIN]: Alert request complete"); + } + + private void XmlRpcLoadHeightmapMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Load height maps request started"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + +// m_log.DebugFormat("[RADMIN]: Load Terrain: XmlRpc {0}", request); + // foreach (string k in requestData.Keys) + // { + // m_log.DebugFormat("[RADMIN]: Load Terrain: XmlRpc {0}: >{1}< {2}", + // k, (string)requestData[k], ((string)requestData[k]).Length); + // } + + CheckStringParameters(requestData, responseData, new string[] { "filename" }); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + if (scene != null) + { + string file = (string)requestData["filename"]; + + responseData["accepted"] = true; + + LoadHeightmap(file, scene.RegionInfo.RegionID); + + responseData["success"] = true; + } + else + { + responseData["success"] = false; + } + + m_log.Info("[RADMIN]: Load height maps request complete"); + } + + private void XmlRpcSaveHeightmapMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Save height maps request started"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + +// m_log.DebugFormat("[RADMIN]: Save Terrain: XmlRpc {0}", request.ToString()); + + CheckStringParameters(requestData, responseData, new string[] { "filename" }); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + if (scene != null) + { + string file = (string)requestData["filename"]; + m_log.InfoFormat("[RADMIN]: Terrain Saving: {0}", file); + + responseData["accepted"] = true; + + ITerrainModule terrainModule = scene.RequestModuleInterface(); + if (null == terrainModule) throw new Exception("terrain module not available"); + + terrainModule.SaveToFile(file); + + responseData["success"] = true; + } + else + { + responseData["success"] = false; + } + + m_log.Info("[RADMIN]: Save height maps request complete"); + } + + private void XmlRpcShutdownMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Shutdown Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + responseData["accepted"] = true; + response.Value = responseData; + + int timeout = 2000; + string message; + + if (requestData.ContainsKey("shutdown") + && ((string) requestData["shutdown"] == "delayed") + && requestData.ContainsKey("milliseconds")) + { + timeout = Int32.Parse(requestData["milliseconds"].ToString()); + + message + = "Region is going down in " + ((int) (timeout/1000)).ToString() + + " second(s). Please save what you are doing and log out."; + } + else + { + message = "Region is going down now."; + } + + m_application.SceneManager.ForEachScene( + delegate(Scene scene) + { + IDialogModule dialogModule = scene.RequestModuleInterface(); + if (dialogModule != null) + dialogModule.SendGeneralAlert(message); + }); + + // Perform shutdown + System.Timers.Timer shutdownTimer = new System.Timers.Timer(timeout); // Wait before firing + shutdownTimer.AutoReset = false; + shutdownTimer.Elapsed += new ElapsedEventHandler(shutdownTimer_Elapsed); + lock (shutdownTimer) + { + shutdownTimer.Start(); + } + + responseData["success"] = true; + + m_log.Info("[RADMIN]: Shutdown Administrator Request complete"); + } + + private void shutdownTimer_Elapsed(object sender, ElapsedEventArgs e) + { + m_application.Shutdown(); + } + + /// + /// Create a new region. + /// + /// incoming XML RPC request + /// + /// XmlRpcCreateRegionMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// region_name + /// desired region name + /// region_id + /// (optional) desired region UUID + /// region_x + /// desired region X coordinate (integer) + /// region_y + /// desired region Y coordinate (integer) + /// estate_owner_first + /// firstname of estate owner (formerly region master) + /// (required if new estate is being created, optional otherwise) + /// estate_owner_last + /// lastname of estate owner (formerly region master) + /// (required if new estate is being created, optional otherwise) + /// estate_owner_uuid + /// explicit UUID to use for estate owner (optional) + /// listen_ip + /// internal IP address (dotted quad) + /// listen_port + /// internal port (integer) + /// external_address + /// external IP address + /// persist + /// if true, persist the region info + /// ('true' or 'false') + /// public + /// if true, the region is public + /// ('true' or 'false') (optional, default: true) + /// enable_voice + /// if true, enable voice on all parcels, + /// ('true' or 'false') (optional, default: false) + /// estate_name + /// the name of the estate to join (or to create if it doesn't + /// already exist) + /// region_file + /// The name of the file to persist the region specifications to. + /// If omitted, the region_file_template setting from OpenSim.ini will be used. (optional) + /// + /// + /// XmlRpcCreateRegionMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// region_uuid + /// UUID of the newly created region + /// region_name + /// name of the newly created region + /// + /// + private void XmlRpcCreateRegionMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: CreateRegion: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + int m_regionLimit = m_config.GetInt("region_limit", 0); + bool m_enableVoiceForNewRegions = m_config.GetBoolean("create_region_enable_voice", false); + bool m_publicAccess = m_config.GetBoolean("create_region_public", true); + + CheckStringParameters(requestData, responseData, new string[] + { + "region_name", + "listen_ip", "external_address", + "estate_name" + }); + CheckIntegerParams(requestData, responseData, new string[] {"region_x", "region_y", "listen_port"}); + + // check whether we still have space left (iff we are using limits) + if (m_regionLimit != 0 && m_application.SceneManager.Scenes.Count >= m_regionLimit) + throw new Exception(String.Format("cannot instantiate new region, server capacity {0} already reached; delete regions first", + m_regionLimit)); + // extract or generate region ID now + Scene scene = null; + UUID regionID = UUID.Zero; + if (requestData.ContainsKey("region_id") && + !String.IsNullOrEmpty((string) requestData["region_id"])) + { + regionID = (UUID) (string) requestData["region_id"]; + if (m_application.SceneManager.TryGetScene(regionID, out scene)) + throw new Exception( + String.Format("region UUID already in use by region {0}, UUID {1}, <{2},{3}>", + scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY)); + } + else + { + regionID = UUID.Random(); + m_log.DebugFormat("[RADMIN] CreateRegion: new region UUID {0}", regionID); + } + + // create volatile or persistent region info + RegionInfo region = new RegionInfo(); + + region.RegionID = regionID; + region.originRegionID = regionID; + region.RegionName = (string) requestData["region_name"]; + region.RegionLocX = Convert.ToUInt32(requestData["region_x"]); + region.RegionLocY = Convert.ToUInt32(requestData["region_y"]); + + // check for collisions: region name, region UUID, + // region location + if (m_application.SceneManager.TryGetScene(region.RegionName, out scene)) + throw new Exception( + String.Format("region name already in use by region {0}, UUID {1}, <{2},{3}>", + scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY)); + + if (m_application.SceneManager.TryGetScene(region.RegionLocX, region.RegionLocY, out scene)) + throw new Exception( + String.Format("region location <{0},{1}> already in use by region {2}, UUID {3}, <{4},{5}>", + region.RegionLocX, region.RegionLocY, + scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY)); + + region.InternalEndPoint = + new IPEndPoint(IPAddress.Parse((string) requestData["listen_ip"]), 0); + + region.InternalEndPoint.Port = Convert.ToInt32(requestData["listen_port"]); + if (0 == region.InternalEndPoint.Port) throw new Exception("listen_port is 0"); + if (m_application.SceneManager.TryGetScene(region.InternalEndPoint, out scene)) + throw new Exception( + String.Format( + "region internal IP {0} and port {1} already in use by region {2}, UUID {3}, <{4},{5}>", + region.InternalEndPoint.Address, + region.InternalEndPoint.Port, + scene.RegionInfo.RegionName, scene.RegionInfo.RegionID, + scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY)); + + region.ExternalHostName = (string) requestData["external_address"]; + + bool persist = Convert.ToBoolean(requestData["persist"]); + if (persist) + { + // default place for region configuration files is in the + // Regions directory of the config dir (aka /bin) + string regionConfigPath = Path.Combine(Util.configDir(), "Regions"); + try + { + // OpenSim.ini can specify a different regions dir + IConfig startupConfig = (IConfig) m_configSource.Configs["Startup"]; + regionConfigPath = startupConfig.GetString("regionload_regionsdir", regionConfigPath).Trim(); + } + catch (Exception) + { + // No INI setting recorded. + } + + string regionIniPath; + + if (requestData.Contains("region_file")) + { + // Make sure that the file to be created is in a subdirectory of the region storage directory. + string requestedFilePath = Path.Combine(regionConfigPath, (string) requestData["region_file"]); + string requestedDirectory = Path.GetDirectoryName(Path.GetFullPath(requestedFilePath)); + if (requestedDirectory.StartsWith(Path.GetFullPath(regionConfigPath))) + regionIniPath = requestedFilePath; + else + throw new Exception("Invalid location for region file."); + } + else + { + regionIniPath = Path.Combine(regionConfigPath, + String.Format( + m_config.GetString("region_file_template", + "{0}x{1}-{2}.ini"), + region.RegionLocX.ToString(), + region.RegionLocY.ToString(), + regionID.ToString(), + region.InternalEndPoint.Port.ToString(), + region.RegionName.Replace(" ", "_").Replace(":", "_"). + Replace("/", "_"))); + } + + m_log.DebugFormat("[RADMIN] CreateRegion: persisting region {0} to {1}", + region.RegionID, regionIniPath); + region.SaveRegionToFile("dynamic region", regionIniPath); + } + else + { + region.Persistent = false; + } + + // Set the estate + + // Check for an existing estate + List estateIDs = m_application.EstateDataService.GetEstates((string) requestData["estate_name"]); + if (estateIDs.Count < 1) + { + UUID userID = UUID.Zero; + if (requestData.ContainsKey("estate_owner_uuid")) + { + // ok, client wants us to use an explicit UUID + // regardless of what the avatar name provided + userID = new UUID((string) requestData["estate_owner_uuid"]); + + // Check that the specified user exists + Scene currentOrFirst = m_application.SceneManager.CurrentOrFirstScene; + IUserAccountService accountService = currentOrFirst.UserAccountService; + UserAccount user = accountService.GetUserAccount(currentOrFirst.RegionInfo.ScopeID, userID); + + if (user == null) + throw new Exception("Specified user was not found."); + } + else if (requestData.ContainsKey("estate_owner_first") & requestData.ContainsKey("estate_owner_last")) + { + // We need to look up the UUID for the avatar with the provided name. + string ownerFirst = (string) requestData["estate_owner_first"]; + string ownerLast = (string) requestData["estate_owner_last"]; + + Scene currentOrFirst = m_application.SceneManager.CurrentOrFirstScene; + IUserAccountService accountService = currentOrFirst.UserAccountService; + UserAccount user = accountService.GetUserAccount(currentOrFirst.RegionInfo.ScopeID, + ownerFirst, ownerLast); + + // Check that the specified user exists + if (user == null) + throw new Exception("Specified user was not found."); + + userID = user.PrincipalID; + } + else + { + throw new Exception("Estate owner details not provided."); + } + + // Create a new estate with the name provided + region.EstateSettings = m_application.EstateDataService.CreateNewEstate(); + + region.EstateSettings.EstateName = (string) requestData["estate_name"]; + region.EstateSettings.EstateOwner = userID; + // Persistence does not seem to effect the need to save a new estate + m_application.EstateDataService.StoreEstateSettings(region.EstateSettings); + + if (!m_application.EstateDataService.LinkRegion(region.RegionID, (int) region.EstateSettings.EstateID)) + throw new Exception("Failed to join estate."); + } + else + { + int estateID = estateIDs[0]; + + region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(region.RegionID, false); + + if (region.EstateSettings.EstateID != estateID) + { + // The region is already part of an estate, but not the one we want. + region.EstateSettings = m_application.EstateDataService.LoadEstateSettings(estateID); + + if (!m_application.EstateDataService.LinkRegion(region.RegionID, estateID)) + throw new Exception("Failed to join estate."); + } + } + + // Create the region and perform any initial initialization + + IScene newScene; + m_application.CreateRegion(region, out newScene); + newScene.Start(); + + // If an access specification was provided, use it. + // Otherwise accept the default. + newScene.RegionInfo.EstateSettings.PublicAccess = GetBoolean(requestData, "public", m_publicAccess); + m_application.EstateDataService.StoreEstateSettings(newScene.RegionInfo.EstateSettings); + + // enable voice on newly created region if + // requested by either the XmlRpc request or the + // configuration + if (GetBoolean(requestData, "enable_voice", m_enableVoiceForNewRegions)) + { + List parcels = ((Scene)newScene).LandChannel.AllParcels(); + + foreach (ILandObject parcel in parcels) + { + parcel.LandData.Flags |= (uint) ParcelFlags.AllowVoiceChat; + parcel.LandData.Flags |= (uint) ParcelFlags.UseEstateVoiceChan; + ((Scene)newScene).LandChannel.UpdateLandObject(parcel.LandData.LocalID, parcel.LandData); + } + } + + //Load Heightmap if specified to new region + if (requestData.Contains("heightmap_file")) + { + LoadHeightmap((string)requestData["heightmap_file"], region.RegionID); + } + + responseData["success"] = true; + responseData["region_name"] = region.RegionName; + responseData["region_id"] = region.RegionID.ToString(); + + m_log.Info("[RADMIN]: CreateRegion: request complete"); + } + } + + /// + /// Delete a new region. + /// + /// incoming XML RPC request + /// + /// XmlRpcDeleteRegionMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// region_name + /// desired region name + /// region_id + /// (optional) desired region UUID + /// + /// + /// XmlRpcDeleteRegionMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// + /// + private void XmlRpcDeleteRegionMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: DeleteRegion: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + CheckStringParameters(requestData, responseData, new string[] {"region_name"}); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + m_application.RemoveRegion(scene, true); + + responseData["success"] = true; + responseData["region_name"] = scene.RegionInfo.RegionName; + responseData["region_id"] = scene.RegionInfo.RegionID; + + m_log.Info("[RADMIN]: DeleteRegion: request complete"); + } + } + + /// + /// Close a region. + /// + /// incoming XML RPC request + /// + /// XmlRpcCloseRegionMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// region_name + /// desired region name + /// region_id + /// (optional) desired region UUID + /// + /// + /// XmlRpcShutdownRegionMethod returns + /// + /// namedescription + /// success + /// true or false + /// region_name + /// the region name if success is true + /// error + /// error message if success is false + /// + /// + private void XmlRpcCloseRegionMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: CloseRegion: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + m_application.CloseRegion(scene); + + responseData["success"] = true; + responseData["region_name"] = scene.RegionInfo.RegionName; + responseData["region_id"] = scene.RegionInfo.RegionID; + + response.Value = responseData; + + m_log.Info("[RADMIN]: CloseRegion: request complete"); + } + } + + /// + /// Change characteristics of an existing region. + /// + /// incoming XML RPC request + /// + /// XmlRpcModifyRegionMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// region_name + /// desired region name + /// region_id + /// (optional) desired region UUID + /// public + /// if true, set the region to public + /// ('true' or 'false'), else to private + /// enable_voice + /// if true, enable voice on all parcels of + /// the region, else disable + /// + /// + /// XmlRpcModifyRegionMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// + /// + private void XmlRpcModifyRegionMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: ModifyRegion: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + // Modify access + scene.RegionInfo.EstateSettings.PublicAccess = + GetBoolean(requestData,"public", scene.RegionInfo.EstateSettings.PublicAccess); + if (scene.RegionInfo.Persistent) + m_application.EstateDataService.StoreEstateSettings(scene.RegionInfo.EstateSettings); + + if (requestData.ContainsKey("enable_voice")) + { + bool enableVoice = GetBoolean(requestData, "enable_voice", true); + List parcels = ((Scene)scene).LandChannel.AllParcels(); + + foreach (ILandObject parcel in parcels) + { + if (enableVoice) + { + parcel.LandData.Flags |= (uint)ParcelFlags.AllowVoiceChat; + parcel.LandData.Flags |= (uint)ParcelFlags.UseEstateVoiceChan; + } + else + { + parcel.LandData.Flags &= ~(uint)ParcelFlags.AllowVoiceChat; + parcel.LandData.Flags &= ~(uint)ParcelFlags.UseEstateVoiceChan; + } + scene.LandChannel.UpdateLandObject(parcel.LandData.LocalID, parcel.LandData); + } + } + + responseData["success"] = true; + responseData["region_name"] = scene.RegionInfo.RegionName; + responseData["region_id"] = scene.RegionInfo.RegionID; + + m_log.Info("[RADMIN]: ModifyRegion: request complete"); + } + } + + /// + /// Create a new user account. + /// + /// incoming XML RPC request + /// + /// XmlRpcCreateUserMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// user_firstname + /// avatar's first name + /// user_lastname + /// avatar's last name + /// user_password + /// avatar's password + /// user_email + /// email of the avatar's owner (optional) + /// start_region_x + /// avatar's start region coordinates, X value + /// start_region_y + /// avatar's start region coordinates, Y value + /// + /// + /// XmlRpcCreateUserMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// avatar_uuid + /// UUID of the newly created avatar + /// account; UUID.Zero if failed. + /// + /// + /// + private void XmlRpcCreateUserMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: CreateUser: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + // check completeness + CheckStringParameters(requestData, responseData, new string[] + { + "user_firstname", + "user_lastname", "user_password", + }); + CheckIntegerParams(requestData, responseData, new string[] {"start_region_x", "start_region_y"}); + + // do the job + string firstName = (string) requestData["user_firstname"]; + string lastName = (string) requestData["user_lastname"]; + string password = (string) requestData["user_password"]; + + uint regionXLocation = Convert.ToUInt32(requestData["start_region_x"]); + uint regionYLocation = Convert.ToUInt32(requestData["start_region_y"]); + + string email = ""; // empty string for email + if (requestData.Contains("user_email")) + email = (string)requestData["user_email"]; + + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + UUID scopeID = scene.RegionInfo.ScopeID; + + UserAccount account = CreateUser(scopeID, firstName, lastName, password, email); + + if (null == account) + throw new Exception(String.Format("failed to create new user {0} {1}", + firstName, lastName)); + + // Set home position + + GridRegion home = scene.GridService.GetRegionByPosition(scopeID, + (int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation)); + if (null == home) + { + m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", firstName, lastName); + } + else + { + scene.GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + m_log.DebugFormat("[RADMIN]: Set home region {0} for updated user account {1} {2}", home.RegionID, firstName, lastName); + } + + // Establish the avatar's initial appearance + + UpdateUserAppearance(responseData, requestData, account.PrincipalID); + + responseData["success"] = true; + responseData["avatar_uuid"] = account.PrincipalID.ToString(); + + m_log.InfoFormat("[RADMIN]: CreateUser: User {0} {1} created, UUID {2}", firstName, lastName, account.PrincipalID); + } + catch (Exception e) + { + responseData["avatar_uuid"] = UUID.Zero.ToString(); + + throw e; + } + + m_log.Info("[RADMIN]: CreateUser: request complete"); + } + } + + /// + /// Check whether a certain user account exists. + /// + /// incoming XML RPC request + /// + /// XmlRpcUserExistsMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// user_firstname + /// avatar's first name + /// user_lastname + /// avatar's last name + /// + /// + /// XmlRpcCreateUserMethod returns + /// + /// namedescription + /// user_firstname + /// avatar's first name + /// user_lastname + /// avatar's last name + /// user_lastlogin + /// avatar's last login time (secs since UNIX epoch) + /// success + /// true or false + /// error + /// error message if success is false + /// + /// + private void XmlRpcUserExistsMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: UserExists: new request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + // check completeness + CheckStringParameters(requestData, responseData, new string[] {"user_firstname", "user_lastname"}); + + string firstName = (string) requestData["user_firstname"]; + string lastName = (string) requestData["user_lastname"]; + + responseData["user_firstname"] = firstName; + responseData["user_lastname"] = lastName; + + UUID scopeID = m_application.SceneManager.CurrentOrFirstScene.RegionInfo.ScopeID; + + UserAccount account = m_application.SceneManager.CurrentOrFirstScene.UserAccountService.GetUserAccount(scopeID, firstName, lastName); + + if (null == account) + { + responseData["success"] = false; + responseData["lastlogin"] = 0; + } + else + { + GridUserInfo userInfo = m_application.SceneManager.CurrentOrFirstScene.GridUserService.GetGridUserInfo(account.PrincipalID.ToString()); + if (userInfo != null) + responseData["lastlogin"] = Util.ToUnixTime(userInfo.Login); + else + responseData["lastlogin"] = 0; + + responseData["success"] = true; + } + + m_log.Info("[RADMIN]: UserExists: request complete"); + } + + /// + /// Update a user account. + /// + /// incoming XML RPC request + /// + /// XmlRpcUpdateUserAccountMethod takes the following XMLRPC + /// parameters (changeable ones are optional) + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// user_firstname + /// avatar's first name (cannot be changed) + /// user_lastname + /// avatar's last name (cannot be changed) + /// user_password + /// avatar's password (changeable) + /// start_region_x + /// avatar's start region coordinates, X + /// value (changeable) + /// start_region_y + /// avatar's start region coordinates, Y + /// value (changeable) + /// about_real_world (not implemented yet) + /// "about" text of avatar owner (changeable) + /// about_virtual_world (not implemented yet) + /// "about" text of avatar (changeable) + /// + /// + /// XmlRpcCreateUserMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// avatar_uuid + /// UUID of the updated avatar + /// account; UUID.Zero if failed. + /// + /// + /// + private void XmlRpcUpdateUserAccountMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: UpdateUserAccount: new request"); + m_log.Warn("[RADMIN]: This method needs update for 0.7"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + // check completeness + CheckStringParameters(requestData, responseData, new string[] { + "user_firstname", + "user_lastname"}); + + // do the job + string firstName = (string) requestData["user_firstname"]; + string lastName = (string) requestData["user_lastname"]; + + string password = String.Empty; + uint? regionXLocation = null; + uint? regionYLocation = null; + // uint? ulaX = null; + // uint? ulaY = null; + // uint? ulaZ = null; + // uint? usaX = null; + // uint? usaY = null; + // uint? usaZ = null; + // string aboutFirstLive = String.Empty; + // string aboutAvatar = String.Empty; + + if (requestData.ContainsKey("user_password")) password = (string) requestData["user_password"]; + if (requestData.ContainsKey("start_region_x")) + regionXLocation = Convert.ToUInt32(requestData["start_region_x"]); + if (requestData.ContainsKey("start_region_y")) + regionYLocation = Convert.ToUInt32(requestData["start_region_y"]); + + // if (requestData.ContainsKey("start_lookat_x")) + // ulaX = Convert.ToUInt32((Int32) requestData["start_lookat_x"]); + // if (requestData.ContainsKey("start_lookat_y")) + // ulaY = Convert.ToUInt32((Int32) requestData["start_lookat_y"]); + // if (requestData.ContainsKey("start_lookat_z")) + // ulaZ = Convert.ToUInt32((Int32) requestData["start_lookat_z"]); + + // if (requestData.ContainsKey("start_standat_x")) + // usaX = Convert.ToUInt32((Int32) requestData["start_standat_x"]); + // if (requestData.ContainsKey("start_standat_y")) + // usaY = Convert.ToUInt32((Int32) requestData["start_standat_y"]); + // if (requestData.ContainsKey("start_standat_z")) + // usaZ = Convert.ToUInt32((Int32) requestData["start_standat_z"]); + // if (requestData.ContainsKey("about_real_world")) + // aboutFirstLive = (string)requestData["about_real_world"]; + // if (requestData.ContainsKey("about_virtual_world")) + // aboutAvatar = (string)requestData["about_virtual_world"]; + + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + UUID scopeID = scene.RegionInfo.ScopeID; + UserAccount account = scene.UserAccountService.GetUserAccount(scopeID, firstName, lastName); + + if (null == account) + throw new Exception(String.Format("avatar {0} {1} does not exist", firstName, lastName)); + + if (!String.IsNullOrEmpty(password)) + { + m_log.DebugFormat("[RADMIN]: UpdateUserAccount: updating password for avatar {0} {1}", firstName, lastName); + ChangeUserPassword(firstName, lastName, password); + } + + // if (null != usaX) userProfile.HomeLocationX = (uint) usaX; + // if (null != usaY) userProfile.HomeLocationY = (uint) usaY; + // if (null != usaZ) userProfile.HomeLocationZ = (uint) usaZ; + + // if (null != ulaX) userProfile.HomeLookAtX = (uint) ulaX; + // if (null != ulaY) userProfile.HomeLookAtY = (uint) ulaY; + // if (null != ulaZ) userProfile.HomeLookAtZ = (uint) ulaZ; + + // if (String.Empty != aboutFirstLive) userProfile.FirstLifeAboutText = aboutFirstLive; + // if (String.Empty != aboutAvatar) userProfile.AboutText = aboutAvatar; + + // Set home position + + if ((null != regionXLocation) && (null != regionYLocation)) + { + GridRegion home = scene.GridService.GetRegionByPosition(scopeID, + (int)Util.RegionToWorldLoc((uint)regionXLocation), (int)Util.RegionToWorldLoc((uint)regionYLocation)); + if (null == home) { + m_log.WarnFormat("[RADMIN]: Unable to set home region for updated user account {0} {1}", firstName, lastName); + } else { + scene.GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + m_log.DebugFormat("[RADMIN]: Set home region {0} for updated user account {1} {2}", home.RegionID, firstName, lastName); + } + } + + // User has been created. Now establish gender and appearance. + + UpdateUserAppearance(responseData, requestData, account.PrincipalID); + + responseData["success"] = true; + responseData["avatar_uuid"] = account.PrincipalID.ToString(); + + m_log.InfoFormat("[RADMIN]: UpdateUserAccount: account for user {0} {1} updated, UUID {2}", + firstName, lastName, + account.PrincipalID); + } + catch (Exception e) + { + responseData["avatar_uuid"] = UUID.Zero.ToString(); + + throw e; + } + + m_log.Info("[RADMIN]: UpdateUserAccount: request complete"); + } + } + + /// + /// Authenticate an user. + /// + /// incoming XML RPC request + /// + /// XmlRpcAuthenticateUserMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// user_firstname + /// avatar's first name + /// user_lastname + /// avatar's last name + /// user_password + /// MD5 hash of avatar's password + /// token_lifetime + /// the lifetime of the returned token (upper bounded to 30s) + /// + /// + /// XmlRpcAuthenticateUserMethod returns + /// + /// namedescription + /// success + /// true or false + /// token + /// the authentication token sent by OpenSim + /// error + /// error message if success is false + /// + /// + private void XmlRpcAuthenticateUserMethod(XmlRpcRequest request, XmlRpcResponse response, + IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: AuthenticateUser: new request"); + + var responseData = (Hashtable)response.Value; + var requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + CheckStringParameters(requestData, responseData, new[] + { + "user_firstname", + "user_lastname", + "user_password", + "token_lifetime" + }); + + var firstName = (string)requestData["user_firstname"]; + var lastName = (string)requestData["user_lastname"]; + var password = (string)requestData["user_password"]; + + var scene = m_application.SceneManager.CurrentOrFirstScene; + + if (scene.Equals(null)) + { + m_log.Debug("scene does not exist"); + throw new Exception("Scene does not exist."); + } + + var scopeID = scene.RegionInfo.ScopeID; + var account = scene.UserAccountService.GetUserAccount(scopeID, firstName, lastName); + + if (account.Equals(null) || account.PrincipalID.Equals(UUID.Zero)) + { + m_log.DebugFormat("avatar {0} {1} does not exist", firstName, lastName); + throw new Exception(String.Format("avatar {0} {1} does not exist", firstName, lastName)); + } + + if (String.IsNullOrEmpty(password)) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: no password provided for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("no password provided for {0} {1}", firstName, + lastName)); + } + + int lifetime; + if (int.TryParse((string)requestData["token_lifetime"], NumberStyles.Integer, CultureInfo.InvariantCulture, out lifetime) == false) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: no token lifetime provided for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("no token lifetime provided for {0} {1}", firstName, + lastName)); + } + + // Upper bound on lifetime set to 30s. + if (lifetime > 30) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: token lifetime longer than 30s for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("token lifetime longer than 30s for {0} {1}", firstName, + lastName)); + } + + var authModule = scene.RequestModuleInterface(); + if (authModule == null) + { + m_log.Debug("[RADMIN]: AuthenticateUser: no authentication module loded"); + throw new Exception("no authentication module loaded"); + } + + var token = authModule.Authenticate(account.PrincipalID, password, lifetime); + if (String.IsNullOrEmpty(token)) + { + m_log.DebugFormat("[RADMIN]: AuthenticateUser: authentication failed for {0} {1}", firstName, + lastName); + throw new Exception(String.Format("authentication failed for {0} {1}", firstName, + lastName)); + } + + m_log.DebugFormat("[RADMIN]: AuthenticateUser: account for user {0} {1} identified with token {2}", + firstName, lastName, token); + + responseData["token"] = token; + responseData["success"] = true; + + } + catch (Exception e) + { + responseData["success"] = false; + responseData["error"] = e.Message; + throw e; + } + + m_log.Info("[RADMIN]: AuthenticateUser: request complete"); + } + } + + /// + /// Load an OAR file into a region.. + /// + /// incoming XML RPC request + /// + /// XmlRpcLoadOARMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// filename + /// file name of the OAR file + /// region_uuid + /// UUID of the region + /// region_name + /// region name + /// merge + /// true if oar should be merged + /// skip-assets + /// true if assets should be skiped + /// + /// + /// region_uuid takes precedence over + /// region_name if both are present; one of both + /// must be present. + /// + /// XmlRpcLoadOARMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// + /// + private void XmlRpcLoadOARMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Load OAR Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + CheckStringParameters(requestData, responseData, new string[] {"filename"}); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + string filename = (string) requestData["filename"]; + + bool mergeOar = false; + bool skipAssets = false; + + if ((string)requestData["merge"] == "true") + { + mergeOar = true; + } + if ((string)requestData["skip-assets"] == "true") + { + skipAssets = true; + } + + IRegionArchiverModule archiver = scene.RequestModuleInterface(); + Dictionary archiveOptions = new Dictionary(); + if (mergeOar) archiveOptions.Add("merge", null); + if (skipAssets) archiveOptions.Add("skipAssets", null); + if (archiver != null) + archiver.DearchiveRegion(filename, Guid.Empty, archiveOptions); + else + throw new Exception("Archiver module not present for scene"); + + responseData["loaded"] = true; + } + catch (Exception e) + { + responseData["loaded"] = false; + + throw e; + } + + m_log.Info("[RADMIN]: Load OAR Administrator Request complete"); + } + } + + /// + /// Save a region to an OAR file + /// + /// incoming XML RPC request + /// + /// XmlRpcSaveOARMethod takes the following XMLRPC + /// parameters + /// + /// parameter namedescription + /// password + /// admin password as set in OpenSim.ini + /// filename + /// file name for the OAR file + /// region_uuid + /// UUID of the region + /// region_name + /// region name + /// profile + /// profile url + /// noassets + /// true if no assets should be saved + /// all + /// true to save all the regions in the simulator + /// perm + /// C and/or T + /// + /// + /// region_uuid takes precedence over + /// region_name if both are present; one of both + /// must be present. + /// + /// XmlRpcLoadOARMethod returns + /// + /// namedescription + /// success + /// true or false + /// error + /// error message if success is false + /// + /// + private void XmlRpcSaveOARMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Save OAR Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + try + { + CheckStringParameters(requestData, responseData, new string[] {"filename"}); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + string filename = (string)requestData["filename"]; + + Dictionary options = new Dictionary(); + + //if (requestData.Contains("version")) + //{ + // options["version"] = (string)requestData["version"]; + //} + + if (requestData.Contains("home")) + { + options["home"] = (string)requestData["home"]; + } + + if ((string)requestData["noassets"] == "true") + { + options["noassets"] = (string)requestData["noassets"] ; + } + + if (requestData.Contains("perm")) + { + options["checkPermissions"] = (string)requestData["perm"]; + } + + if ((string)requestData["all"] == "true") + { + options["all"] = (string)requestData["all"]; + } + + IRegionArchiverModule archiver = scene.RequestModuleInterface(); + + if (archiver != null) + { + Guid requestId = Guid.NewGuid(); + scene.EventManager.OnOarFileSaved += RemoteAdminOarSaveCompleted; + + m_log.InfoFormat( + "[RADMIN]: Submitting save OAR request for {0} to file {1}, request ID {2}", + scene.Name, filename, requestId); + + archiver.ArchiveRegion(filename, requestId, options); + + lock (m_saveOarLock) + Monitor.Wait(m_saveOarLock,5000); + + scene.EventManager.OnOarFileSaved -= RemoteAdminOarSaveCompleted; + } + else + { + throw new Exception("Archiver module not present for scene"); + } + + responseData["saved"] = true; + } + catch (Exception e) + { + responseData["saved"] = false; + + throw e; + } + + m_log.Info("[RADMIN]: Save OAR Request complete"); + } + + private void RemoteAdminOarSaveCompleted(Guid uuid, string name) + { + if (name != "") + m_log.ErrorFormat("[RADMIN]: Saving of OAR file with request ID {0} failed with message {1}", uuid, name); + else + m_log.DebugFormat("[RADMIN]: Saved OAR file for request {0}", uuid); + + lock (m_saveOarLock) + Monitor.Pulse(m_saveOarLock); + } + + private void XmlRpcLoadXMLMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Load XML Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + lock (m_requestLock) + { + try + { + CheckStringParameters(requestData, responseData, new string[] {"filename"}); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + string filename = (string) requestData["filename"]; + + responseData["switched"] = true; + + string xml_version = "1"; + if (requestData.Contains("xml_version")) + { + xml_version = (string) requestData["xml_version"]; + } + + switch (xml_version) + { + case "1": + m_application.SceneManager.LoadCurrentSceneFromXml(filename, true, new Vector3(0, 0, 0)); + break; + + case "2": + m_application.SceneManager.LoadCurrentSceneFromXml2(filename); + break; + + default: + throw new Exception(String.Format("unknown Xml{0} format", xml_version)); + } + + responseData["loaded"] = true; + } + catch (Exception e) + { + responseData["loaded"] = false; + responseData["switched"] = false; + + throw e; + } + + m_log.Info("[RADMIN]: Load XML Administrator Request complete"); + } + } + + private void XmlRpcSaveXMLMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Save XML Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + try + { + CheckStringParameters(requestData, responseData, new string[] {"filename"}); + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + string filename = (string) requestData["filename"]; + + responseData["switched"] = true; + + string xml_version = "1"; + if (requestData.Contains("xml_version")) + { + xml_version = (string) requestData["xml_version"]; + } + + switch (xml_version) + { + case "1": + m_application.SceneManager.SaveCurrentSceneToXml(filename); + break; + + case "2": + m_application.SceneManager.SaveCurrentSceneToXml2(filename); + break; + + default: + throw new Exception(String.Format("unknown Xml{0} format", xml_version)); + } + + responseData["saved"] = true; + } + catch (Exception e) + { + responseData["saved"] = false; + responseData["switched"] = false; + + throw e; + } + + m_log.Info("[RADMIN]: Save XML Administrator Request complete"); + } + + private void XmlRpcRegionQueryMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Query XML Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + int health = scene.GetHealth(); + responseData["health"] = health; + + responseData["success"] = true; + m_log.Info("[RADMIN]: Query XML Administrator Request complete"); + } + + private void XmlRpcConsoleCommandMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Command XML Administrator Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + CheckStringParameters(requestData, responseData, new string[] {"command"}); + + MainConsole.Instance.RunCommand(requestData["command"].ToString()); + + m_log.Info("[RADMIN]: Command XML Administrator Request complete"); + } + + private void XmlRpcAccessListClear(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Access List Clear Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + responseData["success"] = true; + + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + scene.RegionInfo.EstateSettings.EstateAccess = new UUID[]{}; + + if (scene.RegionInfo.Persistent) + m_application.EstateDataService.StoreEstateSettings(scene.RegionInfo.EstateSettings); + + m_log.Info("[RADMIN]: Access List Clear Request complete"); + } + + private void XmlRpcAccessListAdd(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Access List Add Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + int addedUsers = 0; + + if (requestData.Contains("users")) + { + UUID scopeID = scene.RegionInfo.ScopeID; + IUserAccountService userService = scene.UserAccountService; + Hashtable users = (Hashtable) requestData["users"]; + List uuids = new List(); + foreach (string name in users.Values) + { + string[] parts = name.Split(); + UserAccount account = userService.GetUserAccount(scopeID, parts[0], parts[1]); + if (account != null) + { + uuids.Add(account.PrincipalID); + m_log.DebugFormat("[RADMIN]: adding \"{0}\" to ACL for \"{1}\"", name, scene.RegionInfo.RegionName); + } + } + List accessControlList = new List(scene.RegionInfo.EstateSettings.EstateAccess); + foreach (UUID uuid in uuids) + { + if (!accessControlList.Contains(uuid)) + { + accessControlList.Add(uuid); + addedUsers++; + } + } + scene.RegionInfo.EstateSettings.EstateAccess = accessControlList.ToArray(); + if (scene.RegionInfo.Persistent) + m_application.EstateDataService.StoreEstateSettings(scene.RegionInfo.EstateSettings); + } + + responseData["added"] = addedUsers; + + m_log.Info("[RADMIN]: Access List Add Request complete"); + } + + private void XmlRpcAccessListRemove(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Access List Remove Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + int removedUsers = 0; + + if (requestData.Contains("users")) + { + UUID scopeID = scene.RegionInfo.ScopeID; + IUserAccountService userService = scene.UserAccountService; + //UserProfileCacheService ups = m_application.CommunicationsManager.UserProfileCacheService; + Hashtable users = (Hashtable) requestData["users"]; + List uuids = new List(); + foreach (string name in users.Values) + { + string[] parts = name.Split(); + UserAccount account = userService.GetUserAccount(scopeID, parts[0], parts[1]); + if (account != null) + { + uuids.Add(account.PrincipalID); + } + } + List accessControlList = new List(scene.RegionInfo.EstateSettings.EstateAccess); + foreach (UUID uuid in uuids) + { + if (accessControlList.Contains(uuid)) + { + accessControlList.Remove(uuid); + removedUsers++; + } + } + scene.RegionInfo.EstateSettings.EstateAccess = accessControlList.ToArray(); + if (scene.RegionInfo.Persistent) + m_application.EstateDataService.StoreEstateSettings(scene.RegionInfo.EstateSettings); + } + + responseData["removed"] = removedUsers; + responseData["success"] = true; + + m_log.Info("[RADMIN]: Access List Remove Request complete"); + } + + private void XmlRpcAccessListList(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Access List List Request"); + + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + CheckRegionParams(requestData, responseData); + + Scene scene = null; + GetSceneFromRegionParams(requestData, responseData, out scene); + + UUID[] accessControlList = scene.RegionInfo.EstateSettings.EstateAccess; + Hashtable users = new Hashtable(); + + foreach (UUID user in accessControlList) + { + UUID scopeID = scene.RegionInfo.ScopeID; + UserAccount account = scene.UserAccountService.GetUserAccount(scopeID, user); + if (account != null) + { + users[user.ToString()] = account.FirstName + " " + account.LastName; + } + } + + responseData["users"] = users; + responseData["success"] = true; + + m_log.Info("[RADMIN]: Access List List Request complete"); + } + + private void XmlRpcEstateReload(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + m_log.Info("[RADMIN]: Received Estate Reload Request"); + + Hashtable responseData = (Hashtable)response.Value; +// Hashtable requestData = (Hashtable)request.Params[0]; + + m_application.SceneManager.ForEachScene(s => + s.RegionInfo.EstateSettings = m_application.EstateDataService.LoadEstateSettings(s.RegionInfo.RegionID, false) + ); + + responseData["success"] = true; + + m_log.Info("[RADMIN]: Estate Reload Request complete"); + } + + private void XmlRpcGetAgentsMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + bool includeChildren = false; + + if (requestData.Contains("include_children")) + bool.TryParse((string)requestData["include_children"], out includeChildren); + + Scene scene; + GetSceneFromRegionParams(requestData, responseData, out scene); + + ArrayList xmlRpcRegions = new ArrayList(); + responseData["regions"] = xmlRpcRegions; + + Hashtable xmlRpcRegion = new Hashtable(); + xmlRpcRegions.Add(xmlRpcRegion); + + xmlRpcRegion["name"] = scene.Name; + xmlRpcRegion["id"] = scene.RegionInfo.RegionID.ToString(); + + List agents = scene.GetScenePresences(); + ArrayList xmlrpcAgents = new ArrayList(); + + foreach (ScenePresence agent in agents) + { + if (agent.IsChildAgent && !includeChildren) + continue; + + Hashtable xmlRpcAgent = new Hashtable(); + xmlRpcAgent.Add("name", agent.Name); + xmlRpcAgent.Add("id", agent.UUID.ToString()); + xmlRpcAgent.Add("type", agent.PresenceType.ToString()); + xmlRpcAgent.Add("current_parcel_id", agent.currentParcelUUID.ToString()); + + Vector3 pos = agent.AbsolutePosition; + xmlRpcAgent.Add("pos_x", pos.X.ToString()); + xmlRpcAgent.Add("pos_y", pos.Y.ToString()); + xmlRpcAgent.Add("pos_z", pos.Z.ToString()); + + Vector3 lookAt = agent.Lookat; + xmlRpcAgent.Add("lookat_x", lookAt.X.ToString()); + xmlRpcAgent.Add("lookat_y", lookAt.Y.ToString()); + xmlRpcAgent.Add("lookat_z", lookAt.Z.ToString()); + + Vector3 vel = agent.Velocity; + xmlRpcAgent.Add("vel_x", vel.X.ToString()); + xmlRpcAgent.Add("vel_y", vel.Y.ToString()); + xmlRpcAgent.Add("vel_z", vel.Z.ToString()); + + xmlRpcAgent.Add("is_flying", agent.Flying.ToString()); + xmlRpcAgent.Add("is_sat_on_ground", agent.SitGround.ToString()); + xmlRpcAgent.Add("is_sat_on_object", agent.IsSatOnObject.ToString()); + + xmlrpcAgents.Add(xmlRpcAgent); + } + + m_log.DebugFormat( + "[REMOTE ADMIN]: XmlRpcGetAgents found {0} agents in {1}", xmlrpcAgents.Count, scene.Name); + + xmlRpcRegion["agents"] = xmlrpcAgents; + responseData["success"] = true; + } + + private void XmlRpcTeleportAgentMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + Hashtable responseData = (Hashtable)response.Value; + Hashtable requestData = (Hashtable)request.Params[0]; + + UUID agentId; + string regionName = null; + Vector3 pos, lookAt; + ScenePresence sp = null; + + if (requestData.Contains("agent_first_name") && requestData.Contains("agent_last_name")) + { + string firstName = requestData["agent_first_name"].ToString(); + string lastName = requestData["agent_last_name"].ToString(); + m_application.SceneManager.TryGetRootScenePresenceByName(firstName, lastName, out sp); + + if (sp == null) + throw new Exception( + string.Format( + "No agent found with agent_first_name {0} and agent_last_name {1}", firstName, lastName)); + } + else if (requestData.Contains("agent_id")) + { + string rawAgentId = (string)requestData["agent_id"]; + + if (!UUID.TryParse(rawAgentId, out agentId)) + throw new Exception(string.Format("agent_id {0} does not have the correct id format", rawAgentId)); + + m_application.SceneManager.TryGetRootScenePresence(agentId, out sp); + + if (sp == null) + throw new Exception(string.Format("No agent with agent_id {0} found in this simulator", agentId)); + } + else + { + throw new Exception("No agent_id or agent_first_name and agent_last_name parameters specified"); + } + + if (requestData.Contains("region_name")) + regionName = (string)requestData["region_name"]; + + pos.X = ParseFloat(requestData, "pos_x", sp.AbsolutePosition.X); + pos.Y = ParseFloat(requestData, "pos_y", sp.AbsolutePosition.Y); + pos.Z = ParseFloat(requestData, "pos_z", sp.AbsolutePosition.Z); + lookAt.X = ParseFloat(requestData, "lookat_x", sp.Lookat.X); + lookAt.Y = ParseFloat(requestData, "lookat_y", sp.Lookat.Y); + lookAt.Z = ParseFloat(requestData, "lookat_z", sp.Lookat.Z); + + sp.Scene.RequestTeleportLocation( + sp.ControllingClient, regionName, pos, lookAt, (uint)Constants.TeleportFlags.ViaLocation); + + // We have no way of telling the failure of the actual teleport + responseData["success"] = true; + } + + private void XmlRpcResetLand(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + Hashtable responseData = (Hashtable)response.Value; + + string musicURL = string.Empty; + UUID groupID = UUID.Zero; + uint flags = 0; + bool set_group = false, set_music = false, set_flags = false; + + if (requestData.Contains("group") && requestData["group"] != null) + set_group = UUID.TryParse(requestData["group"].ToString(), out groupID); + if (requestData.Contains("music") && requestData["music"] != null) + { + musicURL = requestData["music"].ToString(); + set_music = true; + } + if (requestData.Contains("flags") && requestData["flags"] != null) + set_flags = UInt32.TryParse(requestData["flags"].ToString(), out flags); + + m_log.InfoFormat("[RADMIN]: Received Reset Land Request group={0} musicURL={1} flags={2}", + (set_group ? groupID.ToString() : "unchanged"), + (set_music ? musicURL : "unchanged"), + (set_flags ? flags.ToString() : "unchanged")); + + m_application.SceneManager.ForEachScene(delegate(Scene s) + { + List parcels = s.LandChannel.AllParcels(); + foreach (ILandObject p in parcels) + { + if (set_music) + p.LandData.MusicURL = musicURL; + + if (set_group) + p.LandData.GroupID = groupID; + + if (set_flags) + p.LandData.Flags = flags; + + s.LandChannel.UpdateLandObject(p.LandData.LocalID, p.LandData); + } + } + ); + + responseData["success"] = true; + + m_log.Info("[RADMIN]: Reset Land Request complete"); + } + + + /// + /// Parse a float with the given parameter name from a request data hash table. + /// + /// + /// Will throw an exception if parameter is not a float. + /// Will not throw if parameter is not found, passes back default value instead. + /// + /// + /// + /// + /// + private static float ParseFloat(Hashtable requestData, string paramName, float defaultVal) + { + if (requestData.Contains(paramName)) + { + string rawVal = (string)requestData[paramName]; + float val; + + if (!float.TryParse(rawVal, out val)) + throw new Exception(string.Format("{0} {1} is not a valid float", paramName, rawVal)); + else + return val; + } + else + { + return defaultVal; + } + } + + private static void CheckStringParameters(Hashtable requestData, Hashtable responseData, string[] param) + { + foreach (string parameter in param) + { + if (!requestData.Contains(parameter)) + { + responseData["accepted"] = false; + throw new Exception(String.Format("missing string parameter {0}", parameter)); + } + if (String.IsNullOrEmpty((string) requestData[parameter])) + { + responseData["accepted"] = false; + throw new Exception(String.Format("parameter {0} is empty", parameter)); + } + } + } + + private static void CheckIntegerParams(Hashtable requestData, Hashtable responseData, string[] param) + { + foreach (string parameter in param) + { + if (!requestData.Contains(parameter)) + { + responseData["accepted"] = false; + throw new Exception(String.Format("missing integer parameter {0}", parameter)); + } + } + } + + private void CheckRegionParams(Hashtable requestData, Hashtable responseData) + { + //Checks if region parameters exist and gives exeption if no parameters are given + if ((requestData.ContainsKey("region_id") && !String.IsNullOrEmpty((string)requestData["region_id"])) || + (requestData.ContainsKey("region_name") && !String.IsNullOrEmpty((string)requestData["region_name"]))) + { + return; + } + else + { + responseData["accepted"] = false; + throw new Exception("no region_name or region_id given"); + } + } + + private void GetSceneFromRegionParams(Hashtable requestData, Hashtable responseData, out Scene scene) + { + scene = null; + + if (requestData.ContainsKey("region_id") && + !String.IsNullOrEmpty((string)requestData["region_id"])) + { + UUID regionID = (UUID)(string)requestData["region_id"]; + if (!m_application.SceneManager.TryGetScene(regionID, out scene)) + { + responseData["error"] = String.Format("Region ID {0} not found", regionID); + throw new Exception(String.Format("Region ID {0} not found", regionID)); + } + } + else if (requestData.ContainsKey("region_name") && + !String.IsNullOrEmpty((string)requestData["region_name"])) + { + string regionName = (string)requestData["region_name"]; + if (!m_application.SceneManager.TryGetScene(regionName, out scene)) + { + responseData["error"] = String.Format("Region {0} not found", regionName); + throw new Exception(String.Format("Region {0} not found", regionName)); + } + } + else + { + responseData["error"] = "no region_name or region_id given"; + throw new Exception("no region_name or region_id given"); + } + return; + } + + private bool GetBoolean(Hashtable requestData, string tag, bool defaultValue) + { + // If an access value has been provided, apply it. + if (requestData.Contains(tag)) + { + switch (((string)requestData[tag]).ToLower()) + { + case "true" : + case "t" : + case "1" : + return true; + case "false" : + case "f" : + case "0" : + return false; + default : + return defaultValue; + } + } + else + return defaultValue; + } + + private int GetIntegerAttribute(XmlNode node, string attribute, int defaultValue) + { + try { return Convert.ToInt32(node.Attributes[attribute].Value); } catch{} + return defaultValue; + } + + private uint GetUnsignedAttribute(XmlNode node, string attribute, uint defaultValue) + { + try { return Convert.ToUInt32(node.Attributes[attribute].Value); } catch{} + return defaultValue; + } + + private string GetStringAttribute(XmlNode node, string attribute, string defaultValue) + { + try { return node.Attributes[attribute].Value; } catch{} + return defaultValue; + } + + public void Dispose() + { + } + + /// + /// Create a user + /// + /// + /// + /// + /// + /// + private UserAccount CreateUser(UUID scopeID, string firstName, string lastName, string password, string email) + { + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + IUserAccountService userAccountService = scene.UserAccountService; + IGridService gridService = scene.GridService; + IAuthenticationService authenticationService = scene.AuthenticationService; + IGridUserService gridUserService = scene.GridUserService; + IInventoryService inventoryService = scene.InventoryService; + + UserAccount account = userAccountService.GetUserAccount(scopeID, firstName, lastName); + if (null == account) + { + account = new UserAccount(scopeID, UUID.Random(), firstName, lastName, email); + if (account.ServiceURLs == null || (account.ServiceURLs != null && account.ServiceURLs.Count == 0)) + { + account.ServiceURLs = new Dictionary(); + account.ServiceURLs["HomeURI"] = string.Empty; + account.ServiceURLs["InventoryServerURI"] = string.Empty; + account.ServiceURLs["AssetServerURI"] = string.Empty; + } + + if (userAccountService.StoreUserAccount(account)) + { + bool success; + if (authenticationService != null) + { + success = authenticationService.SetPassword(account.PrincipalID, password); + if (!success) + m_log.WarnFormat("[RADMIN]: Unable to set password for account {0} {1}.", + firstName, lastName); + } + + GridRegion home = null; + if (gridService != null) + { + List defaultRegions = gridService.GetDefaultRegions(UUID.Zero); + if (defaultRegions != null && defaultRegions.Count >= 1) + home = defaultRegions[0]; + + if (gridUserService != null && home != null) + gridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + else + m_log.WarnFormat("[RADMIN]: Unable to set home for account {0} {1}.", + firstName, lastName); + } + else + m_log.WarnFormat("[RADMIN]: Unable to retrieve home region for account {0} {1}.", + firstName, lastName); + + if (inventoryService != null) + { + success = inventoryService.CreateUserInventory(account.PrincipalID); + if (!success) + m_log.WarnFormat("[RADMIN]: Unable to create inventory for account {0} {1}.", + firstName, lastName); + } + + m_log.InfoFormat("[RADMIN]: Account {0} {1} created successfully", firstName, lastName); + return account; + } else { + m_log.ErrorFormat("[RADMIN]: Account creation failed for account {0} {1}", firstName, lastName); + } + } + else + { + m_log.ErrorFormat("[RADMIN]: A user with the name {0} {1} already exists!", firstName, lastName); + } + return null; + } + + /// + /// Change password + /// + /// + /// + /// + private bool ChangeUserPassword(string firstName, string lastName, string password) + { + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + IUserAccountService userAccountService = scene.UserAccountService; + IAuthenticationService authenticationService = scene.AuthenticationService; + + UserAccount account = userAccountService.GetUserAccount(UUID.Zero, firstName, lastName); + if (null != account) + { + bool success = false; + if (authenticationService != null) + success = authenticationService.SetPassword(account.PrincipalID, password); + + if (!success) + { + m_log.WarnFormat("[RADMIN]: Unable to set password for account {0} {1}.", + firstName, lastName); + return false; + } + return true; + } + else + { + m_log.ErrorFormat("[RADMIN]: No such user"); + return false; + } + } + + private bool LoadHeightmap(string file, UUID regionID) + { + m_log.InfoFormat("[RADMIN]: Terrain Loading: {0}", file); + + Scene region = null; + + if (!m_application.SceneManager.TryGetScene(regionID, out region)) + { + m_log.InfoFormat("[RADMIN]: unable to get a scene with that name: {0}", regionID.ToString()); + return false; + } + + ITerrainModule terrainModule = region.RequestModuleInterface(); + if (null == terrainModule) throw new Exception("terrain module not available"); + if (Uri.IsWellFormedUriString(file, UriKind.Absolute)) + { + m_log.Info("[RADMIN]: Terrain path is URL"); + Uri result; + if (Uri.TryCreate(file, UriKind.RelativeOrAbsolute, out result)) + { + // the url is valid + string fileType = file.Substring(file.LastIndexOf('/') + 1); + terrainModule.LoadFromStream(fileType, result); + } + } + else + { + terrainModule.LoadFromFile(file); + } + + m_log.Info("[RADMIN]: Load height maps request complete"); + + return true; + } + + + /// + /// This method is called by the user-create and user-modify methods to establish + /// or change, the user's appearance. Default avatar names can be specified via + /// the config file, but must correspond to avatars in the default appearance + /// file, or pre-existing in the user database. + /// This should probably get moved into somewhere more core eventually. + /// + private void UpdateUserAppearance(Hashtable responseData, Hashtable requestData, UUID userid) + { + m_log.DebugFormat("[RADMIN]: updateUserAppearance"); + + string defaultMale = m_config.GetString("default_male", "Default Male"); + string defaultFemale = m_config.GetString("default_female", "Default Female"); + string defaultNeutral = m_config.GetString("default_female", "Default Default"); + string model = String.Empty; + + // Has a gender preference been supplied? + + if (requestData.Contains("gender")) + { + switch ((string)requestData["gender"]) + { + case "m" : + case "male" : + model = defaultMale; + break; + case "f" : + case "female" : + model = defaultFemale; + break; + case "n" : + case "neutral" : + default : + model = defaultNeutral; + break; + } + } + + // Has an explicit model been specified? + + if (requestData.Contains("model") && (String.IsNullOrEmpty((string)requestData["gender"]))) + { + model = (string)requestData["model"]; + } + + // No appearance attributes were set + + if (String.IsNullOrEmpty(model)) + { + m_log.DebugFormat("[RADMIN]: Appearance update not requested"); + return; + } + + m_log.DebugFormat("[RADMIN]: Setting appearance for avatar {0}, using model <{1}>", userid, model); + + string[] modelSpecifiers = model.Split(); + if (modelSpecifiers.Length != 2) + { + m_log.WarnFormat("[RADMIN]: User appearance not set for {0}. Invalid model name : <{1}>", userid, model); + // modelSpecifiers = dmodel.Split(); + return; + } + + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + UUID scopeID = scene.RegionInfo.ScopeID; + UserAccount modelProfile = scene.UserAccountService.GetUserAccount(scopeID, modelSpecifiers[0], modelSpecifiers[1]); + + if (modelProfile == null) + { + m_log.WarnFormat("[RADMIN]: Requested model ({0}) not found. Appearance unchanged", model); + return; + } + + // Set current user's appearance. This bit is easy. The appearance structure is populated with + // actual asset ids, however to complete the magic we need to populate the inventory with the + // assets in question. + + EstablishAppearance(userid, modelProfile.PrincipalID); + + m_log.DebugFormat("[RADMIN]: Finished setting appearance for avatar {0}, using model {1}", + userid, model); + } + + /// + /// This method is called by updateAvatarAppearance once any specified model has been + /// ratified, or an appropriate default value has been adopted. The intended prototype + /// is known to exist, as is the target avatar. + /// + private void EstablishAppearance(UUID destination, UUID source) + { + m_log.DebugFormat("[RADMIN]: Initializing inventory for {0} from {1}", destination, source); + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + + // If the model has no associated appearance we're done. + AvatarAppearance avatarAppearance = scene.AvatarService.GetAppearance(source); + if (avatarAppearance == null) + return; + + // Simple appearance copy or copy Clothing and Bodyparts folders? + bool copyFolders = m_config.GetBoolean("copy_folders", false); + + if (!copyFolders) + { + // Simple copy of wearables and appearance update + try + { + CopyWearablesAndAttachments(destination, source, avatarAppearance); + + scene.AvatarService.SetAppearance(destination, avatarAppearance); + } + catch (Exception e) + { + m_log.WarnFormat("[RADMIN]: Error transferring appearance for {0} : {1}", + destination, e.Message); + } + + return; + } + + // Copy Clothing and Bodypart folders and appearance update + try + { + Dictionary inventoryMap = new Dictionary(); + CopyInventoryFolders(destination, source, FolderType.Clothing, inventoryMap, avatarAppearance); + CopyInventoryFolders(destination, source, FolderType.BodyPart, inventoryMap, avatarAppearance); + + AvatarWearable[] wearables = avatarAppearance.Wearables; + + for (int i=0; i + /// This method is called by establishAppearance to do a copy all inventory items + /// worn or attached to the Clothing inventory folder of the receiving avatar. + /// In parallel the avatar wearables and attachments are updated. + /// + private void CopyWearablesAndAttachments(UUID destination, UUID source, AvatarAppearance avatarAppearance) + { + IInventoryService inventoryService = m_application.SceneManager.CurrentOrFirstScene.InventoryService; + + // Get Clothing folder of receiver + InventoryFolderBase destinationFolder = inventoryService.GetFolderForType(destination, FolderType.Clothing); + + if (destinationFolder == null) + throw new Exception("Cannot locate folder(s)"); + + // Missing destination folder? This should *never* be the case + if (destinationFolder.Type != (short)FolderType.Clothing) + { + destinationFolder = new InventoryFolderBase(); + + destinationFolder.ID = UUID.Random(); + destinationFolder.Name = "Clothing"; + destinationFolder.Owner = destination; + destinationFolder.Type = (short)FolderType.Clothing; + destinationFolder.ParentID = inventoryService.GetRootFolder(destination).ID; + destinationFolder.Version = 1; + inventoryService.AddFolder(destinationFolder); // store base record + m_log.ErrorFormat("[RADMIN]: Created folder for destination {0}", source); + } + + // Wearables + AvatarWearable[] wearables = avatarAppearance.Wearables; + AvatarWearable wearable; + + for (int i = 0; i attachments = avatarAppearance.GetAttachments(); + + foreach (AvatarAttachment attachment in attachments) + { + int attachpoint = attachment.AttachPoint; + UUID itemID = attachment.ItemID; + + if (itemID != UUID.Zero) + { + // Get inventory item and copy it + InventoryItemBase item = new InventoryItemBase(itemID, source); + item = inventoryService.GetItem(item); + + if (item != null) + { + InventoryItemBase destinationItem = new InventoryItemBase(UUID.Random(), destination); + destinationItem.Name = item.Name; + destinationItem.Owner = destination; + destinationItem.Description = item.Description; + destinationItem.InvType = item.InvType; + destinationItem.CreatorId = item.CreatorId; + destinationItem.CreatorData = item.CreatorData; + destinationItem.NextPermissions = item.NextPermissions; + destinationItem.CurrentPermissions = item.CurrentPermissions; + destinationItem.BasePermissions = item.BasePermissions; + destinationItem.EveryOnePermissions = item.EveryOnePermissions; + destinationItem.GroupPermissions = item.GroupPermissions; + destinationItem.AssetType = item.AssetType; + destinationItem.AssetID = item.AssetID; + destinationItem.GroupID = item.GroupID; + destinationItem.GroupOwned = item.GroupOwned; + destinationItem.SalePrice = item.SalePrice; + destinationItem.SaleType = item.SaleType; + destinationItem.Flags = item.Flags; + destinationItem.CreationDate = item.CreationDate; + destinationItem.Folder = destinationFolder.ID; + ApplyNextOwnerPermissions(destinationItem); + + m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(destinationItem); + m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", destinationItem.ID, destinationFolder.ID); + + // Attach item + avatarAppearance.SetAttachment(attachpoint, destinationItem.ID, destinationItem.AssetID); + m_log.DebugFormat("[RADMIN]: Attached {0}", destinationItem.ID); + } + else + { + m_log.WarnFormat("[RADMIN]: Error transferring {0} to folder {1}", itemID, destinationFolder.ID); + } + } + } + } + + /// + /// This method is called by establishAppearance to copy inventory folders to make + /// copies of Clothing and Bodyparts inventory folders and attaches worn attachments + /// + private void CopyInventoryFolders(UUID destination, UUID source, FolderType assetType, Dictionary inventoryMap, + AvatarAppearance avatarAppearance) + { + IInventoryService inventoryService = m_application.SceneManager.CurrentOrFirstScene.InventoryService; + + InventoryFolderBase sourceFolder = inventoryService.GetFolderForType(source, assetType); + InventoryFolderBase destinationFolder = inventoryService.GetFolderForType(destination, assetType); + + if (sourceFolder == null || destinationFolder == null) + throw new Exception("Cannot locate folder(s)"); + + // Missing source folder? This should *never* be the case + if (sourceFolder.Type != (short)assetType) + { + sourceFolder = new InventoryFolderBase(); + sourceFolder.ID = UUID.Random(); + if (assetType == FolderType.Clothing) + { + sourceFolder.Name = "Clothing"; + } + else + { + sourceFolder.Name = "Body Parts"; + } + sourceFolder.Owner = source; + sourceFolder.Type = (short)assetType; + sourceFolder.ParentID = inventoryService.GetRootFolder(source).ID; + sourceFolder.Version = 1; + inventoryService.AddFolder(sourceFolder); // store base record + m_log.ErrorFormat("[RADMIN] Created folder for source {0}", source); + } + + // Missing destination folder? This should *never* be the case + if (destinationFolder.Type != (short)assetType) + { + destinationFolder = new InventoryFolderBase(); + destinationFolder.ID = UUID.Random(); + if (assetType == FolderType.Clothing) + { + destinationFolder.Name = "Clothing"; + } + else + { + destinationFolder.Name = "Body Parts"; + } + destinationFolder.Owner = destination; + destinationFolder.Type = (short)assetType; + destinationFolder.ParentID = inventoryService.GetRootFolder(destination).ID; + destinationFolder.Version = 1; + inventoryService.AddFolder(destinationFolder); // store base record + m_log.ErrorFormat("[RADMIN]: Created folder for destination {0}", source); + } + + InventoryFolderBase extraFolder; + List folders = inventoryService.GetFolderContent(source, sourceFolder.ID).Folders; + + foreach (InventoryFolderBase folder in folders) + { + extraFolder = new InventoryFolderBase(); + extraFolder.ID = UUID.Random(); + extraFolder.Name = folder.Name; + extraFolder.Owner = destination; + extraFolder.Type = folder.Type; + extraFolder.Version = folder.Version; + extraFolder.ParentID = destinationFolder.ID; + inventoryService.AddFolder(extraFolder); + + m_log.DebugFormat("[RADMIN]: Added folder {0} to folder {1}", extraFolder.ID, sourceFolder.ID); + + List items = inventoryService.GetFolderContent(source, folder.ID).Items; + + foreach (InventoryItemBase item in items) + { + InventoryItemBase destinationItem = new InventoryItemBase(UUID.Random(), destination); + destinationItem.Name = item.Name; + destinationItem.Owner = destination; + destinationItem.Description = item.Description; + destinationItem.InvType = item.InvType; + destinationItem.CreatorId = item.CreatorId; + destinationItem.CreatorData = item.CreatorData; + destinationItem.NextPermissions = item.NextPermissions; + destinationItem.CurrentPermissions = item.CurrentPermissions; + destinationItem.BasePermissions = item.BasePermissions; + destinationItem.EveryOnePermissions = item.EveryOnePermissions; + destinationItem.GroupPermissions = item.GroupPermissions; + destinationItem.AssetType = item.AssetType; + destinationItem.AssetID = item.AssetID; + destinationItem.GroupID = item.GroupID; + destinationItem.GroupOwned = item.GroupOwned; + destinationItem.SalePrice = item.SalePrice; + destinationItem.SaleType = item.SaleType; + destinationItem.Flags = item.Flags; + destinationItem.CreationDate = item.CreationDate; + destinationItem.Folder = extraFolder.ID; + ApplyNextOwnerPermissions(destinationItem); + + m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(destinationItem); + inventoryMap.Add(item.ID, destinationItem.ID); + m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", destinationItem.ID, extraFolder.ID); + + // Attach item, if original is attached + int attachpoint = avatarAppearance.GetAttachpoint(item.ID); + if (attachpoint != 0) + { + avatarAppearance.SetAttachment(attachpoint, destinationItem.ID, destinationItem.AssetID); + m_log.DebugFormat("[RADMIN]: Attached {0}", destinationItem.ID); + } + } + } + } + + /// + /// Apply next owner permissions. + /// + private void ApplyNextOwnerPermissions(InventoryItemBase item) + { + if (item.InvType == (int)InventoryType.Object) + { + uint perms = item.CurrentPermissions; + PermissionsUtil.ApplyFoldedPermissions(item.CurrentPermissions, ref perms); + item.CurrentPermissions = perms; + } + + item.CurrentPermissions &= item.NextPermissions; + item.BasePermissions &= item.NextPermissions; + item.EveryOnePermissions &= item.NextPermissions; + // item.OwnerChanged = true; + // item.PermsMask = 0; + // item.PermsGranter = UUID.Zero; + } + + /// + /// This method is called if a given model avatar name can not be found. If the external + /// file has already been loaded once, then control returns immediately. If not, then it + /// looks for a default appearance file. This file contains XML definitions of zero or more named + /// avatars, each avatar can specify zero or more "outfits". Each outfit is a collection + /// of items that together, define a particular ensemble for the avatar. Each avatar should + /// indicate which outfit is the default, and this outfit will be automatically worn. The + /// other outfits are provided to allow "real" avatars a way to easily change their outfits. + /// + private bool CreateDefaultAvatars() + { + // Only load once + if (m_defaultAvatarsLoaded) + { + return false; + } + + m_log.DebugFormat("[RADMIN]: Creating default avatar entries"); + + m_defaultAvatarsLoaded = true; + + // Load processing starts here... + + try + { + string defaultAppearanceFileName = null; + + //m_config may be null if RemoteAdmin configuration secition is missing or disabled in OpenSim.ini + if (m_config != null) + { + defaultAppearanceFileName = m_config.GetString("default_appearance", "default_appearance.xml"); + } + + if (File.Exists(defaultAppearanceFileName)) + { + XmlDocument doc = new XmlDocument(); + string name = "*unknown*"; + string email = "anon@anon"; + uint regionXLocation = 1000; + uint regionYLocation = 1000; + string password = UUID.Random().ToString(); // No requirement to sign-in. + UUID ID = UUID.Zero; + AvatarAppearance avatarAppearance; + XmlNodeList avatars; + XmlNodeList assets; + XmlNode perms = null; + bool include = false; + bool select = false; + + Scene scene = m_application.SceneManager.CurrentOrFirstScene; + IInventoryService inventoryService = scene.InventoryService; + IAssetService assetService = scene.AssetService; + + doc.LoadXml(File.ReadAllText(defaultAppearanceFileName)); + + // Load up any included assets. Duplicates will be ignored + assets = doc.GetElementsByTagName("RequiredAsset"); + foreach (XmlNode assetNode in assets) + { + AssetBase asset = new AssetBase(UUID.Random(), GetStringAttribute(assetNode, "name", ""), SByte.Parse(GetStringAttribute(assetNode, "type", "")), UUID.Zero.ToString()); + asset.Description = GetStringAttribute(assetNode,"desc",""); + asset.Local = Boolean.Parse(GetStringAttribute(assetNode,"local","")); + asset.Temporary = Boolean.Parse(GetStringAttribute(assetNode,"temporary","")); + asset.Data = Convert.FromBase64String(assetNode.InnerText); + assetService.Store(asset); + } + + avatars = doc.GetElementsByTagName("Avatar"); + + // The document may contain multiple avatars + + foreach (XmlElement avatar in avatars) + { + m_log.DebugFormat("[RADMIN]: Loading appearance for {0}, gender = {1}", + GetStringAttribute(avatar,"name","?"), GetStringAttribute(avatar,"gender","?")); + + // Create the user identified by the avatar entry + + try + { + // Only the name value is mandatory + name = GetStringAttribute(avatar,"name",name); + email = GetStringAttribute(avatar,"email",email); + regionXLocation = GetUnsignedAttribute(avatar,"regx",regionXLocation); + regionYLocation = GetUnsignedAttribute(avatar,"regy",regionYLocation); + password = GetStringAttribute(avatar,"password",password); + + string[] names = name.Split(); + UUID scopeID = scene.RegionInfo.ScopeID; + UserAccount account = scene.UserAccountService.GetUserAccount(scopeID, names[0], names[1]); + if (null == account) + { + account = CreateUser(scopeID, names[0], names[1], password, email); + if (null == account) + { + m_log.ErrorFormat("[RADMIN]: Avatar {0} {1} was not created", names[0], names[1]); + return false; + } + } + + // Set home position + + GridRegion home = scene.GridService.GetRegionByPosition(scopeID, + (int)Util.RegionToWorldLoc(regionXLocation), (int)Util.RegionToWorldLoc(regionYLocation)); + if (null == home) { + m_log.WarnFormat("[RADMIN]: Unable to set home region for newly created user account {0} {1}", names[0], names[1]); + } else { + scene.GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + m_log.DebugFormat("[RADMIN]: Set home region {0} for updated user account {1} {2}", home.RegionID, names[0], names[1]); + } + + ID = account.PrincipalID; + + m_log.DebugFormat("[RADMIN]: User {0}[{1}] created or retrieved", name, ID); + include = true; + } + catch (Exception e) + { + m_log.DebugFormat("[RADMIN]: Error creating user {0} : {1}", name, e.Message); + include = false; + } + + // OK, User has been created OK, now we can install the inventory. + // First retrieve the current inventory (the user may already exist) + // Note that althought he inventory is retrieved, the hierarchy has + // not been interpreted at all. + + if (include) + { + // Setup for appearance processing + avatarAppearance = scene.AvatarService.GetAppearance(ID); + if (avatarAppearance == null) + avatarAppearance = new AvatarAppearance(); + + AvatarWearable[] wearables = avatarAppearance.Wearables; + for (int i=0; i folders = inventoryService.GetFolderContent(ID, clothingFolder.ID).Folders; + extraFolder = null; + + foreach (InventoryFolderBase folder in folders) + { + if (folder.Name == outfitName) + { + extraFolder = folder; + break; + } + } + + // Otherwise, we must create the folder. + if (extraFolder == null) + { + m_log.DebugFormat("[RADMIN]: Creating outfit folder {0} for {1}", outfitName, name); + extraFolder = new InventoryFolderBase(); + extraFolder.ID = UUID.Random(); + extraFolder.Name = outfitName; + extraFolder.Owner = ID; + extraFolder.Type = (short)FolderType.Clothing; + extraFolder.Version = 1; + extraFolder.ParentID = clothingFolder.ID; + inventoryService.AddFolder(extraFolder); + m_log.DebugFormat("[RADMIN]: Adding outfile folder {0} to folder {1}", extraFolder.ID, clothingFolder.ID); + } + + // Now get the pieces that make up the outfit + XmlNodeList items = outfit.GetElementsByTagName("Item"); + + foreach (XmlElement item in items) + { + assetid = UUID.Zero; + XmlNodeList children = item.ChildNodes; + foreach (XmlNode child in children) + { + switch (child.Name) + { + case "Permissions" : + m_log.DebugFormat("[RADMIN]: Permissions specified"); + perms = child; + break; + case "Asset" : + assetid = new UUID(child.InnerText); + break; + } + } + + InventoryItemBase inventoryItem = null; + + // Check if asset is in inventory already + inventoryItem = null; + List inventoryItems = inventoryService.GetFolderContent(ID, extraFolder.ID).Items; + + foreach (InventoryItemBase listItem in inventoryItems) + { + if (listItem.AssetID == assetid) + { + inventoryItem = listItem; + break; + } + } + + // Create inventory item + if (inventoryItem == null) + { + inventoryItem = new InventoryItemBase(UUID.Random(), ID); + inventoryItem.Name = GetStringAttribute(item,"name",""); + inventoryItem.Description = GetStringAttribute(item,"desc",""); + inventoryItem.InvType = GetIntegerAttribute(item,"invtype",-1); + inventoryItem.CreatorId = GetStringAttribute(item,"creatorid",""); + inventoryItem.CreatorData = GetStringAttribute(item, "creatordata", ""); + inventoryItem.NextPermissions = GetUnsignedAttribute(perms, "next", 0x7fffffff); + inventoryItem.CurrentPermissions = GetUnsignedAttribute(perms,"current",0x7fffffff); + inventoryItem.BasePermissions = GetUnsignedAttribute(perms,"base",0x7fffffff); + inventoryItem.EveryOnePermissions = GetUnsignedAttribute(perms,"everyone",0x7fffffff); + inventoryItem.GroupPermissions = GetUnsignedAttribute(perms,"group",0x7fffffff); + inventoryItem.AssetType = GetIntegerAttribute(item,"assettype",-1); + inventoryItem.AssetID = assetid; // associated asset + inventoryItem.GroupID = (UUID)GetStringAttribute(item,"groupid",""); + inventoryItem.GroupOwned = (GetStringAttribute(item,"groupowned","false") == "true"); + inventoryItem.SalePrice = GetIntegerAttribute(item,"saleprice",0); + inventoryItem.SaleType = (byte)GetIntegerAttribute(item,"saletype",0); + inventoryItem.Flags = GetUnsignedAttribute(item,"flags",0); + inventoryItem.CreationDate = GetIntegerAttribute(item,"creationdate",Util.UnixTimeSinceEpoch()); + inventoryItem.Folder = extraFolder.ID; // Parent folder + + m_application.SceneManager.CurrentOrFirstScene.AddInventoryItem(inventoryItem); + m_log.DebugFormat("[RADMIN]: Added item {0} to folder {1}", inventoryItem.ID, extraFolder.ID); + } + + // Attach item, if attachpoint is specified + int attachpoint = GetIntegerAttribute(item,"attachpoint",0); + if (attachpoint != 0) + { + avatarAppearance.SetAttachment(attachpoint, inventoryItem.ID, inventoryItem.AssetID); + m_log.DebugFormat("[RADMIN]: Attached {0}", inventoryItem.ID); + } + + // Record whether or not the item is to be initially worn + try + { + if (select && (GetStringAttribute(item, "wear", "false") == "true")) + { + avatarAppearance.Wearables[inventoryItem.Flags].Wear(inventoryItem.ID, inventoryItem.AssetID); + } + } + catch (Exception e) + { + m_log.WarnFormat("[RADMIN]: Error wearing item {0} : {1}", inventoryItem.ID, e.Message); + } + } // foreach item in outfit + m_log.DebugFormat("[RADMIN]: Outfit {0} load completed", outfitName); + } // foreach outfit + m_log.DebugFormat("[RADMIN]: Inventory update complete for {0}", name); + scene.AvatarService.SetAppearance(ID, avatarAppearance); + } + catch (Exception e) + { + m_log.WarnFormat("[RADMIN]: Inventory processing incomplete for user {0} : {1}", + name, e.Message); + } + } // End of include + } + m_log.DebugFormat("[RADMIN]: Default avatar loading complete"); + } + else + { + m_log.DebugFormat("[RADMIN]: No default avatar information available"); + return false; + } + } + catch (Exception e) + { + m_log.WarnFormat("[RADMIN]: Exception whilst loading default avatars ; {0}", e.Message); + return false; + } + + return true; + } + } +} diff --git a/OpenSim/Capabilities/Caps.cs b/OpenSim/Capabilities/Caps.cs new file mode 100644 index 0000000000..049afab467 --- /dev/null +++ b/OpenSim/Capabilities/Caps.cs @@ -0,0 +1,259 @@ +/* + * 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.Reflection; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; + +// using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Framework.Capabilities +{ + /// + /// XXX Probably not a particularly nice way of allow us to get the scene presence from the scene (chiefly so that + /// we can popup a message on the user's client if the inventory service has permanently failed). But I didn't want + /// to just pass the whole Scene into CAPS. + /// + public delegate IClientAPI GetClientDelegate(UUID agentID); + + public class Caps + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_httpListenerHostName; + private uint m_httpListenPort; + + /// + /// This is the uuid portion of every CAPS path. It is used to make capability urls private to the requester. + /// + private string m_capsObjectPath; + public string CapsObjectPath { get { return m_capsObjectPath; } } + + private CapsHandlers m_capsHandlers; + + private Dictionary m_pollServiceHandlers + = new Dictionary(); + + private Dictionary m_externalCapsHandlers = new Dictionary(); + + private IHttpServer m_httpListener; + private UUID m_agentID; + private string m_regionName; + + public UUID AgentID + { + get { return m_agentID; } + } + + public string RegionName + { + get { return m_regionName; } + } + + public string HostName + { + get { return m_httpListenerHostName; } + } + + public uint Port + { + get { return m_httpListenPort; } + } + + public IHttpServer HttpListener + { + get { return m_httpListener; } + } + + public bool SSLCaps + { + get { return m_httpListener.UseSSL; } + } + + public string SSLCommonName + { + get { return m_httpListener.SSLCommonName; } + } + + public CapsHandlers CapsHandlers + { + get { return m_capsHandlers; } + } + + public Dictionary ExternalCapsHandlers + { + get { return m_externalCapsHandlers; } + } + + public Caps(IHttpServer httpServer, string httpListen, uint httpPort, string capsPath, + UUID agent, string regionName) + { + m_capsObjectPath = capsPath; + m_httpListener = httpServer; + m_httpListenerHostName = httpListen; + + m_httpListenPort = httpPort; + + if (httpServer != null && httpServer.UseSSL) + { + m_httpListenPort = httpServer.SSLPort; + httpListen = httpServer.SSLCommonName; + httpPort = httpServer.SSLPort; + } + + m_agentID = agent; + m_capsHandlers = new CapsHandlers(httpServer, httpListen, httpPort, (httpServer == null) ? false : httpServer.UseSSL); + m_regionName = regionName; + } + + /// + /// Register a handler. This allows modules to register handlers. + /// + /// + /// + 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 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); + + m_pollServiceHandlers.Add(capName, pollServiceHandler); + + m_httpListener.AddPollServiceHTTPHandler(pollServiceHandler.Url, 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)); + } + + /// + /// Register an external handler. The service for this capability is somewhere else + /// given by the URL. + /// + /// + /// + public void RegisterHandler(string capsName, string url) + { + m_externalCapsHandlers.Add(capsName, url); + } + + /// + /// Remove all CAPS service handlers. + /// + public void DeregisterHandlers() + { + foreach (string capsName in m_capsHandlers.Caps) + { + m_capsHandlers.Remove(capsName); + } + + foreach (PollServiceEventArgs handler in m_pollServiceHandlers.Values) + { + m_httpListener.RemovePollServiceHTTPHandler("", handler.Url); + } + } + + public bool TryGetPollHandler(string name, out PollServiceEventArgs pollHandler) + { + return m_pollServiceHandlers.TryGetValue(name, out pollHandler); + } + + public Dictionary GetPollHandlers() + { + return new Dictionary(m_pollServiceHandlers); + } + + /// + /// Return an LLSD-serializable Hashtable describing the + /// capabilities and their handler details. + /// + /// If true, then exclude the seed cap. + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) + { + Hashtable caps = CapsHandlers.GetCapsDetails(excludeSeed, requestedCaps); + + lock (m_pollServiceHandlers) + { + foreach (KeyValuePair kvp in m_pollServiceHandlers) + { + 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.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl)); + + caps[kvp.Key] = string.Format("{0}://{1}:{2}{3}", protocol, hostName, port, kvp.Value.Url); + } + } + + // Add the external too + foreach (KeyValuePair kvp in ExternalCapsHandlers) + { + if (!requestedCaps.Contains(kvp.Key)) + continue; + + caps[kvp.Key] = kvp.Value; + } + + return caps; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/CapsHandlers.cs b/OpenSim/Capabilities/CapsHandlers.cs new file mode 100644 index 0000000000..890df9091c --- /dev/null +++ b/OpenSim/Capabilities/CapsHandlers.cs @@ -0,0 +1,200 @@ +/* + * 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; +using System.Collections.Generic; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Framework.Capabilities +{ + /// + /// CapsHandlers is a cap handler container but also takes + /// care of adding and removing cap handlers to and from the + /// supplied BaseHttpServer. + /// + public class CapsHandlers + { + private Dictionary m_capsHandlers = new Dictionary(); + private IHttpServer m_httpListener; + private string m_httpListenerHostName; + private uint m_httpListenerPort; + private bool m_useSSL = false; + + /// + /// CapsHandlers is a cap handler container but also takes + /// care of adding and removing cap handlers to and from the + /// supplied BaseHttpServer. + /// + /// base HTTP server + /// host name of the HTTP server + /// HTTP port + public CapsHandlers(BaseHttpServer httpListener, string httpListenerHostname, uint httpListenerPort) + : this(httpListener,httpListenerHostname,httpListenerPort, false) + { + } + + /// + /// CapsHandlers is a cap handler container but also takes + /// care of adding and removing cap handlers to and from the + /// supplied BaseHttpServer. + /// + /// base HTTP server + /// host name of the HTTP + /// server + /// HTTP port + public CapsHandlers(IHttpServer httpListener, string httpListenerHostname, uint httpListenerPort, bool https) + { + m_httpListener = httpListener; + m_httpListenerHostName = httpListenerHostname; + m_httpListenerPort = httpListenerPort; + m_useSSL = https; + if (httpListener != null && m_useSSL) + { + m_httpListenerHostName = httpListener.SSLCommonName; + m_httpListenerPort = httpListener.SSLPort; + } + } + + /// + /// Remove the cap handler for a capability. + /// + /// name of the capability of the cap + /// handler to be removed + public void Remove(string capsName) + { + lock (m_capsHandlers) + { + m_httpListener.RemoveStreamHandler("POST", m_capsHandlers[capsName].Path); + m_httpListener.RemoveStreamHandler("GET", m_capsHandlers[capsName].Path); + m_capsHandlers.Remove(capsName); + } + } + + public bool ContainsCap(string cap) + { + lock (m_capsHandlers) + return m_capsHandlers.ContainsKey(cap); + } + + /// + /// The indexer allows us to treat the CapsHandlers object + /// in an intuitive dictionary like way. + /// + /// + /// The indexer will throw an exception when you try to + /// retrieve a cap handler for a cap that is not contained in + /// CapsHandlers. + /// + public IRequestHandler this[string idx] + { + get + { + lock (m_capsHandlers) + return m_capsHandlers[idx]; + } + + set + { + lock (m_capsHandlers) + { + if (m_capsHandlers.ContainsKey(idx)) + { + m_httpListener.RemoveStreamHandler("POST", m_capsHandlers[idx].Path); + m_capsHandlers.Remove(idx); + } + + if (null == value) return; + + m_capsHandlers[idx] = value; + m_httpListener.AddStreamHandler(value); + } + } + } + + /// + /// Return the list of cap names for which this CapsHandlers + /// object contains cap handlers. + /// + public string[] Caps + { + get + { + lock (m_capsHandlers) + { + string[] __keys = new string[m_capsHandlers.Keys.Count]; + m_capsHandlers.Keys.CopyTo(__keys, 0); + return __keys; + } + } + } + + /// + /// Return an LLSD-serializable Hashtable describing the + /// capabilities and their handler details. + /// + /// If true, then exclude the seed cap. + public Hashtable GetCapsDetails(bool excludeSeed, List requestedCaps) + { + Hashtable caps = new Hashtable(); + string protocol = "http://"; + + if (m_useSSL) + protocol = "https://"; + + string baseUrl = protocol + m_httpListenerHostName + ":" + m_httpListenerPort.ToString(); + + lock (m_capsHandlers) + { + foreach (string capsName in m_capsHandlers.Keys) + { + if (excludeSeed && "SEED" == capsName) + continue; + + if (requestedCaps != null && !requestedCaps.Contains(capsName)) + continue; + + caps[capsName] = baseUrl + m_capsHandlers[capsName].Path; + } + } + + return caps; + } + + /// + /// Returns a copy of the dictionary of all the HTTP cap handlers + /// + /// + /// The dictionary copy. The key is the capability name, the value is the HTTP handler. + /// + public Dictionary GetCapsHandlers() + { + lock (m_capsHandlers) + return new Dictionary(m_capsHandlers); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs b/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs new file mode 100644 index 0000000000..426174d252 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/AvatarPickerSearch/AvatarPickerSearchHandler.cs @@ -0,0 +1,116 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Reflection; +using System.Web; +using log4net; +using Nini.Config; +using OpenMetaverse; +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 AvatarPickerSearchHandler : BaseStreamHandler + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IPeople m_PeopleService; + + public AvatarPickerSearchHandler(string path, IPeople peopleService, string name, string description) + : base("GET", path, name, description) + { + m_PeopleService = peopleService; + } + + 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 names = query.GetOne("names"); + string psize = query.GetOne("page_size"); + string pnumber = query.GetOne("page"); + + if (m_PeopleService == null) + return FailureResponse(names, (int)System.Net.HttpStatusCode.InternalServerError, httpResponse); + + if (string.IsNullOrEmpty(names) || names.Length < 3) + return FailureResponse(names, (int)System.Net.HttpStatusCode.BadRequest, httpResponse); + + m_log.DebugFormat("[AVATAR PICKER SEARCH]: search for {0}", names); + + int page_size = (string.IsNullOrEmpty(psize) ? 500 : Int32.Parse(psize)); + int page_number = (string.IsNullOrEmpty(pnumber) ? 1 : Int32.Parse(pnumber)); + + // Full content request + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.OK; + //httpResponse.ContentLength = ??; + httpResponse.ContentType = "application/llsd+xml"; + + List users = m_PeopleService.GetUserData(names, page_size, page_number); + + LLSDAvatarPicker osdReply = new LLSDAvatarPicker(); + osdReply.next_page_url = httpRequest.RawUrl; + foreach (UserData u in users) + osdReply.agents.Array.Add(ConvertUserData(u)); + + string reply = LLSDHelpers.SerialiseLLSDReply(osdReply); + return System.Text.Encoding.UTF8.GetBytes(reply); + } + + private LLSDPerson ConvertUserData(UserData user) + { + LLSDPerson p = new LLSDPerson(); + p.legacy_first_name = user.FirstName; + p.legacy_last_name = user.LastName; + p.display_name = user.FirstName + " " + user.LastName; + if (user.LastName.StartsWith("@")) + p.username = user.FirstName.ToLower() + user.LastName.ToLower(); + else + p.username = user.FirstName.ToLower() + "." + user.LastName.ToLower(); + p.id = user.Id; + p.is_display_name_default = false; + return p; + } + + private byte[] FailureResponse(string names, int statuscode, IOSHttpResponse httpResponse) + { + m_log.Error("[AVATAR PICKER SEARCH]: Error searching for " + names); + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return System.Text.Encoding.UTF8.GetBytes(string.Empty); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs new file mode 100644 index 0000000000..7197049f16 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescHandler.cs @@ -0,0 +1,848 @@ +/* + * 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.Linq; +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 FetchInvDescHandler + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + private ILibraryService m_LibraryService; + private IScene m_Scene; +// private object m_fetchLock = new Object(); + + public FetchInvDescHandler(IInventoryService invService, ILibraryService libService, IScene s) + { + m_InventoryService = invService; + m_LibraryService = libService; + m_Scene = s; + } + + + public string FetchInventoryDescendentsRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + //m_log.DebugFormat("[XXX]: FetchInventoryDescendentsRequest in {0}, {1}", (m_Scene == null) ? "none" : m_Scene.Name, 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("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000"); + + // another hack 1 results in a + // System.ArgumentException: Object type System.Int32 cannot + // be converted to target type: System.Boolean + // + request = request.Replace("fetch_folders0", "fetch_folders0"); + request = request.Replace("fetch_folders1", "fetch_folders1"); + + 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 = ""; + string bad_folders_response = ""; + + List folders = new List(); + for (int i = 0; i < foldersrequested.Count; i++) + { + 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); + continue; + } + + // Filter duplicate folder ids that bad viewers may send + if (folders.Find(f => f.folder_id == llsdRequest.folder_id) == null) + folders.Add(llsdRequest); + + } + + if (folders.Count > 0) + { + List bad_folders = new List(); + List invcollSet = Fetch(folders, bad_folders); + //m_log.DebugFormat("[XXX]: Got {0} folders from a request of {1}", invcollSet.Count, folders.Count); + + if (invcollSet == null) + { + m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Multiple folder fetch failed. Trying old protocol."); +#pragma warning disable 0612 + return FetchInventoryDescendentsRequest(foldersrequested, httpRequest, httpResponse); +#pragma warning restore 0612 + } + + string inventoryitemstr = string.Empty; + foreach (InventoryCollectionWithDescendents icoll in invcollSet) + { + LLSDInventoryDescendents reply = ToLLSD(icoll.Collection, icoll.Descendents); + + inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); + inventoryitemstr = inventoryitemstr.Replace("folders", ""); + inventoryitemstr = inventoryitemstr.Replace("", ""); + + response += inventoryitemstr; + } + + //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Bad folders {0}", string.Join(", ", bad_folders)); + foreach (UUID bad in bad_folders) + bad_folders_response += "" + bad + ""; + } + + if (response.Length == 0) + { + /* Viewers expect a bad_folders array when not available */ + if (bad_folders_response.Length != 0) + { + response = "bad_folders" + bad_folders_response + ""; + } + else + { + response = "folders"; + } + } + else + { + if (bad_folders_response.Length != 0) + { + response = "folders" + response + "bad_folders" + bad_folders_response + ""; + } + else + { + response = "folders" + response + ""; + } + } + + //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Replying to CAPS fetch inventory request for {0} folders. Item count {1}", folders.Count, item_count); + //m_log.Debug("[WEB FETCH INV DESC HANDLER] " + response); + + return response; + + } + + /// + /// Construct an LLSD reply packet to a CAPS inventory request + /// + /// + /// + 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(); + inv.Items = new List(); + int version = 0; + int descendents = 0; + +#pragma warning disable 0612 + inv = Fetch( + invFetch.owner_id, invFetch.folder_id, invFetch.owner_id, + invFetch.fetch_folders, invFetch.fetch_items, invFetch.sort_order, out version, out descendents); +#pragma warning restore 0612 + + if (inv != null && inv.Folders != null) + { + foreach (InventoryFolderBase invFolder in inv.Folders) + { + contents.categories.Array.Add(ConvertInventoryFolder(invFolder)); + } + + descendents += inv.Folders.Count; + } + + if (inv != null && 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; + } + + private LLSDInventoryDescendents ToLLSD(InventoryCollection inv, int descendents) + { + LLSDInventoryDescendents reply = new LLSDInventoryDescendents(); + LLSDInventoryFolderContents contents = new LLSDInventoryFolderContents(); + contents.agent_id = inv.OwnerID; + contents.owner_id = inv.OwnerID; + contents.folder_id = inv.FolderID; + + reply.folders.Array.Add(contents); + + 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 = inv.Version; + + return reply; + } + /// + /// Old style. Soon to be deprecated. + /// + /// + /// + /// + /// + [Obsolete] + private string FetchInventoryDescendentsRequest(ArrayList foldersrequested, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Received request for {0} folders", foldersrequested.Count); + + string response = ""; + string bad_folders_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); + + if (null == reply) + { + bad_folders_response += "" + llsdRequest.folder_id.ToString() + ""; + } + else + { + inventoryitemstr = LLSDHelpers.SerialiseLLSDReply(reply); + inventoryitemstr = inventoryitemstr.Replace("folders", ""); + inventoryitemstr = inventoryitemstr.Replace("", ""); + } + + response += inventoryitemstr; + } + + if (response.Length == 0) + { + /* Viewers expect a bad_folders array when not available */ + if (bad_folders_response.Length != 0) + { + response = "bad_folders" + bad_folders_response + ""; + } + else + { + response = "folders"; + } + } + else + { + if (bad_folders_response.Length != 0) + { + response = "folders" + response + "bad_folders" + bad_folders_response + ""; + } + else + { + response = "folders" + response + ""; + } + } + + // 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; + + // } + } + + /// + /// Handle the caps inventory descendents fetch. + /// + /// + /// + /// + /// + /// + /// + /// + /// An empty InventoryCollection if the inventory look up failed + [Obsolete] + 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(); + ret.Items = fold.RequestListOfItems(); + descendents = ret.Folders.Count + ret.Items.Count; + + return ret; + } + } + + InventoryCollection contents = new InventoryCollection(); + + if (folderID != UUID.Zero) + { + InventoryCollection fetchedContents = m_InventoryService.GetFolderContent(agentID, folderID); + + if (fetchedContents == null) + { + m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of folder {0} for user {1}", folderID, agentID); + return contents; + } + contents = fetchedContents; + 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 itemsToReturn = contents.Items; + List originalItems = new List(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 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 linkedItemFolderIdsToSend = new HashSet(); +// 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; + + } + + private void AddLibraryFolders(List fetchFolders, List result) + { + InventoryFolderImpl fold; + if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null) + { + List libfolders = fetchFolders.FindAll(f => f.owner_id == m_LibraryService.LibraryRootFolder.Owner); + fetchFolders.RemoveAll(f => libfolders.Contains(f)); + + //m_log.DebugFormat("[XXX]: Found {0} library folders in request", libfolders.Count); + + foreach (LLSDFetchInventoryDescendents f in libfolders) + { + if ((fold = m_LibraryService.LibraryRootFolder.FindFolder(f.folder_id)) != null) + { + InventoryCollectionWithDescendents ret = new InventoryCollectionWithDescendents(); + ret.Collection = new InventoryCollection(); + ret.Collection.Folders = new List(); + ret.Collection.Items = fold.RequestListOfItems(); + ret.Collection.OwnerID = m_LibraryService.LibraryRootFolder.Owner; + ret.Collection.FolderID = f.folder_id; + ret.Collection.Version = fold.Version; + + ret.Descendents = ret.Collection.Items.Count; + result.Add(ret); + + //m_log.DebugFormat("[XXX]: Added libfolder {0} ({1}) {2}", ret.Collection.FolderID, ret.Collection.OwnerID); + } + } + } + } + + private List Fetch(List fetchFolders, List bad_folders) + { + //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 result = new List(); + + AddLibraryFolders(fetchFolders, result); + + // 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) + LLSDFetchInventoryDescendents zero = fetchFolders.Find(f => f.folder_id == UUID.Zero); + if (zero != null) + { + fetchFolders.Remove(zero); + BadFolder(zero, null, bad_folders); + } + + if (fetchFolders.Count > 0) + { + UUID[] fids = new UUID[fetchFolders.Count]; + int i = 0; + foreach (LLSDFetchInventoryDescendents f in fetchFolders) + fids[i++] = f.folder_id; + + //m_log.DebugFormat("[XXX]: {0}", string.Join(",", fids)); + + InventoryCollection[] fetchedContents = m_InventoryService.GetMultipleFoldersContent(fetchFolders[0].owner_id, fids); + + if (fetchedContents == null || (fetchedContents != null && fetchedContents.Length == 0)) + { + m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Could not get contents of multiple folders for user {0}", fetchFolders[0].owner_id); + foreach (LLSDFetchInventoryDescendents freq in fetchFolders) + BadFolder(freq, null, bad_folders); + return null; + } + + 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 = fetchFolders[i++]; + + InventoryCollectionWithDescendents coll = new InventoryCollectionWithDescendents(); + coll.Collection = contents; + + if (BadFolder(freq, contents, bad_folders)) + continue; + + // Next: link management + ProcessLinks(freq, coll); + + result.Add(coll); + } + } + + return result; + } + + private bool BadFolder(LLSDFetchInventoryDescendents freq, InventoryCollection contents, List bad_folders) + { + bool bad = false; + if (contents == null) + { + bad_folders.Add(freq.folder_id); + bad = true; + } + + // The inventory server isn't sending FolderID in the collection... + // Must fetch it individually + else if (contents.FolderID == UUID.Zero) + { + InventoryFolderBase containingFolder = new InventoryFolderBase(); + containingFolder.ID = freq.folder_id; + containingFolder.Owner = freq.owner_id; + containingFolder = m_InventoryService.GetFolder(containingFolder); + + if (containingFolder != null) + { + contents.FolderID = containingFolder.ID; + contents.OwnerID = containingFolder.Owner; + contents.Version = containingFolder.Version; + } + else + { + // Was it really a request for folder Zero? + // This is an overkill, but Firestorm really asks for folder Zero. + // I'm leaving the code here for the time being, but commented. + if (freq.folder_id == UUID.Zero) + { + //coll.Collection.OwnerID = freq.owner_id; + //coll.Collection.FolderID = contents.FolderID; + //containingFolder = m_InventoryService.GetRootFolder(freq.owner_id); + //if (containingFolder != null) + //{ + // m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Request for parent of folder {0}", containingFolder.ID); + // coll.Collection.Folders.Clear(); + // coll.Collection.Folders.Add(containingFolder); + // if (m_LibraryService != null && m_LibraryService.LibraryRootFolder != null) + // { + // InventoryFolderBase lib = new InventoryFolderBase(m_LibraryService.LibraryRootFolder.ID, m_LibraryService.LibraryRootFolder.Owner); + // lib.Name = m_LibraryService.LibraryRootFolder.Name; + // lib.Type = m_LibraryService.LibraryRootFolder.Type; + // lib.Version = m_LibraryService.LibraryRootFolder.Version; + // coll.Collection.Folders.Add(lib); + // } + // coll.Collection.Items.Clear(); + //} + } + else + { + m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: Unable to fetch folder {0}", freq.folder_id); + bad_folders.Add(freq.folder_id); + } + bad = true; + } + } + + return bad; + } + + private void ProcessLinks(LLSDFetchInventoryDescendents freq, InventoryCollectionWithDescendents coll) + { + InventoryCollection contents = coll.Collection; + + if (freq.fetch_items && contents.Items != null) + { + List itemsToReturn = contents.Items; + + // descendents must only include the links, not the linked items we add + coll.Descendents = itemsToReturn.Count; + + // Add target items for links in this folder before the links themselves. + List itemIDs = new List(); + List folderIDs = new List(); + foreach (InventoryItemBase item in itemsToReturn) + { + //m_log.DebugFormat("[XXX]: {0} {1}", item.Name, item.AssetType); + if (item.AssetType == (int)AssetType.Link) + itemIDs.Add(item.AssetID); + + else if (item.AssetType == (int)AssetType.LinkFolder) + folderIDs.Add(item.AssetID); + } + + //m_log.DebugFormat("[XXX]: folder {0} has {1} links and {2} linkfolders", contents.FolderID, itemIDs.Count, folderIDs.Count); + + // Scan for folder links and insert the items they target and those links at the head of the return data + if (folderIDs.Count > 0) + { + InventoryCollection[] linkedFolders = m_InventoryService.GetMultipleFoldersContent(coll.Collection.OwnerID, folderIDs.ToArray()); + foreach (InventoryCollection linkedFolderContents in linkedFolders) + { + if (linkedFolderContents == null) + continue; + + List links = linkedFolderContents.Items; + + itemsToReturn.InsertRange(0, links); + + } + } + + if (itemIDs.Count > 0) + { + InventoryItemBase[] linked = m_InventoryService.GetMultipleItems(freq.owner_id, itemIDs.ToArray()); + if (linked == null) + { + // OMG!!! One by one!!! This is fallback code, in case the backend isn't updated + m_log.WarnFormat("[WEB FETCH INV DESC HANDLER]: GetMultipleItems failed. Falling back to fetching inventory items one by one."); + linked = new InventoryItemBase[itemIDs.Count]; + int i = 0; + InventoryItemBase item = new InventoryItemBase(); + item.Owner = freq.owner_id; + foreach (UUID id in itemIDs) + { + item.ID = id; + linked[i++] = m_InventoryService.GetItem(item); + } + } + + //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Processing folder {0}. Existing items:", freq.folder_id); + //foreach (InventoryItemBase item in itemsToReturn) + // m_log.DebugFormat("[XXX]: {0} {1} {2}", item.Name, item.AssetType, item.Folder); + + if (linked != null) + { + 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) + { + itemsToReturn.Insert(0, linkedItem); + //m_log.DebugFormat("[WEB FETCH INV DESC HANDLER]: Added {0} {1} {2}", linkedItem.Name, linkedItem.AssetType, linkedItem.Folder); + } + } + } + } + } + + } + + /// + /// Convert an internal inventory folder object into an LLSD object. + /// + /// + /// + 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; + } + + /// + /// Convert an internal inventory item object into an LLSD object. + /// + /// + /// + 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; + } + } + + class InventoryCollectionWithDescendents + { + public InventoryCollection Collection; + public int Descendents; + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs new file mode 100644 index 0000000000..9dcfaa48f1 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInvDescServerConnector.cs @@ -0,0 +1,82 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Capabilities.Handlers +{ + public class FetchInvDescServerConnector : ServiceConnector + { + private IInventoryService m_InventoryService; + private ILibraryService m_LibraryService; + private string m_ConfigName = "CapsService"; + + public FetchInvDescServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string invService = serverConfig.GetString("InventoryService", String.Empty); + + if (invService == String.Empty) + throw new Exception("No InventoryService in config file"); + + Object[] args = new Object[] { config }; + m_InventoryService = + ServerUtils.LoadPlugin(invService, args); + + if (m_InventoryService == null) + throw new Exception(String.Format("Failed to load InventoryService from {0}; config is {1}", invService, m_ConfigName)); + + string libService = serverConfig.GetString("LibraryService", String.Empty); + m_LibraryService = + ServerUtils.LoadPlugin(libService, args); + + FetchInvDescHandler webFetchHandler = new FetchInvDescHandler(m_InventoryService, m_LibraryService, null); + IRequestHandler reqHandler + = new RestStreamHandler( + "POST", + "/CAPS/WebFetchInvDesc/" /*+ UUID.Random()*/, + webFetchHandler.FetchInventoryDescendentsRequest, + "FetchInvDescendents", + null); + server.AddStreamHandler(reqHandler); + } + + } +} diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs new file mode 100644 index 0000000000..c904392633 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/FetchInventory2Handler.cs @@ -0,0 +1,141 @@ +/* + * 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.Reflection; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Capabilities; +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"]; + + string reply; + LLSDFetchInventory llsdReply = new LLSDFetchInventory(); + + UUID[] itemIDs = new UUID[itemsRequested.Count]; + int i = 0; + foreach (OSDMap osdItemId in itemsRequested) + { + itemIDs[i++] = osdItemId["item_id"].AsUUID(); + } + + InventoryItemBase[] items = m_inventoryService.GetMultipleItems(m_agentID, itemIDs); + + if (items == null) + { + // OMG!!! One by one!!! This is fallback code, in case the backend isn't updated + m_log.WarnFormat("[FETCH INVENTORY HANDLER]: GetMultipleItems failed. Falling back to fetching inventory items one by one."); + items = new InventoryItemBase[itemsRequested.Count]; + i = 0; + InventoryItemBase item = new InventoryItemBase(); + item.Owner = m_agentID; + foreach (UUID id in itemIDs) + { + item.ID = id; + items[i++] = m_inventoryService.GetItem(item); + } + } + + foreach (InventoryItemBase item in items) + { + 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; + } + + /// + /// Convert an internal inventory item object into an LLSD object. + /// + /// + /// + 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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventory2HandlerTests.cs b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventory2HandlerTests.cs new file mode 100644 index 0000000000..8af3c6485d --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventory2HandlerTests.cs @@ -0,0 +1,170 @@ +/* + * 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 + // + // /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 = "itemsitem_id"; + request += "10000000-0000-0000-0000-000000000001"; // Notecard 1 + request += ""; + + 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 = "items"; + request += "item_id10000000-0000-0000-0000-000000000001"; // Notecard 1 + request += "item_id10000000-0000-0000-0000-000000000002"; // Notecard 2 + request += "item_id10000000-0000-0000-0000-000000000003"; // Notecard 3 + request += "item_id10000000-0000-0000-0000-000000000004"; // Notecard 4 + request += "item_id10000000-0000-0000-0000-000000000005"; // Notecard 5 + request += ""; + + 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"); + } + + } + +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs new file mode 100644 index 0000000000..2d5531a1ef --- /dev/null +++ b/OpenSim/Capabilities/Handlers/FetchInventory/Tests/FetchInventoryDescendents2HandlerTests.cs @@ -0,0 +1,292 @@ +/* + * 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 FetchInventoryDescendents2HandlerTests : OpenSimTestCase + { + private UUID m_userID = UUID.Zero; + 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 + // + // /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); + 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); + } + + [Test] + public void Test_001_SimpleFolder() + { + TestHelpers.InMethod(); + + Init(); + + FetchInvDescHandler handler = new FetchInvDescHandler(m_scene.InventoryService, null, m_scene); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + + string request = "foldersfetch_folders1fetch_items1folder_id"; + request += m_rootFolderID; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + + string llsdresponse = handler.FetchInventoryDescendentsRequest(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("00000000-0000-0000-0000-000000000000"), Is.True, "Response should contain userID"); + + string descendents = "descendents" + m_rootDescendents + ""; + 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); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + + string request = "folders"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_rootFolderID; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_notecardsFolder; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += ""; + + string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp); + Console.WriteLine(llsdresponse); + + string descendents = "descendents" + m_rootDescendents + ""; + Assert.That(llsdresponse.Contains(descendents), Is.True, "Incorrect number of descendents for root folder"); + descendents = "descendents2"; + 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); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + + string request = "foldersfetch_folders1fetch_items1folder_id"; + request += "f0000000-0000-0000-0000-00000000000f"; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + + string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp); + Console.WriteLine(llsdresponse); + + string descendents = "descendents2"; + 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"); + + // 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); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + + string request = "folders"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_rootFolderID; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_notecardsFolder; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_rootFolderID; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += "fetch_folders1fetch_items1folder_id"; + request += m_notecardsFolder; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + request += ""; + + string llsdresponse = handler.FetchInventoryDescendentsRequest(request, "/FETCH", string.Empty, req, resp); + Console.WriteLine(llsdresponse); + + string root_folder = "folder_id" + m_rootFolderID + ""; + string notecards_folder = "folder_id" + m_notecardsFolder + ""; + + 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); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + + string request = "foldersfetch_folders1fetch_items1folder_id"; + request += UUID.Zero; + request += "owner_id00000000-0000-0000-0000-000000000000sort_order1"; + + string llsdresponse = handler.FetchInventoryDescendentsRequest(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("bad_folders00000000-0000-0000-0000-000000000000"), Is.True, "Folder Zero should be a bad folder"); + + Console.WriteLine(llsdresponse); + } + + } + +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesHandler.cs b/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesHandler.cs new file mode 100644 index 0000000000..589602df9c --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesHandler.cs @@ -0,0 +1,120 @@ +/* + * 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.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; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; +using OSDArray = OpenMetaverse.StructuredData.OSDArray; + +namespace OpenSim.Capabilities.Handlers +{ + public class GetDisplayNamesHandler : BaseStreamHandler + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IUserManagement m_UserManagement; + + public GetDisplayNamesHandler(string path, IUserManagement umService, string name, string description) + : base("GET", path, name, description) + { + m_UserManagement = umService; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + m_log.DebugFormat("[GET_DISPLAY_NAMES]: called {0}", httpRequest.Url.Query); + + NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); + string[] ids = query.GetValues("ids"); + + + if (m_UserManagement == null) + { + m_log.Error("[GET_DISPLAY_NAMES]: Cannot fetch display names without a user management component"); + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError; + return new byte[0]; + } + + OSDMap osdReply = new OSDMap(); + OSDArray agents = new OSDArray(); + + osdReply["agents"] = agents; + foreach (string id in ids) + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(id, out uuid)) + { + string name = m_UserManagement.GetUserName(uuid); + if (!string.IsNullOrEmpty(name)) + { + string[] parts = name.Split(new char[] {' '}); + OSDMap osdname = new OSDMap(); + osdname["display_name_next_update"] = OSD.FromDate(DateTime.MinValue); + osdname["display_name_expires"] = OSD.FromDate(DateTime.Now.AddMonths(1)); + osdname["display_name"] = OSD.FromString(name); + osdname["legacy_first_name"] = parts[0]; + osdname["legacy_last_name"] = parts[1]; + osdname["username"] = OSD.FromString(name); + osdname["id"] = OSD.FromUUID(uuid); + osdname["is_display_name_default"] = OSD.FromBoolean(true); + + agents.Add(osdname); + } + } + } + + // Full content request + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.OK; + //httpResponse.ContentLength = ??; + httpResponse.ContentType = "application/llsd+xml"; + + string reply = OSDParser.SerializeLLSDXmlString(osdReply); + return System.Text.Encoding.UTF8.GetBytes(reply); + + } + + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesServerConnector.cs b/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesServerConnector.cs new file mode 100644 index 0000000000..d42de56e9f --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetDisplayNames/GetDisplayNamesServerConnector.cs @@ -0,0 +1,71 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Capabilities.Handlers +{ + public class GetDisplayNamesServerConnector : ServiceConnector + { + private IUserManagement m_UserManagement; + private string m_ConfigName = "CapsService"; + + public GetDisplayNamesServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string umService = serverConfig.GetString("AssetService", String.Empty); + + if (umService == String.Empty) + throw new Exception("No AssetService in config file"); + + Object[] args = new Object[] { config }; + m_UserManagement = + ServerUtils.LoadPlugin(umService, args); + + if (m_UserManagement == null) + throw new Exception(String.Format("Failed to load UserManagement from {0}; config is {1}", umService, m_ConfigName)); + + string rurl = serverConfig.GetString("GetTextureRedirectURL"); + + server.AddStreamHandler( + new GetDisplayNamesHandler("/CAPS/agents/", m_UserManagement, "GetDisplayNames", null)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs new file mode 100644 index 0000000000..6b67da1e3a --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshHandler.cs @@ -0,0 +1,253 @@ +/* + * 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 log4net; +using OpenMetaverse; +using OpenMetaverse.Imaging; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; +using System; +using System.Collections.Specialized; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Web; + +namespace OpenSim.Capabilities.Handlers +{ + public class GetMeshHandler : BaseStreamHandler + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IAssetService m_assetService; + + // TODO: Change this to a config option + private string m_RedirectURL = null; + + public GetMeshHandler(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("mesh_id"); + + if (m_assetService == null) + { + m_log.Error("[GETMESH]: Cannot fetch mesh " + textureStr + " without an asset service"); + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + } + + UUID meshID; + if (!String.IsNullOrEmpty(textureStr) && UUID.TryParse(textureStr, out meshID)) + { + // OK, we have an array with preferred formats, possibly with only one entry + + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + AssetBase mesh; + + if (!String.IsNullOrEmpty(m_RedirectURL)) + { + // Only try to fetch locally cached meshes. Misses are redirected + mesh = m_assetService.GetCached(meshID.ToString()); + + if (mesh != null) + { + if (mesh.Type != (sbyte)AssetType.Mesh) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + } + WriteMeshData(httpRequest, httpResponse, mesh); + } + else + { + string textureUrl = m_RedirectURL + "?mesh_id="+ meshID.ToString(); + m_log.Debug("[GETMESH]: Redirecting mesh request to " + textureUrl); + httpResponse.StatusCode = (int)OSHttpStatusCode.RedirectMovedPermanently; + httpResponse.RedirectLocation = textureUrl; + return null; + } + } + else // no redirect + { + // try the cache + mesh = m_assetService.GetCached(meshID.ToString()); + + if (mesh == null) + { + // Fetch locally or remotely. Misses return a 404 + mesh = m_assetService.Get(meshID.ToString()); + + if (mesh != null) + { + if (mesh.Type != (sbyte)AssetType.Mesh) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return null; + } + WriteMeshData(httpRequest, httpResponse, mesh); + return null; + } + } + else // it was on the cache + { + if (mesh.Type != (sbyte)AssetType.Mesh) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return null; + } + WriteMeshData(httpRequest, httpResponse, mesh); + return null; + } + } + + // not found + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return null; + } + else + { + m_log.Warn("[GETTEXTURE]: Failed to parse a mesh_id from GetMesh request: " + httpRequest.Url); + } + + return null; + } + + private void WriteMeshData(IOSHttpRequest request, IOSHttpResponse response, AssetBase texture) + { + string range = request.Headers.GetOne("Range"); + + if (!String.IsNullOrEmpty(range)) + { + // 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) + { + 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; + + if (0 == start && len == texture.Data.Length) + { + response.StatusCode = (int)System.Net.HttpStatusCode.OK; + } + else + { + response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent; + response.AddHeader("Content-Range", String.Format("bytes {0}-{1}/{2}", start, end, texture.Data.Length)); + } + + response.ContentLength = len; + response.ContentType = "application/vnd.ll.mesh"; + + response.Body.Write(texture.Data, start, len); + } + } + else + { + m_log.Warn("[GETMESH]: Malformed Range header: " + range); + response.StatusCode = (int)System.Net.HttpStatusCode.BadRequest; + } + } + else + { + // Full content request + response.StatusCode = (int)System.Net.HttpStatusCode.OK; + response.ContentLength = texture.Data.Length; + response.ContentType = "application/vnd.ll.mesh"; + response.Body.Write(texture.Data, 0, texture.Data.Length); + } + } + + /// + /// Parse a range header. + /// + /// + /// 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. + /// + /// + /// Start of the range. Undefined if this was not a number. + /// End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number. + 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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs new file mode 100644 index 0000000000..19de3cfea5 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetMesh/GetMeshServerConnector.cs @@ -0,0 +1,76 @@ +/* + * 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 Nini.Config; +using OpenMetaverse; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using System; + +namespace OpenSim.Capabilities.Handlers +{ + public class GetMeshServerConnector : ServiceConnector + { + private IAssetService m_AssetService; + private string m_ConfigName = "CapsService"; + + public GetMeshServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("AssetService", String.Empty); + + if (assetService == String.Empty) + throw new Exception("No AssetService in config file"); + + Object[] args = new Object[] { config }; + m_AssetService = + ServerUtils.LoadPlugin(assetService, args); + + 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"); + + server.AddStreamHandler( + new GetTextureHandler("/CAPS/GetMesh/" /*+ UUID.Random() */, m_AssetService, "GetMesh", null, rurl)); + + rurl = serverConfig.GetString("GetMesh2RedirectURL"); + + server.AddStreamHandler( + new GetTextureHandler("/CAPS/GetMesh2/" /*+ UUID.Random() */, m_AssetService, "GetMesh2", null, rurl)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs new file mode 100644 index 0000000000..828e943df8 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs @@ -0,0 +1,431 @@ +/* + * 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.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 GetTextureHandler : 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 GetTextureHandler(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; + } + + 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; + } + + /// + /// + /// + /// + /// + /// + /// + /// False for "caller try another codec"; true otherwise + private bool FetchTexture(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse, UUID textureID, string format) + { +// m_log.DebugFormat("[GETTEXTURE]: {0} with requested format {1}", textureID, format); + AssetBase texture; + + string fullID = textureID.ToString(); + if (format != DefaultFormat) + fullID = fullID + "-" + format; + + if (!String.IsNullOrEmpty(m_RedirectURL)) + { + // Only try to fetch locally cached textures. Misses are redirected + texture = m_assetService.GetCached(fullID); + + if (texture != null) + { + if (texture.Type != (sbyte)AssetType.Texture) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.NotFound; + return true; + } + WriteTextureData(httpRequest, httpResponse, texture, format); + } + else + { + string textureUrl = m_RedirectURL + "?texture_id="+ textureID.ToString(); + m_log.Debug("[GETTEXTURE]: Redirecting texture request to " + textureUrl); + httpResponse.StatusCode = (int)OSHttpStatusCode.RedirectMovedPermanently; + httpResponse.RedirectLocation = textureUrl; + return true; + } + } + else // no redirect + { + // try the cache + texture = m_assetService.GetCached(fullID); + + 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; + newTexture.Local = 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; + } + } + + // 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.Body.Write(texture.Data, start, 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.Body.Write(texture.Data, 0, 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); + } + + /// + /// Parse a range header. + /// + /// + /// 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. + /// + /// + /// Start of the range. Undefined if this was not a number. + /// End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number. + 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 = 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)) + { + // Save to bitmap + mTexture = new Bitmap(image); + + 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 (imgstream != null) + { + imgstream.Close(); + 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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs new file mode 100644 index 0000000000..fa0b2280ef --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureServerConnector.cs @@ -0,0 +1,71 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Capabilities.Handlers +{ + public class GetTextureServerConnector : ServiceConnector + { + private IAssetService m_AssetService; + private string m_ConfigName = "CapsService"; + + public GetTextureServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("AssetService", String.Empty); + + if (assetService == String.Empty) + throw new Exception("No AssetService in config file"); + + Object[] args = new Object[] { config }; + m_AssetService = + ServerUtils.LoadPlugin(assetService, args); + + 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 GetTextureHandler("/CAPS/GetTexture/" /*+ UUID.Random() */, m_AssetService, "GetTexture", null, rurl)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs b/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs new file mode 100644 index 0000000000..e5d9618196 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/GetTexture/Tests/GetTextureHandlerTests.cs @@ -0,0 +1,62 @@ +/* + * 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.Net; +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.Tests.Common; + +namespace OpenSim.Capabilities.Handlers.GetTexture.Tests +{ + [TestFixture] + public class GetTextureHandlerTests : OpenSimTestCase + { + [Test] + public void TestTextureNotFound() + { + TestHelpers.InMethod(); + + // 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); + TestOSHttpRequest req = new TestOSHttpRequest(); + TestOSHttpResponse resp = new TestOSHttpResponse(); + req.Url = new Uri("http://localhost/?texture_id=00000000-0000-1111-9999-000000000012"); + handler.Handle(null, null, req, resp); + Assert.That(resp.StatusCode, Is.EqualTo((int)System.Net.HttpStatusCode.NotFound)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..35796496ac --- /dev/null +++ b/OpenSim/Capabilities/Handlers/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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("0.8.2.*")] + diff --git a/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureHandler.cs b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureHandler.cs new file mode 100644 index 0000000000..8849a59ce3 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureHandler.cs @@ -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; + } + + /// + /// Handle a request from the client for a Uri to upload a baked texture. + /// + /// + /// + /// + /// + /// + /// The upload response if the request is successful, null otherwise. + 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; + } + + /// + /// Called when a baked texture has been successfully uploaded by a client. + /// + /// + /// + 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 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); + } + + /// + /// Handle raw uploaded baked texture data. + /// + /// + /// + /// + /// + public string uploaderCaps(byte[] data, string path, string param) + { + Action 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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs new file mode 100644 index 0000000000..10ea8eefe6 --- /dev/null +++ b/OpenSim/Capabilities/Handlers/UploadBakedTexture/UploadBakedTextureServerConnector.cs @@ -0,0 +1,76 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Capabilities.Handlers +{ + public class UploadBakedTextureServerConnector : ServiceConnector + { + private IAssetService m_AssetService; + private string m_ConfigName = "CapsService"; + + public UploadBakedTextureServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("AssetService", String.Empty); + + if (assetService == String.Empty) + throw new Exception("No AssetService in config file"); + + Object[] args = new Object[] { config }; + m_AssetService = + ServerUtils.LoadPlugin(assetService, args); + + if (m_AssetService == null) + throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName)); + + // NEED TO FIX THIS + OpenSim.Framework.Capabilities.Caps caps = new OpenSim.Framework.Capabilities.Caps(server, "", server.Port, "", UUID.Zero, ""); + server.AddStreamHandler(new RestStreamHandler( + "POST", + "/CAPS/UploadBakedTexture/", + new UploadBakedTextureHandler(caps, m_AssetService, true).UploadBakedTexture, + "UploadBakedTexture", + "Upload Baked Texture Capability")); + + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSD.cs b/OpenSim/Capabilities/LLSD.cs new file mode 100644 index 0000000000..c59cede3f4 --- /dev/null +++ b/OpenSim/Capabilities/LLSD.cs @@ -0,0 +1,684 @@ +/* + * 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.Globalization; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using System.Xml; +using OpenMetaverse; + +namespace OpenSim.Framework.Capabilities +{ + /// + /// Borrowed from (a older version of) libsl for now, as their new llsd code doesn't work we our decoding code. + /// + public static class LLSD + { + /// + /// + /// + public class LLSDParseException : Exception + { + public LLSDParseException(string message) : base(message) + { + } + } + + /// + /// + /// + public class LLSDSerializeException : Exception + { + public LLSDSerializeException(string message) : base(message) + { + } + } + + /// + /// + /// + /// + /// + public static object LLSDDeserialize(byte[] b) + { + using (MemoryStream ms = new MemoryStream(b, false)) + { + return LLSDDeserialize(ms); + } + } + + /// + /// + /// + /// + /// + public static object LLSDDeserialize(Stream st) + { + using (XmlTextReader reader = new XmlTextReader(st)) + { + reader.Read(); + SkipWS(reader); + + if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "llsd") + throw new LLSDParseException("Expected "); + + reader.Read(); + object ret = LLSDParseOne(reader); + SkipWS(reader); + + if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "llsd") + throw new LLSDParseException("Expected "); + + return ret; + } + } + + /// + /// + /// + /// + /// + public static byte[] LLSDSerialize(object obj) + { + 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.Close(); + + return Util.UTF8.GetBytes(sw.ToString()); + } + + /// + /// + /// + /// + /// + public static void LLSDWriteOne(XmlTextWriter writer, object obj) + { + if (obj == null) + { + writer.WriteStartElement(String.Empty, "undef", String.Empty); + writer.WriteEndElement(); + return; + } + + if (obj is string) + { + writer.WriteStartElement(String.Empty, "string", String.Empty); + writer.WriteString((string) obj); + writer.WriteEndElement(); + } + else if (obj is int) + { + writer.WriteStartElement(String.Empty, "integer", String.Empty); + writer.WriteString(obj.ToString()); + writer.WriteEndElement(); + } + else if (obj is double) + { + writer.WriteStartElement(String.Empty, "real", String.Empty); + writer.WriteString(obj.ToString()); + writer.WriteEndElement(); + } + else if (obj is bool) + { + bool b = (bool) obj; + writer.WriteStartElement(String.Empty, "boolean", String.Empty); + writer.WriteString(b ? "1" : "0"); + writer.WriteEndElement(); + } + else if (obj is ulong) + { + throw new Exception("ulong in LLSD is currently not implemented, fix me!"); + } + else if (obj is UUID) + { + UUID u = (UUID) obj; + writer.WriteStartElement(String.Empty, "uuid", String.Empty); + writer.WriteString(u.ToString()); + writer.WriteEndElement(); + } + else if (obj is Hashtable) + { + Hashtable h = obj as Hashtable; + writer.WriteStartElement(String.Empty, "map", String.Empty); + foreach (string key in h.Keys) + { + writer.WriteStartElement(String.Empty, "key", String.Empty); + writer.WriteString(key); + writer.WriteEndElement(); + LLSDWriteOne(writer, h[key]); + } + writer.WriteEndElement(); + } + else if (obj is ArrayList) + { + ArrayList a = obj as ArrayList; + writer.WriteStartElement(String.Empty, "array", String.Empty); + foreach (object item in a) + { + LLSDWriteOne(writer, item); + } + writer.WriteEndElement(); + } + else if (obj is byte[]) + { + byte[] b = obj as byte[]; + writer.WriteStartElement(String.Empty, "binary", String.Empty); + + writer.WriteStartAttribute(String.Empty, "encoding", String.Empty); + writer.WriteString("base64"); + writer.WriteEndAttribute(); + + //// Calculate the length of the base64 output + //long length = (long)(4.0d * b.Length / 3.0d); + //if (length % 4 != 0) length += 4 - (length % 4); + + //// Create the char[] for base64 output and fill it + //char[] tmp = new char[length]; + //int i = Convert.ToBase64CharArray(b, 0, b.Length, tmp, 0); + + //writer.WriteString(new String(tmp)); + + writer.WriteString(Convert.ToBase64String(b)); + writer.WriteEndElement(); + } + else + { + throw new LLSDSerializeException("Unknown type " + obj.GetType().Name); + } + } + + /// + /// + /// + /// + /// + public static object LLSDParseOne(XmlTextReader reader) + { + SkipWS(reader); + if (reader.NodeType != XmlNodeType.Element) + throw new LLSDParseException("Expected an element"); + + string dtype = reader.LocalName; + object ret = null; + + switch (dtype) + { + case "undef": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return null; + } + + reader.Read(); + SkipWS(reader); + ret = null; + break; + } + case "boolean": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return false; + } + + reader.Read(); + string s = reader.ReadString().Trim(); + + if (s == String.Empty || s == "false" || s == "0") + ret = false; + else if (s == "true" || s == "1") + ret = true; + else + throw new LLSDParseException("Bad boolean value " + s); + + break; + } + case "integer": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return 0; + } + + reader.Read(); + ret = Convert.ToInt32(reader.ReadString().Trim()); + break; + } + case "real": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return 0.0f; + } + + reader.Read(); + ret = Convert.ToDouble(reader.ReadString().Trim()); + break; + } + case "uuid": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return UUID.Zero; + } + + reader.Read(); + ret = new UUID(reader.ReadString().Trim()); + break; + } + case "string": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return String.Empty; + } + + reader.Read(); + ret = reader.ReadString(); + break; + } + case "binary": + { + if (reader.IsEmptyElement) + { + reader.Read(); + return new byte[0]; + } + + if (reader.GetAttribute("encoding") != null && + reader.GetAttribute("encoding") != "base64") + { + throw new LLSDParseException("Unknown encoding: " + reader.GetAttribute("encoding")); + } + + reader.Read(); + FromBase64Transform b64 = new FromBase64Transform(FromBase64TransformMode.IgnoreWhiteSpaces); + byte[] inp = Util.UTF8.GetBytes(reader.ReadString()); + ret = b64.TransformFinalBlock(inp, 0, inp.Length); + break; + } + case "date": + { + reader.Read(); + throw new Exception("LLSD TODO: date"); + } + case "map": + { + return LLSDParseMap(reader); + } + case "array": + { + return LLSDParseArray(reader); + } + default: + throw new LLSDParseException("Unknown element <" + dtype + ">"); + } + + if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != dtype) + { + throw new LLSDParseException("Expected "); + } + + reader.Read(); + return ret; + } + + /// + /// + /// + /// + /// + public static Hashtable LLSDParseMap(XmlTextReader reader) + { + Hashtable ret = new Hashtable(); + + if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "map") + throw new LLSDParseException("Expected "); + + if (reader.IsEmptyElement) + { + reader.Read(); + return ret; + } + + reader.Read(); + + while (true) + { + SkipWS(reader); + if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "map") + { + reader.Read(); + break; + } + + if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "key") + throw new LLSDParseException("Expected "); + + string key = reader.ReadString(); + + if (reader.NodeType != XmlNodeType.EndElement || reader.LocalName != "key") + throw new LLSDParseException("Expected "); + + reader.Read(); + object val = LLSDParseOne(reader); + ret[key] = val; + } + + return ret; // TODO + } + + /// + /// + /// + /// + /// + public static ArrayList LLSDParseArray(XmlTextReader reader) + { + ArrayList ret = new ArrayList(); + + if (reader.NodeType != XmlNodeType.Element || reader.LocalName != "array") + throw new LLSDParseException("Expected "); + + if (reader.IsEmptyElement) + { + reader.Read(); + return ret; + } + + reader.Read(); + + while (true) + { + SkipWS(reader); + + if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "array") + { + reader.Read(); + break; + } + + ret.Insert(ret.Count, LLSDParseOne(reader)); + } + + return ret; // TODO + } + + /// + /// + /// + /// + /// + private static string GetSpaces(int count) + { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < count; i++) b.Append(" "); + return b.ToString(); + } + + /// + /// + /// + /// + /// + /// + public static String LLSDDump(object obj, int indent) + { + if (obj == null) + { + return GetSpaces(indent) + "- undef\n"; + } + else if (obj is string) + { + return GetSpaces(indent) + "- string \"" + (string) obj + "\"\n"; + } + else if (obj is int) + { + return GetSpaces(indent) + "- integer " + obj.ToString() + "\n"; + } + else if (obj is double) + { + return GetSpaces(indent) + "- float " + obj.ToString() + "\n"; + } + else if (obj is UUID) + { + return GetSpaces(indent) + "- uuid " + ((UUID) obj).ToString() + Environment.NewLine; + } + else if (obj is Hashtable) + { + StringBuilder ret = new StringBuilder(); + ret.Append(GetSpaces(indent) + "- map" + Environment.NewLine); + Hashtable map = (Hashtable) obj; + + foreach (string key in map.Keys) + { + ret.Append(GetSpaces(indent + 2) + "- key \"" + key + "\"" + Environment.NewLine); + ret.Append(LLSDDump(map[key], indent + 3)); + } + + return ret.ToString(); + } + else if (obj is ArrayList) + { + StringBuilder ret = new StringBuilder(); + ret.Append(GetSpaces(indent) + "- array\n"); + ArrayList list = (ArrayList) obj; + + foreach (object item in list) + { + ret.Append(LLSDDump(item, indent + 2)); + } + + return ret.ToString(); + } + else if (obj is byte[]) + { + return GetSpaces(indent) + "- binary\n" + Utils.BytesToHexString((byte[]) obj, GetSpaces(indent)) + + Environment.NewLine; + } + else + { + return GetSpaces(indent) + "- unknown type " + obj.GetType().Name + Environment.NewLine; + } + } + + public static object ParseTerseLLSD(string llsd) + { + int notused; + return ParseTerseLLSD(llsd, out notused); + } + + public static object ParseTerseLLSD(string llsd, out int endPos) + { + if (llsd.Length == 0) + { + endPos = 0; + return null; + } + + // Identify what type of object this is + switch (llsd[0]) + { + case '!': + throw new LLSDParseException("Undefined value type encountered"); + case '1': + endPos = 1; + return true; + case '0': + endPos = 1; + return false; + case 'i': + { + if (llsd.Length < 2) throw new LLSDParseException("Integer value type with no value"); + int value; + endPos = FindEnd(llsd, 1); + + if (Int32.TryParse(llsd.Substring(1, endPos - 1), out value)) + return value; + else + throw new LLSDParseException("Failed to parse integer value type"); + } + case 'r': + { + if (llsd.Length < 2) throw new LLSDParseException("Real value type with no value"); + double value; + endPos = FindEnd(llsd, 1); + + if (Double.TryParse(llsd.Substring(1, endPos - 1), NumberStyles.Float, + Utils.EnUsCulture.NumberFormat, out value)) + return value; + else + throw new LLSDParseException("Failed to parse double value type"); + } + case 'u': + { + if (llsd.Length < 17) throw new LLSDParseException("UUID value type with no value"); + UUID value; + endPos = FindEnd(llsd, 1); + + if (UUID.TryParse(llsd.Substring(1, endPos - 1), out value)) + return value; + else + throw new LLSDParseException("Failed to parse UUID value type"); + } + case 'b': + //byte[] value = new byte[llsd.Length - 1]; + // This isn't the actual binary LLSD format, just the terse format sent + // at login so I don't even know if there is a binary type + throw new LLSDParseException("Binary value type is unimplemented"); + case 's': + case 'l': + if (llsd.Length < 2) throw new LLSDParseException("String value type with no value"); + endPos = FindEnd(llsd, 1); + return llsd.Substring(1, endPos - 1); + case 'd': + // Never seen one before, don't know what the format is + throw new LLSDParseException("Date value type is unimplemented"); + case '[': + { + if (llsd.IndexOf(']') == -1) throw new LLSDParseException("Invalid array"); + + int pos = 0; + ArrayList array = new ArrayList(); + + while (llsd[pos] != ']') + { + ++pos; + + // Advance past comma if need be + if (llsd[pos] == ',') ++pos; + + // Allow a single whitespace character + if (pos < llsd.Length && llsd[pos] == ' ') ++pos; + + int end; + array.Add(ParseTerseLLSD(llsd.Substring(pos), out end)); + pos += end; + } + + endPos = pos + 1; + return array; + } + case '{': + { + if (llsd.IndexOf('}') == -1) throw new LLSDParseException("Invalid map"); + + int pos = 0; + Hashtable hashtable = new Hashtable(); + + while (llsd[pos] != '}') + { + ++pos; + + // Advance past comma if need be + if (llsd[pos] == ',') ++pos; + + // Allow a single whitespace character + if (pos < llsd.Length && llsd[pos] == ' ') ++pos; + + if (llsd[pos] != '\'') throw new LLSDParseException("Expected a map key"); + int endquote = llsd.IndexOf('\'', pos + 1); + if (endquote == -1 || (endquote + 1) >= llsd.Length || llsd[endquote + 1] != ':') + throw new LLSDParseException("Invalid map format"); + string key = llsd.Substring(pos, endquote - pos); + key = key.Replace("'", String.Empty); + pos += (endquote - pos) + 2; + + int end; + hashtable.Add(key, ParseTerseLLSD(llsd.Substring(pos), out end)); + pos += end; + } + + endPos = pos + 1; + return hashtable; + } + default: + throw new Exception("Unknown value type"); + } + } + + private static int FindEnd(string llsd, int start) + { + int end = llsd.IndexOfAny(new char[] {',', ']', '}'}); + if (end == -1) end = llsd.Length - 1; + return end; + } + + /// + /// + /// + /// + private static void SkipWS(XmlTextReader reader) + { + while ( + reader.NodeType == XmlNodeType.Comment || + reader.NodeType == XmlNodeType.Whitespace || + reader.NodeType == XmlNodeType.SignificantWhitespace || + reader.NodeType == XmlNodeType.XmlDeclaration) + { + reader.Read(); + } + } + } +} diff --git a/OpenSim/Capabilities/LLSDArray.cs b/OpenSim/Capabilities/LLSDArray.cs new file mode 100644 index 0000000000..3459e49655 --- /dev/null +++ b/OpenSim/Capabilities/LLSDArray.cs @@ -0,0 +1,41 @@ +/* + * 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 +{ + [LLSDType("ARRAY")] + public class OSDArray + { + public ArrayList Array = new ArrayList(); + + public OSDArray() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDAssetUploadComplete.cs b/OpenSim/Capabilities/LLSDAssetUploadComplete.cs new file mode 100644 index 0000000000..ab6cee55b7 --- /dev/null +++ b/OpenSim/Capabilities/LLSDAssetUploadComplete.cs @@ -0,0 +1,45 @@ +/* + * 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; + +namespace OpenSim.Framework.Capabilities +{ + [LLSDType("MAP")] + public class LLSDAssetUploadComplete + { + public string new_asset = String.Empty; + public UUID new_inventory_item = UUID.Zero; + public string state = String.Empty; + //public bool success = false; + + public LLSDAssetUploadComplete() + { + } + } +} diff --git a/OpenSim/Capabilities/LLSDAssetUploadRequest.cs b/OpenSim/Capabilities/LLSDAssetUploadRequest.cs new file mode 100644 index 0000000000..6e66f0acee --- /dev/null +++ b/OpenSim/Capabilities/LLSDAssetUploadRequest.cs @@ -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. + */ + +using System; +using OpenMetaverse; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDAssetUploadRequest + { + public string asset_type = String.Empty; + public string description = String.Empty; + public UUID folder_id = UUID.Zero; + public string inventory_type = String.Empty; + public string name = String.Empty; + + public LLSDAssetUploadRequest() + { + } + } +} diff --git a/OpenSim/Capabilities/LLSDAssetUploadResponse.cs b/OpenSim/Capabilities/LLSDAssetUploadResponse.cs new file mode 100644 index 0000000000..0d6f7f9423 --- /dev/null +++ b/OpenSim/Capabilities/LLSDAssetUploadResponse.cs @@ -0,0 +1,56 @@ +/* + * 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; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDAssetUploadResponse + { + public string uploader = String.Empty; + public string state = String.Empty; + + public LLSDAssetUploadResponse() + { + } + } + + [OSDMap] + public class LLSDNewFileAngentInventoryVariablePriceReplyResponse + { + public int resource_cost; + public string state; + public int upload_price; + public string rsvp; + + public LLSDNewFileAngentInventoryVariablePriceReplyResponse() + { + state = "confirm_upload"; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDAvatarPicker.cs b/OpenSim/Capabilities/LLSDAvatarPicker.cs new file mode 100644 index 0000000000..d0b3f3a5ec --- /dev/null +++ b/OpenSim/Capabilities/LLSDAvatarPicker.cs @@ -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 OpenMetaverse; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDAvatarPicker + { + public string next_page_url; + // an array of LLSDPerson + public OSDArray agents = new OSDArray(); + } + + [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; + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDCapEvent.cs b/OpenSim/Capabilities/LLSDCapEvent.cs new file mode 100644 index 0000000000..63abd6225e --- /dev/null +++ b/OpenSim/Capabilities/LLSDCapEvent.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + [LLSDType("MAP")] + public class LLSDCapEvent + { + public int id = 0; + public OSDArray events = new OSDArray(); + + public LLSDCapEvent() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDEmpty.cs b/OpenSim/Capabilities/LLSDEmpty.cs new file mode 100644 index 0000000000..f94fcba10b --- /dev/null +++ b/OpenSim/Capabilities/LLSDEmpty.cs @@ -0,0 +1,37 @@ +/* + * 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 +{ + [LLSDType("MAP")] + public class LLSDEmpty + { + public LLSDEmpty() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDEnvironmentSettings.cs b/OpenSim/Capabilities/LLSDEnvironmentSettings.cs new file mode 100644 index 0000000000..39019af22c --- /dev/null +++ b/OpenSim/Capabilities/LLSDEnvironmentSettings.cs @@ -0,0 +1,68 @@ +/* + * 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; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDEnvironmentRequest + { + public UUID messageID; + public UUID regionID; + } + + [OSDMap] + public class LLSDEnvironmentSetResponse + { + public UUID regionID; + public UUID messageID; + public Boolean success; + public String fail_reason; + } + + public class EnvironmentSettings + { + /// + /// generates a empty llsd settings response for viewer + /// + /// the message UUID + /// the region UUID + public static string EmptySettings(UUID messageID, UUID regionID) + { + OSDArray arr = new OSDArray(); + LLSDEnvironmentRequest msg = new LLSDEnvironmentRequest(); + msg.messageID = messageID; + msg.regionID = regionID; + arr.Array.Add(msg); + return LLSDHelpers.SerialiseLLSDReply(arr); + } + } + +} diff --git a/OpenSim/Capabilities/LLSDHelpers.cs b/OpenSim/Capabilities/LLSDHelpers.cs new file mode 100644 index 0000000000..8f1a40e632 --- /dev/null +++ b/OpenSim/Capabilities/LLSDHelpers.cs @@ -0,0 +1,172 @@ +/* + * 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.IO; +using System.Reflection; +using System.Xml; + +namespace OpenSim.Framework.Capabilities +{ + public class LLSDHelpers + { +// private static readonly log4net.ILog m_log +// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static string SerialiseLLSDReply(object obj) + { + 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(); + } + + private static void SerializeOSDType(XmlTextWriter writer, object obj) + { + Type myType = obj.GetType(); + LLSDType[] llsdattributes = (LLSDType[]) myType.GetCustomAttributes(typeof (LLSDType), false); + if (llsdattributes.Length > 0) + { + switch (llsdattributes[0].ObjectType) + { + case "MAP": + writer.WriteStartElement(String.Empty, "map", String.Empty); + FieldInfo[] fields = myType.GetFields(); + for (int i = 0; i < fields.Length; i++) + { + if (fields[i] != null && fields[i].GetValue(obj) != null) + { + object fieldValue = fields[i].GetValue(obj); + LLSDType[] fieldAttributes = + (LLSDType[]) fieldValue.GetType().GetCustomAttributes(typeof (LLSDType), false); + if (fieldAttributes.Length > 0) + { + writer.WriteStartElement(String.Empty, "key", String.Empty); + string fieldName = fields[i].Name; + fieldName = fieldName.Replace("___", "-"); + writer.WriteString(fieldName); + writer.WriteEndElement(); + SerializeOSDType(writer, fieldValue); + } + else + { + writer.WriteStartElement(String.Empty, "key", String.Empty); + string fieldName = fields[i].Name; + fieldName = fieldName.Replace("___", "-"); + writer.WriteString(fieldName); + writer.WriteEndElement(); + LLSD.LLSDWriteOne(writer, fieldValue); + // OpenMetaverse.StructuredData.LLSDParser.SerializeXmlElement( + // writer, OpenMetaverse.StructuredData.OSD.FromObject(fieldValue)); + } + } + else + { + // TODO from ADAM: There is a nullref being caused by fields[i] being null + // on some computers. Unsure what is causing this, but would appreciate + // if sdague could take a look at this. + } + } + writer.WriteEndElement(); + break; + case "ARRAY": + // OSDArray arrayObject = obj as OSDArray; + // ArrayList a = arrayObject.Array; + ArrayList a = (ArrayList) obj.GetType().GetField("Array").GetValue(obj); + if (a != null) + { + writer.WriteStartElement(String.Empty, "array", String.Empty); + foreach (object item in a) + { + SerializeOSDType(writer, item); + } + writer.WriteEndElement(); + } + break; + } + } + else + { + LLSD.LLSDWriteOne(writer, obj); + //OpenMetaverse.StructuredData.LLSDParser.SerializeXmlElement( + // writer, OpenMetaverse.StructuredData.OSD.FromObject(obj)); + } + } + + public static object DeserialiseOSDMap(Hashtable llsd, object obj) + { + Type myType = obj.GetType(); + LLSDType[] llsdattributes = (LLSDType[]) myType.GetCustomAttributes(typeof (LLSDType), false); + if (llsdattributes.Length > 0) + { + switch (llsdattributes[0].ObjectType) + { + case "MAP": + IDictionaryEnumerator enumerator = llsd.GetEnumerator(); + while (enumerator.MoveNext()) + { + string keyName = (string)enumerator.Key; + keyName = keyName.Replace("-","_"); + FieldInfo field = myType.GetField(keyName); + if (field != null) + { + // if (enumerator.Value is OpenMetaverse.StructuredData.OSDMap) + if (enumerator.Value is Hashtable) + { + object fieldValue = field.GetValue(obj); + DeserialiseOSDMap((Hashtable) enumerator.Value, fieldValue); + // DeserialiseOSDMap((OpenMetaverse.StructuredData.OSDMap) enumerator.Value, fieldValue); + } + else if (enumerator.Value is ArrayList) + { + object fieldValue = field.GetValue(obj); + fieldValue.GetType().GetField("Array").SetValue(fieldValue, enumerator.Value); + //TODO + // 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 + { + field.SetValue(obj, enumerator.Value); + } + } + } + break; + } + } + return obj; + } + } +} diff --git a/OpenSim/Capabilities/LLSDInventoryFolder.cs b/OpenSim/Capabilities/LLSDInventoryFolder.cs new file mode 100644 index 0000000000..d085430e8a --- /dev/null +++ b/OpenSim/Capabilities/LLSDInventoryFolder.cs @@ -0,0 +1,41 @@ +/* + * 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 +{ + [OSDMap] + public class LLSDInventoryFolder + { + public UUID folder_id; + public UUID parent_id; + public string name; + public int type; + public int preferred_type; + } +} diff --git a/OpenSim/Capabilities/LLSDInventoryItem.cs b/OpenSim/Capabilities/LLSDInventoryItem.cs new file mode 100644 index 0000000000..958e8079b4 --- /dev/null +++ b/OpenSim/Capabilities/LLSDInventoryItem.cs @@ -0,0 +1,105 @@ +/* + * 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 +{ + [OSDMap] + public class LLSDInventoryItem + { + public UUID parent_id; + + public UUID asset_id; + public UUID item_id; + public LLSDPermissions permissions; + public int type; + public int inv_type; + public int flags; + + public LLSDSaleInfo sale_info; + public string name; + public string desc; + public int created_at; + } + + [OSDMap] + public class LLSDPermissions + { + public UUID creator_id; + public UUID owner_id; + public UUID group_id; + public int base_mask; + public int owner_mask; + public int group_mask; + public int everyone_mask; + public int next_owner_mask; + public bool is_owner_group; + } + + [OSDMap] + public class LLSDSaleInfo + { + public int sale_price; + public int sale_type; + } + + [OSDMap] + public class LLSDInventoryDescendents + { + public OSDArray folders = new OSDArray(); + } + + [OSDMap] + public class LLSDFetchInventoryDescendents + { + public UUID folder_id; + public UUID owner_id; + public int sort_order; + public bool fetch_folders; + public bool fetch_items; + } + + [OSDMap] + public class LLSDInventoryFolderContents + { + public UUID agent_id; + public int descendents; + public UUID folder_id; + public OSDArray categories = new OSDArray(); + public OSDArray items = new OSDArray(); + public UUID owner_id; + public int version; + } + + [OSDMap] + public class LLSDFetchInventory + { + public UUID agent_id; + public OSDArray items = new OSDArray(); + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDItemUpdate.cs b/OpenSim/Capabilities/LLSDItemUpdate.cs new file mode 100644 index 0000000000..96e2b6186e --- /dev/null +++ b/OpenSim/Capabilities/LLSDItemUpdate.cs @@ -0,0 +1,41 @@ +/* + * 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 +{ + [OSDMap] + public class LLSDItemUpdate + { + public UUID item_id; + + public LLSDItemUpdate() + { + } + } +} diff --git a/OpenSim/Capabilities/LLSDMapLayer.cs b/OpenSim/Capabilities/LLSDMapLayer.cs new file mode 100644 index 0000000000..4aeb1ff97e --- /dev/null +++ b/OpenSim/Capabilities/LLSDMapLayer.cs @@ -0,0 +1,45 @@ +/* + * 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 OSDMapLayer + { + public int Left = 0; + public int Right = 0; + public int Top = 0; + public int Bottom = 0; + public UUID ImageID = UUID.Zero; + + public OSDMapLayer() + { + } + } +} diff --git a/OpenSim/Capabilities/LLSDMapLayerResponse.cs b/OpenSim/Capabilities/LLSDMapLayerResponse.cs new file mode 100644 index 0000000000..839e34c98c --- /dev/null +++ b/OpenSim/Capabilities/LLSDMapLayerResponse.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + [LLSDType("MAP")] + public class LLSDMapLayerResponse + { + public LLSDMapRequest AgentData = new LLSDMapRequest(); + public OSDArray LayerData = new OSDArray(); + + public LLSDMapLayerResponse() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDMapRequest.cs b/OpenSim/Capabilities/LLSDMapRequest.cs new file mode 100644 index 0000000000..debf387ddd --- /dev/null +++ b/OpenSim/Capabilities/LLSDMapRequest.cs @@ -0,0 +1,39 @@ +/* + * 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 +{ + [LLSDType("MAP")] + public class LLSDMapRequest + { + public int Flags = 0; + + public LLSDMapRequest() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDMethod.cs b/OpenSim/Capabilities/LLSDMethod.cs new file mode 100644 index 0000000000..cd2574d76e --- /dev/null +++ b/OpenSim/Capabilities/LLSDMethod.cs @@ -0,0 +1,31 @@ +/* + * 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 +{ + public delegate TResponse LLSDMethod(TRequest request); +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDMethodString.cs b/OpenSim/Capabilities/LLSDMethodString.cs new file mode 100644 index 0000000000..38700d5bce --- /dev/null +++ b/OpenSim/Capabilities/LLSDMethodString.cs @@ -0,0 +1,31 @@ +/* + * 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 +{ + public delegate TResponse LLSDMethodString(TRequest request, string path); +} diff --git a/OpenSim/Capabilities/LLSDParcelVoiceInfoResponse.cs b/OpenSim/Capabilities/LLSDParcelVoiceInfoResponse.cs new file mode 100644 index 0000000000..b34a6685c4 --- /dev/null +++ b/OpenSim/Capabilities/LLSDParcelVoiceInfoResponse.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDRemoteParcelResponse.cs b/OpenSim/Capabilities/LLSDRemoteParcelResponse.cs new file mode 100644 index 0000000000..13d69d3b64 --- /dev/null +++ b/OpenSim/Capabilities/LLSDRemoteParcelResponse.cs @@ -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() + { + } + } +} diff --git a/OpenSim/Capabilities/LLSDStreamHandler.cs b/OpenSim/Capabilities/LLSDStreamHandler.cs new file mode 100644 index 0000000000..4fa1153cd6 --- /dev/null +++ b/OpenSim/Capabilities/LLSDStreamHandler.cs @@ -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.Collections; +using System.IO; +using System.Text; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Framework.Capabilities +{ + public class LLSDStreamhandler : BaseStreamHandler + where TRequest : new() + { + private LLSDMethod m_method; + + public LLSDStreamhandler(string httpMethod, string path, LLSDMethod method) + : this(httpMethod, path, method, null, null) {} + + public LLSDStreamhandler( + string httpMethod, string path, LLSDMethod method, string name, string description) + : base(httpMethod, path, name, description) + { + m_method = method; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + //Encoding encoding = Util.UTF8; + //StreamReader streamReader = new StreamReader(request, false); + + //string requestBody = streamReader.ReadToEnd(); + //streamReader.Close(); + + // OpenMetaverse.StructuredData.OSDMap hash = (OpenMetaverse.StructuredData.OSDMap) + // OpenMetaverse.StructuredData.LLSDParser.DeserializeXml(new XmlTextReader(request)); + + Hashtable hash = (Hashtable) LLSD.LLSDDeserialize(request); + TRequest llsdRequest = new TRequest(); + LLSDHelpers.DeserialiseOSDMap(hash, llsdRequest); + + TResponse response = m_method(llsdRequest); + + return Util.UTF8NoBomEncoding.GetBytes(LLSDHelpers.SerialiseLLSDReply(response)); + } + } +} diff --git a/OpenSim/Capabilities/LLSDTaskInventoryUploadComplete.cs b/OpenSim/Capabilities/LLSDTaskInventoryUploadComplete.cs new file mode 100644 index 0000000000..47fdacaf9e --- /dev/null +++ b/OpenSim/Capabilities/LLSDTaskInventoryUploadComplete.cs @@ -0,0 +1,50 @@ +/* + * 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 +{ + [OSDMap] + public class LLSDTaskInventoryUploadComplete + { + /// + /// The task inventory item that was updated + /// + public UUID item_id; + + /// + /// The task that was updated + /// + public UUID task_id; + + /// + /// State of the upload. So far have only even seen this set to "complete" + /// + public string state; + } +} diff --git a/OpenSim/Capabilities/LLSDTaskScriptUpdate.cs b/OpenSim/Capabilities/LLSDTaskScriptUpdate.cs new file mode 100644 index 0000000000..9d7c17f84a --- /dev/null +++ b/OpenSim/Capabilities/LLSDTaskScriptUpdate.cs @@ -0,0 +1,50 @@ +/* + * 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 +{ + [OSDMap] + public class LLSDTaskScriptUpdate + { + /// + /// The item containing the script to update + /// + public UUID item_id; + + /// + /// The task containing the script + /// + public UUID task_id; + + /// + /// Signals whether the script is currently active + /// + public int is_script_running; + } +} diff --git a/OpenSim/Capabilities/LLSDTaskScriptUploadComplete.cs b/OpenSim/Capabilities/LLSDTaskScriptUploadComplete.cs new file mode 100644 index 0000000000..d308831b3a --- /dev/null +++ b/OpenSim/Capabilities/LLSDTaskScriptUploadComplete.cs @@ -0,0 +1,54 @@ +/* + * 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; +using System; +using System.Collections; + +namespace OpenSim.Framework.Capabilities +{ + [OSDMap] + public class LLSDTaskScriptUploadComplete + { + /// + /// The task inventory item that was updated + /// + public UUID new_asset; + + /// + /// Was it compiled? + /// + public bool compiled; + + /// + /// State of the upload. So far have only even seen this set to "complete" + /// + public string state; + + public OSDArray errors; + } +} diff --git a/OpenSim/Capabilities/LLSDTest.cs b/OpenSim/Capabilities/LLSDTest.cs new file mode 100644 index 0000000000..5f77c3d6ca --- /dev/null +++ b/OpenSim/Capabilities/LLSDTest.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + [LLSDType("MAP")] + public class LLSDTest + { + public int Test1 = 20; + public int Test2 = 10; + + public LLSDTest() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDType.cs b/OpenSim/Capabilities/LLSDType.cs new file mode 100644 index 0000000000..d5ca1ab9bf --- /dev/null +++ b/OpenSim/Capabilities/LLSDType.cs @@ -0,0 +1,55 @@ +/* + * 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; + +namespace OpenSim.Framework.Capabilities +{ + [AttributeUsage(AttributeTargets.Class)] + public class LLSDType : Attribute + { + protected string myType; + + public LLSDType(string type) + { + myType = type; + } + + public string ObjectType + { + get { return myType; } + } + } + + [AttributeUsage(AttributeTargets.Class)] + public class OSDMap : LLSDType + { + public OSDMap() : base("MAP") + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/LLSDVoiceAccountResponse.cs b/OpenSim/Capabilities/LLSDVoiceAccountResponse.cs new file mode 100644 index 0000000000..53c11e7af2 --- /dev/null +++ b/OpenSim/Capabilities/LLSDVoiceAccountResponse.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/OpenSim/Capabilities/Properties/AssemblyInfo.cs b/OpenSim/Capabilities/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f8a9daeac0 --- /dev/null +++ b/OpenSim/Capabilities/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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")] diff --git a/OpenSim/ConsoleClient/ConsoleClient.cs b/OpenSim/ConsoleClient/ConsoleClient.cs new file mode 100644 index 0000000000..7c003eaf81 --- /dev/null +++ b/OpenSim/ConsoleClient/ConsoleClient.cs @@ -0,0 +1,225 @@ +/* + * 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 Nini.Config; +using log4net; +using System.Reflection; +using System; +using System.IO; +using System.Xml; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenMetaverse; + +namespace OpenSim.ConsoleClient +{ + public class OpenSimConsoleClient + { + protected static ServicesServerBase m_Server = null; + private static string m_Host; + private static int m_Port; + private static string m_User; + private static string m_Pass; + private static UUID m_SessionID; + + static int Main(string[] args) + { + m_Server = new ServicesServerBase("Client", args); + + IConfig serverConfig = m_Server.Config.Configs["Startup"]; + if (serverConfig == null) + { + System.Console.WriteLine("Startup config section missing in .ini file"); + throw new Exception("Configuration error"); + } + + ArgvConfigSource argvConfig = new ArgvConfigSource(args); + + argvConfig.AddSwitch("Startup", "host", "h"); + argvConfig.AddSwitch("Startup", "port", "p"); + argvConfig.AddSwitch("Startup", "user", "u"); + argvConfig.AddSwitch("Startup", "pass", "P"); + + m_Server.Config.Merge(argvConfig); + + m_User = serverConfig.GetString("user", "Test"); + m_Host = serverConfig.GetString("host", "localhost"); + m_Port = serverConfig.GetInt("port", 8003); + m_Pass = serverConfig.GetString("pass", "secret"); + + Requester.MakeRequest("http://"+m_Host+":"+m_Port.ToString()+"/StartSession/", String.Format("USER={0}&PASS={1}", m_User, m_Pass), LoginReply); + + string pidFile = serverConfig.GetString("PIDFile", String.Empty); + + while (m_Server.Running) + { + System.Threading.Thread.Sleep(500); + // MainConsole.Instance.Prompt(); + } + + if (pidFile != String.Empty) + File.Delete(pidFile); + + Environment.Exit(0); + + return 0; + } + + private static void SendCommand(string module, string[] cmd) + { + string sendCmd = ""; + string[] cmdlist = new string[cmd.Length - 1]; + + sendCmd = cmd[0]; + + if (cmd.Length > 1) + { + Array.Copy(cmd, 1, cmdlist, 0, cmd.Length - 1); + sendCmd += " \"" + String.Join("\" \"", cmdlist) + "\""; + } + + Requester.MakeRequest("http://"+m_Host+":"+m_Port.ToString()+"/SessionCommand/", String.Format("ID={0}&COMMAND={1}", m_SessionID, sendCmd), CommandReply); + } + + public static void LoginReply(string requestUrl, string requestData, string replyData) + { + XmlDocument doc = new XmlDocument(); + + doc.LoadXml(replyData); + + XmlNodeList rootL = doc.GetElementsByTagName("ConsoleSession"); + if (rootL.Count != 1) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + XmlElement rootNode = (XmlElement)rootL[0]; + + if (rootNode == null) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + XmlNodeList helpNodeL = rootNode.GetElementsByTagName("HelpTree"); + if (helpNodeL.Count != 1) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + XmlElement helpNode = (XmlElement)helpNodeL[0]; + if (helpNode == null) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + XmlNodeList sessionL = rootNode.GetElementsByTagName("SessionID"); + if (sessionL.Count != 1) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + XmlElement sessionNode = (XmlElement)sessionL[0]; + if (sessionNode == null) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + if (!UUID.TryParse(sessionNode.InnerText, out m_SessionID)) + { + MainConsole.Instance.Output("Connection data info was not valid"); + Environment.Exit(1); + } + + MainConsole.Instance.Commands.FromXml(helpNode, SendCommand); + + Requester.MakeRequest("http://"+m_Host+":"+m_Port.ToString()+"/ReadResponses/"+m_SessionID.ToString()+"/", String.Empty, ReadResponses); + } + + public static void ReadResponses(string requestUrl, string requestData, string replyData) + { + XmlDocument doc = new XmlDocument(); + + doc.LoadXml(replyData); + + XmlNodeList rootNodeL = doc.GetElementsByTagName("ConsoleSession"); + if (rootNodeL.Count != 1 || rootNodeL[0] == null) + { + Requester.MakeRequest(requestUrl, requestData, ReadResponses); + return; + } + + List lines = new List(); + + foreach (XmlNode part in rootNodeL[0].ChildNodes) + { + if (part.Name != "Line") + continue; + + lines.Add(part.InnerText); + } + + // Cut down scrollback to 100 lines (4 screens) + // for the command line client + // + while (lines.Count > 100) + lines.RemoveAt(0); + + string prompt = String.Empty; + + foreach (string l in lines) + { + string[] parts = l.Split(new char[] {':'}, 3); + if (parts.Length != 3) + continue; + + if (parts[2].StartsWith("+++") || parts[2].StartsWith("-++")) + prompt = parts[2]; + else + MainConsole.Instance.Output(parts[2].Trim(), parts[1]); + } + + + Requester.MakeRequest(requestUrl, requestData, ReadResponses); + + if (prompt.StartsWith("+++")) + MainConsole.Instance.ReadLine(prompt.Substring(3), true, true); + else if (prompt.StartsWith("-++")) + SendCommand(String.Empty, new string[] { MainConsole.Instance.ReadLine(prompt.Substring(3), false, true) }); + } + + public static void CommandReply(string requestUrl, string requestData, string replyData) + { + } + } +} diff --git a/OpenSim/ConsoleClient/Properties/AssemblyInfo.cs b/OpenSim/ConsoleClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9c0c784838 --- /dev/null +++ b/OpenSim/ConsoleClient/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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")] diff --git a/OpenSim/ConsoleClient/Requester.cs b/OpenSim/ConsoleClient/Requester.cs new file mode 100644 index 0000000000..0a213288da --- /dev/null +++ b/OpenSim/ConsoleClient/Requester.cs @@ -0,0 +1,85 @@ +/* + * 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.Text; +using System.Xml; +using System.Xml.Serialization; +using log4net; + +namespace OpenSim.ConsoleClient +{ + public delegate void ReplyDelegate(string requestUrl, string requestData, string replyData); + + public class Requester + { + public static void MakeRequest(string requestUrl, string data, + ReplyDelegate action) + { + WebRequest request = WebRequest.Create(requestUrl); + + request.Method = "POST"; + + request.ContentType = "application/x-www-form-urlencoded"; + + byte[] buffer = Encoding.ASCII.GetBytes(data); + int length = (int) buffer.Length; + request.ContentLength = length; + + request.BeginGetRequestStream(delegate(IAsyncResult res) + { + Stream requestStream = request.EndGetRequestStream(res); + + requestStream.Write(buffer, 0, length); + + request.BeginGetResponse(delegate(IAsyncResult ar) + { + 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(); + + } + catch (System.InvalidOperationException) + { + } + } + + action(requestUrl, data, reply); + }, null); + }, null); + } + } +} diff --git a/OpenSim/Data/AssetDataBase.cs b/OpenSim/Data/AssetDataBase.cs new file mode 100644 index 0000000000..1bb432cc56 --- /dev/null +++ b/OpenSim/Data/AssetDataBase.cs @@ -0,0 +1,52 @@ +/* + * 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.Text; +using System.Text.RegularExpressions; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + public abstract class AssetDataBase : IAssetDataPlugin + { + public abstract AssetBase GetAsset(UUID uuid); + public abstract void StoreAsset(AssetBase asset); + public abstract bool[] AssetsExist(UUID[] uuids); + + public abstract List FetchAssetMetadataSet(int start, int count); + + public abstract string Version { get; } + public abstract string Name { get; } + public abstract void Initialise(string connect); + public abstract void Initialise(); + public abstract void Dispose(); + public abstract bool Delete(string id); + } +} diff --git a/OpenSim/Data/DBGuids.cs b/OpenSim/Data/DBGuids.cs new file mode 100644 index 0000000000..ad1c19c6ee --- /dev/null +++ b/OpenSim/Data/DBGuids.cs @@ -0,0 +1,71 @@ +/* + * 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.Text; +using OpenMetaverse; + +namespace OpenSim.Data +{ + + public static class DBGuid + { + /// 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 + /// + /// + /// + /// + public static UUID FromDB(object id) + { + if ((id == null) || (id == DBNull.Value)) + return UUID.Zero; + + if (id.GetType() == typeof(Guid)) + return new UUID((Guid)id); + + if (id.GetType() == typeof(byte[])) + { + 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); + } + + throw new Exception("Failed to convert db value to UUID: " + id.ToString()); + } + } +} diff --git a/OpenSim/Data/IAgentPreferencesData.cs b/OpenSim/Data/IAgentPreferencesData.cs new file mode 100644 index 0000000000..87632990ac --- /dev/null +++ b/OpenSim/Data/IAgentPreferencesData.cs @@ -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. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + public class AgentPreferencesData + { + public Dictionary Data; + } + + public interface IAgentPreferencesData + { + bool Store(AgentPreferencesData data); + AgentPreferencesData GetPrefs(UUID agentID); + } +} + diff --git a/OpenSim/Data/IAssetData.cs b/OpenSim/Data/IAssetData.cs new file mode 100644 index 0000000000..a41e310891 --- /dev/null +++ b/OpenSim/Data/IAssetData.cs @@ -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. + */ + +using System.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + public interface IAssetDataPlugin : IPlugin + { + AssetBase GetAsset(UUID uuid); + void StoreAsset(AssetBase asset); + bool[] AssetsExist(UUID[] uuids); + List FetchAssetMetadataSet(int start, int count); + void Initialise(string connect); + bool Delete(string id); + } +} diff --git a/OpenSim/Data/IAuthenticationData.cs b/OpenSim/Data/IAuthenticationData.cs new file mode 100644 index 0000000000..7753e04a8c --- /dev/null +++ b/OpenSim/Data/IAuthenticationData.cs @@ -0,0 +1,56 @@ +/* + * 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.Data +{ + public class AuthenticationData + { + public UUID PrincipalID; + public Dictionary Data; + } + + /// + /// An interface for connecting to the authentication datastore + /// + public interface IAuthenticationData + { + AuthenticationData Get(UUID principalID); + + bool Store(AuthenticationData data); + + bool SetDataItem(UUID principalID, string item, string value); + + bool SetToken(UUID principalID, string token, int lifetime); + + bool CheckToken(UUID principalID, string token, int lifetime); + } +} diff --git a/OpenSim/Data/IAvatarData.cs b/OpenSim/Data/IAvatarData.cs new file mode 100644 index 0000000000..0a18e211f4 --- /dev/null +++ b/OpenSim/Data/IAvatarData.cs @@ -0,0 +1,49 @@ +/* + * 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.Data +{ + // This MUST be a ref type! + public class AvatarBaseData + { + public UUID PrincipalID; + public Dictionary Data; + } + + public interface IAvatarData + { + AvatarBaseData[] Get(string field, string val); + bool Store(AvatarBaseData data); + bool Delete(UUID principalID, string name); + bool Delete(string field, string val); + } +} diff --git a/OpenSim/Data/IEstateDataStore.cs b/OpenSim/Data/IEstateDataStore.cs new file mode 100644 index 0000000000..f9070eaf2f --- /dev/null +++ b/OpenSim/Data/IEstateDataStore.cs @@ -0,0 +1,120 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + public interface IEstateDataStore + { + /// + /// Initialise the data store. + /// + /// + void Initialise(string connectstring); + + /// + /// Load estate settings for a region. + /// + /// + /// If true, then an estate is created if one is not found. + /// + EstateSettings LoadEstateSettings(UUID regionID, bool create); + + /// + /// Load estate settings for an estate ID. + /// + /// + /// + EstateSettings LoadEstateSettings(int estateID); + + /// + /// Create a new estate. + /// + /// + /// A + /// + EstateSettings CreateNewEstate(); + + /// + /// Load/Get all estate settings. + /// + /// An empty list if no estates were found. + List LoadEstateSettingsAll(); + + /// + /// Store estate settings. + /// + /// + /// This is also called by EstateSettings.Save() + /// + void StoreEstateSettings(EstateSettings es); + + /// + /// Get estate IDs. + /// + /// Name of estate to search for. This is the exact name, no parttern matching is done. + /// + List GetEstates(string search); + + /// + /// Get the IDs of all estates owned by the given user. + /// + /// An empty list if no estates were found. + List GetEstatesByOwner(UUID ownerID); + + /// + /// Get the IDs of all estates. + /// + /// An empty list if no estates were found. + List GetEstatesAll(); + + /// + /// Link a region to an estate. + /// + /// + /// + /// true if the link succeeded, false otherwise + bool LinkRegion(UUID regionID, int estateID); + + /// + /// Get the UUIDs of all the regions in an estate. + /// + /// + /// + List GetRegions(int estateID); + + /// + /// Delete an estate + /// + /// + /// true if the delete succeeded, false otherwise + bool DeleteEstate(int estateID); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IFSAssetData.cs b/OpenSim/Data/IFSAssetData.cs new file mode 100644 index 0000000000..8751dc08ab --- /dev/null +++ b/OpenSim/Data/IFSAssetData.cs @@ -0,0 +1,47 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + public delegate string FSStoreDelegate(AssetBase asset, bool force); + + public interface IFSAssetDataPlugin : IPlugin + { + bool[] AssetsExist(UUID[] uuids); + void Initialise(string connect, string realm, int SkipAccessTimeDays); + bool Delete(string id); + + AssetMetadata Get(string id, out string hash); + bool Store(AssetMetadata metadata, string hash); + void Import(string conn, string table, int start, int count, bool force, FSStoreDelegate store); + int Count(); + } +} diff --git a/OpenSim/Data/IFriendsData.cs b/OpenSim/Data/IFriendsData.cs new file mode 100644 index 0000000000..3fdf87b51a --- /dev/null +++ b/OpenSim/Data/IFriendsData.cs @@ -0,0 +1,53 @@ +/* + * 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.Data +{ + public class FriendsData + { + public string PrincipalID; + public string Friend; + public Dictionary Data; + } + + /// + /// An interface for connecting to the friends datastore + /// + public interface IFriendsData + { + bool Store(FriendsData data); + bool Delete(UUID ownerID, string friend); + bool Delete(string ownerID, string friend); + FriendsData[] GetFriends(UUID principalID); + FriendsData[] GetFriends(string principalID); + } +} diff --git a/OpenSim/Data/IGridUserData.cs b/OpenSim/Data/IGridUserData.cs new file mode 100644 index 0000000000..9afa477f30 --- /dev/null +++ b/OpenSim/Data/IGridUserData.cs @@ -0,0 +1,56 @@ +/* + * 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.Data +{ + // This MUST be a ref type! + public class GridUserData + { + public string UserID; + public Dictionary Data; + + public GridUserData() + { + Data = new Dictionary(); + } + } + + /// + /// An interface for connecting to the user grid datastore + /// + public interface IGridUserData + { + GridUserData Get(string userID); + GridUserData[] GetAll(string query); + bool Store(GridUserData data); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IGroupsData.cs b/OpenSim/Data/IGroupsData.cs new file mode 100644 index 0000000000..c11e6499a5 --- /dev/null +++ b/OpenSim/Data/IGroupsData.cs @@ -0,0 +1,144 @@ +/* + * 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.Generic; +using OpenSim.Data; +using OpenMetaverse; + +namespace OpenSim.Data +{ + public class GroupData + { + public UUID GroupID; + public Dictionary Data; + } + + public class MembershipData + { + public UUID GroupID; + public string PrincipalID; + public Dictionary Data; + } + + public class RoleData + { + public UUID GroupID; + public UUID RoleID; + public Dictionary Data; + } + + public class RoleMembershipData + { + public UUID GroupID; + public UUID RoleID; + public string PrincipalID; + } + + public class PrincipalData + { + public string PrincipalID; + public UUID ActiveGroupID; + } + + public class InvitationData + { + public UUID InviteID; + public UUID GroupID; + public UUID RoleID; + public string PrincipalID; + public Dictionary Data; + } + + public class NoticeData + { + public UUID GroupID; + public UUID NoticeID; + public Dictionary Data; + } + + + public interface IGroupsData + { + // groups table + bool StoreGroup(GroupData data); + GroupData RetrieveGroup(UUID groupID); + GroupData RetrieveGroup(string name); + GroupData[] RetrieveGroups(string pattern); + bool DeleteGroup(UUID groupID); + int GroupsCount(); + + // membership table + MembershipData RetrieveMember(UUID groupID, string pricipalID); + MembershipData[] RetrieveMembers(UUID groupID); + MembershipData[] RetrieveMemberships(string pricipalID); + bool StoreMember(MembershipData data); + bool DeleteMember(UUID groupID, string pricipalID); + int MemberCount(UUID groupID); + + // roles table + bool StoreRole(RoleData data); + RoleData RetrieveRole(UUID groupID, UUID roleID); + RoleData[] RetrieveRoles(UUID groupID); + bool DeleteRole(UUID groupID, UUID roleID); + int RoleCount(UUID groupID); + + // rolememberhip table + RoleMembershipData[] RetrieveRolesMembers(UUID groupID); + RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID); + RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID); + RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID); + int RoleMemberCount(UUID groupID, UUID roleID); + bool StoreRoleMember(RoleMembershipData data); + bool DeleteRoleMember(RoleMembershipData data); + bool DeleteMemberAllRoles(UUID groupID, string principalID); + + // principals table + bool StorePrincipal(PrincipalData data); + PrincipalData RetrievePrincipal(string principalID); + bool DeletePrincipal(string principalID); + + // invites table + bool StoreInvitation(InvitationData data); + InvitationData RetrieveInvitation(UUID inviteID); + InvitationData RetrieveInvitation(UUID groupID, string principalID); + bool DeleteInvite(UUID inviteID); + void DeleteOldInvites(); + + // notices table + bool StoreNotice(NoticeData data); + NoticeData RetrieveNotice(UUID noticeID); + NoticeData[] RetrieveNotices(UUID groupID); + bool DeleteNotice(UUID noticeID); + void DeleteOldNotices(); + + // combinations + MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID); + MembershipData[] RetrievePrincipalGroupMemberships(string principalID); + + // Misc + } +} diff --git a/OpenSim/Data/IHGTravelingData.cs b/OpenSim/Data/IHGTravelingData.cs new file mode 100644 index 0000000000..452af7bc50 --- /dev/null +++ b/OpenSim/Data/IHGTravelingData.cs @@ -0,0 +1,59 @@ +/* + * 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.Data +{ + // This MUST be a ref type! + public class HGTravelingData + { + public UUID SessionID; + public UUID UserID; + public Dictionary Data; + + public HGTravelingData() + { + Data = new Dictionary(); + } + } + + /// + /// An interface for connecting to the user grid datastore + /// + public interface IHGTravelingData + { + HGTravelingData Get(UUID sessionID); + HGTravelingData[] GetSessions(UUID userID); + bool Store(HGTravelingData data); + bool Delete(UUID sessionID); + void DeleteOld(); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IInventoryData.cs b/OpenSim/Data/IInventoryData.cs new file mode 100644 index 0000000000..74d5d37bf9 --- /dev/null +++ b/OpenSim/Data/IInventoryData.cs @@ -0,0 +1,158 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + /// + /// An interface for accessing inventory data from a storage server + /// + public interface IInventoryDataPlugin : IPlugin + { + /// + /// Initialises the interface + /// + void Initialise(string connect); + + /// + /// Returns all descendent folders of this folder. Does not return the parent folder itself. + /// + /// The folder to get subfolders for + /// A list of inventory folders + List getFolderHierarchy(UUID parentID); + + /// + /// Returns a list of inventory items contained within the specified folder + /// + /// The UUID of the target folder + /// A List of InventoryItemBase items + List getInventoryInFolder(UUID folderID); + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + List getUserRootFolders(UUID user); + + /// + /// Returns the users inventory root folder. + /// + /// The UUID of the user who is having inventory being returned + /// Root inventory folder, null if no root inventory folder was found + InventoryFolderBase getUserRootFolder(UUID user); + + /// + /// Returns a list of inventory folders contained in the folder 'parentID' + /// + /// The folder to get subfolders for + /// A list of inventory folders + List getInventoryFolders(UUID parentID); + + /// + /// Returns an inventory item by its UUID + /// + /// The UUID of the item to be returned + /// A class containing item information + InventoryItemBase getInventoryItem(UUID item); + + /// + /// Returns a specified inventory folder by its UUID + /// + /// The UUID of the folder to be returned + /// A class containing folder information + InventoryFolderBase getInventoryFolder(UUID folder); + + /// + /// Creates a new inventory item based on item + /// + /// The item to be created + void addInventoryItem(InventoryItemBase item); + + /// + /// Updates an inventory item with item (updates based on ID) + /// + /// The updated item + void updateInventoryItem(InventoryItemBase item); + + /// + /// + /// + /// + void deleteInventoryItem(UUID item); + + /// + /// + /// + /// + InventoryItemBase queryInventoryItem(UUID item); + + /// + /// + /// + /// + InventoryFolderBase queryInventoryFolder(UUID folder); + + /// + /// Adds a new folder specified by folder + /// + /// The inventory folder + void addInventoryFolder(InventoryFolderBase folder); + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + void updateInventoryFolder(InventoryFolderBase folder); + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + void moveInventoryFolder(InventoryFolderBase folder); + + /// + /// Deletes a folder. Thie will delete both the folder itself and its contents (items and descendent folders) + /// + /// The id of the folder + void deleteInventoryFolder(UUID folder); + + /// + /// Returns all activated gesture-items in the inventory of the specified avatar. + /// + /// + /// The of the avatar + /// + /// + /// The list of gestures (s) + /// + List fetchActiveGestures(UUID avatarID); + } +} diff --git a/OpenSim/Data/IOfflineIMData.cs b/OpenSim/Data/IOfflineIMData.cs new file mode 100644 index 0000000000..58501a3b7d --- /dev/null +++ b/OpenSim/Data/IOfflineIMData.cs @@ -0,0 +1,50 @@ +/* + * 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.Generic; +using OpenSim.Data; +using OpenMetaverse; + +namespace OpenSim.Data +{ + public class OfflineIMData + { + public UUID PrincipalID; + public UUID FromID; + public Dictionary Data; + } + + + public interface IOfflineIMData + { + OfflineIMData[] Get(string field, string val); + long GetCount(string field, string key); + bool Store(OfflineIMData data); + bool Delete(string field, string val); + void DeleteOld(); + } +} diff --git a/OpenSim/Data/IPresenceData.cs b/OpenSim/Data/IPresenceData.cs new file mode 100644 index 0000000000..9ec48b0e2a --- /dev/null +++ b/OpenSim/Data/IPresenceData.cs @@ -0,0 +1,58 @@ +/* + * 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.Data +{ + // This MUST be a ref type! + public class PresenceData + { + public string UserID; + public UUID RegionID; + public UUID SessionID; + public Dictionary Data; + } + + /// + /// An interface for connecting to the presence datastore + /// + public interface IPresenceData + { + bool Store(PresenceData data); + + PresenceData Get(UUID sessionID); + void LogoutRegionAgents(UUID regionID); + bool ReportAgent(UUID sessionID, UUID regionID); + PresenceData[] Get(string field, string data); + bool Delete(string field, string val); + bool VerifyAgent(UUID agentId, UUID secureSessionID); + } +} diff --git a/OpenSim/Data/IProfilesData.cs b/OpenSim/Data/IProfilesData.cs new file mode 100644 index 0000000000..7fb075d3d5 --- /dev/null +++ b/OpenSim/Data/IProfilesData.cs @@ -0,0 +1,58 @@ +/* + * 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 OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + + public interface IProfilesData + { + OSDArray GetClassifiedRecords(UUID creatorId); + bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result); + bool DeleteClassifiedRecord(UUID recordId); + OSDArray GetAvatarPicks(UUID avatarId); + UserProfilePick GetPickInfo(UUID avatarId, UUID pickId); + bool UpdatePicksRecord(UserProfilePick pick); + bool DeletePicksRecord(UUID pickId); + bool GetAvatarNotes(ref UserProfileNotes note); + bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result); + bool GetAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result); + bool UpdateAvatarInterests(UserProfileProperties up, ref string result); + bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result); + bool UpdateUserPreferences(ref UserPreferences pref, ref string result); + bool GetUserPreferences(ref UserPreferences pref, ref string result); + bool GetUserAppData(ref UserAppData props, ref string result); + bool SetUserAppData(UserAppData props, ref string result); + OSDArray GetUserImageAssets(UUID avatarId); + } +} + diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs new file mode 100644 index 0000000000..ca9b32716c --- /dev/null +++ b/OpenSim/Data/IRegionData.cs @@ -0,0 +1,110 @@ +/* + * 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.Data +{ + public class RegionData + { + public UUID RegionID; + public UUID ScopeID; + public string RegionName; + + /// + /// The position in meters of this region. + /// + public int posX; + + /// + /// The position in meters of this region. + /// + public int posY; + + public int sizeX; + public int sizeY; + + /// + /// Return the x-coordinate of this region in region units. + /// + public int coordX { get { return (int)Util.WorldToRegionLoc((uint)posX); } } + + /// + /// Return the y-coordinate of this region in region units. + /// + public int coordY { get { return (int)Util.WorldToRegionLoc((uint)posY); } } + + public Dictionary Data; + } + + /// + /// An interface for connecting to the authentication datastore + /// + public interface IRegionData + { + RegionData Get(UUID regionID, UUID ScopeID); + List Get(string regionName, UUID ScopeID); + RegionData Get(int x, int y, UUID ScopeID); + List Get(int xStart, int yStart, int xEnd, int yEnd, UUID ScopeID); + + bool Store(RegionData data); + + bool SetDataItem(UUID principalID, string item, string value); + + bool Delete(UUID regionID); + + List GetDefaultRegions(UUID scopeID); + List GetDefaultHypergridRegions(UUID scopeID); + List GetFallbackRegions(UUID scopeID, int x, int y); + List GetHyperlinks(UUID scopeID); + } + + public class RegionDataDistanceCompare : IComparer + { + private Vector2 m_origin; + + public RegionDataDistanceCompare(int x, int y) + { + m_origin = new Vector2(x, y); + } + + public int Compare(RegionData regionA, RegionData regionB) + { + Vector2 vectorA = new Vector2(regionA.posX, regionA.posY); + Vector2 vectorB = new Vector2(regionB.posX, regionB.posY); + return Math.Sign(VectorDistance(m_origin, vectorA) - VectorDistance(m_origin, vectorB)); + } + + private float VectorDistance(Vector2 x, Vector2 y) + { + return (x - y).Length(); + } + } +} diff --git a/OpenSim/Data/IUserAccountData.cs b/OpenSim/Data/IUserAccountData.cs new file mode 100644 index 0000000000..906ba6c561 --- /dev/null +++ b/OpenSim/Data/IUserAccountData.cs @@ -0,0 +1,54 @@ +/* + * 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.Data +{ + public class UserAccountData + { + public UUID PrincipalID; + public UUID ScopeID; + public string FirstName; + public string LastName; + public Dictionary Data; + } + + /// + /// An interface for connecting to the user accounts datastore + /// + public interface IUserAccountData + { + UserAccountData[] Get(string[] fields, string[] values); + bool Store(UserAccountData data); + bool Delete(string field, string val); + UserAccountData[] GetUsers(UUID scopeID, string query); + } +} diff --git a/OpenSim/Data/IXAssetDataPlugin.cs b/OpenSim/Data/IXAssetDataPlugin.cs new file mode 100644 index 0000000000..2d247974a2 --- /dev/null +++ b/OpenSim/Data/IXAssetDataPlugin.cs @@ -0,0 +1,47 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data +{ + /// + /// This interface exists to distinguish between the normal IAssetDataPlugin and the one used by XAssetService + /// for now. + /// + public interface IXAssetDataPlugin : IPlugin + { + AssetBase GetAsset(UUID uuid); + void StoreAsset(AssetBase asset); + bool[] AssetsExist(UUID[] uuids); + List FetchAssetMetadataSet(int start, int count); + void Initialise(string connect); + bool Delete(string id); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IXGroupData.cs b/OpenSim/Data/IXGroupData.cs new file mode 100644 index 0000000000..e5821ef2dc --- /dev/null +++ b/OpenSim/Data/IXGroupData.cs @@ -0,0 +1,118 @@ +/* + * 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.Data +{ + public class XGroup + { + public UUID groupID; + public UUID ownerRoleID; + public string name; + public string charter; + public bool showInList; + public UUID insigniaID; + public int membershipFee; + public bool openEnrollment; + public bool allowPublish; + public bool maturePublish; + public UUID founderID; + public ulong everyonePowers; + public ulong ownersPowers; + + public Dictionary members = new Dictionary(); + public Dictionary notices = new Dictionary(); + + public XGroup Clone() + { + XGroup clone = (XGroup)MemberwiseClone(); + clone.members = new Dictionary(); + clone.notices = new Dictionary(); + + foreach (KeyValuePair kvp in members) + clone.members[kvp.Key] = kvp.Value.Clone(); + + foreach (KeyValuePair kvp in notices) + clone.notices[kvp.Key] = kvp.Value.Clone(); + + return clone; + } + } + + public class XGroupMember + { + public UUID agentID; + public UUID groupID; + public UUID roleID; + public bool acceptNotices = true; + public bool listInProfile = true; + + public XGroupMember Clone() + { + return (XGroupMember)MemberwiseClone(); + } + } + + public class XGroupNotice + { + public UUID groupID; + public UUID noticeID; + public uint timestamp; + public string fromName; + public string subject; + public string message; + public byte[] binaryBucket; + public bool hasAttachment; + public int assetType; + + public XGroupNotice Clone() + { + XGroupNotice clone = (XGroupNotice)MemberwiseClone(); + clone.binaryBucket = (byte[])binaryBucket.Clone(); + + return clone; + } + } + + /// + /// Early stub interface for groups data, not final. + /// + /// + /// Currently in-use only for regression test purposes. + /// + public interface IXGroupData + { + bool StoreGroup(XGroup group); + XGroup GetGroup(UUID groupID); + Dictionary GetGroups(); + bool DeleteGroup(UUID groupID); + } +} \ No newline at end of file diff --git a/OpenSim/Data/IXInventoryData.cs b/OpenSim/Data/IXInventoryData.cs new file mode 100644 index 0000000000..ca4750660a --- /dev/null +++ b/OpenSim/Data/IXInventoryData.cs @@ -0,0 +1,138 @@ +/* + * 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.Data +{ + public class XInventoryFolder + { + public string folderName; + public int type; + public int version; + public UUID folderID; + public UUID agentID; + public UUID parentFolderID; + + public XInventoryFolder Clone() + { + return (XInventoryFolder)MemberwiseClone(); + } + } + + public class XInventoryItem + { + public UUID assetID; + public int assetType; + public string inventoryName; + public string inventoryDescription; + public int inventoryNextPermissions; + public int inventoryCurrentPermissions; + public int invType; + public string creatorID; + public int inventoryBasePermissions; + public int inventoryEveryOnePermissions; + public int salePrice; + public int saleType; + public int creationDate; + public UUID groupID; + public int groupOwned; + public int flags; + public UUID inventoryID; + public UUID avatarID; + public UUID parentFolderID; + public int inventoryGroupPermissions; + + public XInventoryItem Clone() + { + return (XInventoryItem)MemberwiseClone(); + } + } + + public interface IXInventoryData + { + XInventoryFolder[] GetFolders(string[] fields, string[] vals); + XInventoryItem[] GetItems(string[] fields, string[] vals); + + bool StoreFolder(XInventoryFolder folder); + bool StoreItem(XInventoryItem item); + + /// + /// Delete folders where field == val + /// + /// + /// + /// true if the delete was successful, false if it was not + bool DeleteFolders(string field, string val); + + /// + /// Delete folders where field1 == val1, field2 == val2... + /// + /// + /// + /// true if the delete was successful, false if it was not + bool DeleteFolders(string[] fields, string[] vals); + + /// + /// Delete items where field == val + /// + /// + /// + /// true if the delete was successful, false if it was not + bool DeleteItems(string field, string val); + + /// + /// Delete items where field1 == val1, field2 == val2... + /// + /// + /// + /// true if the delete was successful, false if it was not + bool DeleteItems(string[] fields, string[] vals); + + /// + /// Move an item to another folder. + /// + /// /returns> + /// UUID of the item + /// UUID of the new parent folder. + bool MoveItem(string id, string newParentFolderID); + + /// + /// Move a folder to another folder. + /// + /// /returns> + /// UUID of the item + /// UUID of the new parent folder. + bool MoveFolder(string id, string newParentFolderID); + + XInventoryItem[] GetActiveGestures(UUID principalID); + int GetAssetPermissions(UUID principalID, UUID assetID); + } +} diff --git a/OpenSim/Data/IniConfig.cs b/OpenSim/Data/IniConfig.cs new file mode 100644 index 0000000000..ff5a6cf1e7 --- /dev/null +++ b/OpenSim/Data/IniConfig.cs @@ -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.IO; +using System.Text.RegularExpressions; + +/* + Taken from public code listing at by Alex Pinsker + http://alexpinsker.blogspot.com/2005/12/reading-ini-file-from-c_113432097333021549.html + */ + +namespace OpenSim.Data +{ + /// + /// Parse settings from ini-like files + /// + public class IniFile + { + static IniFile() + { + _iniKeyValuePatternRegex = new Regex( + @"((\s)*(?([^\=^\s^\n]+))[\s^\n]* + # key part (surrounding whitespace stripped) + \= + (\s)*(?([^\n^\s]+(\n){0,1}))) + # value part (surrounding whitespace stripped) + ", + RegexOptions.IgnorePatternWhitespace | + RegexOptions.Compiled | + RegexOptions.CultureInvariant); + } + + private static Regex _iniKeyValuePatternRegex; + + public IniFile(string iniFileName) + { + _iniFileName = iniFileName; + } + + public string ParseFileReadValue(string key) + { + using (StreamReader reader = + new StreamReader(_iniFileName)) + { + do + { + string line = reader.ReadLine(); + Match match = + _iniKeyValuePatternRegex.Match(line); + if (match.Success) + { + string currentKey = + match.Groups["Key"].Value as string; + if (currentKey != null && + currentKey.Trim().CompareTo(key) == 0) + { + string value = + match.Groups["Value"].Value as string; + return value; + } + } + } while (reader.Peek() != -1); + } + return null; + } + + public string IniFileName + { + get { return _iniFileName; } + } + + private string _iniFileName; + } +} diff --git a/OpenSim/Data/Migration.cs b/OpenSim/Data/Migration.cs new file mode 100644 index 0000000000..d60647015f --- /dev/null +++ b/OpenSim/Data/Migration.cs @@ -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.Data; +using System.Data.Common; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; + +namespace OpenSim.Data +{ + /// + /// + /// The Migration theory is based on the ruby on rails concept. + /// Each database driver is going to be allowed to have files in + /// Resources that specify the database migrations. They will be + /// of the form: + /// + /// 001_Users.sql + /// 002_Users.sql + /// 003_Users.sql + /// 001_Prims.sql + /// 002_Prims.sql + /// ...etc... + /// + /// When a database driver starts up, it specifies a resource that + /// needs to be brought up to the current revision. For instance: + /// + /// Migration um = new Migration(DbConnection, Assembly, "Users"); + /// um.Update(); + /// + /// This works out which version Users is at, and applies all the + /// revisions past it to it. If there is no users table, all + /// revisions are applied in order. Consider each future + /// migration to be an incremental roll forward of the tables in + /// question. + /// + /// Assembly must be specifically passed in because otherwise you + /// get the assembly that Migration.cs is part of, and what you + /// really want is the assembly of your database class. + /// + /// + public class Migration + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string _type; + protected DbConnection _conn; + protected Assembly _assem; + + private Regex _match_old; + private Regex _match_new; + + /// Have the parameterless constructor just so we can specify it as a generic parameter with the new() constraint. + /// Currently this is only used in the tests. A Migration instance created this way must be then + /// initialized with Initialize(). Regular creation should be through the parameterized constructors. + /// + public Migration() + { + } + + public Migration(DbConnection conn, Assembly assem, string subtype, string type) + { + Initialize(conn, assem, type, subtype); + } + + public Migration(DbConnection conn, Assembly assem, string type) + { + Initialize(conn, assem, type, ""); + } + + /// Must be called after creating with the parameterless constructor. + /// NOTE that the Migration class now doesn't access database in any way during initialization. + /// Specifically, it won't check if the [migrations] table exists. Such checks are done later: + /// automatically on Update(), or you can explicitly call InitMigrationsTable(). + /// + /// + /// + /// + /// + public void Initialize (DbConnection conn, Assembly assem, string type, string subtype) + { + _type = type; + _conn = conn; + _assem = assem; + _match_old = new Regex(subtype + @"\.(\d\d\d)_" + _type + @"\.sql"); + string s = String.IsNullOrEmpty(subtype) ? _type : _type + @"\." + subtype; + _match_new = new Regex(@"\." + s + @"\.migrations(?:\.(?\d+)$|.*)"); + } + + public void InitMigrationsTable() + { + // NOTE: normally when the [migrations] table is created, the version record for 'migrations' is + // added immediately. However, if for some reason the table is there but empty, we want to handle that as well. + int ver = FindVersion(_conn, "migrations"); + if (ver <= 0) // -1 = no table, 0 = no version record + { + if (ver < 0) + ExecuteScript("create table migrations(name varchar(100), version int)"); + InsertVersion("migrations", 1); + } + } + + /// Executes a script, possibly in a database-specific way. + /// It can be redefined for a specific DBMS, if necessary. Specifically, + /// to avoid problems with proc definitions in MySQL, we must use + /// MySqlScript class instead of just DbCommand. We don't want to bring + /// MySQL references here, so instead define a MySQLMigration class + /// in OpenSim.Data.MySQL + /// + /// + /// Array of strings, one-per-batch (often just one) + protected virtual void ExecuteScript(DbConnection conn, string[] script) + { + using (DbCommand cmd = conn.CreateCommand()) + { + cmd.CommandTimeout = 0; + foreach (string sql in script) + { + cmd.CommandText = sql; + try + { + cmd.ExecuteNonQuery(); + } + catch(Exception e) + { + throw new Exception(e.Message + " in SQL: " + sql); + } + } + } + } + + protected void ExecuteScript(DbConnection conn, string sql) + { + ExecuteScript(conn, new string[]{sql}); + } + + protected void ExecuteScript(string sql) + { + ExecuteScript(_conn, sql); + } + + protected void ExecuteScript(string[] script) + { + ExecuteScript(_conn, script); + } + + public void Update() + { + InitMigrationsTable(); + + int version = FindVersion(_conn, _type); + + SortedList migrations = GetMigrationsAfter(version); + if (migrations.Count < 1) + return; + + // to prevent people from killing long migrations. + m_log.InfoFormat("[MIGRATIONS]: Upgrading {0} to latest revision {1}.", _type, migrations.Keys[migrations.Count - 1]); + m_log.Info("[MIGRATIONS]: NOTE - this may take a while, don't interrupt this process!"); + + foreach (KeyValuePair kvp in migrations) + { + int newversion = kvp.Key; + // we need to up the command timeout to infinite as we might be doing long migrations. + + /* [AlexRa 01-May-10]: We can't always just run any SQL in a single batch (= ExecuteNonQuery()). Things like + * stored proc definitions might have to be sent to the server each in a separate batch. + * This is certainly so for MS SQL; not sure how the MySQL connector sorts out the mess + * with 'delimiter @@'/'delimiter ;' around procs. So each "script" this code executes now is not + * a single string, but an array of strings, executed separately. + */ + try + { + ExecuteScript(kvp.Value); + } + catch (Exception e) + { + m_log.DebugFormat("[MIGRATIONS]: Cmd was {0}", e.Message.Replace("\n", " ")); + m_log.Debug("[MIGRATIONS]: An error has occurred in the migration. If you're running OpenSim for the first time then you can probably safely ignore this, since certain migration commands attempt to fetch data out of old tables. However, if you're using an existing database and you see database related errors while running OpenSim then you will need to fix these problems manually. Continuing."); + ExecuteScript("ROLLBACK;"); + } + + if (version == 0) + { + InsertVersion(_type, newversion); + } + else + { + UpdateVersion(_type, newversion); + } + version = newversion; + } + } + + public int Version + { + get { return FindVersion(_conn, _type); } + set { + if (Version < 1) + { + InsertVersion(_type, value); + } + else + { + UpdateVersion(_type, value); + } + } + } + + protected virtual int FindVersion(DbConnection conn, string type) + { + int version = 0; + using (DbCommand cmd = conn.CreateCommand()) + { + try + { + cmd.CommandText = "select version from migrations where name='" + type + "' order by version desc"; + using (DbDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + version = Convert.ToInt32(reader["version"]); + } + reader.Close(); + } + } + catch + { + // Something went wrong (probably no table), so we're at version -1 + version = -1; + } + } + return version; + } + + private void InsertVersion(string type, int version) + { + m_log.InfoFormat("[MIGRATIONS]: Creating {0} at version {1}", type, version); + ExecuteScript("insert into migrations(name, version) values('" + type + "', " + version + ")"); + } + + private void UpdateVersion(string type, int version) + { + m_log.InfoFormat("[MIGRATIONS]: Updating {0} to version {1}", type, version); + ExecuteScript("update migrations set version=" + version + " where name='" + type + "'"); + } + + private delegate void FlushProc(); + + /// Scans for migration resources in either old-style "scattered" (one file per version) + /// or new-style "integrated" format (single file with ":VERSION nnn" sections). + /// In the new-style migrations it also recognizes ':GO' separators for parts of the SQL script + /// that must be sent to the server separately. The old-style migrations are loaded each in one piece + /// and don't support the ':GO' feature. + /// + /// The version we are currently at. Scan for any higher versions + /// A list of string arrays, representing the scripts. + private SortedList GetMigrationsAfter(int after) + { + SortedList migrations = new SortedList(); + + string[] names = _assem.GetManifestResourceNames(); + if (names.Length == 0) // should never happen + return migrations; + + Array.Sort(names); // we want all the migrations ordered + + int nLastVerFound = 0; + Match m = null; + string sFile = Array.FindLast(names, nm => { m = _match_new.Match(nm); return m.Success; }); // ; nm.StartsWith(sPrefix, StringComparison.InvariantCultureIgnoreCase + + if ((m != null) && !String.IsNullOrEmpty(sFile)) + { + /* The filename should be '.migrations[.NNN]' where NNN + * is the last version number defined in the file. If the '.NNN' part is recognized, the code can skip + * the file without looking inside if we have a higher version already. Without the suffix we read + * the file anyway and use the version numbers inside. Any unrecognized suffix (such as '.sql') + * is valid but ignored. + * + * NOTE that we expect only one 'merged' migration file. If there are several, we take the last one. + * If you are numbering them, leave only the latest one in the project or at least make sure they numbered + * to come up in the correct order (e.g. 'SomeStore.migrations.001' rather than 'SomeStore.migrations.1') + */ + + if (m.Groups.Count > 1 && int.TryParse(m.Groups[1].Value, out nLastVerFound)) + { + if (nLastVerFound <= after) + goto scan_old_style; + } + + System.Text.StringBuilder sb = new System.Text.StringBuilder(4096); + int nVersion = -1; + + List script = new List(); + + FlushProc flush = delegate() + { + if (sb.Length > 0) // last SQL stmt to script list + { + script.Add(sb.ToString()); + sb.Length = 0; + } + + if ((nVersion > 0) && (nVersion > after) && (script.Count > 0) && !migrations.ContainsKey(nVersion)) // script to the versioned script list + { + migrations[nVersion] = script.ToArray(); + } + script.Clear(); + }; + + using (Stream resource = _assem.GetManifestResourceStream(sFile)) + using (StreamReader resourceReader = new StreamReader(resource)) + { + int nLineNo = 0; + while (!resourceReader.EndOfStream) + { + string sLine = resourceReader.ReadLine(); + nLineNo++; + + if (String.IsNullOrEmpty(sLine) || sLine.StartsWith("#")) // ignore a comment or empty line + continue; + + if (sLine.Trim().Equals(":GO", StringComparison.InvariantCultureIgnoreCase)) + { + if (sb.Length == 0) continue; + if (nVersion > after) + script.Add(sb.ToString()); + sb.Length = 0; + continue; + } + + if (sLine.StartsWith(":VERSION ", StringComparison.InvariantCultureIgnoreCase)) // ":VERSION nnn" + { + flush(); + + int n = sLine.IndexOf('#'); // Comment is allowed in version sections, ignored + if (n >= 0) + sLine = sLine.Substring(0, n); + + if (!int.TryParse(sLine.Substring(9).Trim(), out nVersion)) + { + m_log.ErrorFormat("[MIGRATIONS]: invalid version marker at {0}: line {1}. Migration failed!", sFile, nLineNo); + break; + } + } + else + { + sb.AppendLine(sLine); + } + } + flush(); + + // If there are scattered migration files as well, only look for those with higher version numbers. + if (after < nVersion) + after = nVersion; + } + } + +scan_old_style: + // scan "old style" migration pieces anyway, ignore any versions already filled from the single file + foreach (string s in names) + { + m = _match_old.Match(s); + if (m.Success) + { + int version = int.Parse(m.Groups[1].ToString()); + if ((version > after) && !migrations.ContainsKey(version)) + { + using (Stream resource = _assem.GetManifestResourceStream(s)) + { + using (StreamReader resourceReader = new StreamReader(resource)) + { + string sql = resourceReader.ReadToEnd(); + migrations.Add(version, new string[]{sql}); + } + } + } + } + } + + if (migrations.Count < 1) + m_log.DebugFormat("[MIGRATIONS]: {0} data tables already up to date at revision {1}", _type, after); + + return migrations; + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLAgentPreferencesData.cs b/OpenSim/Data/MySQL/MySQLAgentPreferencesData.cs new file mode 100644 index 0000000000..ed0ab9890d --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLAgentPreferencesData.cs @@ -0,0 +1,56 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySQLAgentPreferencesData : MySQLGenericTableHandler, IAgentPreferencesData + { + public MySQLAgentPreferencesData(string connectionString, string realm) + : base(connectionString, realm, "AgentPrefs") + { + } + + public AgentPreferencesData GetPrefs(UUID agentID) + { + AgentPreferencesData[] ret = Get("PrincipalID", agentID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + } +} + diff --git a/OpenSim/Data/MySQL/MySQLAssetData.cs b/OpenSim/Data/MySQL/MySQLAssetData.cs new file mode 100644 index 0000000000..5d8da17e5e --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLAssetData.cs @@ -0,0 +1,367 @@ +/* + * 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.Data; +using System.Reflection; +using System.Collections.Generic; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for the Asset Server + /// + public class MySQLAssetData : AssetDataBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #region IPlugin Members + + public override string Version { get { return "1.0.0.0"; } } + + /// + /// Initialises Asset interface + /// + /// + /// Loads and initialises the MySQL storage plugin. + /// Warns and uses the obsolete mysql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public override void Initialise(string connect) + { + m_connectionString = connect; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "AssetStore"); + m.Update(); + } + } + + public override void Initialise() + { + throw new NotImplementedException(); + } + + public override void Dispose() { } + + /// + /// The name of this DB provider + /// + override public string Name + { + get { return "MySQL Asset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from database + /// + /// Asset UUID to fetch + /// Return the asset + /// On failure : throw an exception and attempt to reconnect to database + override public AssetBase GetAsset(UUID assetID) + { + AssetBase asset = null; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand( + "SELECT name, description, assetType, local, temporary, asset_flags, CreatorID, data FROM assets WHERE id=?id", + dbcon)) + { + cmd.Parameters.AddWithValue("?id", assetID.ToString()); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { + asset = new AssetBase(assetID, (string)dbReader["name"], (sbyte)dbReader["assetType"], dbReader["CreatorID"].ToString()); + asset.Data = (byte[])dbReader["data"]; + asset.Description = (string)dbReader["description"]; + + string local = dbReader["local"].ToString(); + if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + asset.Local = true; + else + asset.Local = false; + + asset.Temporary = Convert.ToBoolean(dbReader["temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + } + } + } + catch (Exception e) + { + m_log.Error( + string.Format("[ASSETS DB]: MySql failure fetching asset {0}. Exception ", assetID), e); + } + } + } + + return asset; + } + + /// + /// Create an asset in database, or update it if existing. + /// + /// Asset UUID to create + /// On failure : Throw an exception and attempt to reconnect to database + override public void StoreAsset(AssetBase asset) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = + new MySqlCommand( + "replace INTO assets(id, name, description, assetType, local, temporary, create_time, access_time, asset_flags, CreatorID, data)" + + "VALUES(?id, ?name, ?description, ?assetType, ?local, ?temporary, ?create_time, ?access_time, ?asset_flags, ?CreatorID, ?data)", + dbcon)) + { + string assetName = asset.Name; + if (asset.Name.Length > AssetBase.MAX_ASSET_NAME) + { + assetName = asset.Name.Substring(0, AssetBase.MAX_ASSET_NAME); + m_log.WarnFormat( + "[ASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > AssetBase.MAX_ASSET_DESC) + { + assetDescription = asset.Description.Substring(0, AssetBase.MAX_ASSET_DESC); + m_log.WarnFormat( + "[ASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + try + { + using (cmd) + { + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.AddWithValue("?id", asset.ID); + cmd.Parameters.AddWithValue("?name", assetName); + cmd.Parameters.AddWithValue("?description", assetDescription); + cmd.Parameters.AddWithValue("?assetType", asset.Type); + cmd.Parameters.AddWithValue("?local", asset.Local); + cmd.Parameters.AddWithValue("?temporary", asset.Temporary); + cmd.Parameters.AddWithValue("?create_time", now); + cmd.Parameters.AddWithValue("?access_time", now); + cmd.Parameters.AddWithValue("?CreatorID", asset.Metadata.CreatorID); + cmd.Parameters.AddWithValue("?asset_flags", (int)asset.Flags); + cmd.Parameters.AddWithValue("?data", asset.Data); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.Error( + string.Format( + "[ASSET DB]: MySQL failure creating asset {0} with name {1}. Exception ", + asset.FullID, asset.Name) + , e); + } + } + } + } + + private void UpdateAccessTime(AssetBase asset) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd + = new MySqlCommand("update assets set access_time=?access_time where id=?id", dbcon)) + { + try + { + using (cmd) + { + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.AddWithValue("?id", asset.ID); + cmd.Parameters.AddWithValue("?access_time", now); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.Error( + string.Format( + "[ASSETS DB]: Failure updating access_time for asset {0} with name {1}. Exception ", + asset.FullID, asset.Name), + e); + } + } + } + } + + /// + /// Check if the assets exist in the database. + /// + /// The assets' IDs + /// For each asset: true if it exists, false otherwise + public override bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exist = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) + { + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + UUID id = DBGuid.FromDB(dbReader["id"]); + exist.Add(id); + } + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exist.Contains(uuids[i]); + + return results; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public override List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd + = new MySqlCommand( + "SELECT name,description,assetType,temporary,id,asset_flags,CreatorID FROM assets LIMIT ?start, ?count", + dbcon)) + { + cmd.Parameters.AddWithValue("?start", start); + cmd.Parameters.AddWithValue("?count", count); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.Name = (string)dbReader["name"]; + metadata.Description = (string)dbReader["description"]; + metadata.Type = (sbyte)dbReader["assetType"]; + metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + metadata.FullID = DBGuid.FromDB(dbReader["id"]); + metadata.CreatorID = dbReader["CreatorID"].ToString(); + + // Current SHA1s are not stored/computed. + metadata.SHA1 = new byte[] { }; + + retList.Add(metadata); + } + } + } + catch (Exception e) + { + m_log.Error( + string.Format( + "[ASSETS DB]: MySql failure fetching asset set from {0}, count {1}. Exception ", + start, count), + e); + } + } + } + + return retList; + } + + public override bool Delete(string id) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand("delete from assets where id=?id", dbcon)) + { + cmd.Parameters.AddWithValue("?id", id); + cmd.ExecuteNonQuery(); + } + } + + return true; + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLAuthenticationData.cs b/OpenSim/Data/MySQL/MySQLAuthenticationData.cs new file mode 100644 index 0000000000..76274972a8 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLAuthenticationData.cs @@ -0,0 +1,227 @@ +/* + * 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 System.Data; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySqlAuthenticationData : MySqlFramework, IAuthenticationData + { + private string m_Realm; + private List m_ColumnNames; + private int m_LastExpire; + // private string m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySqlAuthenticationData(string connectionString, string realm) + : base(connectionString) + { + m_Realm = realm; + m_connectionString = connectionString; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "AuthStore"); + m.Update(); + } + } + + public AuthenticationData Get(UUID principalID) + { + AuthenticationData ret = new AuthenticationData(); + ret.Data = new Dictionary(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd + = new MySqlCommand("select * from `" + m_Realm + "` where UUID = ?principalID", dbcon)) + { + cmd.Parameters.AddWithValue("?principalID", principalID.ToString()); + + IDataReader result = cmd.ExecuteReader(); + + if (result.Read()) + { + ret.PrincipalID = principalID; + + CheckColumnNames(result); + + foreach (string s in m_ColumnNames) + { + if (s == "UUID") + continue; + + ret.Data[s] = result[s].ToString(); + } + + return ret; + } + else + { + return null; + } + } + } + } + + private void CheckColumnNames(IDataReader result) + { + if (m_ColumnNames != null) + return; + + List columnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + columnNames.Add(row["ColumnName"].ToString()); + + m_ColumnNames = columnNames; + } + + public bool Store(AuthenticationData data) + { + if (data.Data.ContainsKey("UUID")) + data.Data.Remove("UUID"); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + string update = "update `"+m_Realm+"` set "; + bool first = true; + foreach (string field in fields) + { + if (!first) + update += ", "; + update += "`" + field + "` = ?"+field; + + first = false; + + cmd.Parameters.AddWithValue("?"+field, data.Data[field]); + } + + update += " where UUID = ?principalID"; + + cmd.CommandText = update; + cmd.Parameters.AddWithValue("?principalID", data.PrincipalID.ToString()); + + if (ExecuteNonQuery(cmd) < 1) + { + string insert = "insert into `" + m_Realm + "` (`UUID`, `" + + String.Join("`, `", fields) + + "`) values (?principalID, ?" + String.Join(", ?", fields) + ")"; + + cmd.CommandText = insert; + + if (ExecuteNonQuery(cmd) < 1) + return false; + } + } + + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + using (MySqlCommand cmd + = new MySqlCommand("update `" + m_Realm + "` set `" + item + "` = ?" + item + " where UUID = ?UUID")) + { + cmd.Parameters.AddWithValue("?"+item, value); + cmd.Parameters.AddWithValue("?UUID", principalID.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + using (MySqlCommand cmd + = new MySqlCommand( + "insert into tokens (UUID, token, validity) values (?principalID, ?token, date_add(now(), interval ?lifetime minute))")) + { + cmd.Parameters.AddWithValue("?principalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?token", token); + cmd.Parameters.AddWithValue("?lifetime", lifetime.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + using (MySqlCommand cmd + = new MySqlCommand( + "update tokens set validity = date_add(now(), interval ?lifetime minute) where UUID = ?principalID and token = ?token and validity > now()")) + { + cmd.Parameters.AddWithValue("?principalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?token", token); + cmd.Parameters.AddWithValue("?lifetime", lifetime.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + + private void DoExpire() + { + using (MySqlCommand cmd = new MySqlCommand("delete from tokens where validity < now()")) + { + ExecuteNonQuery(cmd); + } + + m_LastExpire = System.Environment.TickCount; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLAvatarData.cs b/OpenSim/Data/MySQL/MySQLAvatarData.cs new file mode 100644 index 0000000000..6a2f5d814d --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLAvatarData.cs @@ -0,0 +1,68 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for the Grid Server + /// + public class MySQLAvatarData : MySQLGenericTableHandler, + IAvatarData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySQLAvatarData(string connectionString, string realm) : + base(connectionString, realm, "Avatar") + { + } + + public bool Delete(UUID principalID, string name) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = ?PrincipalID and `Name` = ?Name", m_Realm); + cmd.Parameters.AddWithValue("?PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?Name", name); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLEstateData.cs b/OpenSim/Data/MySQL/MySQLEstateData.cs new file mode 100644 index 0000000000..fe1487bba8 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLEstateData.cs @@ -0,0 +1,594 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + public class MySQLEstateStore : IEstateDataStore + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_connectionString; + + private FieldInfo[] m_Fields; + private Dictionary m_FieldMap = + new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySQLEstateStore() + { + } + + public MySQLEstateStore(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + + try + { + m_log.Info("[REGION DB]: MySql - connecting: " + Util.GetDisplayConnectionString(m_connectionString)); + } + catch (Exception e) + { + m_log.Debug("Exception: password not found in connection string\n" + e.ToString()); + } + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "EstateStore"); + m.Update(); + + Type t = typeof(EstateSettings); + m_Fields = t.GetFields(BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + foreach (FieldInfo f in m_Fields) + { + if (f.Name.Substring(0, 2) == "m_") + m_FieldMap[f.Name.Substring(2)] = f; + } + } + } + + private string[] FieldList + { + get { return new List(m_FieldMap.Keys).ToArray(); } + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + string sql = "select estate_settings." + String.Join(",estate_settings.", FieldList) + + " from estate_map left join estate_settings on estate_map.EstateID = estate_settings.EstateID where estate_settings.EstateID is not null and RegionID = ?RegionID"; + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = sql; + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + + EstateSettings e = DoLoad(cmd, regionID, create); + if (!create && e.EstateID == 0) // Not found + return null; + + return e; + } + } + + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private EstateSettings DoLoad(MySqlCommand cmd, UUID regionID, bool create) + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + cmd.Connection = dbcon; + + bool found = false; + + using (IDataReader r = cmd.ExecuteReader()) + { + if (r.Read()) + { + found = true; + + foreach (string name in FieldList) + { + if (m_FieldMap[name].FieldType == typeof(bool)) + { + m_FieldMap[name].SetValue(es, Convert.ToInt32(r[name]) != 0); + } + else if (m_FieldMap[name].FieldType == typeof(UUID)) + { + m_FieldMap[name].SetValue(es, DBGuid.FromDB(r[name])); + } + else + { + m_FieldMap[name].SetValue(es, r[name]); + } + } + } + } + + if (!found && create) + { + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); + } + } + + LoadBanList(es); + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + return es; + } + + private void DoCreate(EstateSettings es) + { + // Migration case + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = "insert into estate_settings (" + String.Join(",", names.ToArray()) + ") values ( ?" + String.Join(", ?", names.ToArray()) + ")"; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd2 = dbcon.CreateCommand()) + { + cmd2.CommandText = sql; + cmd2.Parameters.Clear(); + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd2.Parameters.AddWithValue("?" + name, "1"); + else + cmd2.Parameters.AddWithValue("?" + name, "0"); + } + else + { + cmd2.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd2.ExecuteNonQuery(); + + cmd2.CommandText = "select LAST_INSERT_ID() as id"; + cmd2.Parameters.Clear(); + + using (IDataReader r = cmd2.ExecuteReader()) + { + r.Read(); + es.EstateID = Convert.ToUInt32(r["id"]); + } + + es.Save(); + } + } + } + + public void StoreEstateSettings(EstateSettings es) + { + string sql = "replace into estate_settings (" + String.Join(",", FieldList) + ") values ( ?" + String.Join(", ?", FieldList) + ")"; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = sql; + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.AddWithValue("?" + name, "1"); + else + cmd.Parameters.AddWithValue("?" + name, "0"); + } + else + { + cmd.Parameters.AddWithValue("?" + name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + } + } + + SaveBanList(es); + SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers); + SaveUUIDList(es.EstateID, "estate_users", es.EstateAccess); + SaveUUIDList(es.EstateID, "estate_groups", es.EstateGroups); + } + + private void LoadBanList(EstateSettings es) + { + es.ClearBans(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select bannedUUID from estateban where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", es.EstateID); + + using (IDataReader r = cmd.ExecuteReader()) + { + while (r.Read()) + { + EstateBan eb = new EstateBan(); + + UUID uuid = new UUID(); + UUID.TryParse(r["bannedUUID"].ToString(), out uuid); + + eb.BannedUserID = uuid; + eb.BannedHostAddress = "0.0.0.0"; + eb.BannedHostIPMask = "0.0.0.0"; + es.AddBan(eb); + } + } + } + } + } + + private void SaveBanList(EstateSettings es) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from estateban where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", es.EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) values ( ?EstateID, ?bannedUUID, '', '', '' )"; + + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters.AddWithValue("?EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue("?bannedUUID", b.BannedUserID.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + } + + void SaveUUIDList(uint EstateID, string table, UUID[] data) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from " + table + " where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into " + table + " (EstateID, uuid) values ( ?EstateID, ?uuid )"; + + foreach (UUID uuid in data) + { + cmd.Parameters.AddWithValue("?EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue("?uuid", uuid.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + } + + UUID[] LoadUUIDList(uint EstateID, string table) + { + List uuids = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select uuid from " + table + " where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", EstateID); + + using (IDataReader r = cmd.ExecuteReader()) + { + while (r.Read()) + { + // EstateBan eb = new EstateBan(); + uuids.Add(DBGuid.FromDB(r["uuid"])); + } + } + } + } + + return uuids.ToArray(); + } + + public EstateSettings LoadEstateSettings(int estateID) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + string sql = "select estate_settings." + String.Join(",estate_settings.", FieldList) + " from estate_settings where EstateID = ?EstateID"; + + cmd.CommandText = sql; + cmd.Parameters.AddWithValue("?EstateID", estateID); + + EstateSettings e = DoLoad(cmd, UUID.Zero, false); + if (e.EstateID != estateID) + return null; + return e; + } + } + + public List LoadEstateSettingsAll() + { + List allEstateSettings = new List(); + + List allEstateIds = GetEstatesAll(); + + foreach (int estateId in allEstateIds) + allEstateSettings.Add(LoadEstateSettings(estateId)); + + return allEstateSettings; + } + + public List GetEstatesAll() + { + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select estateID from estate_settings"; + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + + dbcon.Close(); + } + + return result; + } + + public List GetEstates(string search) + { + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select estateID from estate_settings where EstateName = ?EstateName"; + cmd.Parameters.AddWithValue("?EstateName", search); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + + dbcon.Close(); + } + + return result; + } + + public List GetEstatesByOwner(UUID ownerID) + { + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select estateID from estate_settings where EstateOwner = ?EstateOwner"; + cmd.Parameters.AddWithValue("?EstateOwner", ownerID); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + + dbcon.Close(); + } + + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + MySqlTransaction transaction = dbcon.BeginTransaction(); + + try + { + // Delete any existing association of this region with an estate. + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.Transaction = transaction; + cmd.CommandText = "delete from estate_map where RegionID = ?RegionID"; + cmd.Parameters.AddWithValue("?RegionID", regionID); + + cmd.ExecuteNonQuery(); + } + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.Transaction = transaction; + cmd.CommandText = "insert into estate_map values (?RegionID, ?EstateID)"; + cmd.Parameters.AddWithValue("?RegionID", regionID); + cmd.Parameters.AddWithValue("?EstateID", estateID); + + int ret = cmd.ExecuteNonQuery(); + + if (ret != 0) + transaction.Commit(); + else + transaction.Rollback(); + + dbcon.Close(); + + return (ret != 0); + } + } + catch (MySqlException ex) + { + m_log.Error("[REGION DB]: LinkRegion failed: " + ex.Message); + transaction.Rollback(); + } + + dbcon.Close(); + } + + return false; + } + + public List GetRegions(int estateID) + { + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + try + { + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select RegionID from estate_map where EstateID = ?EstateID"; + cmd.Parameters.AddWithValue("?EstateID", estateID.ToString()); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while(reader.Read()) + result.Add(DBGuid.FromDB(reader["RegionID"])); + reader.Close(); + } + } + } + catch (Exception e) + { + m_log.Error("[REGION DB]: Error reading estate map. " + e.ToString()); + return result; + } + dbcon.Close(); + } + + return result; + } + + public bool DeleteEstate(int estateID) + { + return false; + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLFSAssetData.cs b/OpenSim/Data/MySQL/MySQLFSAssetData.cs new file mode 100644 index 0000000000..19e23b5827 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLFSAssetData.cs @@ -0,0 +1,414 @@ +/* + * 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.Collections.Generic; +using System.Data; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; + +namespace OpenSim.Data.MySQL +{ + public class MySQLFSAssetData : IFSAssetDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected MySqlConnection m_Connection = null; + protected string m_ConnectionString; + protected string m_Table; + protected Object m_connLock = new Object(); + + /// + /// Number of days that must pass before we update the access time on an asset when it has been fetched + /// Config option to change this is "DaysBetweenAccessTimeUpdates" + /// + private int DaysBetweenAccessTimeUpdates = 0; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySQLFSAssetData() + { + } + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + // Loads and initialises the MySQL storage plugin and checks for migrations + public void Initialise(string connect, string realm, int UpdateAccessTime) + { + m_ConnectionString = connect; + m_Table = realm; + + DaysBetweenAccessTimeUpdates = UpdateAccessTime; + + try + { + OpenDatabase(); + + Migration m = new Migration(m_Connection, Assembly, "FSAssetStore"); + m.Update(); + } + catch (MySqlException e) + { + m_log.ErrorFormat("[FSASSETS]: Can't connect to database: {0}", e.Message.ToString()); + } + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + public void Dispose() { } + + public string Name + { + get { return "MySQL FSAsset storage engine"; } + } + + #endregion + + private bool OpenDatabase() + { + try + { + m_Connection = new MySqlConnection(m_ConnectionString); + + m_Connection.Open(); + } + catch (MySqlException e) + { + m_log.ErrorFormat("[FSASSETS]: Can't connect to database: {0}", + e.Message.ToString()); + + return false; + } + + return true; + } + + private IDataReader ExecuteReader(MySqlCommand c) + { + IDataReader r = null; + MySqlConnection connection = (MySqlConnection) ((ICloneable)m_Connection).Clone(); + connection.Open(); + c.Connection = connection; + + r = c.ExecuteReader(); + + return r; + } + + private void ExecuteNonQuery(MySqlCommand c) + { + lock (m_connLock) + { + bool errorSeen = false; + + while (true) + { + try + { + c.ExecuteNonQuery(); + } + catch (MySqlException) + { + System.Threading.Thread.Sleep(500); + + m_Connection.Close(); + m_Connection = (MySqlConnection) ((ICloneable)m_Connection).Clone(); + m_Connection.Open(); + c.Connection = m_Connection; + + if (!errorSeen) + { + errorSeen = true; + continue; + } + m_log.ErrorFormat("[FSASSETS] MySQL command: {0}", c.CommandText); + throw; + } + + break; + } + } + } + + #region IFSAssetDataPlugin Members + + public AssetMetadata Get(string id, out string hash) + { + hash = String.Empty; + + MySqlCommand cmd = new MySqlCommand(); + + cmd.CommandText = String.Format("select id, name, description, type, hash, create_time, access_time, asset_flags from {0} where id = ?id", m_Table); + cmd.Parameters.AddWithValue("?id", id); + + IDataReader reader = ExecuteReader(cmd); + + if (!reader.Read()) + { + reader.Close(); + FreeCommand(cmd); + return null; + } + + AssetMetadata meta = new AssetMetadata(); + + hash = reader["hash"].ToString(); + + meta.ID = id; + meta.FullID = new UUID(id); + + meta.Name = reader["name"].ToString(); + meta.Description = reader["description"].ToString(); + meta.Type = (sbyte)Convert.ToInt32(reader["type"]); + meta.ContentType = SLUtil.SLAssetTypeToContentType(meta.Type); + meta.CreationDate = Util.ToDateTime(Convert.ToInt32(reader["create_time"])); + meta.Flags = (AssetFlags)Convert.ToInt32(reader["asset_flags"]); + + int AccessTime = Convert.ToInt32(reader["access_time"]); + + reader.Close(); + + UpdateAccessTime(AccessTime, cmd); + + FreeCommand(cmd); + + return meta; + } + + private void UpdateAccessTime(int AccessTime, MySqlCommand cmd) + { + // Reduce DB work by only updating access time if asset hasn't recently been accessed + // 0 By Default, Config option is "DaysBetweenAccessTimeUpdates" + if (DaysBetweenAccessTimeUpdates > 0 && (DateTime.UtcNow - Utils.UnixTimeToDateTime(AccessTime)).TotalDays < DaysBetweenAccessTimeUpdates) + return; + + cmd.CommandText = String.Format("UPDATE {0} SET `access_time` = UNIX_TIMESTAMP() WHERE `id` = ?id", m_Table); + + cmd.ExecuteNonQuery(); + } + + protected void FreeCommand(MySqlCommand cmd) + { + MySqlConnection c = cmd.Connection; + cmd.Dispose(); + c.Close(); + c.Dispose(); + } + + public bool Store(AssetMetadata meta, string hash) + { + try + { + string oldhash; + AssetMetadata existingAsset = Get(meta.ID, out oldhash); + + MySqlCommand cmd = m_Connection.CreateCommand(); + + cmd.Parameters.AddWithValue("?id", meta.ID); + cmd.Parameters.AddWithValue("?name", meta.Name); + cmd.Parameters.AddWithValue("?description", meta.Description); + cmd.Parameters.AddWithValue("?type", meta.Type.ToString()); + cmd.Parameters.AddWithValue("?hash", hash); + cmd.Parameters.AddWithValue("?asset_flags", meta.Flags); + + if (existingAsset == null) + { + cmd.CommandText = String.Format("insert into {0} (id, name, description, type, hash, asset_flags, create_time, access_time) values ( ?id, ?name, ?description, ?type, ?hash, ?asset_flags, UNIX_TIMESTAMP(), UNIX_TIMESTAMP())", m_Table); + + ExecuteNonQuery(cmd); + + cmd.Dispose(); + + return true; + } + + //cmd.CommandText = String.Format("update {0} set hash = ?hash, access_time = UNIX_TIMESTAMP() where id = ?id", m_Table); + + //ExecuteNonQuery(cmd); + + cmd.Dispose(); + return false; + } + catch(Exception e) + { + m_log.Error("[FSAssets] Failed to store asset with ID " + meta.ID); + m_log.Error(e.ToString()); + return false; + } + } + + /// + /// Check if the assets exist in the database. + /// + /// The asset UUID's + /// For each asset: true if it exists, false otherwise + public bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exists = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format("select id from {1} where id in ({0})", ids, m_Table); + + using (MySqlCommand cmd = m_Connection.CreateCommand()) + { + cmd.CommandText = sql; + + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + UUID id = DBGuid.FromDB(dbReader["ID"]); + exists.Add(id); + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exists.Contains(uuids[i]); + return results; + } + + public int Count() + { + MySqlCommand cmd = m_Connection.CreateCommand(); + + cmd.CommandText = String.Format("select count(*) as count from {0}", m_Table); + + IDataReader reader = ExecuteReader(cmd); + + reader.Read(); + + int count = Convert.ToInt32(reader["count"]); + + reader.Close(); + FreeCommand(cmd); + + return count; + } + + public bool Delete(string id) + { + using (MySqlCommand cmd = m_Connection.CreateCommand()) + { + cmd.CommandText = String.Format("delete from {0} where id = ?id", m_Table); + + cmd.Parameters.AddWithValue("?id", id); + + ExecuteNonQuery(cmd); + } + + return true; + } + + public void Import(string conn, string table, int start, int count, bool force, FSStoreDelegate store) + { + MySqlConnection importConn; + + try + { + importConn = new MySqlConnection(conn); + + importConn.Open(); + } + catch (MySqlException e) + { + m_log.ErrorFormat("[FSASSETS]: Can't connect to database: {0}", + e.Message.ToString()); + + return; + } + + int imported = 0; + + MySqlCommand cmd = importConn.CreateCommand(); + + string limit = String.Empty; + if (count != -1) + { + limit = String.Format(" limit {0},{1}", start, count); + } + + cmd.CommandText = String.Format("select * from {0}{1}", table, limit); + + MainConsole.Instance.Output("Querying database"); + IDataReader reader = cmd.ExecuteReader(); + + MainConsole.Instance.Output("Reading data"); + + while (reader.Read()) + { + if ((imported % 100) == 0) + { + MainConsole.Instance.Output(String.Format("{0} assets imported so far", imported)); + } + + AssetBase asset = new AssetBase(); + AssetMetadata meta = new AssetMetadata(); + + meta.ID = reader["id"].ToString(); + meta.FullID = new UUID(meta.ID); + + meta.Name = reader["name"].ToString(); + meta.Description = reader["description"].ToString(); + meta.Type = (sbyte)Convert.ToInt32(reader["assetType"]); + meta.ContentType = SLUtil.SLAssetTypeToContentType(meta.Type); + meta.CreationDate = Util.ToDateTime(Convert.ToInt32(reader["create_time"])); + + asset.Metadata = meta; + asset.Data = (byte[])reader["data"]; + + store(asset, force); + + imported++; + } + + reader.Close(); + cmd.Dispose(); + importConn.Close(); + + MainConsole.Instance.Output(String.Format("Import done, {0} assets imported", imported)); + } + + #endregion + } +} diff --git a/OpenSim/Data/MySQL/MySQLFramework.cs b/OpenSim/Data/MySQL/MySQLFramework.cs new file mode 100644 index 0000000000..5820a906c9 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLFramework.cs @@ -0,0 +1,73 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A database interface class to a user profile storage system + /// + public class MySqlFramework + { + private static readonly log4net.ILog m_log = + log4net.LogManager.GetLogger( + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_connectionString; + + protected MySqlFramework(string connectionString) + { + m_connectionString = connectionString; + } + + protected int ExecuteNonQuery(MySqlCommand cmd) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + try + { + return cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return 0; + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLFriendsData.cs b/OpenSim/Data/MySQL/MySQLFriendsData.cs new file mode 100644 index 0000000000..6ba9fbd33b --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLFriendsData.cs @@ -0,0 +1,86 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySqlFriendsData : MySQLGenericTableHandler, IFriendsData + { + public MySqlFriendsData(string connectionString, string realm) + : base(connectionString, realm, "FriendsStore") + { + } + + public bool Delete(UUID principalID, string friend) + { + return Delete(principalID.ToString(), friend); + } + + public override bool Delete(string principalID, string friend) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where PrincipalID = ?PrincipalID and Friend = ?Friend", m_Realm); + cmd.Parameters.AddWithValue("?PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?Friend", friend); + + ExecuteNonQuery(cmd); + } + + return true; + } + + public FriendsData[] GetFriends(UUID principalID) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = ?PrincipalID", m_Realm); + cmd.Parameters.AddWithValue("?PrincipalID", principalID.ToString()); + + return DoQuery(cmd); + } + } + + public FriendsData[] GetFriends(string principalID) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID LIKE ?PrincipalID", m_Realm); + cmd.Parameters.AddWithValue("?PrincipalID", principalID.ToString() + '%'); + + return DoQuery(cmd); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs new file mode 100644 index 0000000000..35fa89f416 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -0,0 +1,365 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.MySQL +{ + public class MySQLGenericTableHandler : MySqlFramework where T: class, new() + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_Fields = + new Dictionary(); + + protected List m_ColumnNames = null; + protected string m_Realm; + protected FieldInfo m_DataField = null; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySQLGenericTableHandler(string connectionString, + string realm, string storeName) : base(connectionString) + { + m_Realm = realm; + m_connectionString = connectionString; + + if (storeName != String.Empty) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, storeName); + m.Update(); + } + } + + Type t = typeof(T); + FieldInfo[] fields = t.GetFields(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + if (fields.Length == 0) + return; + + foreach (FieldInfo f in fields) + { + if (f.Name != "Data") + m_Fields[f.Name] = f; + else + m_DataField = f; + } + } + + private void CheckColumnNames(IDataReader reader) + { + if (m_ColumnNames != null) + return; + + List columnNames = new List(); + + DataTable schemaTable = reader.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null && + (!m_Fields.ContainsKey(row["ColumnName"].ToString()))) + columnNames.Add(row["ColumnName"].ToString()); + } + + m_ColumnNames = columnNames; + } + + public virtual T[] Get(string field, string key) + { + return Get(new string[] { field }, new string[] { key }); + } + + public virtual T[] Get(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return new T[0]; + + List terms = new List(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + for (int i = 0 ; i < fields.Length ; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("`" + fields[i] + "` = ?" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + } + + protected T[] DoQuery(MySqlCommand cmd) + { + List result = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + using (IDataReader reader = cmd.ExecuteReader()) + { + if (reader == null) + return new T[0]; + + CheckColumnNames(reader); + + while (reader.Read()) + { + T row = new T(); + + foreach (string name in m_Fields.Keys) + { + if (reader[name] is DBNull) + { + continue; + } + if (m_Fields[name].FieldType == typeof(bool)) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].FieldType == typeof(UUID)) + { + m_Fields[name].SetValue(row, DBGuid.FromDB(reader[name])); + } + else if (m_Fields[name].FieldType == typeof(int)) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else + { + m_Fields[name].SetValue(row, reader[name]); + } + } + + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); + + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + if (data[col] == null) + data[col] = String.Empty; + } + + m_DataField.SetValue(row, data); + } + + result.Add(row); + } + } + } + + return result.ToArray(); + } + + public virtual T[] Get(string where) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + } + + public virtual bool Store(T row) + { +// m_log.DebugFormat("[MYSQL GENERIC TABLE HANDLER]: Store(T row) invoked"); + + using (MySqlCommand cmd = new MySqlCommand()) + { + string query = ""; + List names = new List(); + List values = new List(); + + foreach (FieldInfo fi in m_Fields.Values) + { + names.Add(fi.Name); + values.Add("?" + fi.Name); + + // Temporarily return more information about what field is unexpectedly null for + // http://opensimulator.org/mantis/view.php?id=5403. This might be due to a bug in the + // InventoryTransferModule or we may be required to substitute a DBNull here. + if (fi.GetValue(row) == null) + throw new NullReferenceException( + string.Format( + "[MYSQL GENERIC TABLE HANDLER]: Trying to store field {0} for {1} which is unexpectedly null", + fi.Name, row)); + + cmd.Parameters.AddWithValue(fi.Name, fi.GetValue(row).ToString()); + } + + if (m_DataField != null) + { + Dictionary data = + (Dictionary)m_DataField.GetValue(row); + + foreach (KeyValuePair kvp in data) + { + names.Add(kvp.Key); + values.Add("?" + kvp.Key); + cmd.Parameters.AddWithValue("?" + kvp.Key, kvp.Value); + } + } + + query = String.Format("replace into {0} (`", m_Realm) + String.Join("`,`", names.ToArray()) + "`) values (" + String.Join(",", values.ToArray()) + ")"; + + cmd.CommandText = query; + + if (ExecuteNonQuery(cmd) > 0) + return true; + + return false; + } + } + + public virtual bool Delete(string field, string key) + { + return Delete(new string[] { field }, new string[] { key }); + } + + public virtual bool Delete(string[] fields, string[] keys) + { +// m_log.DebugFormat( +// "[MYSQL GENERIC TABLE HANDLER]: Delete(string[] fields, string[] keys) invoked with {0}:{1}", +// string.Join(",", fields), string.Join(",", keys)); + + if (fields.Length != keys.Length) + return false; + + List terms = new List(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + for (int i = 0 ; i < fields.Length ; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("`" + fields[i] + "` = ?" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("delete from {0} where {1}", m_Realm, where); + + cmd.CommandText = query; + + return ExecuteNonQuery(cmd) > 0; + } + } + + public long GetCount(string field, string key) + { + return GetCount(new string[] { field }, new string[] { key }); + } + + public long GetCount(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return 0; + + List terms = new List(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("`" + fields[i] + "` = ?" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + Object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public long GetCount(string where) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public object DoQueryScalar(MySqlCommand cmd) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + return cmd.ExecuteScalar(); + } + } + + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLGridUserData.cs b/OpenSim/Data/MySQL/MySQLGridUserData.cs new file mode 100644 index 0000000000..00560c11db --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLGridUserData.cs @@ -0,0 +1,64 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for user grid data + /// + public class MySQLGridUserData : MySQLGenericTableHandler, IGridUserData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySQLGridUserData(string connectionString, string realm) : base(connectionString, realm, "GridUserStore") {} + + public new GridUserData Get(string userID) + { + GridUserData[] ret = Get("UserID", userID); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("UserID LIKE '{0}%'", userID)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLGroupsData.cs b/OpenSim/Data/MySQL/MySQLGroupsData.cs new file mode 100644 index 0000000000..afa499e126 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLGroupsData.cs @@ -0,0 +1,484 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Data.MySQL; + +using OpenMetaverse; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySQLGroupsData : IGroupsData + { + private MySqlGroupsGroupsHandler m_Groups; + private MySqlGroupsMembershipHandler m_Membership; + private MySqlGroupsRolesHandler m_Roles; + private MySqlGroupsRoleMembershipHandler m_RoleMembership; + private MySqlGroupsInvitesHandler m_Invites; + private MySqlGroupsNoticesHandler m_Notices; + private MySqlGroupsPrincipalsHandler m_Principals; + + public MySQLGroupsData(string connectionString, string realm) + { + m_Groups = new MySqlGroupsGroupsHandler(connectionString, realm + "_groups", realm + "_Store"); + m_Membership = new MySqlGroupsMembershipHandler(connectionString, realm + "_membership"); + m_Roles = new MySqlGroupsRolesHandler(connectionString, realm + "_roles"); + m_RoleMembership = new MySqlGroupsRoleMembershipHandler(connectionString, realm + "_rolemembership"); + m_Invites = new MySqlGroupsInvitesHandler(connectionString, realm + "_invites"); + m_Notices = new MySqlGroupsNoticesHandler(connectionString, realm + "_notices"); + m_Principals = new MySqlGroupsPrincipalsHandler(connectionString, realm + "_principals"); + } + + #region groups table + public bool StoreGroup(GroupData data) + { + return m_Groups.Store(data); + } + + public GroupData RetrieveGroup(UUID groupID) + { + GroupData[] groups = m_Groups.Get("GroupID", groupID.ToString()); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData RetrieveGroup(string name) + { + GroupData[] groups = m_Groups.Get("Name", name); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData[] RetrieveGroups(string pattern) + { + if (string.IsNullOrEmpty(pattern)) + pattern = "1"; + else + pattern = string.Format("Name LIKE '%{0}%'", MySqlHelper.EscapeString(pattern)); + + return m_Groups.Get(string.Format("ShowInList=1 AND ({0}) ORDER BY Name LIMIT 100", pattern)); + } + + public bool DeleteGroup(UUID groupID) + { + return m_Groups.Delete("GroupID", groupID.ToString()); + } + + public int GroupsCount() + { + return (int)m_Groups.GetCount("Location=\"\""); + } + + #endregion + + #region membership table + public MembershipData[] RetrieveMembers(UUID groupID) + { + return m_Membership.Get("GroupID", groupID.ToString()); + } + + public MembershipData RetrieveMember(UUID groupID, string pricipalID) + { + MembershipData[] m = m_Membership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + if (m != null && m.Length > 0) + return m[0]; + + return null; + } + + public MembershipData[] RetrieveMemberships(string pricipalID) + { + return m_Membership.Get("PrincipalID", pricipalID.ToString()); + } + + public bool StoreMember(MembershipData data) + { + return m_Membership.Store(data); + } + + public bool DeleteMember(UUID groupID, string pricipalID) + { + return m_Membership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + } + + public int MemberCount(UUID groupID) + { + return (int)m_Membership.GetCount("GroupID", groupID.ToString()); + } + #endregion + + #region roles table + public bool StoreRole(RoleData data) + { + return m_Roles.Store(data); + } + + public RoleData RetrieveRole(UUID groupID, UUID roleID) + { + RoleData[] data = m_Roles.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public RoleData[] RetrieveRoles(UUID groupID) + { + //return m_Roles.RetrieveRoles(groupID); + return m_Roles.Get("GroupID", groupID.ToString()); + } + + public bool DeleteRole(UUID groupID, UUID roleID) + { + return m_Roles.Delete(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public int RoleCount(UUID groupID) + { + return (int)m_Roles.GetCount("GroupID", groupID.ToString()); + } + + + #endregion + + #region rolememberhip table + public RoleMembershipData[] RetrieveRolesMembers(UUID groupID) + { + RoleMembershipData[] data = m_RoleMembership.Get("GroupID", groupID.ToString()); + + return data; + } + + public RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + return data; + } + + public RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID.ToString() }); + + return data; + } + + public RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID", "PrincipalID" }, + new string[] { groupID.ToString(), roleID.ToString(), principalID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public int RoleMemberCount(UUID groupID, UUID roleID) + { + return (int)m_RoleMembership.GetCount(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public bool StoreRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Store(data); + } + + public bool DeleteRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "RoleID", "PrincipalID"}, + new string[] { data.GroupID.ToString(), data.RoleID.ToString(), data.PrincipalID }); + } + + public bool DeleteMemberAllRoles(UUID groupID, string principalID) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + } + + #endregion + + #region principals table + public bool StorePrincipal(PrincipalData data) + { + return m_Principals.Store(data); + } + + public PrincipalData RetrievePrincipal(string principalID) + { + PrincipalData[] p = m_Principals.Get("PrincipalID", principalID); + if (p != null && p.Length > 0) + return p[0]; + + return null; + } + + public bool DeletePrincipal(string principalID) + { + return m_Principals.Delete("PrincipalID", principalID); + } + #endregion + + #region invites table + + public bool StoreInvitation(InvitationData data) + { + return m_Invites.Store(data); + } + + public InvitationData RetrieveInvitation(UUID inviteID) + { + InvitationData[] invites = m_Invites.Get("InviteID", inviteID.ToString()); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public InvitationData RetrieveInvitation(UUID groupID, string principalID) + { + InvitationData[] invites = m_Invites.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public bool DeleteInvite(UUID inviteID) + { + return m_Invites.Delete("InviteID", inviteID.ToString()); + } + + public void DeleteOldInvites() + { + m_Invites.DeleteOld(); + } + + #endregion + + #region notices table + + public bool StoreNotice(NoticeData data) + { + return m_Notices.Store(data); + } + + public NoticeData RetrieveNotice(UUID noticeID) + { + NoticeData[] notices = m_Notices.Get("NoticeID", noticeID.ToString()); + + if (notices != null && notices.Length > 0) + return notices[0]; + + return null; + } + + public NoticeData[] RetrieveNotices(UUID groupID) + { + NoticeData[] notices = m_Notices.Get("GroupID", groupID.ToString()); + + return notices; + } + + public bool DeleteNotice(UUID noticeID) + { + return m_Notices.Delete("NoticeID", noticeID.ToString()); + } + + public void DeleteOldNotices() + { + m_Notices.DeleteOld(); + } + + #endregion + + #region combinations + public MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID) + { + // TODO + return null; + } + public MembershipData[] RetrievePrincipalGroupMemberships(string principalID) + { + // TODO + return null; + } + + #endregion + } + + public class MySqlGroupsGroupsHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsGroupsHandler(string connectionString, string realm, string store) + : base(connectionString, realm, store) + { + } + + } + + public class MySqlGroupsMembershipHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsRolesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsRolesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsRoleMembershipHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsRoleMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class MySqlGroupsInvitesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsInvitesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); + cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class MySqlGroupsNoticesHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsNoticesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + uint now = (uint)Util.UnixTimeSinceEpoch(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < ?tstamp", m_Realm); + cmd.Parameters.AddWithValue("?tstamp", now - 14 * 24 * 60 * 60); // > 2 weeks old + + ExecuteNonQuery(cmd); + } + + } + } + + public class MySqlGroupsPrincipalsHandler : MySQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public MySqlGroupsPrincipalsHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLHGTravelData.cs b/OpenSim/Data/MySQL/MySQLHGTravelData.cs new file mode 100644 index 0000000000..e81b880c95 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLHGTravelData.cs @@ -0,0 +1,80 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for user grid data + /// + public class MySQLHGTravelData : MySQLGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySQLHGTravelData(string connectionString, string realm) : base(connectionString, realm, "HGTravelStore") { } + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < NOW() - INTERVAL 2 DAY", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLInventoryData.cs b/OpenSim/Data/MySQL/MySQLInventoryData.cs new file mode 100644 index 0000000000..e9b10f3cdf --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLInventoryData.cs @@ -0,0 +1,902 @@ +/* + * 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 MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL interface for the inventory server + /// + public class MySQLInventoryData : IInventoryDataPlugin + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_connectionString; + private object m_dbLock = new object(); + + public string Version { get { return "1.0.0.0"; } } + + public void Initialise() + { + m_log.Info("[MySQLInventoryData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException (Name); + } + + /// + /// Initialises Inventory interface + /// + /// + /// Loads and initialises the MySQL storage plugin + /// warns and uses the obsolete mysql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public void Initialise(string connect) + { + m_connectionString = connect; + + // This actually does the roll forward assembly stuff + Assembly assem = GetType().Assembly; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, assem, "InventoryStore"); + m.Update(); + } + } + + /// + /// The name of this DB provider + /// + /// Name of DB provider + public string Name + { + get { return "MySQL Inventory Data Interface"; } + } + + /// + /// Closes this DB provider + /// + /// do nothing + public void Dispose() + { + // Do nothing. + } + + /// + /// Returns a list of items in a specified folder + /// + /// The folder to search + /// A list containing inventory items + public List getInventoryInFolder(UUID folderID) + { + try + { + lock (m_dbLock) + { + List items = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryitems WHERE parentFolderID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", folderID.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + while (reader.Read()) + { + // A null item (because something went wrong) breaks everything in the folder + InventoryItemBase item = readInventoryItem(reader); + if (item != null) + items.Add(item); + } + + return items; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whose inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + try + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand( + "SELECT * FROM inventoryfolders WHERE parentFolderID = ?zero AND agentID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", user.ToString()); + result.Parameters.AddWithValue("?zero", UUID.Zero.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + List items = new List(); + while (reader.Read()) + items.Add(readInventoryFolder(reader)); + + return items; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + + /// + /// see + /// + /// The user UUID + /// + public InventoryFolderBase getUserRootFolder(UUID user) + { + try + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand( + "SELECT * FROM inventoryfolders WHERE parentFolderID = ?zero AND agentID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", user.ToString()); + result.Parameters.AddWithValue("?zero", UUID.Zero.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + List items = new List(); + while (reader.Read()) + items.Add(readInventoryFolder(reader)); + + InventoryFolderBase rootFolder = null; + + // There should only ever be one root folder for a user. However, if there's more + // than one we'll simply use the first one rather than failing. It would be even + // nicer to print some message to this effect, but this feels like it's too low a + // to put such a message out, and it's too minor right now to spare the time to + // suitably refactor. + if (items.Count > 0) + rootFolder = items[0]; + + return rootFolder; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + /// + /// Return a list of folders in a users inventory contained within the specified folder. + /// This method is only used in tests - in normal operation the user always have one, + /// and only one, root folder. + /// + /// The folder to search + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + try + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryfolders WHERE parentFolderID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", parentID.ToString()); + using (MySqlDataReader reader = result.ExecuteReader()) + { + List items = new List(); + + while (reader.Read()) + items.Add(readInventoryFolder(reader)); + + return items; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + /// + /// Reads a one item from an SQL result + /// + /// The SQL Result + /// the item read + private static InventoryItemBase readInventoryItem(MySqlDataReader reader) + { + try + { + InventoryItemBase item = new InventoryItemBase(); + + // TODO: this is to handle a case where NULLs creep in there, which we are not sure is endemic to the system, or legacy. It would be nice to live fix these. + // (DBGuid.FromDB() reads db NULLs as well, returns UUID.Zero) + item.CreatorId = reader["creatorID"].ToString(); + + // Be a bit safer in parsing these because the + // database doesn't enforce them to be not null, and + // the inventory still works if these are weird in the + // db + + // (Empty is Ok, but "weird" will throw!) + item.Owner = DBGuid.FromDB(reader["avatarID"]); + item.GroupID = DBGuid.FromDB(reader["groupID"]); + + // Rest of the parsing. If these UUID's fail, we're dead anyway + item.ID = DBGuid.FromDB(reader["inventoryID"]); + item.AssetID = DBGuid.FromDB(reader["assetID"]); + item.AssetType = (int) reader["assetType"]; + item.Folder = DBGuid.FromDB(reader["parentFolderID"]); + item.Name = (string)(reader["inventoryName"] ?? String.Empty); + item.Description = (string)(reader["inventoryDescription"] ?? String.Empty); + item.NextPermissions = (uint) reader["inventoryNextPermissions"]; + item.CurrentPermissions = (uint) reader["inventoryCurrentPermissions"]; + item.InvType = (int) reader["invType"]; + item.BasePermissions = (uint) reader["inventoryBasePermissions"]; + item.EveryOnePermissions = (uint) reader["inventoryEveryOnePermissions"]; + item.GroupPermissions = (uint) reader["inventoryGroupPermissions"]; + item.SalePrice = (int) reader["salePrice"]; + item.SaleType = unchecked((byte)(Convert.ToSByte(reader["saleType"]))); + item.CreationDate = (int) reader["creationDate"]; + item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); + item.Flags = (uint) reader["flags"]; + + return item; + } + catch (MySqlException e) + { + m_log.Error(e.ToString()); + } + + return null; + } + + /// + /// Returns a specified inventory item + /// + /// The item to return + /// An inventory item + public InventoryItemBase getInventoryItem(UUID itemID) + { + try + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryitems WHERE inventoryID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", itemID.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + InventoryItemBase item = null; + if (reader.Read()) + item = readInventoryItem(reader); + + return item; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + } + return null; + } + + /// + /// Reads a list of inventory folders returned by a query. + /// + /// A MySQL Data Reader + /// A List containing inventory folders + protected static InventoryFolderBase readInventoryFolder(MySqlDataReader reader) + { + try + { + InventoryFolderBase folder = new InventoryFolderBase(); + folder.Owner = DBGuid.FromDB(reader["agentID"]); + folder.ParentID = DBGuid.FromDB(reader["parentFolderID"]); + folder.ID = DBGuid.FromDB(reader["folderID"]); + folder.Name = (string) reader["folderName"]; + folder.Type = (short) reader["type"]; + folder.Version = (ushort) ((int) reader["version"]); + return folder; + } + catch (Exception e) + { + m_log.Error(e.Message, e); + } + + return null; + } + + + /// + /// Returns a specified inventory folder + /// + /// The folder to return + /// A folder class + public InventoryFolderBase getInventoryFolder(UUID folderID) + { + try + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryfolders WHERE folderID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", folderID.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + InventoryFolderBase folder = null; + if (reader.Read()) + folder = readInventoryFolder(reader); + + return folder; + } + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + /// + /// Adds a specified item to the database + /// + /// The inventory item + public void addInventoryItem(InventoryItemBase item) + { + string sql = + "REPLACE INTO inventoryitems (inventoryID, assetID, assetType, parentFolderID, avatarID, inventoryName" + + ", inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType" + + ", creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, inventoryGroupPermissions, salePrice, saleType" + + ", creationDate, groupID, groupOwned, flags) VALUES "; + sql += + "(?inventoryID, ?assetID, ?assetType, ?parentFolderID, ?avatarID, ?inventoryName, ?inventoryDescription" + + ", ?inventoryNextPermissions, ?inventoryCurrentPermissions, ?invType, ?creatorID" + + ", ?inventoryBasePermissions, ?inventoryEveryOnePermissions, ?inventoryGroupPermissions, ?salePrice, ?saleType, ?creationDate" + + ", ?groupID, ?groupOwned, ?flags)"; + + string itemName = item.Name; + if (item.Name.Length > 64) + { + itemName = item.Name.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length + " to " + itemName.Length + " characters on add item"); + } + + string itemDesc = item.Description; + if (item.Description.Length > 128) + { + itemDesc = item.Description.Substring(0, 128); + m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length + " to " + itemDesc.Length + " characters on add item"); + } + + try + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand result = new MySqlCommand(sql, dbcon)) + { + result.Parameters.AddWithValue("?inventoryID", item.ID.ToString()); + result.Parameters.AddWithValue("?assetID", item.AssetID.ToString()); + result.Parameters.AddWithValue("?assetType", item.AssetType.ToString()); + result.Parameters.AddWithValue("?parentFolderID", item.Folder.ToString()); + result.Parameters.AddWithValue("?avatarID", item.Owner.ToString()); + result.Parameters.AddWithValue("?inventoryName", itemName); + result.Parameters.AddWithValue("?inventoryDescription", itemDesc); + result.Parameters.AddWithValue("?inventoryNextPermissions", item.NextPermissions.ToString()); + result.Parameters.AddWithValue("?inventoryCurrentPermissions", + item.CurrentPermissions.ToString()); + result.Parameters.AddWithValue("?invType", item.InvType); + result.Parameters.AddWithValue("?creatorID", item.CreatorId); + result.Parameters.AddWithValue("?inventoryBasePermissions", item.BasePermissions); + result.Parameters.AddWithValue("?inventoryEveryOnePermissions", item.EveryOnePermissions); + result.Parameters.AddWithValue("?inventoryGroupPermissions", item.GroupPermissions); + result.Parameters.AddWithValue("?salePrice", item.SalePrice); + result.Parameters.AddWithValue("?saleType", unchecked((sbyte)item.SaleType)); + result.Parameters.AddWithValue("?creationDate", item.CreationDate); + result.Parameters.AddWithValue("?groupID", item.GroupID); + result.Parameters.AddWithValue("?groupOwned", item.GroupOwned); + result.Parameters.AddWithValue("?flags", item.Flags); + + lock (m_dbLock) + result.ExecuteNonQuery(); + + result.Dispose(); + } + + using (MySqlCommand result = new MySqlCommand("update inventoryfolders set version=version+1 where folderID = ?folderID", dbcon)) + { + result.Parameters.AddWithValue("?folderID", item.Folder.ToString()); + + lock (m_dbLock) + result.ExecuteNonQuery(); + } + } + } + catch (MySqlException e) + { + m_log.Error(e.ToString()); + } + } + + /// + /// Updates the specified inventory item + /// + /// Inventory item to update + public void updateInventoryItem(InventoryItemBase item) + { + addInventoryItem(item); + } + + /// + /// Detele the specified inventory item + /// + /// The inventory item UUID to delete + public void deleteInventoryItem(UUID itemID) + { + try + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand("DELETE FROM inventoryitems WHERE inventoryID=?uuid", dbcon)) + { + cmd.Parameters.AddWithValue("?uuid", itemID.ToString()); + + lock (m_dbLock) + cmd.ExecuteNonQuery(); + } + } + } + catch (MySqlException e) + { + m_log.Error(e.Message, e); + } + } + + public InventoryItemBase queryInventoryItem(UUID itemID) + { + return getInventoryItem(itemID); + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + /// + /// Creates a new inventory folder + /// + /// Folder to create + public void addInventoryFolder(InventoryFolderBase folder) + { + string sql = + "REPLACE INTO inventoryfolders (folderID, agentID, parentFolderID, folderName, type, version) VALUES "; + sql += "(?folderID, ?agentID, ?parentFolderID, ?folderName, ?type, ?version)"; + + string folderName = folder.Name; + if (folderName.Length > 64) + { + folderName = folderName.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + folder.Name.Length + " to " + folderName.Length + " characters on add folder"); + } + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) + { + cmd.Parameters.AddWithValue("?folderID", folder.ID.ToString()); + cmd.Parameters.AddWithValue("?agentID", folder.Owner.ToString()); + cmd.Parameters.AddWithValue("?parentFolderID", folder.ParentID.ToString()); + cmd.Parameters.AddWithValue("?folderName", folderName); + cmd.Parameters.AddWithValue("?type", folder.Type); + cmd.Parameters.AddWithValue("?version", folder.Version); + + try + { + lock (m_dbLock) + { + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.Error(e.ToString()); + } + } + } + } + + /// + /// Updates an inventory folder + /// + /// Folder to update + public void updateInventoryFolder(InventoryFolderBase folder) + { + addInventoryFolder(folder); + } + + /// + /// Move an inventory folder + /// + /// Folder to move + /// UPDATE inventoryfolders SET parentFolderID=?parentFolderID WHERE folderID=?folderID + public void moveInventoryFolder(InventoryFolderBase folder) + { + string sql = + "UPDATE inventoryfolders SET parentFolderID=?parentFolderID WHERE folderID=?folderID"; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) + { + cmd.Parameters.AddWithValue("?folderID", folder.ID.ToString()); + cmd.Parameters.AddWithValue("?parentFolderID", folder.ParentID.ToString()); + + try + { + lock (m_dbLock) + { + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.Error(e.ToString()); + } + } + } + } + + /// + /// Append a list of all the child folders of a parent folder + /// + /// list where folders will be appended + /// ID of parent + protected void getInventoryFolders(ref List folders, UUID parentID) + { + List subfolderList = getInventoryFolders(parentID); + + foreach (InventoryFolderBase f in subfolderList) + folders.Add(f); + } + + + /// + /// See IInventoryDataPlugin + /// + /// + /// + public List getFolderHierarchy(UUID parentID) + { + /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one + * - We will only need to hit the database twice instead of n times. + * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned + * by the same person, each user only has 1 inventory heirarchy + * - The returned list is not ordered, instead of breadth-first ordered + There are basically 2 usage cases for getFolderHeirarchy: + 1) Getting the user's entire inventory heirarchy when they log in + 2) Finding a subfolder heirarchy to delete when emptying the trash. + This implementation will pull all inventory folders from the database, and then prune away any folder that + is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the + database than to make n requests. This pays off only if requested heirarchy is large. + By making this choice, we are making the worst case better at the cost of making the best case worse. + This way is generally better because we don't have to rebuild the connection/sql query per subfolder, + even if we end up getting more data from the SQL server than we need. + - Francis + */ + try + { + List folders = new List(); + Dictionary> hashtable = new Dictionary>(); ; + List parentFolder = new List(); + bool buildResultsFromHashTable = false; + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + /* Fetch the parent folder from the database to determine the agent ID, and if + * we're querying the root of the inventory folder tree */ + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryfolders WHERE folderID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", parentID.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + // Should be at most 1 result + while (reader.Read()) + parentFolder.Add(readInventoryFolder(reader)); + } + } + + if (parentFolder.Count >= 1) // No result means parent folder does not exist + { + if (parentFolder[0].ParentID == UUID.Zero) // We are querying the root folder + { + /* Get all of the agent's folders from the database, put them in a list and return it */ + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryfolders WHERE agentID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", parentFolder[0].Owner.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + while (reader.Read()) + { + InventoryFolderBase curFolder = readInventoryFolder(reader); + if (curFolder.ID != parentID) // Do not need to add the root node of the tree to the list + folders.Add(curFolder); + } + } + } + } // if we are querying the root folder + else // else we are querying a subtree of the inventory folder tree + { + /* Get all of the agent's folders from the database, put them all in a hash table + * indexed by their parent ID */ + using (MySqlCommand result = new MySqlCommand("SELECT * FROM inventoryfolders WHERE agentID = ?uuid", dbcon)) + { + result.Parameters.AddWithValue("?uuid", parentFolder[0].Owner.ToString()); + + using (MySqlDataReader reader = result.ExecuteReader()) + { + while (reader.Read()) + { + InventoryFolderBase curFolder = readInventoryFolder(reader); + if (hashtable.ContainsKey(curFolder.ParentID)) // Current folder already has a sibling + hashtable[curFolder.ParentID].Add(curFolder); // append to sibling list + else // else current folder has no known (yet) siblings + { + List siblingList = new List(); + siblingList.Add(curFolder); + // Current folder has no known (yet) siblings + hashtable.Add(curFolder.ParentID, siblingList); + } + } // while more items to read from the database + } + } + + // Set flag so we know we need to build the results from the hash table after + // we unlock the database + buildResultsFromHashTable = true; + + } // else we are querying a subtree of the inventory folder tree + } // if folder parentID exists + + if (buildResultsFromHashTable) + { + /* We have all of the user's folders stored in a hash table indexed by their parent ID + * and we need to return the requested subtree. We will build the requested subtree + * by performing a breadth-first-search on the hash table */ + if (hashtable.ContainsKey(parentID)) + folders.AddRange(hashtable[parentID]); + for (int i = 0; i < folders.Count; i++) // **Note: folders.Count is *not* static + if (hashtable.ContainsKey(folders[i].ID)) + folders.AddRange(hashtable[folders[i].ID]); + } + } + } // lock (database) + + return folders; + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + + /// + /// Delete a folder from database + /// + /// the folder UUID + protected void deleteOneFolder(UUID folderID) + { + try + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + // System folders can never be deleted. Period. + using (MySqlCommand cmd = new MySqlCommand("DELETE FROM inventoryfolders WHERE folderID=?uuid and type=-1", dbcon)) + { + cmd.Parameters.AddWithValue("?uuid", folderID.ToString()); + + lock (m_dbLock) + cmd.ExecuteNonQuery(); + } + } + } + catch (MySqlException e) + { + m_log.Error(e.Message, e); + } + } + + /// + /// Delete all item in a folder + /// + /// the folder UUID + protected void deleteItemsInFolder(UUID folderID) + { + try + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand("DELETE FROM inventoryitems WHERE parentFolderID=?uuid", dbcon)) + { + cmd.Parameters.AddWithValue("?uuid", folderID.ToString()); + + lock (m_dbLock) + cmd.ExecuteNonQuery(); + } + } + } + catch (MySqlException e) + { + m_log.Error(e.ToString()); + } + } + + /// + /// Deletes an inventory folder + /// + /// Id of folder to delete + public void deleteInventoryFolder(UUID folderID) + { + List subFolders = getFolderHierarchy(folderID); + + //Delete all sub-folders + foreach (InventoryFolderBase f in subFolders) + { + deleteOneFolder(f.ID); + deleteItemsInFolder(f.ID); + } + + //Delete the actual row + deleteOneFolder(folderID); + deleteItemsInFolder(folderID); + } + + public List fetchActiveGestures(UUID avatarID) + { + lock (m_dbLock) + { + try + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand sqlCmd = new MySqlCommand( + "SELECT * FROM inventoryitems WHERE avatarId = ?uuid AND assetType = ?type and flags & 1", dbcon)) + { + sqlCmd.Parameters.AddWithValue("?uuid", avatarID.ToString()); + sqlCmd.Parameters.AddWithValue("?type", (int)AssetType.Gesture); + + using (MySqlDataReader result = sqlCmd.ExecuteReader()) + { + List list = new List(); + while (result.Read()) + { + InventoryItemBase item = readInventoryItem(result); + if (item != null) + list.Add(item); + } + return list; + } + } + } + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return null; + } + } + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLMigrations.cs b/OpenSim/Data/MySQL/MySQLMigrations.cs new file mode 100644 index 0000000000..81a0e837ea --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLMigrations.cs @@ -0,0 +1,83 @@ +/* + * 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.Data; +using System.Data.Common; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; +using log4net; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// This is a MySQL-customized migration processor. The only difference is in how + /// it executes SQL scripts (using MySqlScript instead of MyCommand) + /// + /// + public class MySqlMigration : Migration + { + public MySqlMigration() + : base() + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string subtype, string type) : + base(conn, assem, subtype, type) + { + } + + public MySqlMigration(DbConnection conn, Assembly assem, string type) : + base(conn, assem, type) + { + } + + protected override void ExecuteScript(DbConnection conn, string[] script) + { + if (!(conn is MySqlConnection)) + { + base.ExecuteScript(conn, script); + return; + } + + MySqlScript scr = new MySqlScript((MySqlConnection)conn); + { + foreach (string sql in script) + { + scr.Query = sql; + scr.Error += delegate(object sender, MySqlScriptErrorEventArgs args) + { + throw new Exception(sql); + }; + scr.Execute(); + } + } + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLOfflineIMData.cs b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs new file mode 100644 index 0000000000..bafd204a32 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLOfflineIMData.cs @@ -0,0 +1,59 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Data.MySQL; + +using OpenMetaverse; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySQLOfflineIMData : MySQLGenericTableHandler, IOfflineIMData + { + public MySQLOfflineIMData(string connectionString, string realm) + : base(connectionString, realm, "IM_Store") + { + } + + public void DeleteOld() + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < NOW() - INTERVAL 2 WEEK", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLPresenceData.cs b/OpenSim/Data/MySQL/MySQLPresenceData.cs new file mode 100644 index 0000000000..3f906399ec --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLPresenceData.cs @@ -0,0 +1,113 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for the Grid Server + /// + public class MySQLPresenceData : MySQLGenericTableHandler, + IPresenceData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySQLPresenceData(string connectionString, string realm) : + base(connectionString, realm, "Presence") + { + } + + public PresenceData Get(UUID sessionID) + { + PresenceData[] ret = Get("SessionID", + sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public void LogoutRegionAgents(UUID regionID) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where `RegionID`=?RegionID", m_Realm); + + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + + ExecuteNonQuery(cmd); + } + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + PresenceData[] pd = Get("SessionID", sessionID.ToString()); + if (pd.Length == 0) + return false; + + if (regionID == UUID.Zero) + return false; + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("update {0} set RegionID=?RegionID, LastSeen=NOW() where `SessionID`=?SessionID", m_Realm); + + cmd.Parameters.AddWithValue("?SessionID", sessionID.ToString()); + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + + if (ExecuteNonQuery(cmd) == 0) + return false; + } + + return true; + } + + public bool VerifyAgent(UUID agentId, UUID secureSessionID) + { + PresenceData[] ret = Get("SecureSessionID", + secureSessionID.ToString()); + + if (ret.Length == 0) + return false; + + if(ret[0].UserID != agentId.ToString()) + return false; + + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs new file mode 100644 index 0000000000..2ad7590759 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -0,0 +1,345 @@ +/* + * 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.Data; +using System.Reflection; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; +using RegionFlags = OpenSim.Framework.RegionFlags; + +namespace OpenSim.Data.MySQL +{ + public class MySqlRegionData : MySqlFramework, IRegionData + { + private string m_Realm; + private List m_ColumnNames; + //private string m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySqlRegionData(string connectionString, string realm) + : base(connectionString) + { + m_Realm = realm; + m_connectionString = connectionString; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "GridStore"); + m.Update(); + } + } + + public List Get(string regionName, UUID scopeID) + { + string command = "select * from `"+m_Realm+"` where regionName like ?regionName"; + if (scopeID != UUID.Zero) + command += " and ScopeID = ?scopeID"; + + command += " order by regionName"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Parameters.AddWithValue("?regionName", regionName); + cmd.Parameters.AddWithValue("?scopeID", scopeID.ToString()); + + return RunCommand(cmd); + } + } + + public RegionData Get(int posX, int posY, UUID scopeID) + { + string command = "select * from `"+m_Realm+"` where locX = ?posX and locY = ?posY"; + if (scopeID != UUID.Zero) + command += " and ScopeID = ?scopeID"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Parameters.AddWithValue("?posX", posX.ToString()); + cmd.Parameters.AddWithValue("?posY", posY.ToString()); + cmd.Parameters.AddWithValue("?scopeID", scopeID.ToString()); + + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public RegionData Get(UUID regionID, UUID scopeID) + { + string command = "select * from `"+m_Realm+"` where uuid = ?regionID"; + if (scopeID != UUID.Zero) + command += " and ScopeID = ?scopeID"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Parameters.AddWithValue("?regionID", regionID.ToString()); + cmd.Parameters.AddWithValue("?scopeID", scopeID.ToString()); + + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public List Get(int startX, int startY, int endX, int endY, UUID scopeID) + { + string command = "select * from `"+m_Realm+"` where locX between ?startX and ?endX and locY between ?startY and ?endY"; + if (scopeID != UUID.Zero) + command += " and ScopeID = ?scopeID"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Parameters.AddWithValue("?startX", startX.ToString()); + cmd.Parameters.AddWithValue("?startY", startY.ToString()); + cmd.Parameters.AddWithValue("?endX", endX.ToString()); + cmd.Parameters.AddWithValue("?endY", endY.ToString()); + cmd.Parameters.AddWithValue("?scopeID", scopeID.ToString()); + + return RunCommand(cmd); + } + } + + public List RunCommand(MySqlCommand cmd) + { + List retList = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + using (IDataReader result = cmd.ExecuteReader()) + { + while (result.Read()) + { + RegionData ret = new RegionData(); + ret.Data = new Dictionary(); + + ret.RegionID = DBGuid.FromDB(result["uuid"]); + ret.ScopeID = DBGuid.FromDB(result["ScopeID"]); + + ret.RegionName = result["regionName"].ToString(); + ret.posX = Convert.ToInt32(result["locX"]); + ret.posY = Convert.ToInt32(result["locY"]); + ret.sizeX = Convert.ToInt32(result["sizeX"]); + ret.sizeY = Convert.ToInt32(result["sizeY"]); + + CheckColumnNames(result); + + foreach (string s in m_ColumnNames) + { + if (s == "uuid") + continue; + if (s == "ScopeID") + continue; + if (s == "regionName") + continue; + if (s == "locX") + continue; + if (s == "locY") + continue; + + object value = result[s]; + if (value is DBNull) + ret.Data[s] = null; + else + ret.Data[s] = result[s].ToString(); + } + + retList.Add(ret); + } + } + } + + return retList; + } + + private void CheckColumnNames(IDataReader result) + { + if (m_ColumnNames != null) + return; + + List columnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null) + columnNames.Add(row["ColumnName"].ToString()); + } + + m_ColumnNames = columnNames; + } + + public bool Store(RegionData data) + { + if (data.Data.ContainsKey("uuid")) + data.Data.Remove("uuid"); + if (data.Data.ContainsKey("ScopeID")) + data.Data.Remove("ScopeID"); + if (data.Data.ContainsKey("regionName")) + data.Data.Remove("regionName"); + if (data.Data.ContainsKey("posX")) + data.Data.Remove("posX"); + if (data.Data.ContainsKey("posY")) + data.Data.Remove("posY"); + if (data.Data.ContainsKey("sizeX")) + data.Data.Remove("sizeX"); + if (data.Data.ContainsKey("sizeY")) + data.Data.Remove("sizeY"); + if (data.Data.ContainsKey("locX")) + data.Data.Remove("locX"); + if (data.Data.ContainsKey("locY")) + data.Data.Remove("locY"); + + if (data.RegionName.Length > 128) + data.RegionName = data.RegionName.Substring(0, 128); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + string update = "update `" + m_Realm + "` set locX=?posX, locY=?posY, sizeX=?sizeX, sizeY=?sizeY"; + foreach (string field in fields) + { + update += ", "; + update += "`" + field + "` = ?" + field; + + cmd.Parameters.AddWithValue("?" + field, data.Data[field]); + } + + update += " where uuid = ?regionID"; + + if (data.ScopeID != UUID.Zero) + update += " and ScopeID = ?scopeID"; + + cmd.CommandText = update; + cmd.Parameters.AddWithValue("?regionID", data.RegionID.ToString()); + cmd.Parameters.AddWithValue("?regionName", data.RegionName); + cmd.Parameters.AddWithValue("?scopeID", data.ScopeID.ToString()); + cmd.Parameters.AddWithValue("?posX", data.posX.ToString()); + cmd.Parameters.AddWithValue("?posY", data.posY.ToString()); + cmd.Parameters.AddWithValue("?sizeX", data.sizeX.ToString()); + cmd.Parameters.AddWithValue("?sizeY", data.sizeY.ToString()); + + if (ExecuteNonQuery(cmd) < 1) + { + string insert = "insert into `" + m_Realm + "` (`uuid`, `ScopeID`, `locX`, `locY`, `sizeX`, `sizeY`, `regionName`, `" + + String.Join("`, `", fields) + + "`) values ( ?regionID, ?scopeID, ?posX, ?posY, ?sizeX, ?sizeY, ?regionName, ?" + String.Join(", ?", fields) + ")"; + + cmd.CommandText = insert; + + if (ExecuteNonQuery(cmd) < 1) + { + return false; + } + } + } + + return true; + } + + public bool SetDataItem(UUID regionID, string item, string value) + { + using (MySqlCommand cmd = new MySqlCommand("update `" + m_Realm + "` set `" + item + "` = ?" + item + " where uuid = ?UUID")) + { + cmd.Parameters.AddWithValue("?" + item, value); + cmd.Parameters.AddWithValue("?UUID", regionID.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + + public bool Delete(UUID regionID) + { + using (MySqlCommand cmd = new MySqlCommand("delete from `" + m_Realm + "` where uuid = ?UUID")) + { + cmd.Parameters.AddWithValue("?UUID", regionID.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + return true; + } + + return false; + } + + public List GetDefaultRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultRegion, scopeID); + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + List regions = Get((int)RegionFlags.FallbackRegion, scopeID); + RegionDataDistanceCompare distanceComparer = new RegionDataDistanceCompare(x, y); + regions.Sort(distanceComparer); + return regions; + } + + public List GetHyperlinks(UUID scopeID) + { + return Get((int)RegionFlags.Hyperlink, scopeID); + } + + private List Get(int regionFlags, UUID scopeID) + { + string command = "select * from `" + m_Realm + "` where (flags & " + regionFlags.ToString() + ") <> 0"; + if (scopeID != UUID.Zero) + command += " and ScopeID = ?scopeID"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Parameters.AddWithValue("?scopeID", scopeID.ToString()); + + return RunCommand(cmd); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs new file mode 100644 index 0000000000..bb0ab751f6 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -0,0 +1,2093 @@ +/* + * 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.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Threading; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for the Region Server + /// + public class MySQLSimulationData : ISimulationDataStore + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[REGION DB MYSQL]"; + + private string m_connectionString; + + /// + /// This lock was being used to serialize database operations when the connection was shared, but this has + /// been unnecessary for a long time after we switched to using MySQL's underlying connection pooling instead. + /// FIXME: However, the locks remain in many places since they are effectively providing a level of + /// transactionality. This should be replaced by more efficient database transactions which would not require + /// unrelated operations to block each other or unrelated operations on the same tables from blocking each + /// other. + /// + private object m_dbLock = new object(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public MySQLSimulationData() + { + } + + public MySQLSimulationData(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + // Apply new Migrations + // + Migration m = new Migration(dbcon, Assembly, "RegionStore"); + m.Update(); + } + } + + private IDataReader ExecuteReader(MySqlCommand c) + { + IDataReader r = null; + + try + { + r = c.ExecuteReader(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} MySQL error in ExecuteReader: {1}", LogHeader, e); + throw; + } + + return r; + } + + private void ExecuteNonQuery(MySqlCommand c) + { + try + { + c.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[REGION DB]: MySQL error in ExecuteNonQuery: " + e.Message); + throw; + } + } + + public void Dispose() {} + + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + uint flags = obj.RootPart.GetEffectiveObjectFlags(); + + // Eligibility check + // + // PrimFlags.Temporary is not used in OpenSim code and cannot + // be guaranteed to always be clear. Don't check it. +// if ((flags & (uint)PrimFlags.Temporary) != 0) +// return; + if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) + return; + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + foreach (SceneObjectPart prim in obj.Parts) + { + cmd.Parameters.Clear(); + + cmd.CommandText = "replace into prims (" + + "UUID, CreationDate, " + + "Name, Text, Description, " + + "SitName, TouchName, ObjectFlags, " + + "OwnerMask, NextOwnerMask, GroupMask, " + + "EveryoneMask, BaseMask, PositionX, " + + "PositionY, PositionZ, GroupPositionX, " + + "GroupPositionY, GroupPositionZ, VelocityX, " + + "VelocityY, VelocityZ, AngularVelocityX, " + + "AngularVelocityY, AngularVelocityZ, " + + "AccelerationX, AccelerationY, " + + "AccelerationZ, RotationX, " + + "RotationY, RotationZ, " + + "RotationW, SitTargetOffsetX, " + + "SitTargetOffsetY, SitTargetOffsetZ, " + + "SitTargetOrientW, SitTargetOrientX, " + + "SitTargetOrientY, SitTargetOrientZ, " + + "RegionUUID, CreatorID, " + + "OwnerID, GroupID, " + + "LastOwnerID, SceneGroupID, " + + "PayPrice, PayButton1, " + + "PayButton2, PayButton3, " + + "PayButton4, LoopedSound, " + + "LoopedSoundGain, TextureAnimation, " + + "OmegaX, OmegaY, OmegaZ, " + + "CameraEyeOffsetX, CameraEyeOffsetY, " + + "CameraEyeOffsetZ, CameraAtOffsetX, " + + "CameraAtOffsetY, CameraAtOffsetZ, " + + "ForceMouselook, ScriptAccessPin, " + + "AllowedDrop, DieAtEdge, " + + "SalePrice, SaleType, " + + "ColorR, ColorG, ColorB, ColorA, " + + "ParticleSystem, ClickAction, Material, " + + "CollisionSound, CollisionSoundVolume, " + + "PassTouches, " + + "LinkNumber, MediaURL, AttachedPosX, " + + "AttachedPosY, AttachedPosZ, KeyframeMotion, " + + "PhysicsShapeType, Density, GravityModifier, " + + "Friction, Restitution, DynAttrs " + + ") values (" + "?UUID, " + + "?CreationDate, ?Name, ?Text, " + + "?Description, ?SitName, ?TouchName, " + + "?ObjectFlags, ?OwnerMask, ?NextOwnerMask, " + + "?GroupMask, ?EveryoneMask, ?BaseMask, " + + "?PositionX, ?PositionY, ?PositionZ, " + + "?GroupPositionX, ?GroupPositionY, " + + "?GroupPositionZ, ?VelocityX, " + + "?VelocityY, ?VelocityZ, ?AngularVelocityX, " + + "?AngularVelocityY, ?AngularVelocityZ, " + + "?AccelerationX, ?AccelerationY, " + + "?AccelerationZ, ?RotationX, " + + "?RotationY, ?RotationZ, " + + "?RotationW, ?SitTargetOffsetX, " + + "?SitTargetOffsetY, ?SitTargetOffsetZ, " + + "?SitTargetOrientW, ?SitTargetOrientX, " + + "?SitTargetOrientY, ?SitTargetOrientZ, " + + "?RegionUUID, ?CreatorID, ?OwnerID, " + + "?GroupID, ?LastOwnerID, ?SceneGroupID, " + + "?PayPrice, ?PayButton1, ?PayButton2, " + + "?PayButton3, ?PayButton4, ?LoopedSound, " + + "?LoopedSoundGain, ?TextureAnimation, " + + "?OmegaX, ?OmegaY, ?OmegaZ, " + + "?CameraEyeOffsetX, ?CameraEyeOffsetY, " + + "?CameraEyeOffsetZ, ?CameraAtOffsetX, " + + "?CameraAtOffsetY, ?CameraAtOffsetZ, " + + "?ForceMouselook, ?ScriptAccessPin, " + + "?AllowedDrop, ?DieAtEdge, ?SalePrice, " + + "?SaleType, ?ColorR, ?ColorG, " + + "?ColorB, ?ColorA, ?ParticleSystem, " + + "?ClickAction, ?Material, ?CollisionSound, " + + "?CollisionSoundVolume, ?PassTouches, " + + "?LinkNumber, ?MediaURL, ?AttachedPosX, " + + "?AttachedPosY, ?AttachedPosZ, ?KeyframeMotion, " + + "?PhysicsShapeType, ?Density, ?GravityModifier, " + + "?Friction, ?Restitution, ?DynAttrs)"; + + FillPrimCommand(cmd, prim, obj.UUID, regionUUID); + + ExecuteNonQuery(cmd); + + cmd.Parameters.Clear(); + + cmd.CommandText = "replace into primshapes (" + + "UUID, Shape, ScaleX, ScaleY, " + + "ScaleZ, PCode, PathBegin, PathEnd, " + + "PathScaleX, PathScaleY, PathShearX, " + + "PathShearY, PathSkew, PathCurve, " + + "PathRadiusOffset, PathRevolutions, " + + "PathTaperX, PathTaperY, PathTwist, " + + "PathTwistBegin, ProfileBegin, ProfileEnd, " + + "ProfileCurve, ProfileHollow, Texture, " + + "ExtraParams, State, LastAttachPoint, Media) " + + "values (?UUID, " + + "?Shape, ?ScaleX, ?ScaleY, ?ScaleZ, " + + "?PCode, ?PathBegin, ?PathEnd, " + + "?PathScaleX, ?PathScaleY, " + + "?PathShearX, ?PathShearY, " + + "?PathSkew, ?PathCurve, ?PathRadiusOffset, " + + "?PathRevolutions, ?PathTaperX, " + + "?PathTaperY, ?PathTwist, " + + "?PathTwistBegin, ?ProfileBegin, " + + "?ProfileEnd, ?ProfileCurve, " + + "?ProfileHollow, ?Texture, ?ExtraParams, " + + "?State, ?LastAttachPoint, ?Media)"; + + FillShapeCommand(cmd, prim); + + ExecuteNonQuery(cmd); + } + } + } + } + } + + public void RemoveObject(UUID obj, UUID regionUUID) + { +// m_log.DebugFormat("[REGION DB]: Deleting scene object {0} from {1} in database", obj, regionUUID); + + List uuids = new List(); + + // Formerly, this used to check the region UUID. + // That makes no sense, as we remove the contents of a prim + // unconditionally, but the prim dependent on the region ID. + // So, we would destroy an object and cause hard to detect + // issues if we delete the contents only. Deleting it all may + // cause the loss of a prim, but is cleaner. + // It's also faster because it uses the primary key. + // + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select UUID from prims where SceneGroupID= ?UUID"; + cmd.Parameters.AddWithValue("UUID", obj.ToString()); + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + uuids.Add(DBGuid.FromDB(reader["UUID"].ToString())); + } + + // delete the main prims + cmd.CommandText = "delete from prims where SceneGroupID= ?UUID"; + ExecuteNonQuery(cmd); + } + } + } + + // there is no way this should be < 1 unless there is + // a very corrupt database, but in that case be extra + // safe anyway. + if (uuids.Count > 0) + { + RemoveShapes(uuids); + RemoveItems(uuids); + } + } + + /// + /// Remove all persisted items of the given prim. + /// The caller must acquire the necessrary synchronization locks + /// + /// the Item UUID + private void RemoveItems(UUID uuid) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from primitems where PrimID = ?PrimID"; + cmd.Parameters.AddWithValue("PrimID", uuid.ToString()); + + ExecuteNonQuery(cmd); + } + } + } + } + + /// + /// Remove all persisted shapes for a list of prims + /// The caller must acquire the necessrary synchronization locks + /// + /// the list of UUIDs + private void RemoveShapes(List uuids) + { + lock (m_dbLock) + { + string sql = "delete from primshapes where "; + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + for (int i = 0; i < uuids.Count; i++) + { + if ((i + 1) == uuids.Count) + {// end of the list + sql += "(UUID = ?UUID" + i + ")"; + } + else + { + sql += "(UUID = ?UUID" + i + ") or "; + } + } + cmd.CommandText = sql; + + for (int i = 0; i < uuids.Count; i++) + cmd.Parameters.AddWithValue("UUID" + i, uuids[i].ToString()); + + ExecuteNonQuery(cmd); + } + } + } + } + + /// + /// Remove all persisted items for a list of prims + /// The caller must acquire the necessrary synchronization locks + /// + /// the list of UUIDs + private void RemoveItems(List uuids) + { + lock (m_dbLock) + { + string sql = "delete from primitems where "; + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + for (int i = 0; i < uuids.Count; i++) + { + if ((i + 1) == uuids.Count) + { + // end of the list + sql += "(PrimID = ?PrimID" + i + ")"; + } + else + { + sql += "(PrimID = ?PrimID" + i + ") or "; + } + } + cmd.CommandText = sql; + + for (int i = 0; i < uuids.Count; i++) + cmd.Parameters.AddWithValue("PrimID" + i, uuids[i].ToString()); + + ExecuteNonQuery(cmd); + } + } + } + } + + public List LoadObjects(UUID regionID) + { + const int ROWS_PER_QUERY = 5000; + + Dictionary prims = new Dictionary(ROWS_PER_QUERY); + Dictionary objects = new Dictionary(); + int count = 0; + + #region Prim Loading + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = + "SELECT * FROM prims LEFT JOIN primshapes ON prims.UUID = primshapes.UUID WHERE RegionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); + cmd.CommandTimeout = 3600; + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + { + SceneObjectPart prim = BuildPrim(reader); + if (reader["Shape"] is DBNull) + prim.Shape = PrimitiveBaseShape.Default; + else + prim.Shape = BuildShape(reader); + + UUID parentID = DBGuid.FromDB(reader["SceneGroupID"].ToString()); + if (parentID != prim.UUID) + prim.ParentUUID = parentID; + + prims[prim.UUID] = prim; + + ++count; + if (count % ROWS_PER_QUERY == 0) + m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); + } + } + } + } + } + + #endregion Prim Loading + + #region SceneObjectGroup Creation + + // Create all of the SOGs from the root prims first + foreach (SceneObjectPart prim in prims.Values) + { + if (prim.ParentUUID == UUID.Zero) + { + objects[prim.UUID] = new SceneObjectGroup(prim); + } + } + + // Add all of the children objects to the SOGs + foreach (SceneObjectPart prim in prims.Values) + { + SceneObjectGroup sog; + if (prim.UUID != prim.ParentUUID) + { + if (objects.TryGetValue(prim.ParentUUID, out sog)) + { + int originalLinkNum = prim.LinkNum; + + sog.AddPart(prim); + + // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. + // We override that here + if (originalLinkNum != 0) + prim.LinkNum = originalLinkNum; + } + else + { + m_log.WarnFormat( + "[REGION DB]: Database contains an orphan child prim {0} {1} in region {2} pointing to missing parent {3}. This prim will not be loaded.", + prim.Name, prim.UUID, regionID, prim.ParentUUID); + } + } + } + + #endregion SceneObjectGroup Creation + + m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + + #region Prim Inventory Loading + + // Instead of attempting to LoadItems on every prim, + // most of which probably have no items... get a + // list from DB of all prims which have items and + // LoadItems only on those + List primsWithInventory = new List(); + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand itemCmd = dbcon.CreateCommand()) + { + itemCmd.CommandText = "SELECT DISTINCT primID FROM primitems"; + using (IDataReader itemReader = ExecuteReader(itemCmd)) + { + while (itemReader.Read()) + { + if (!(itemReader["primID"] is DBNull)) + { + UUID primID = DBGuid.FromDB(itemReader["primID"].ToString()); + if (prims.ContainsKey(primID)) + primsWithInventory.Add(prims[primID]); + } + } + } + } + } + } + + foreach (SceneObjectPart prim in primsWithInventory) + { + LoadItems(prim); + } + + #endregion Prim Inventory Loading + + m_log.DebugFormat("[REGION DB]: Loaded inventory from {0} objects", primsWithInventory.Count); + + return new List(objects.Values); + } + + /// + /// Load in a prim's persisted inventory. + /// + /// The prim + private void LoadItems(SceneObjectPart prim) + { + lock (m_dbLock) + { + List inventory = new List(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select * from primitems where PrimID = ?PrimID"; + cmd.Parameters.AddWithValue("PrimID", prim.UUID.ToString()); + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + { + TaskInventoryItem item = BuildItem(reader); + + item.ParentID = prim.UUID; // Values in database are often wrong + inventory.Add(item); + } + } + } + } + + prim.Inventory.RestoreInventoryItems(inventory); + } + } + + // Legacy entry point for when terrain was always a 256x256 hieghtmap + public void StoreTerrain(double[,] ter, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(ter), regionID); + } + + public void StoreTerrain(TerrainData terrData, UUID regionID) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from terrain where RegionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); + + ExecuteNonQuery(cmd); + + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + + m_log.InfoFormat("{0} Storing terrain. X={1}, Y={2}, rev={3}", + LogHeader, terrData.SizeX, terrData.SizeY, terrainDBRevision); + + cmd.CommandText = "insert into terrain (RegionUUID, Revision, Heightfield)" + + "values (?RegionUUID, ?Revision, ?Heightfield)"; + + cmd.Parameters.AddWithValue("Revision", terrainDBRevision); + cmd.Parameters.AddWithValue("Heightfield", terrainDBblob); + + ExecuteNonQuery(cmd); + } + } + } + } + + // Legacy region loading + public double[,] LoadTerrain(UUID regionID) + { + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select RegionUUID, Revision, Heightfield " + + "from terrain where RegionUUID = ?RegionUUID " + + "order by Revision desc limit 1"; + cmd.Parameters.AddWithValue("RegionUUID", regionID.ToString()); + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + { + int rev = Convert.ToInt32(reader["Revision"]); + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); + } + } + } + } + } + + return terrData; + } + + public void RemoveLandObject(UUID globalID) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from land where UUID = ?UUID"; + cmd.Parameters.AddWithValue("UUID", globalID.ToString()); + + ExecuteNonQuery(cmd); + } + } + } + } + + public void StoreLandObject(ILandObject parcel) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "replace into land (UUID, RegionUUID, " + + "LocalLandID, Bitmap, Name, Description, " + + "OwnerUUID, IsGroupOwned, Area, AuctionID, " + + "Category, ClaimDate, ClaimPrice, GroupUUID, " + + "SalePrice, LandStatus, LandFlags, LandingType, " + + "MediaAutoScale, MediaTextureUUID, MediaURL, " + + "MusicURL, PassHours, PassPrice, SnapshotUUID, " + + "UserLocationX, UserLocationY, UserLocationZ, " + + "UserLookAtX, UserLookAtY, UserLookAtZ, " + + "AuthbuyerID, OtherCleanTime, Dwell, MediaType, MediaDescription, " + + "MediaSize, MediaLoop, ObscureMusic, ObscureMedia) values (" + + "?UUID, ?RegionUUID, " + + "?LocalLandID, ?Bitmap, ?Name, ?Description, " + + "?OwnerUUID, ?IsGroupOwned, ?Area, ?AuctionID, " + + "?Category, ?ClaimDate, ?ClaimPrice, ?GroupUUID, " + + "?SalePrice, ?LandStatus, ?LandFlags, ?LandingType, " + + "?MediaAutoScale, ?MediaTextureUUID, ?MediaURL, " + + "?MusicURL, ?PassHours, ?PassPrice, ?SnapshotUUID, " + + "?UserLocationX, ?UserLocationY, ?UserLocationZ, " + + "?UserLookAtX, ?UserLookAtY, ?UserLookAtZ, " + + "?AuthbuyerID, ?OtherCleanTime, ?Dwell, ?MediaType, ?MediaDescription, "+ + "CONCAT(?MediaWidth, ',', ?MediaHeight), ?MediaLoop, ?ObscureMusic, ?ObscureMedia)"; + + FillLandCommand(cmd, parcel.LandData, parcel.RegionUUID); + + ExecuteNonQuery(cmd); + + cmd.CommandText = "delete from landaccesslist where LandUUID = ?UUID"; + + ExecuteNonQuery(cmd); + + cmd.Parameters.Clear(); + cmd.CommandText = "insert into landaccesslist (LandUUID, " + + "AccessUUID, Flags, Expires) values (?LandUUID, ?AccessUUID, " + + "?Flags, ?Expires)"; + + foreach (LandAccessEntry entry in parcel.LandData.ParcelAccessList) + { + FillLandAccessCommand(cmd, entry, parcel.LandData.GlobalID); + ExecuteNonQuery(cmd); + cmd.Parameters.Clear(); + } + } + } + } + } + + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + RegionLightShareData nWP = new RegionLightShareData(); + nWP.OnSave += StoreRegionWindlightSettings; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + string command = "select * from `regionwindlight` where region_id = ?regionID"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Connection = dbcon; + + cmd.Parameters.AddWithValue("?regionID", regionUUID.ToString()); + + IDataReader result = ExecuteReader(cmd); + if (!result.Read()) + { + //No result, so store our default windlight profile and return it + nWP.regionID = regionUUID; +// StoreRegionWindlightSettings(nWP); + return nWP; + } + else + { + nWP.regionID = DBGuid.FromDB(result["region_id"]); + nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]); + nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]); + nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]); + nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]); + nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]); + nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]); + nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]); + nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]); + nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]); + nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]); + nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]); + nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]); + nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]); + nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]); + nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]); + nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]); + nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]); + UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture); + nWP.horizon.X = Convert.ToSingle(result["horizon_r"]); + nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]); + nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]); + nWP.horizon.W = Convert.ToSingle(result["horizon_i"]); + nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]); + nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]); + nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]); + nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]); + nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]); + nWP.hazeDensity = Convert.ToSingle(result["haze_density"]); + nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]); + nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]); + nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]); + nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]); + nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]); + nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]); + nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]); + nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]); + nWP.ambient.X = Convert.ToSingle(result["ambient_r"]); + nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]); + nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]); + nWP.ambient.W = Convert.ToSingle(result["ambient_i"]); + nWP.eastAngle = Convert.ToSingle(result["east_angle"]); + nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]); + nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]); + nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]); + nWP.starBrightness = Convert.ToSingle(result["star_brightness"]); + nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]); + nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]); + nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]); + nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]); + nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]); + nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]); + nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]); + nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]); + nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]); + nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]); + nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]); + nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]); + nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]); + nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]); + nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]); + nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]); + nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]); + nWP.valid = true; + } + } + } + + return nWP; + } + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + RegionSettings rs = null; + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select * from regionsettings where regionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("regionUUID", regionUUID); + + using (IDataReader reader = ExecuteReader(cmd)) + { + if (reader.Read()) + { + rs = BuildRegionSettings(reader); + rs.OnSave += StoreRegionSettings; + } + else + { + rs = new RegionSettings(); + rs.RegionUUID = regionUUID; + rs.OnSave += StoreRegionSettings; + + StoreRegionSettings(rs); + } + } + } + } + } + + LoadSpawnPoints(rs); + + return rs; + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "REPLACE INTO `regionwindlight` (`region_id`, `water_color_r`, `water_color_g`, "; + cmd.CommandText += "`water_color_b`, `water_fog_density_exponent`, `underwater_fog_modifier`, "; + cmd.CommandText += "`reflection_wavelet_scale_1`, `reflection_wavelet_scale_2`, `reflection_wavelet_scale_3`, "; + cmd.CommandText += "`fresnel_scale`, `fresnel_offset`, `refract_scale_above`, `refract_scale_below`, "; + cmd.CommandText += "`blur_multiplier`, `big_wave_direction_x`, `big_wave_direction_y`, `little_wave_direction_x`, "; + cmd.CommandText += "`little_wave_direction_y`, `normal_map_texture`, `horizon_r`, `horizon_g`, `horizon_b`, "; + cmd.CommandText += "`horizon_i`, `haze_horizon`, `blue_density_r`, `blue_density_g`, `blue_density_b`, "; + cmd.CommandText += "`blue_density_i`, `haze_density`, `density_multiplier`, `distance_multiplier`, `max_altitude`, "; + cmd.CommandText += "`sun_moon_color_r`, `sun_moon_color_g`, `sun_moon_color_b`, `sun_moon_color_i`, `sun_moon_position`, "; + cmd.CommandText += "`ambient_r`, `ambient_g`, `ambient_b`, `ambient_i`, `east_angle`, `sun_glow_focus`, `sun_glow_size`, "; + cmd.CommandText += "`scene_gamma`, `star_brightness`, `cloud_color_r`, `cloud_color_g`, `cloud_color_b`, `cloud_color_i`, "; + cmd.CommandText += "`cloud_x`, `cloud_y`, `cloud_density`, `cloud_coverage`, `cloud_scale`, `cloud_detail_x`, "; + cmd.CommandText += "`cloud_detail_y`, `cloud_detail_density`, `cloud_scroll_x`, `cloud_scroll_x_lock`, `cloud_scroll_y`, "; + cmd.CommandText += "`cloud_scroll_y_lock`, `draw_classic_clouds`) VALUES (?region_id, ?water_color_r, "; + cmd.CommandText += "?water_color_g, ?water_color_b, ?water_fog_density_exponent, ?underwater_fog_modifier, ?reflection_wavelet_scale_1, "; + cmd.CommandText += "?reflection_wavelet_scale_2, ?reflection_wavelet_scale_3, ?fresnel_scale, ?fresnel_offset, ?refract_scale_above, "; + cmd.CommandText += "?refract_scale_below, ?blur_multiplier, ?big_wave_direction_x, ?big_wave_direction_y, ?little_wave_direction_x, "; + cmd.CommandText += "?little_wave_direction_y, ?normal_map_texture, ?horizon_r, ?horizon_g, ?horizon_b, ?horizon_i, ?haze_horizon, "; + cmd.CommandText += "?blue_density_r, ?blue_density_g, ?blue_density_b, ?blue_density_i, ?haze_density, ?density_multiplier, "; + cmd.CommandText += "?distance_multiplier, ?max_altitude, ?sun_moon_color_r, ?sun_moon_color_g, ?sun_moon_color_b, "; + cmd.CommandText += "?sun_moon_color_i, ?sun_moon_position, ?ambient_r, ?ambient_g, ?ambient_b, ?ambient_i, ?east_angle, "; + cmd.CommandText += "?sun_glow_focus, ?sun_glow_size, ?scene_gamma, ?star_brightness, ?cloud_color_r, ?cloud_color_g, "; + cmd.CommandText += "?cloud_color_b, ?cloud_color_i, ?cloud_x, ?cloud_y, ?cloud_density, ?cloud_coverage, ?cloud_scale, "; + cmd.CommandText += "?cloud_detail_x, ?cloud_detail_y, ?cloud_detail_density, ?cloud_scroll_x, ?cloud_scroll_x_lock, "; + cmd.CommandText += "?cloud_scroll_y, ?cloud_scroll_y_lock, ?draw_classic_clouds)"; + + cmd.Parameters.AddWithValue("region_id", wl.regionID); + cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X); + cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y); + cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z); + cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent); + cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier); + cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X); + cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y); + cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z); + cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale); + cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset); + cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove); + cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow); + cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier); + cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X); + cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y); + cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X); + cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y); + cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture); + cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X); + cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y); + cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z); + cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W); + cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon); + cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X); + cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y); + cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z); + cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W); + cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity); + cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier); + cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier); + cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude); + cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X); + cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y); + cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z); + cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W); + cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition); + cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X); + cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y); + cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z); + cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W); + cmd.Parameters.AddWithValue("east_angle", wl.eastAngle); + cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus); + cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize); + cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma); + cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness); + cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X); + cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y); + cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z); + cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W); + cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X); + cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y); + cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z); + cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage); + cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale); + cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X); + cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y); + cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z); + cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX); + cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock); + cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY); + cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock); + cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds); + + ExecuteNonQuery(cmd); + } + } + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from `regionwindlight` where `region_id`=?regionID"; + cmd.Parameters.AddWithValue("?regionID", regionID.ToString()); + ExecuteNonQuery(cmd); + } + } + } + + #region RegionEnvironmentSettings + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + string command = "select * from `regionenvironment` where region_id = ?region_id"; + + using (MySqlCommand cmd = new MySqlCommand(command)) + { + cmd.Connection = dbcon; + + cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString()); + + IDataReader result = ExecuteReader(cmd); + if (!result.Read()) + { + return String.Empty; + } + else + { + return Convert.ToString(result["llsd_settings"]); + } + } + } + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "REPLACE INTO `regionenvironment` (`region_id`, `llsd_settings`) VALUES (?region_id, ?llsd_settings)"; + + cmd.Parameters.AddWithValue("region_id", regionUUID); + cmd.Parameters.AddWithValue("llsd_settings", settings); + + ExecuteNonQuery(cmd); + } + } + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from `regionenvironment` where region_id = ?region_id"; + cmd.Parameters.AddWithValue("?region_id", regionUUID.ToString()); + ExecuteNonQuery(cmd); + } + } + } + #endregion + + public void StoreRegionSettings(RegionSettings rs) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "replace into regionsettings (regionUUID, " + + "block_terraform, block_fly, allow_damage, " + + "restrict_pushing, allow_land_resell, " + + "allow_land_join_divide, block_show_in_search, " + + "agent_limit, object_bonus, maturity, " + + "disable_scripts, disable_collisions, " + + "disable_physics, terrain_texture_1, " + + "terrain_texture_2, terrain_texture_3, " + + "terrain_texture_4, elevation_1_nw, " + + "elevation_2_nw, elevation_1_ne, " + + "elevation_2_ne, elevation_1_se, " + + "elevation_2_se, elevation_1_sw, " + + "elevation_2_sw, water_height, " + + "terrain_raise_limit, terrain_lower_limit, " + + "use_estate_sun, fixed_sun, sun_position, " + + "covenant, covenant_datetime, Sandbox, sunvectorx, sunvectory, " + + "sunvectorz, loaded_creation_datetime, " + + "loaded_creation_id, map_tile_ID, " + + "TelehubObject, parcel_tile_ID) " + + "values (?RegionUUID, ?BlockTerraform, " + + "?BlockFly, ?AllowDamage, ?RestrictPushing, " + + "?AllowLandResell, ?AllowLandJoinDivide, " + + "?BlockShowInSearch, ?AgentLimit, ?ObjectBonus, " + + "?Maturity, ?DisableScripts, ?DisableCollisions, " + + "?DisablePhysics, ?TerrainTexture1, " + + "?TerrainTexture2, ?TerrainTexture3, " + + "?TerrainTexture4, ?Elevation1NW, ?Elevation2NW, " + + "?Elevation1NE, ?Elevation2NE, ?Elevation1SE, " + + "?Elevation2SE, ?Elevation1SW, ?Elevation2SW, " + + "?WaterHeight, ?TerrainRaiseLimit, " + + "?TerrainLowerLimit, ?UseEstateSun, ?FixedSun, " + + "?SunPosition, ?Covenant, ?CovenantChangedDateTime, ?Sandbox, " + + "?SunVectorX, ?SunVectorY, ?SunVectorZ, " + + "?LoadedCreationDateTime, ?LoadedCreationID, " + + "?TerrainImageID, " + + "?TelehubObject, ?ParcelImageID)"; + + FillRegionSettingsCommand(cmd, rs); + + ExecuteNonQuery(cmd); + } + } + + SaveSpawnPoints(rs); + } + + public List LoadLandObjects(UUID regionUUID) + { + List landData = new List(); + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select * from land where RegionUUID = ?RegionUUID"; + cmd.Parameters.AddWithValue("RegionUUID", regionUUID.ToString()); + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + { + LandData newLand = BuildLandData(reader); + landData.Add(newLand); + } + } + } + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + foreach (LandData land in landData) + { + cmd.Parameters.Clear(); + cmd.CommandText = "select * from landaccesslist where LandUUID = ?LandUUID"; + cmd.Parameters.AddWithValue("LandUUID", land.GlobalID.ToString()); + + using (IDataReader reader = ExecuteReader(cmd)) + { + while (reader.Read()) + { + land.ParcelAccessList.Add(BuildLandAccessData(reader)); + } + } + } + } + } + } + + return landData; + } + + public void Shutdown() + { + } + + private SceneObjectPart BuildPrim(IDataReader row) + { + SceneObjectPart prim = new SceneObjectPart(); + + // depending on the MySQL connector version, CHAR(36) may be already converted to Guid! + prim.UUID = DBGuid.FromDB(row["UUID"]); + prim.CreatorIdentification = (string)row["CreatorID"]; + prim.OwnerID = DBGuid.FromDB(row["OwnerID"]); + prim.GroupID = DBGuid.FromDB(row["GroupID"]); + prim.LastOwnerID = DBGuid.FromDB(row["LastOwnerID"]); + + // explicit conversion of integers is required, which sort + // of sucks. No idea if there is a shortcut here or not. + prim.CreationDate = (int)row["CreationDate"]; + if (row["Name"] != DBNull.Value) + prim.Name = (string)row["Name"]; + else + prim.Name = String.Empty; + // Various text fields + prim.Text = (string)row["Text"]; + prim.Color = Color.FromArgb((int)row["ColorA"], + (int)row["ColorR"], + (int)row["ColorG"], + (int)row["ColorB"]); + prim.Description = (string)row["Description"]; + prim.SitName = (string)row["SitName"]; + prim.TouchName = (string)row["TouchName"]; + // Permissions + prim.Flags = (PrimFlags)(int)row["ObjectFlags"]; + prim.OwnerMask = (uint)(int)row["OwnerMask"]; + prim.NextOwnerMask = (uint)(int)row["NextOwnerMask"]; + prim.GroupMask = (uint)(int)row["GroupMask"]; + prim.EveryoneMask = (uint)(int)row["EveryoneMask"]; + prim.BaseMask = (uint)(int)row["BaseMask"]; + + // Vectors + prim.OffsetPosition = new Vector3( + (float)(double)row["PositionX"], + (float)(double)row["PositionY"], + (float)(double)row["PositionZ"] + ); + prim.GroupPosition = new Vector3( + (float)(double)row["GroupPositionX"], + (float)(double)row["GroupPositionY"], + (float)(double)row["GroupPositionZ"] + ); + prim.Velocity = new Vector3( + (float)(double)row["VelocityX"], + (float)(double)row["VelocityY"], + (float)(double)row["VelocityZ"] + ); + prim.AngularVelocity = new Vector3( + (float)(double)row["AngularVelocityX"], + (float)(double)row["AngularVelocityY"], + (float)(double)row["AngularVelocityZ"] + ); + prim.Acceleration = new Vector3( + (float)(double)row["AccelerationX"], + (float)(double)row["AccelerationY"], + (float)(double)row["AccelerationZ"] + ); + // quaternions + prim.RotationOffset = new Quaternion( + (float)(double)row["RotationX"], + (float)(double)row["RotationY"], + (float)(double)row["RotationZ"], + (float)(double)row["RotationW"] + ); + prim.SitTargetPositionLL = new Vector3( + (float)(double)row["SitTargetOffsetX"], + (float)(double)row["SitTargetOffsetY"], + (float)(double)row["SitTargetOffsetZ"] + ); + prim.SitTargetOrientationLL = new Quaternion( + (float)(double)row["SitTargetOrientX"], + (float)(double)row["SitTargetOrientY"], + (float)(double)row["SitTargetOrientZ"], + (float)(double)row["SitTargetOrientW"] + ); + + prim.PayPrice[0] = (int)row["PayPrice"]; + prim.PayPrice[1] = (int)row["PayButton1"]; + prim.PayPrice[2] = (int)row["PayButton2"]; + prim.PayPrice[3] = (int)row["PayButton3"]; + prim.PayPrice[4] = (int)row["PayButton4"]; + + prim.Sound = DBGuid.FromDB(row["LoopedSound"].ToString()); + prim.SoundGain = (float)(double)row["LoopedSoundGain"]; + prim.SoundFlags = 1; // If it's persisted at all, it's looped + + if (!(row["TextureAnimation"] is DBNull)) + prim.TextureAnimation = (byte[])row["TextureAnimation"]; + if (!(row["ParticleSystem"] is DBNull)) + prim.ParticleSystem = (byte[])row["ParticleSystem"]; + + prim.AngularVelocity = new Vector3( + (float)(double)row["OmegaX"], + (float)(double)row["OmegaY"], + (float)(double)row["OmegaZ"] + ); + + prim.SetCameraEyeOffset(new Vector3( + (float)(double)row["CameraEyeOffsetX"], + (float)(double)row["CameraEyeOffsetY"], + (float)(double)row["CameraEyeOffsetZ"] + )); + + prim.SetCameraAtOffset(new Vector3( + (float)(double)row["CameraAtOffsetX"], + (float)(double)row["CameraAtOffsetY"], + (float)(double)row["CameraAtOffsetZ"] + )); + + prim.SetForceMouselook((sbyte)row["ForceMouselook"] != 0); + prim.ScriptAccessPin = (int)row["ScriptAccessPin"]; + prim.AllowedDrop = ((sbyte)row["AllowedDrop"] != 0); + prim.DIE_AT_EDGE = ((sbyte)row["DieAtEdge"] != 0); + + prim.SalePrice = (int)row["SalePrice"]; + prim.ObjectSaleType = unchecked((byte)(sbyte)row["SaleType"]); + + prim.Material = unchecked((byte)(sbyte)row["Material"]); + + if (!(row["ClickAction"] is DBNull)) + prim.ClickAction = unchecked((byte)(sbyte)row["ClickAction"]); + + prim.CollisionSound = DBGuid.FromDB(row["CollisionSound"]); + prim.CollisionSoundVolume = (float)(double)row["CollisionSoundVolume"]; + + prim.PassTouches = ((sbyte)row["PassTouches"] != 0); + prim.LinkNum = (int)row["LinkNumber"]; + + if (!(row["MediaURL"] is System.DBNull)) + prim.MediaUrl = (string)row["MediaURL"]; + + if (!(row["AttachedPosX"] is System.DBNull)) + { + prim.AttachedPos = new Vector3( + (float)(double)row["AttachedPosX"], + (float)(double)row["AttachedPosY"], + (float)(double)row["AttachedPosZ"] + ); + } + + if (!(row["DynAttrs"] is System.DBNull)) + prim.DynAttrs = DAMap.FromXml((string)row["DynAttrs"]); + else + prim.DynAttrs = new DAMap(); + + if (!(row["KeyframeMotion"] is DBNull)) + { + Byte[] data = (byte[])row["KeyframeMotion"]; + if (data.Length > 0) + prim.KeyframeMotion = KeyframeMotion.FromData(null, data); + else + prim.KeyframeMotion = null; + } + else + { + prim.KeyframeMotion = null; + } + + prim.PhysicsShapeType = (byte)Convert.ToInt32(row["PhysicsShapeType"].ToString()); + prim.Density = (float)(double)row["Density"]; + prim.GravityModifier = (float)(double)row["GravityModifier"]; + prim.Friction = (float)(double)row["Friction"]; + prim.Restitution = (float)(double)row["Restitution"]; + + return prim; + } + + /// + /// Build a prim inventory item from the persisted data. + /// + /// + /// + private static TaskInventoryItem BuildItem(IDataReader row) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = DBGuid.FromDB(row["itemID"]); + taskItem.ParentPartID = DBGuid.FromDB(row["primID"]); + taskItem.AssetID = DBGuid.FromDB(row["assetID"]); + taskItem.ParentID = DBGuid.FromDB(row["parentFolderID"]); + + taskItem.InvType = Convert.ToInt32(row["invType"]); + taskItem.Type = Convert.ToInt32(row["assetType"]); + + taskItem.Name = (String)row["name"]; + taskItem.Description = (String)row["description"]; + taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]); + taskItem.CreatorIdentification = (String)row["creatorID"]; + taskItem.OwnerID = DBGuid.FromDB(row["ownerID"]); + taskItem.LastOwnerID = DBGuid.FromDB(row["lastOwnerID"]); + taskItem.GroupID = DBGuid.FromDB(row["groupID"]); + + taskItem.NextPermissions = Convert.ToUInt32(row["nextPermissions"]); + taskItem.CurrentPermissions = Convert.ToUInt32(row["currentPermissions"]); + taskItem.BasePermissions = Convert.ToUInt32(row["basePermissions"]); + taskItem.EveryonePermissions = Convert.ToUInt32(row["everyonePermissions"]); + taskItem.GroupPermissions = Convert.ToUInt32(row["groupPermissions"]); + taskItem.Flags = Convert.ToUInt32(row["flags"]); + + return taskItem; + } + + private static RegionSettings BuildRegionSettings(IDataReader row) + { + RegionSettings newSettings = new RegionSettings(); + + newSettings.RegionUUID = DBGuid.FromDB(row["regionUUID"]); + newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); + newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); + newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); + newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]); + newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]); + newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]); + newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]); + newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]); + newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]); + newSettings.Maturity = Convert.ToInt32(row["maturity"]); + newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); + newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); + newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); + newSettings.TerrainTexture1 = DBGuid.FromDB(row["terrain_texture_1"]); + newSettings.TerrainTexture2 = DBGuid.FromDB(row["terrain_texture_2"]); + newSettings.TerrainTexture3 = DBGuid.FromDB(row["terrain_texture_3"]); + newSettings.TerrainTexture4 = DBGuid.FromDB(row["terrain_texture_4"]); + newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); + newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); + newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); + newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]); + newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]); + newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]); + newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]); + newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]); + newSettings.WaterHeight = Convert.ToDouble(row["water_height"]); + newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]); + newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]); + newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]); + newSettings.Sandbox = Convert.ToBoolean(row["Sandbox"]); + newSettings.SunVector = new Vector3 ( + Convert.ToSingle(row["sunvectorx"]), + Convert.ToSingle(row["sunvectory"]), + Convert.ToSingle(row["sunvectorz"]) + ); + newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); + newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); + newSettings.Covenant = DBGuid.FromDB(row["covenant"]); + newSettings.CovenantChangedDateTime = Convert.ToInt32(row["covenant_datetime"]); + newSettings.LoadedCreationDateTime = Convert.ToInt32(row["loaded_creation_datetime"]); + + if (row["loaded_creation_id"] is DBNull) + newSettings.LoadedCreationID = ""; + else + newSettings.LoadedCreationID = (String) row["loaded_creation_id"]; + + newSettings.TerrainImageID = DBGuid.FromDB(row["map_tile_ID"]); + newSettings.ParcelImageID = DBGuid.FromDB(row["parcel_tile_ID"]); + newSettings.TelehubObject = DBGuid.FromDB(row["TelehubObject"]); + + return newSettings; + } + + /// + /// + /// + /// + /// + private static LandData BuildLandData(IDataReader row) + { + LandData newData = new LandData(); + + newData.GlobalID = DBGuid.FromDB(row["UUID"]); + newData.LocalID = Convert.ToInt32(row["LocalLandID"]); + + // Bitmap is a byte[512] + newData.Bitmap = (Byte[]) row["Bitmap"]; + + newData.Name = (String) row["Name"]; + newData.Description = (String) row["Description"]; + newData.OwnerID = DBGuid.FromDB(row["OwnerUUID"]); + newData.IsGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]); + newData.Area = Convert.ToInt32(row["Area"]); + newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unimplemented + newData.Category = (ParcelCategory) Convert.ToInt32(row["Category"]); + //Enum libsecondlife.Parcel.ParcelCategory + newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); + newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); + newData.GroupID = DBGuid.FromDB(row["GroupUUID"]); + newData.SalePrice = Convert.ToInt32(row["SalePrice"]); + newData.Status = (ParcelStatus) Convert.ToInt32(row["LandStatus"]); + //Enum. libsecondlife.Parcel.ParcelStatus + newData.Flags = Convert.ToUInt32(row["LandFlags"]); + newData.LandingType = Convert.ToByte(row["LandingType"]); + newData.MediaAutoScale = Convert.ToByte(row["MediaAutoScale"]); + newData.MediaID = DBGuid.FromDB(row["MediaTextureUUID"]); + newData.MediaURL = (String) row["MediaURL"]; + newData.MusicURL = (String) row["MusicURL"]; + newData.PassHours = Convert.ToSingle(row["PassHours"]); + newData.PassPrice = Convert.ToInt32(row["PassPrice"]); + UUID authedbuyer = UUID.Zero; + UUID snapshotID = UUID.Zero; + + UUID.TryParse((string)row["AuthBuyerID"], out authedbuyer); + UUID.TryParse((string)row["SnapshotUUID"], out snapshotID); + newData.OtherCleanTime = Convert.ToInt32(row["OtherCleanTime"]); + newData.Dwell = Convert.ToSingle(row["Dwell"]); + + newData.AuthBuyerID = authedbuyer; + newData.SnapshotID = snapshotID; + try + { + newData.UserLocation = + new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]), + Convert.ToSingle(row["UserLocationZ"])); + newData.UserLookAt = + new Vector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]), + Convert.ToSingle(row["UserLookAtZ"])); + } + catch (InvalidCastException) + { + newData.UserLocation = Vector3.Zero; + newData.UserLookAt = Vector3.Zero; + m_log.ErrorFormat("[PARCEL]: unable to get parcel telehub settings for {1}", newData.Name); + } + + newData.MediaDescription = (string) row["MediaDescription"]; + newData.MediaType = (string) row["MediaType"]; + newData.MediaWidth = Convert.ToInt32((((string) row["MediaSize"]).Split(','))[0]); + newData.MediaHeight = Convert.ToInt32((((string) row["MediaSize"]).Split(','))[1]); + newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]); + newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]); + newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]); + + newData.ParcelAccessList = new List(); + + return newData; + } + + /// + /// + /// + /// + /// + private static LandAccessEntry BuildLandAccessData(IDataReader row) + { + LandAccessEntry entry = new LandAccessEntry(); + entry.AgentID = DBGuid.FromDB(row["AccessUUID"]); + entry.Flags = (AccessList) Convert.ToInt32(row["Flags"]); + entry.Expires = Convert.ToInt32(row["Expires"]); + return entry; + } + + /// + /// Fill the prim command with prim values + /// + /// + /// + /// + /// + private void FillPrimCommand(MySqlCommand cmd, SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + cmd.Parameters.AddWithValue("UUID", prim.UUID.ToString()); + cmd.Parameters.AddWithValue("RegionUUID", regionUUID.ToString()); + cmd.Parameters.AddWithValue("CreationDate", prim.CreationDate); + cmd.Parameters.AddWithValue("Name", prim.Name); + cmd.Parameters.AddWithValue("SceneGroupID", sceneGroupID.ToString()); + // the UUID of the root part for this SceneObjectGroup + // various text fields + cmd.Parameters.AddWithValue("Text", prim.Text); + cmd.Parameters.AddWithValue("ColorR", prim.Color.R); + cmd.Parameters.AddWithValue("ColorG", prim.Color.G); + cmd.Parameters.AddWithValue("ColorB", prim.Color.B); + cmd.Parameters.AddWithValue("ColorA", prim.Color.A); + cmd.Parameters.AddWithValue("Description", prim.Description); + cmd.Parameters.AddWithValue("SitName", prim.SitName); + cmd.Parameters.AddWithValue("TouchName", prim.TouchName); + // permissions + cmd.Parameters.AddWithValue("ObjectFlags", (uint)prim.Flags); + cmd.Parameters.AddWithValue("CreatorID", prim.CreatorIdentification.ToString()); + cmd.Parameters.AddWithValue("OwnerID", prim.OwnerID.ToString()); + cmd.Parameters.AddWithValue("GroupID", prim.GroupID.ToString()); + cmd.Parameters.AddWithValue("LastOwnerID", prim.LastOwnerID.ToString()); + cmd.Parameters.AddWithValue("OwnerMask", prim.OwnerMask); + cmd.Parameters.AddWithValue("NextOwnerMask", prim.NextOwnerMask); + cmd.Parameters.AddWithValue("GroupMask", prim.GroupMask); + cmd.Parameters.AddWithValue("EveryoneMask", prim.EveryoneMask); + cmd.Parameters.AddWithValue("BaseMask", prim.BaseMask); + // vectors + cmd.Parameters.AddWithValue("PositionX", (double)prim.OffsetPosition.X); + cmd.Parameters.AddWithValue("PositionY", (double)prim.OffsetPosition.Y); + cmd.Parameters.AddWithValue("PositionZ", (double)prim.OffsetPosition.Z); + cmd.Parameters.AddWithValue("GroupPositionX", (double)prim.GroupPosition.X); + cmd.Parameters.AddWithValue("GroupPositionY", (double)prim.GroupPosition.Y); + cmd.Parameters.AddWithValue("GroupPositionZ", (double)prim.GroupPosition.Z); + cmd.Parameters.AddWithValue("VelocityX", (double)prim.Velocity.X); + cmd.Parameters.AddWithValue("VelocityY", (double)prim.Velocity.Y); + cmd.Parameters.AddWithValue("VelocityZ", (double)prim.Velocity.Z); + cmd.Parameters.AddWithValue("AngularVelocityX", (double)prim.AngularVelocity.X); + cmd.Parameters.AddWithValue("AngularVelocityY", (double)prim.AngularVelocity.Y); + cmd.Parameters.AddWithValue("AngularVelocityZ", (double)prim.AngularVelocity.Z); + cmd.Parameters.AddWithValue("AccelerationX", (double)prim.Acceleration.X); + cmd.Parameters.AddWithValue("AccelerationY", (double)prim.Acceleration.Y); + cmd.Parameters.AddWithValue("AccelerationZ", (double)prim.Acceleration.Z); + // quaternions + cmd.Parameters.AddWithValue("RotationX", (double)prim.RotationOffset.X); + cmd.Parameters.AddWithValue("RotationY", (double)prim.RotationOffset.Y); + cmd.Parameters.AddWithValue("RotationZ", (double)prim.RotationOffset.Z); + cmd.Parameters.AddWithValue("RotationW", (double)prim.RotationOffset.W); + + // Sit target + Vector3 sitTargetPos = prim.SitTargetPositionLL; + cmd.Parameters.AddWithValue("SitTargetOffsetX", (double)sitTargetPos.X); + cmd.Parameters.AddWithValue("SitTargetOffsetY", (double)sitTargetPos.Y); + cmd.Parameters.AddWithValue("SitTargetOffsetZ", (double)sitTargetPos.Z); + + Quaternion sitTargetOrient = prim.SitTargetOrientationLL; + cmd.Parameters.AddWithValue("SitTargetOrientW", (double)sitTargetOrient.W); + cmd.Parameters.AddWithValue("SitTargetOrientX", (double)sitTargetOrient.X); + cmd.Parameters.AddWithValue("SitTargetOrientY", (double)sitTargetOrient.Y); + cmd.Parameters.AddWithValue("SitTargetOrientZ", (double)sitTargetOrient.Z); + + cmd.Parameters.AddWithValue("PayPrice", prim.PayPrice[0]); + cmd.Parameters.AddWithValue("PayButton1", prim.PayPrice[1]); + cmd.Parameters.AddWithValue("PayButton2", prim.PayPrice[2]); + cmd.Parameters.AddWithValue("PayButton3", prim.PayPrice[3]); + cmd.Parameters.AddWithValue("PayButton4", prim.PayPrice[4]); + + if ((prim.SoundFlags & 1) != 0) // Looped + { + cmd.Parameters.AddWithValue("LoopedSound", prim.Sound.ToString()); + cmd.Parameters.AddWithValue("LoopedSoundGain", prim.SoundGain); + } + else + { + cmd.Parameters.AddWithValue("LoopedSound", UUID.Zero); + cmd.Parameters.AddWithValue("LoopedSoundGain", 0.0f); + } + + cmd.Parameters.AddWithValue("TextureAnimation", prim.TextureAnimation); + cmd.Parameters.AddWithValue("ParticleSystem", prim.ParticleSystem); + + cmd.Parameters.AddWithValue("OmegaX", (double)prim.AngularVelocity.X); + cmd.Parameters.AddWithValue("OmegaY", (double)prim.AngularVelocity.Y); + cmd.Parameters.AddWithValue("OmegaZ", (double)prim.AngularVelocity.Z); + + cmd.Parameters.AddWithValue("CameraEyeOffsetX", (double)prim.GetCameraEyeOffset().X); + cmd.Parameters.AddWithValue("CameraEyeOffsetY", (double)prim.GetCameraEyeOffset().Y); + cmd.Parameters.AddWithValue("CameraEyeOffsetZ", (double)prim.GetCameraEyeOffset().Z); + + cmd.Parameters.AddWithValue("CameraAtOffsetX", (double)prim.GetCameraAtOffset().X); + cmd.Parameters.AddWithValue("CameraAtOffsetY", (double)prim.GetCameraAtOffset().Y); + cmd.Parameters.AddWithValue("CameraAtOffsetZ", (double)prim.GetCameraAtOffset().Z); + + if (prim.GetForceMouselook()) + cmd.Parameters.AddWithValue("ForceMouselook", 1); + else + cmd.Parameters.AddWithValue("ForceMouselook", 0); + + cmd.Parameters.AddWithValue("ScriptAccessPin", prim.ScriptAccessPin); + + if (prim.AllowedDrop) + cmd.Parameters.AddWithValue("AllowedDrop", 1); + else + cmd.Parameters.AddWithValue("AllowedDrop", 0); + + if (prim.DIE_AT_EDGE) + cmd.Parameters.AddWithValue("DieAtEdge", 1); + else + cmd.Parameters.AddWithValue("DieAtEdge", 0); + + cmd.Parameters.AddWithValue("SalePrice", prim.SalePrice); + cmd.Parameters.AddWithValue("SaleType", unchecked((sbyte)(prim.ObjectSaleType))); + + byte clickAction = prim.ClickAction; + cmd.Parameters.AddWithValue("ClickAction", unchecked((sbyte)(clickAction))); + + cmd.Parameters.AddWithValue("Material", unchecked((sbyte)(prim.Material))); + + cmd.Parameters.AddWithValue("CollisionSound", prim.CollisionSound.ToString()); + cmd.Parameters.AddWithValue("CollisionSoundVolume", prim.CollisionSoundVolume); + + if (prim.PassTouches) + cmd.Parameters.AddWithValue("PassTouches", 1); + else + cmd.Parameters.AddWithValue("PassTouches", 0); + + cmd.Parameters.AddWithValue("LinkNumber", prim.LinkNum); + cmd.Parameters.AddWithValue("MediaURL", prim.MediaUrl); + if (prim.AttachedPos != null) + { + cmd.Parameters.AddWithValue("AttachedPosX", (double)prim.AttachedPos.X); + cmd.Parameters.AddWithValue("AttachedPosY", (double)prim.AttachedPos.Y); + cmd.Parameters.AddWithValue("AttachedPosZ", (double)prim.AttachedPos.Z); + } + + if (prim.KeyframeMotion != null) + cmd.Parameters.AddWithValue("KeyframeMotion", prim.KeyframeMotion.Serialize()); + else + cmd.Parameters.AddWithValue("KeyframeMotion", new Byte[0]); + + if (prim.DynAttrs.CountNamespaces > 0) + cmd.Parameters.AddWithValue("DynAttrs", prim.DynAttrs.ToXml()); + else + cmd.Parameters.AddWithValue("DynAttrs", null); + + cmd.Parameters.AddWithValue("PhysicsShapeType", prim.PhysicsShapeType); + cmd.Parameters.AddWithValue("Density", (double)prim.Density); + cmd.Parameters.AddWithValue("GravityModifier", (double)prim.GravityModifier); + cmd.Parameters.AddWithValue("Friction", (double)prim.Friction); + cmd.Parameters.AddWithValue("Restitution", (double)prim.Restitution); + } + + /// + /// + /// + /// + /// + private static void FillItemCommand(MySqlCommand cmd, TaskInventoryItem taskItem) + { + cmd.Parameters.AddWithValue("itemID", taskItem.ItemID); + cmd.Parameters.AddWithValue("primID", taskItem.ParentPartID); + cmd.Parameters.AddWithValue("assetID", taskItem.AssetID); + cmd.Parameters.AddWithValue("parentFolderID", taskItem.ParentID); + + cmd.Parameters.AddWithValue("invType", taskItem.InvType); + cmd.Parameters.AddWithValue("assetType", taskItem.Type); + + cmd.Parameters.AddWithValue("name", taskItem.Name); + cmd.Parameters.AddWithValue("description", taskItem.Description); + cmd.Parameters.AddWithValue("creationDate", taskItem.CreationDate); + cmd.Parameters.AddWithValue("creatorID", taskItem.CreatorIdentification); + cmd.Parameters.AddWithValue("ownerID", taskItem.OwnerID); + cmd.Parameters.AddWithValue("lastOwnerID", taskItem.LastOwnerID); + cmd.Parameters.AddWithValue("groupID", taskItem.GroupID); + cmd.Parameters.AddWithValue("nextPermissions", taskItem.NextPermissions); + cmd.Parameters.AddWithValue("currentPermissions", taskItem.CurrentPermissions); + cmd.Parameters.AddWithValue("basePermissions", taskItem.BasePermissions); + cmd.Parameters.AddWithValue("everyonePermissions", taskItem.EveryonePermissions); + cmd.Parameters.AddWithValue("groupPermissions", taskItem.GroupPermissions); + cmd.Parameters.AddWithValue("flags", taskItem.Flags); + } + + /// + /// + /// + private static void FillRegionSettingsCommand(MySqlCommand cmd, RegionSettings settings) + { + cmd.Parameters.AddWithValue("RegionUUID", settings.RegionUUID.ToString()); + cmd.Parameters.AddWithValue("BlockTerraform", settings.BlockTerraform); + cmd.Parameters.AddWithValue("BlockFly", settings.BlockFly); + cmd.Parameters.AddWithValue("AllowDamage", settings.AllowDamage); + cmd.Parameters.AddWithValue("RestrictPushing", settings.RestrictPushing); + cmd.Parameters.AddWithValue("AllowLandResell", settings.AllowLandResell); + cmd.Parameters.AddWithValue("AllowLandJoinDivide", settings.AllowLandJoinDivide); + cmd.Parameters.AddWithValue("BlockShowInSearch", settings.BlockShowInSearch); + cmd.Parameters.AddWithValue("AgentLimit", settings.AgentLimit); + cmd.Parameters.AddWithValue("ObjectBonus", settings.ObjectBonus); + cmd.Parameters.AddWithValue("Maturity", settings.Maturity); + cmd.Parameters.AddWithValue("DisableScripts", settings.DisableScripts); + cmd.Parameters.AddWithValue("DisableCollisions", settings.DisableCollisions); + cmd.Parameters.AddWithValue("DisablePhysics", settings.DisablePhysics); + cmd.Parameters.AddWithValue("TerrainTexture1", settings.TerrainTexture1.ToString()); + cmd.Parameters.AddWithValue("TerrainTexture2", settings.TerrainTexture2.ToString()); + cmd.Parameters.AddWithValue("TerrainTexture3", settings.TerrainTexture3.ToString()); + cmd.Parameters.AddWithValue("TerrainTexture4", settings.TerrainTexture4.ToString()); + cmd.Parameters.AddWithValue("Elevation1NW", settings.Elevation1NW); + cmd.Parameters.AddWithValue("Elevation2NW", settings.Elevation2NW); + cmd.Parameters.AddWithValue("Elevation1NE", settings.Elevation1NE); + cmd.Parameters.AddWithValue("Elevation2NE", settings.Elevation2NE); + cmd.Parameters.AddWithValue("Elevation1SE", settings.Elevation1SE); + cmd.Parameters.AddWithValue("Elevation2SE", settings.Elevation2SE); + cmd.Parameters.AddWithValue("Elevation1SW", settings.Elevation1SW); + cmd.Parameters.AddWithValue("Elevation2SW", settings.Elevation2SW); + cmd.Parameters.AddWithValue("WaterHeight", settings.WaterHeight); + cmd.Parameters.AddWithValue("TerrainRaiseLimit", settings.TerrainRaiseLimit); + cmd.Parameters.AddWithValue("TerrainLowerLimit", settings.TerrainLowerLimit); + cmd.Parameters.AddWithValue("UseEstateSun", settings.UseEstateSun); + cmd.Parameters.AddWithValue("Sandbox", settings.Sandbox); + cmd.Parameters.AddWithValue("SunVectorX", settings.SunVector.X); + cmd.Parameters.AddWithValue("SunVectorY", settings.SunVector.Y); + cmd.Parameters.AddWithValue("SunVectorZ", settings.SunVector.Z); + cmd.Parameters.AddWithValue("FixedSun", settings.FixedSun); + cmd.Parameters.AddWithValue("SunPosition", settings.SunPosition); + cmd.Parameters.AddWithValue("Covenant", settings.Covenant.ToString()); + cmd.Parameters.AddWithValue("CovenantChangedDateTime", settings.CovenantChangedDateTime); + cmd.Parameters.AddWithValue("LoadedCreationDateTime", settings.LoadedCreationDateTime); + cmd.Parameters.AddWithValue("LoadedCreationID", settings.LoadedCreationID); + cmd.Parameters.AddWithValue("TerrainImageID", settings.TerrainImageID); + + cmd.Parameters.AddWithValue("ParcelImageID", settings.ParcelImageID); + cmd.Parameters.AddWithValue("TelehubObject", settings.TelehubObject); + } + + /// + /// + /// + /// + /// + /// + private static void FillLandCommand(MySqlCommand cmd, LandData land, UUID regionUUID) + { + cmd.Parameters.AddWithValue("UUID", land.GlobalID.ToString()); + cmd.Parameters.AddWithValue("RegionUUID", regionUUID.ToString()); + cmd.Parameters.AddWithValue("LocalLandID", land.LocalID); + + // Bitmap is a byte[512] + cmd.Parameters.AddWithValue("Bitmap", land.Bitmap); + + cmd.Parameters.AddWithValue("Name", land.Name); + cmd.Parameters.AddWithValue("Description", land.Description); + cmd.Parameters.AddWithValue("OwnerUUID", land.OwnerID.ToString()); + cmd.Parameters.AddWithValue("IsGroupOwned", land.IsGroupOwned); + cmd.Parameters.AddWithValue("Area", land.Area); + cmd.Parameters.AddWithValue("AuctionID", land.AuctionID); //Unemplemented + cmd.Parameters.AddWithValue("Category", land.Category); //Enum libsecondlife.Parcel.ParcelCategory + cmd.Parameters.AddWithValue("ClaimDate", land.ClaimDate); + cmd.Parameters.AddWithValue("ClaimPrice", land.ClaimPrice); + cmd.Parameters.AddWithValue("GroupUUID", land.GroupID.ToString()); + cmd.Parameters.AddWithValue("SalePrice", land.SalePrice); + cmd.Parameters.AddWithValue("LandStatus", land.Status); //Enum. libsecondlife.Parcel.ParcelStatus + cmd.Parameters.AddWithValue("LandFlags", land.Flags); + cmd.Parameters.AddWithValue("LandingType", land.LandingType); + cmd.Parameters.AddWithValue("MediaAutoScale", land.MediaAutoScale); + cmd.Parameters.AddWithValue("MediaTextureUUID", land.MediaID.ToString()); + cmd.Parameters.AddWithValue("MediaURL", land.MediaURL); + cmd.Parameters.AddWithValue("MusicURL", land.MusicURL); + cmd.Parameters.AddWithValue("PassHours", land.PassHours); + cmd.Parameters.AddWithValue("PassPrice", land.PassPrice); + cmd.Parameters.AddWithValue("SnapshotUUID", land.SnapshotID.ToString()); + cmd.Parameters.AddWithValue("UserLocationX", land.UserLocation.X); + cmd.Parameters.AddWithValue("UserLocationY", land.UserLocation.Y); + cmd.Parameters.AddWithValue("UserLocationZ", land.UserLocation.Z); + cmd.Parameters.AddWithValue("UserLookAtX", land.UserLookAt.X); + cmd.Parameters.AddWithValue("UserLookAtY", land.UserLookAt.Y); + cmd.Parameters.AddWithValue("UserLookAtZ", land.UserLookAt.Z); + cmd.Parameters.AddWithValue("AuthBuyerID", land.AuthBuyerID); + cmd.Parameters.AddWithValue("OtherCleanTime", land.OtherCleanTime); + cmd.Parameters.AddWithValue("Dwell", land.Dwell); + cmd.Parameters.AddWithValue("MediaDescription", land.MediaDescription); + cmd.Parameters.AddWithValue("MediaType", land.MediaType); + cmd.Parameters.AddWithValue("MediaWidth", land.MediaWidth); + cmd.Parameters.AddWithValue("MediaHeight", land.MediaHeight); + cmd.Parameters.AddWithValue("MediaLoop", land.MediaLoop); + cmd.Parameters.AddWithValue("ObscureMusic", land.ObscureMusic); + cmd.Parameters.AddWithValue("ObscureMedia", land.ObscureMedia); + } + + /// + /// + /// + /// + /// + /// + private static void FillLandAccessCommand(MySqlCommand cmd, LandAccessEntry entry, UUID parcelID) + { + cmd.Parameters.AddWithValue("LandUUID", parcelID.ToString()); + cmd.Parameters.AddWithValue("AccessUUID", entry.AgentID.ToString()); + cmd.Parameters.AddWithValue("Flags", entry.Flags); + cmd.Parameters.AddWithValue("Expires", entry.Expires.ToString()); + } + + /// + /// + /// + /// + /// + private PrimitiveBaseShape BuildShape(IDataReader row) + { + PrimitiveBaseShape s = new PrimitiveBaseShape(); + s.Scale = new Vector3( + (float)(double)row["ScaleX"], + (float)(double)row["ScaleY"], + (float)(double)row["ScaleZ"] + ); + // paths + s.PCode = (byte)(int)row["PCode"]; + s.PathBegin = (ushort)(int)row["PathBegin"]; + s.PathEnd = (ushort)(int)row["PathEnd"]; + s.PathScaleX = (byte)(int)row["PathScaleX"]; + s.PathScaleY = (byte)(int)row["PathScaleY"]; + s.PathShearX = (byte)(int)row["PathShearX"]; + s.PathShearY = (byte)(int)row["PathShearY"]; + s.PathSkew = (sbyte)(int)row["PathSkew"]; + s.PathCurve = (byte)(int)row["PathCurve"]; + s.PathRadiusOffset = (sbyte)(int)row["PathRadiusOffset"]; + s.PathRevolutions = (byte)(int)row["PathRevolutions"]; + s.PathTaperX = (sbyte)(int)row["PathTaperX"]; + s.PathTaperY = (sbyte)(int)row["PathTaperY"]; + s.PathTwist = (sbyte)(int)row["PathTwist"]; + s.PathTwistBegin = (sbyte)(int)row["PathTwistBegin"]; + // profile + s.ProfileBegin = (ushort)(int)row["ProfileBegin"]; + s.ProfileEnd = (ushort)(int)row["ProfileEnd"]; + s.ProfileCurve = (byte)(int)row["ProfileCurve"]; + s.ProfileHollow = (ushort)(int)row["ProfileHollow"]; + s.TextureEntry = (byte[])row["Texture"]; + + s.ExtraParams = (byte[])row["ExtraParams"]; + + s.State = (byte)(int)row["State"]; + s.LastAttachPoint = (byte)(int)row["LastAttachPoint"]; + + if (!(row["Media"] is System.DBNull)) + s.Media = PrimitiveBaseShape.MediaList.FromXml((string)row["Media"]); + + return s; + } + + /// + /// + /// + /// + /// + private void FillShapeCommand(MySqlCommand cmd, SceneObjectPart prim) + { + PrimitiveBaseShape s = prim.Shape; + cmd.Parameters.AddWithValue("UUID", prim.UUID.ToString()); + // shape is an enum + cmd.Parameters.AddWithValue("Shape", 0); + // vectors + cmd.Parameters.AddWithValue("ScaleX", (double)s.Scale.X); + cmd.Parameters.AddWithValue("ScaleY", (double)s.Scale.Y); + cmd.Parameters.AddWithValue("ScaleZ", (double)s.Scale.Z); + // paths + cmd.Parameters.AddWithValue("PCode", s.PCode); + cmd.Parameters.AddWithValue("PathBegin", s.PathBegin); + cmd.Parameters.AddWithValue("PathEnd", s.PathEnd); + cmd.Parameters.AddWithValue("PathScaleX", s.PathScaleX); + cmd.Parameters.AddWithValue("PathScaleY", s.PathScaleY); + cmd.Parameters.AddWithValue("PathShearX", s.PathShearX); + cmd.Parameters.AddWithValue("PathShearY", s.PathShearY); + cmd.Parameters.AddWithValue("PathSkew", s.PathSkew); + cmd.Parameters.AddWithValue("PathCurve", s.PathCurve); + cmd.Parameters.AddWithValue("PathRadiusOffset", s.PathRadiusOffset); + cmd.Parameters.AddWithValue("PathRevolutions", s.PathRevolutions); + cmd.Parameters.AddWithValue("PathTaperX", s.PathTaperX); + cmd.Parameters.AddWithValue("PathTaperY", s.PathTaperY); + cmd.Parameters.AddWithValue("PathTwist", s.PathTwist); + cmd.Parameters.AddWithValue("PathTwistBegin", s.PathTwistBegin); + // profile + cmd.Parameters.AddWithValue("ProfileBegin", s.ProfileBegin); + cmd.Parameters.AddWithValue("ProfileEnd", s.ProfileEnd); + cmd.Parameters.AddWithValue("ProfileCurve", s.ProfileCurve); + cmd.Parameters.AddWithValue("ProfileHollow", s.ProfileHollow); + cmd.Parameters.AddWithValue("Texture", s.TextureEntry); + cmd.Parameters.AddWithValue("ExtraParams", s.ExtraParams); + cmd.Parameters.AddWithValue("State", s.State); + cmd.Parameters.AddWithValue("LastAttachPoint", s.LastAttachPoint); + cmd.Parameters.AddWithValue("Media", null == s.Media ? null : s.Media.ToXml()); + } + + public void StorePrimInventory(UUID primID, ICollection items) + { + lock (m_dbLock) + { + RemoveItems(primID); + + if (items.Count == 0) + return; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "insert into primitems (" + + "invType, assetType, name, " + + "description, creationDate, nextPermissions, " + + "currentPermissions, basePermissions, " + + "everyonePermissions, groupPermissions, " + + "flags, itemID, primID, assetID, " + + "parentFolderID, creatorID, ownerID, " + + "groupID, lastOwnerID) values (?invType, " + + "?assetType, ?name, ?description, " + + "?creationDate, ?nextPermissions, " + + "?currentPermissions, ?basePermissions, " + + "?everyonePermissions, ?groupPermissions, " + + "?flags, ?itemID, ?primID, ?assetID, " + + "?parentFolderID, ?creatorID, ?ownerID, " + + "?groupID, ?lastOwnerID)"; + + foreach (TaskInventoryItem item in items) + { + cmd.Parameters.Clear(); + + FillItemCommand(cmd, item); + + ExecuteNonQuery(cmd); + } + } + } + } + } + + private void LoadSpawnPoints(RegionSettings rs) + { + rs.ClearSpawnPoints(); + + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select Yaw, Pitch, Distance from spawn_points where RegionID = ?RegionID"; + cmd.Parameters.AddWithValue("?RegionID", rs.RegionUUID.ToString()); + + using (IDataReader r = cmd.ExecuteReader()) + { + while (r.Read()) + { + SpawnPoint sp = new SpawnPoint(); + + sp.Yaw = (float)r["Yaw"]; + sp.Pitch = (float)r["Pitch"]; + sp.Distance = (float)r["Distance"]; + + rs.AddSpawnPoint(sp); + } + } + } + } + } + } + + private void SaveSpawnPoints(RegionSettings rs) + { + lock (m_dbLock) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from spawn_points where RegionID = ?RegionID"; + cmd.Parameters.AddWithValue("?RegionID", rs.RegionUUID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into spawn_points (RegionID, Yaw, Pitch, Distance) values ( ?RegionID, ?Yaw, ?Pitch, ?Distance)"; + + foreach (SpawnPoint p in rs.SpawnPoints()) + { + cmd.Parameters.AddWithValue("?RegionID", rs.RegionUUID.ToString()); + cmd.Parameters.AddWithValue("?Yaw", p.Yaw); + cmd.Parameters.AddWithValue("?Pitch", p.Pitch); + cmd.Parameters.AddWithValue("?Distance", p.Distance); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + } + } + + public void SaveExtra(UUID regionID, string name, string val) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "replace into regionextra values (?RegionID, ?Name, ?value)"; + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue("?Name", name); + cmd.Parameters.AddWithValue("?value", val); + + cmd.ExecuteNonQuery(); + } + } + } + + public void RemoveExtra(UUID regionID, string name) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "delete from regionextra where RegionID=?RegionID and Name=?Name"; + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue("?Name", name); + + cmd.ExecuteNonQuery(); + } + } + } + + public Dictionary GetExtra(UUID regionID) + { + Dictionary ret = new Dictionary(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = "select * from regionextra where RegionID=?RegionID"; + cmd.Parameters.AddWithValue("?RegionID", regionID.ToString()); + using (IDataReader r = cmd.ExecuteReader()) + { + while (r.Read()) + { + ret[r["Name"].ToString()] = r["value"].ToString(); + } + } + } + } + + return ret; + } + } +} diff --git a/OpenSim/Data/MySQL/MySQLUserAccountData.cs b/OpenSim/Data/MySQL/MySQLUserAccountData.cs new file mode 100644 index 0000000000..e964295e5f --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLUserAccountData.cs @@ -0,0 +1,85 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using MySql.Data.MySqlClient; + +namespace OpenSim.Data.MySQL +{ + public class MySqlUserAccountData : MySQLGenericTableHandler, IUserAccountData + { + public MySqlUserAccountData(string connectionString, string realm) + : base(connectionString, realm, "UserAccount") + { + } + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { + string[] words = query.Split(new char[] {' '}); + + for (int i = 0 ; i < words.Length ; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + using (MySqlCommand cmd = new MySqlCommand()) + { + if (words.Length == 1) + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID=?ScopeID or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like ?search or LastName like ?search)", m_Realm); + cmd.Parameters.AddWithValue("?search", "%" + words[0] + "%"); + cmd.Parameters.AddWithValue("?ScopeID", scopeID.ToString()); + } + else + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID=?ScopeID or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like ?searchFirst or LastName like ?searchLast)", m_Realm); + cmd.Parameters.AddWithValue("?searchFirst", "%" + words[0] + "%"); + cmd.Parameters.AddWithValue("?searchLast", "%" + words[1] + "%"); + cmd.Parameters.AddWithValue("?ScopeID", scopeID.ToString()); + } + + return DoQuery(cmd); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLUserProfilesData.cs b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs new file mode 100644 index 0000000000..b35595d7d8 --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLUserProfilesData.cs @@ -0,0 +1,1086 @@ +/* + * 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.Data; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; + +namespace OpenSim.Data.MySQL +{ + public class UserProfilesData: IProfilesData + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Properites + string ConnectionString + { + get; set; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #endregion Properties + + #region class Member Functions + public UserProfilesData(string connectionString) + { + ConnectionString = connectionString; + Init(); + } + + void Init() + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "UserProfiles"); + m.Update(); + } + } + #endregion Member Functions + + #region Classifieds Queries + /// + /// Gets the classified records. + /// + /// + /// Array of classified records + /// + /// + /// Creator identifier. + /// + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = ?Id"; + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", creatorId); + using( MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + } + } + } + } + return data; + } + + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + + query += "INSERT INTO classifieds ("; + query += "`classifieduuid`,"; + query += "`creatoruuid`,"; + query += "`creationdate`,"; + query += "`expirationdate`,"; + query += "`category`,"; + query += "`name`,"; + query += "`description`,"; + query += "`parceluuid`,"; + query += "`parentestate`,"; + query += "`snapshotuuid`,"; + query += "`simname`,"; + query += "`posglobal`,"; + query += "`parcelname`,"; + query += "`classifiedflags`,"; + query += "`priceforlisting`) "; + query += "VALUES ("; + query += "?ClassifiedId,"; + query += "?CreatorId,"; + query += "?CreatedDate,"; + query += "?ExpirationDate,"; + query += "?Category,"; + query += "?Name,"; + query += "?Description,"; + query += "?ParcelId,"; + query += "?ParentEstate,"; + query += "?SnapshotId,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?ParcelName,"; + query += "?Flags,"; + query += "?ListingPrice ) "; + query += "ON DUPLICATE KEY UPDATE "; + query += "category=?Category, "; + query += "expirationdate=?ExpirationDate, "; + query += "name=?Name, "; + query += "description=?Description, "; + query += "parentestate=?ParentEstate, "; + query += "posglobal=?GlobalPos, "; + query += "parcelname=?ParcelName, "; + query += "classifiedflags=?Flags, "; + query += "priceforlisting=?ListingPrice, "; + query += "snapshotuuid=?SnapshotId"; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue("?ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue("?Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue("?Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue("?Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue("?SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue("?ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue("?Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue("?ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query += "DELETE FROM classifieds WHERE "; + query += "classifieduuid = ?recordId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?recordId", recordId.ToString()); + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = ?AdId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?AdId", ad.ClassifiedId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = new UUID(reader.GetGuid("creatoruuid")); + ad.ParcelId = new UUID(reader.GetGuid("parceluuid")); + ad.SnapshotId = new UUID(reader.GetGuid("snapshotuuid")); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte)reader.GetUInt32("classifiedflags"); + ad.Category = reader.GetInt32("category"); + ad.Price = reader.GetInt16("priceforlisting"); + ad.Name = reader.GetString("name"); + ad.Description = reader.GetString("description"); + ad.SimName = reader.GetString("simname"); + ad.GlobalPos = reader.GetString("posglobal"); + ad.ParcelName = reader.GetString("parcelname"); + + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + #endregion Classifieds Queries + + #region Picks Queries + public OSDArray GetAvatarPicks(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT `pickuuid`,`name` FROM userpicks WHERE "; + query += "creatoruuid = ?Id"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = ?CreatorId AND "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + pick.Gatekeeper = (string)reader["gatekeeper"]; + bool.TryParse((string)reader["toppick"], out pick.TopPick); + bool.TryParse((string)reader["enabled"], out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.ParcelName = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query += "INSERT INTO userpicks VALUES ("; + query += "?PickId,"; + query += "?CreatorId,"; + query += "?TopPick,"; + query += "?ParcelId,"; + query += "?Name,"; + query += "?Desc,"; + query += "?SnapshotId,"; + query += "?User,"; + query += "?Original,"; + query += "?SimName,"; + query += "?GlobalPos,"; + query += "?SortOrder,"; + query += "?Enabled,"; + query += "?Gatekeeper)"; + query += "ON DUPLICATE KEY UPDATE "; + query += "parceluuid=?ParcelId,"; + query += "name=?Name,"; + query += "description=?Desc,"; + query += "user=?User,"; + query += "simname=?SimName,"; + query += "snapshotuuid=?SnapshotId,"; + query += "pickuuid=?PickId,"; + query += "posglobal=?GlobalPos,"; + query += "gatekeeper=?Gatekeeper"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue("?CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue("?TopPick", pick.TopPick.ToString()); + cmd.Parameters.AddWithValue("?ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue("?Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue("?Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue("?SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue("?User", pick.ParcelName.ToString()); + cmd.Parameters.AddWithValue("?Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue("?SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue("?GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue("?Gatekeeper",pick.Gatekeeper); + cmd.Parameters.AddWithValue("?SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue("?Enabled", pick.Enabled.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = ?PickId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?PickId", pickId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + #endregion Picks Queries + + #region Avatar Notes Queries + public bool GetAvatarNotes(ref UserProfileNotes notes) + { // WIP + string query = string.Empty; + + query += "SELECT `notes` FROM usernotes WHERE "; + query += "useruuid = ?Id AND "; + query += "targetuuid = ?TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue("?TargetId", notes.TargetId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + notes.Notes = OSD.FromString((string)reader["notes"]); + } + else + { + notes.Notes = OSD.FromString(""); + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=?UserId AND "; + query += "targetuuid=?TargetId"; + } + else + { + remove = false; + query += "INSERT INTO usernotes VALUES ( "; + query += "?UserId,"; + query += "?TargetId,"; + query += "?Notes )"; + query += "ON DUPLICATE KEY "; + query += "UPDATE "; + query += "notes=?Notes"; + } + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + if(!remove) + cmd.Parameters.AddWithValue("?Notes", note.Notes); + cmd.Parameters.AddWithValue("?TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue("?UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + + } + #endregion Avatar Notes Queries + + #region Avatar Properties + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = ?Id"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += "?userId, "; + query += "?profilePartner, "; + query += "?profileAllowPublish, "; + query += "?profileMaturePublish, "; + query += "?profileURL, "; + query += "?profileWantToMask, "; + query += "?profileWantToText, "; + query += "?profileSkillsMask, "; + query += "?profileSkillsText, "; + query += "?profileLanguages, "; + query += "?profileImage, "; + query += "?profileAboutText, "; + query += "?profileFirstImage, "; + query += "?profileFirstText)"; + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?userId", props.UserId.ToString()); + put.Parameters.AddWithValue("?profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue("?profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue("?profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue("?profileURL", props.WebUrl); + put.Parameters.AddWithValue("?profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue("?profileWantToText", props.WantToText); + put.Parameters.AddWithValue("?profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue("?profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue("?profileLanguages", props.Language); + put.Parameters.AddWithValue("?profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue("?profileAboutText", props.AboutText); + put.Parameters.AddWithValue("?profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue("?profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": Requst properties exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileURL=?profileURL, "; + query += "profileImage=?image, "; + query += "profileAboutText=?abouttext,"; + query += "profileFirstImage=?firstlifeimage,"; + query += "profileFirstText=?firstlifetext "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?profileURL", props.WebUrl); + cmd.Parameters.AddWithValue("?image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue("?abouttext", props.AboutText); + cmd.Parameters.AddWithValue("?firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue("?firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue("?uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + #endregion Avatar Properties + + #region Avatar Interests + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=?WantMask, "; + query += "profileWantToText=?WantText,"; + query += "profileSkillsMask=?SkillsMask,"; + query += "profileSkillsText=?SkillsText, "; + query += "profileLanguages=?Languages "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?WantMask", up.WantToMask); + cmd.Parameters.AddWithValue("?WantText", up.WantToText); + cmd.Parameters.AddWithValue("?SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue("?SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue("?Languages", up.Language); + cmd.Parameters.AddWithValue("?uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + #endregion Avatar Interests + + public OSDArray GetUserImageAssets(UUID avatarId) + { + OSDArray data = new OSDArray(); + string query = "SELECT `snapshotuuid` FROM {0} WHERE `creatoruuid` = ?Id"; + + // Get classified image assets + + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`classifieds`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = ?Id"; + + using (MySqlCommand cmd = new MySqlCommand(string.Format (query,"`userpicks`"), dbcon)) + { + cmd.Parameters.AddWithValue("?Id", avatarId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + + #region User Preferences + public bool GetUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = ?Id"; + + OSDArray data = new OSDArray(); + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", pref.UserId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader()) + { + if(reader.HasRows) + { + reader.Read(); + bool.TryParse((string)reader["imviaemail"], out pref.IMViaEmail); + bool.TryParse((string)reader["visible"], out pref.Visible); + pref.EMail = (string)reader["email"]; + } + else + { + dbcon.Close(); + dbcon.Open(); + + query = "INSERT INTO usersettings VALUES "; + query += "(?uuid,'false','false', ?Email)"; + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + + put.Parameters.AddWithValue("?Email", pref.EMail); + put.Parameters.AddWithValue("?uuid", pref.UserId.ToString()); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "UPDATE usersettings SET "; + query += "imviaemail=?ImViaEmail, "; + query += "visible=?Visible, "; + query += "email=?EMail "; + query += "WHERE useruuid=?uuid"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?ImViaEmail", pref.IMViaEmail.ToString().ToLower()); + cmd.Parameters.AddWithValue("?Visible", pref.Visible.ToString().ToLower()); + cmd.Parameters.AddWithValue("?uuid", pref.UserId.ToString()); + cmd.Parameters.AddWithValue("?EMail", pref.EMail.ToString().ToLower()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UserPreferencesUpdate exception {0} {1}", e.Message, e.InnerException); + result = e.Message; + return false; + } + return true; + } + #endregion User Preferences + + #region Integration + public bool GetUserAppData(ref UserAppData props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM `userdata` WHERE "; + query += "UserId = ?Id AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue ("?TagId", props.TagId.ToString()); + + using (MySqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.HasRows) + { + reader.Read(); + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += "?UserId,"; + query += "?TagId,"; + query += "?DataKey,"; + query += "?DataVal) "; + + using (MySqlCommand put = new MySqlCommand(query, dbcon)) + { + put.Parameters.AddWithValue("?UserId", props.UserId.ToString()); + put.Parameters.AddWithValue("?TagId", props.TagId.ToString()); + put.Parameters.AddWithValue("?DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue("?DataVal", props.DataVal.ToString()); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "TagId = ?TagId, "; + query += "DataKey = ?DataKey, "; + query += "DataVal = ?DataVal WHERE "; + query += "UserId = ?UserId AND "; + query += "TagId = ?TagId"; + + try + { + using (MySqlConnection dbcon = new MySqlConnection(ConnectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(query, dbcon)) + { + cmd.Parameters.AddWithValue("?UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue("?TagId", props.TagId.ToString()); + cmd.Parameters.AddWithValue("?DataKey", props.DataKey.ToString()); + cmd.Parameters.AddWithValue("?DataVal", props.DataKey.ToString()); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + #endregion Integration + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLXAssetData.cs b/OpenSim/Data/MySQL/MySQLXAssetData.cs new file mode 100644 index 0000000000..af7e87672d --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLXAssetData.cs @@ -0,0 +1,503 @@ +/* + * 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.Data; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.MySQL +{ + public class MySQLXAssetData : IXAssetDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + /// + /// Number of days that must pass before we update the access time on an asset when it has been fetched. + /// + private const int DaysBetweenAccessTimeUpdates = 30; + + private bool m_enableCompression = false; + private string m_connectionString; + + /// + /// We can reuse this for all hashing since all methods are single-threaded through m_dbBLock + /// + private HashAlgorithm hasher = new SHA256CryptoServiceProvider(); + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + /// + /// Initialises Asset interface + /// + /// + /// Loads and initialises the MySQL storage plugin. + /// Warns and uses the obsolete mysql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public void Initialise(string connect) + { + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: THIS PLUGIN IS STRICTLY EXPERIMENTAL."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: DO NOT USE FOR ANY DATA THAT YOU DO NOT MIND LOSING."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: DATABASE TABLES CAN CHANGE AT ANY TIME, CAUSING EXISTING DATA TO BE LOST."); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[MYSQL XASSETDATA]: ***********************************************************"); + + m_connectionString = connect; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "XAssetStore"); + m.Update(); + } + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + public void Dispose() { } + + /// + /// The name of this DB provider + /// + public string Name + { + get { return "MySQL XAsset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from database + /// + /// Asset UUID to fetch + /// Return the asset + /// On failure : throw an exception and attempt to reconnect to database + public AssetBase GetAsset(UUID assetID) + { +// m_log.DebugFormat("[MYSQL XASSET DATA]: Looking for asset {0}", assetID); + + AssetBase asset = null; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand( + "SELECT Name, Description, AccessTime, AssetType, Local, Temporary, AssetFlags, CreatorID, Data FROM XAssetsMeta JOIN XAssetsData ON XAssetsMeta.Hash = XAssetsData.Hash WHERE ID=?ID", + dbcon)) + { + cmd.Parameters.AddWithValue("?ID", assetID.ToString()); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { + asset = new AssetBase(assetID, (string)dbReader["Name"], (sbyte)dbReader["AssetType"], dbReader["CreatorID"].ToString()); + asset.Data = (byte[])dbReader["Data"]; + asset.Description = (string)dbReader["Description"]; + + string local = dbReader["Local"].ToString(); + if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + asset.Local = true; + else + asset.Local = false; + + asset.Temporary = Convert.ToBoolean(dbReader["Temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); + + if (m_enableCompression) + { + using (GZipStream decompressionStream = new GZipStream(new MemoryStream(asset.Data), CompressionMode.Decompress)) + { + MemoryStream outputStream = new MemoryStream(); + WebUtil.CopyStream(decompressionStream, outputStream, int.MaxValue); +// int compressedLength = asset.Data.Length; + asset.Data = outputStream.ToArray(); + +// m_log.DebugFormat( +// "[XASSET DB]: Decompressed {0} {1} to {2} bytes from {3}", +// asset.ID, asset.Name, asset.Data.Length, compressedLength); + } + } + + UpdateAccessTime(asset.Metadata, (int)dbReader["AccessTime"]); + } + } + } + catch (Exception e) + { + m_log.Error(string.Format("[MYSQL XASSET DATA]: Failure fetching asset {0}", assetID), e); + } + } + } + + return asset; + } + + /// + /// Create an asset in database, or update it if existing. + /// + /// Asset UUID to create + /// On failure : Throw an exception and attempt to reconnect to database + public void StoreAsset(AssetBase asset) + { +// m_log.DebugFormat("[XASSETS DB]: Storing asset {0} {1}", asset.Name, asset.ID); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlTransaction transaction = dbcon.BeginTransaction()) + { + string assetName = asset.Name; + if (asset.Name.Length > AssetBase.MAX_ASSET_NAME) + { + assetName = asset.Name.Substring(0, AssetBase.MAX_ASSET_NAME); + m_log.WarnFormat( + "[XASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > AssetBase.MAX_ASSET_DESC) + { + assetDescription = asset.Description.Substring(0, AssetBase.MAX_ASSET_DESC); + m_log.WarnFormat( + "[XASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + if (m_enableCompression) + { + MemoryStream outputStream = new MemoryStream(); + + using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false)) + { +// Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue)); + // We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream. + compressionStream.Close(); + byte[] compressedData = outputStream.ToArray(); + asset.Data = compressedData; + } + } + + byte[] hash = hasher.ComputeHash(asset.Data); + +// m_log.DebugFormat( +// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}", +// asset.ID, asset.Name, hash, compressedData.Length); + + try + { + using (MySqlCommand cmd = + new MySqlCommand( + "replace INTO XAssetsMeta(ID, Hash, Name, Description, AssetType, Local, Temporary, CreateTime, AccessTime, AssetFlags, CreatorID)" + + "VALUES(?ID, ?Hash, ?Name, ?Description, ?AssetType, ?Local, ?Temporary, ?CreateTime, ?AccessTime, ?AssetFlags, ?CreatorID)", + dbcon)) + { + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.AddWithValue("?ID", asset.ID); + cmd.Parameters.AddWithValue("?Hash", hash); + cmd.Parameters.AddWithValue("?Name", assetName); + cmd.Parameters.AddWithValue("?Description", assetDescription); + cmd.Parameters.AddWithValue("?AssetType", asset.Type); + cmd.Parameters.AddWithValue("?Local", asset.Local); + cmd.Parameters.AddWithValue("?Temporary", asset.Temporary); + cmd.Parameters.AddWithValue("?CreateTime", now); + cmd.Parameters.AddWithValue("?AccessTime", now); + cmd.Parameters.AddWithValue("?CreatorID", asset.Metadata.CreatorID); + cmd.Parameters.AddWithValue("?AssetFlags", (int)asset.Flags); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[ASSET DB]: MySQL failure creating asset metadata {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + + if (!ExistsData(dbcon, transaction, hash)) + { + try + { + using (MySqlCommand cmd = + new MySqlCommand( + "INSERT INTO XAssetsData(Hash, Data) VALUES(?Hash, ?Data)", + dbcon)) + { + cmd.Parameters.AddWithValue("?Hash", hash); + cmd.Parameters.AddWithValue("?Data", asset.Data); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[XASSET DB]: MySQL failure creating asset data {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + } + + transaction.Commit(); + } + } + } + + /// + /// Updates the access time of the asset if it was accessed above a given threshhold amount of time. + /// + /// + /// This gives us some insight into assets which haven't ben accessed for a long period. This is only done + /// over the threshold time to avoid excessive database writes as assets are fetched. + /// + /// + /// + private void UpdateAccessTime(AssetMetadata assetMetadata, int accessTime) + { + DateTime now = DateTime.UtcNow; + + if ((now - Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates) + return; + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + MySqlCommand cmd = + new MySqlCommand("update XAssetsMeta set AccessTime=?AccessTime where ID=?ID", dbcon); + + try + { + using (cmd) + { + // create unix epoch time + cmd.Parameters.AddWithValue("?ID", assetMetadata.ID); + cmd.Parameters.AddWithValue("?AccessTime", (int)Utils.DateTimeToUnixTime(now)); + cmd.ExecuteNonQuery(); + } + } + catch (Exception) + { + m_log.ErrorFormat( + "[XASSET MYSQL DB]: Failure updating access_time for asset {0} with name {1}", + assetMetadata.ID, assetMetadata.Name); + } + } + } + + /// + /// We assume we already have the m_dbLock. + /// + /// TODO: need to actually use the transaction. + /// + /// + /// + /// + private bool ExistsData(MySqlConnection dbcon, MySqlTransaction transaction, byte[] hash) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool exists = false; + + using (MySqlCommand cmd = new MySqlCommand("SELECT Hash FROM XAssetsData WHERE Hash=?Hash", dbcon)) + { + cmd.Parameters.AddWithValue("?Hash", hash); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + exists = true; + } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSETS DB]: MySql failure in ExistsData fetching hash {0}. Exception {1}{2}", + hash, e.Message, e.StackTrace); + } + } + + return exists; + } + + /// + /// Check if the assets exist in the database. + /// + /// The asset UUID's + /// For each asset: true if it exists, false otherwise + public bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exists = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format("SELECT ID FROM assets WHERE ID IN ({0})", ids); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + using (MySqlCommand cmd = new MySqlCommand(sql, dbcon)) + { + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + UUID id = DBGuid.FromDB(dbReader["ID"]); + exists.Add(id); + } + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exists.Contains(uuids[i]); + return results; + } + + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + MySqlCommand cmd = new MySqlCommand("SELECT Name, Description, AccessTime, AssetType, Temporary, ID, AssetFlags, CreatorID FROM XAssetsMeta LIMIT ?start, ?count", dbcon); + cmd.Parameters.AddWithValue("?start", start); + cmd.Parameters.AddWithValue("?count", count); + + try + { + using (MySqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.Name = (string)dbReader["Name"]; + metadata.Description = (string)dbReader["Description"]; + metadata.Type = (sbyte)dbReader["AssetType"]; + metadata.Temporary = Convert.ToBoolean(dbReader["Temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["AssetFlags"]); + metadata.FullID = DBGuid.FromDB(dbReader["ID"]); + metadata.CreatorID = dbReader["CreatorID"].ToString(); + + // We'll ignore this for now - it appears unused! +// metadata.SHA1 = dbReader["hash"]); + + UpdateAccessTime(metadata, (int)dbReader["AccessTime"]); + + retList.Add(metadata); + } + } + } + catch (Exception e) + { + m_log.Error("[XASSETS DB]: MySql failure fetching asset set" + Environment.NewLine + e.ToString()); + } + } + + return retList; + } + + public bool Delete(string id) + { +// m_log.DebugFormat("[XASSETS DB]: Deleting asset {0}", id); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand("delete from XAssetsMeta where ID=?ID", dbcon)) + { + cmd.Parameters.AddWithValue("?ID", id); + cmd.ExecuteNonQuery(); + } + + // TODO: How do we deal with data from deleted assets? Probably not easily reapable unless we + // keep a reference count (?) + } + + return true; + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/MySQLXInventoryData.cs b/OpenSim/Data/MySQL/MySQLXInventoryData.cs new file mode 100644 index 0000000000..c74033eefc --- /dev/null +++ b/OpenSim/Data/MySQL/MySQLXInventoryData.cs @@ -0,0 +1,335 @@ +/* + * 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.Data; +using System.Linq; +using System.Reflection; +using log4net; +using MySql.Data.MySqlClient; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.MySQL +{ + /// + /// A MySQL Interface for the Asset Server + /// + public class MySQLXInventoryData : IXInventoryData + { + private MySqlFolderHandler m_Folders; + private MySqlItemHandler m_Items; + + public MySQLXInventoryData(string conn, string realm) + { + m_Folders = new MySqlFolderHandler( + conn, "inventoryfolders", "InventoryStore"); + m_Items = new MySqlItemHandler( + conn, "inventoryitems", String.Empty); + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { + return m_Folders.Get(fields, vals); + } + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { + return m_Items.Get(fields, vals); + } + + public bool StoreFolder(XInventoryFolder folder) + { + if (folder.folderName.Length > 64) + folder.folderName = folder.folderName.Substring(0, 64); + + return m_Folders.Store(folder); + } + + public bool StoreItem(XInventoryItem item) + { + if (item.inventoryName.Length > 64) + item.inventoryName = item.inventoryName.Substring(0, 64); + if (item.inventoryDescription.Length > 128) + item.inventoryDescription = item.inventoryDescription.Substring(0, 128); + + return m_Items.Store(item); + } + + public bool DeleteFolders(string field, string val) + { + return m_Folders.Delete(field, val); + } + + public bool DeleteFolders(string[] fields, string[] vals) + { + return m_Folders.Delete(fields, vals); + } + + public bool DeleteItems(string field, string val) + { + return m_Items.Delete(field, val); + } + + public bool DeleteItems(string[] fields, string[] vals) + { + return m_Items.Delete(fields, vals); + } + + public bool MoveItem(string id, string newParent) + { + return m_Items.MoveItem(id, newParent); + } + + public bool MoveFolder(string id, string newParent) + { + return m_Folders.MoveFolder(id, newParent); + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + return m_Items.GetActiveGestures(principalID); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Items.GetAssetPermissions(principalID, assetID); + } + } + + public class MySqlItemHandler : MySqlInventoryHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySqlItemHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public override bool Delete(string field, string val) + { + XInventoryItem[] retrievedItems = Get(new string[] { field }, new string[] { val }); + if (retrievedItems.Length == 0) + return false; + + if (!base.Delete(field, val)) + return false; + + // Don't increment folder version here since Delete(string, string) calls Delete(string[], string[]) +// IncrementFolderVersion(retrievedItems[0].parentFolderID); + + return true; + } + + public override bool Delete(string[] fields, string[] vals) + { + XInventoryItem[] retrievedItems = Get(fields, vals); + if (retrievedItems.Length == 0) + return false; + + if (!base.Delete(fields, vals)) + return false; + + HashSet deletedItemFolderUUIDs = new HashSet(); + + Array.ForEach(retrievedItems, i => deletedItemFolderUUIDs.Add(i.parentFolderID)); + + foreach (UUID deletedItemFolderUUID in deletedItemFolderUUIDs) + IncrementFolderVersion(deletedItemFolderUUID); + + return true; + } + + public bool MoveItem(string id, string newParent) + { + XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id }); + if (retrievedItems.Length == 0) + return false; + + UUID oldParent = retrievedItems[0].parentFolderID; + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("update {0} set parentFolderID = ?ParentFolderID where inventoryID = ?InventoryID", m_Realm); + cmd.Parameters.AddWithValue("?ParentFolderID", newParent); + cmd.Parameters.AddWithValue("?InventoryID", id); + + if (ExecuteNonQuery(cmd) == 0) + return false; + } + + IncrementFolderVersion(oldParent); + IncrementFolderVersion(newParent); + + return true; + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText = String.Format("select * from inventoryitems where avatarId = ?uuid and assetType = ?type and flags & 1", m_Realm); + + cmd.Parameters.AddWithValue("?uuid", principalID.ToString()); + cmd.Parameters.AddWithValue("?type", (int)AssetType.Gesture); + + return DoQuery(cmd); + } + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.Connection = dbcon; + + cmd.CommandText = String.Format("select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions from inventoryitems where avatarID = ?PrincipalID and assetID = ?AssetID group by assetID", m_Realm); + cmd.Parameters.AddWithValue("?PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?AssetID", assetID.ToString()); + + using (IDataReader reader = cmd.ExecuteReader()) + { + + int perms = 0; + + if (reader.Read()) + { + perms = Convert.ToInt32(reader["inventoryCurrentPermissions"]); + } + + return perms; + } + } + } + } + + public override bool Store(XInventoryItem item) + { + if (!base.Store(item)) + return false; + + IncrementFolderVersion(item.parentFolderID); + + return true; + } + } + + public class MySqlFolderHandler : MySqlInventoryHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public MySqlFolderHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveFolder(string id, string newParentFolderID) + { + XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id }); + + if (folders.Length == 0) + return false; + + UUID oldParentFolderUUID = folders[0].parentFolderID; + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.CommandText + = String.Format( + "update {0} set parentFolderID = ?ParentFolderID where folderID = ?folderID", m_Realm); + cmd.Parameters.AddWithValue("?ParentFolderID", newParentFolderID); + cmd.Parameters.AddWithValue("?folderID", id); + + if (ExecuteNonQuery(cmd) == 0) + return false; + } + + IncrementFolderVersion(oldParentFolderUUID); + IncrementFolderVersion(newParentFolderID); + + return true; + } + + public override bool Store(XInventoryFolder folder) + { + if (!base.Store(folder)) + return false; + + IncrementFolderVersion(folder.parentFolderID); + + return true; + } + } + + public class MySqlInventoryHandler : MySQLGenericTableHandler where T: class, new() + { + public MySqlInventoryHandler(string c, string t, string m) : base(c, t, m) {} + + protected bool IncrementFolderVersion(UUID folderID) + { + return IncrementFolderVersion(folderID.ToString()); + } + + protected bool IncrementFolderVersion(string folderID) + { +// m_log.DebugFormat("[MYSQL FOLDER HANDLER]: Incrementing version on folder {0}", folderID); +// Util.PrintCallStack(); + + using (MySqlConnection dbcon = new MySqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (MySqlCommand cmd = new MySqlCommand()) + { + cmd.Connection = dbcon; + + cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID"); + cmd.Parameters.AddWithValue("?folderID", folderID); + + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception) + { + return false; + } + cmd.Dispose(); + } + + dbcon.Close(); + } + + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b46d175865 --- /dev/null +++ b/OpenSim/Data/MySQL/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Data.MySQL")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.MySQL")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("e49826b2-dcef-41be-a5bd-596733fa3304")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Data/MySQL/Resources/AgentPrefs.migrations b/OpenSim/Data/MySQL/Resources/AgentPrefs.migrations new file mode 100644 index 0000000000..e496f724ef --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/AgentPrefs.migrations @@ -0,0 +1,18 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `AgentPrefs` ( + `PrincipalID` CHAR(36) NOT NULL, + `AccessPrefs` CHAR(2) NOT NULL DEFAULT 'M', + `HoverHeight` DOUBLE(30, 27) NOT NULL DEFAULT 0, + `Language` CHAR(5) NOT NULL DEFAULT 'en-us', + `LanguageIsPublic` BOOLEAN NOT NULL DEFAULT 1, + `PermEveryone` INT(6) NOT NULL DEFAULT 0, + `PermGroup` INT(6) NOT NULL DEFAULT 0, + `PermNextOwner` INT(6) NOT NULL DEFAULT 532480, + UNIQUE KEY `PrincipalID` (`PrincipalID`), + PRIMARY KEY(`PrincipalID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/AssetStore.migrations b/OpenSim/Data/MySQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..661d825569 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/AssetStore.migrations @@ -0,0 +1,81 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `assets` ( + `id` binary(16) NOT NULL, + `name` varchar(64) NOT NULL, + `description` varchar(64) NOT NULL, + `assetType` tinyint(4) NOT NULL, + `invType` tinyint(4) NOT NULL, + `local` tinyint(1) NOT NULL, + `temporary` tinyint(1) NOT NULL, + `data` longblob NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; + +COMMIT; + +# ----------------- +:VERSION 2 + +BEGIN; + +ALTER TABLE assets change id oldid binary(16); +ALTER TABLE assets add id varchar(36) not null default ''; +UPDATE assets set id = concat(substr(hex(oldid),1,8),"-",substr(hex(oldid),9,4),"-",substr(hex(oldid),13,4),"-",substr(hex(oldid),17,4),"-",substr(hex(oldid),21,12)); +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 3 + +BEGIN; + +ALTER TABLE assets change id oldid varchar(36); +ALTER TABLE assets add id char(36) not null default '00000000-0000-0000-0000-000000000000'; +UPDATE assets set id = oldid; +ALTER TABLE assets drop oldid; +ALTER TABLE assets add constraint primary key(id); + +COMMIT; + +# ----------------- +:VERSION 4 + +BEGIN; + +ALTER TABLE assets drop InvType; + +COMMIT; + +# ----------------- +:VERSION 5 + +BEGIN; + +ALTER TABLE assets add create_time integer default 0; +ALTER TABLE assets add access_time integer default 0; + +COMMIT; + +# ----------------- +:VERSION 6 + +DELETE FROM assets WHERE id = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621' + +:VERSION 7 + +ALTER TABLE assets ADD COLUMN asset_flags INTEGER NOT NULL DEFAULT 0; + +:VERSION 8 + +ALTER TABLE assets ADD COLUMN CreatorID varchar(128) NOT NULL DEFAULT ''; + +:VERSION 9 + +BEGIN; +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/AuthStore.migrations b/OpenSim/Data/MySQL/Resources/AuthStore.migrations new file mode 100644 index 0000000000..023c786aa5 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/AuthStore.migrations @@ -0,0 +1,39 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE `auth` ( + `UUID` char(36) NOT NULL, + `passwordHash` char(32) NOT NULL default '', + `passwordSalt` char(32) NOT NULL default '', + `webLoginKey` varchar(255) NOT NULL default '', + PRIMARY KEY (`UUID`) +) ENGINE=InnoDB; + +CREATE TABLE `tokens` ( + `UUID` char(36) NOT NULL, + `token` varchar(255) NOT NULL, + `validity` datetime NOT NULL, + UNIQUE KEY `uuid_token` (`UUID`,`token`), + KEY `UUID` (`UUID`), + KEY `token` (`token`), + KEY `validity` (`validity`) +) ENGINE=InnoDB; + +commit; + +:VERSION 2 # ------------------------------- + +BEGIN; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + +:VERSION 3 # ------------------------------- + +BEGIN; + +ALTER TABLE `auth` ADD COLUMN `accountType` VARCHAR(32) NOT NULL DEFAULT 'UserAccount'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/Avatar.migrations b/OpenSim/Data/MySQL/Resources/Avatar.migrations new file mode 100644 index 0000000000..f7cf1766fc --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Avatar.migrations @@ -0,0 +1,20 @@ +:VERSION 1 + +BEGIN; + +CREATE TABLE Avatars ( + PrincipalID CHAR(36) NOT NULL, + Name VARCHAR(32) NOT NULL, + Value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(PrincipalID, Name), + KEY(PrincipalID)); + +COMMIT; + +:VERSION 2 + +BEGIN; + +alter table Avatars change column Value Value text; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/EstateStore.migrations b/OpenSim/Data/MySQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..2d1c2b50ab --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/EstateStore.migrations @@ -0,0 +1,87 @@ +:VERSION 13 + +# The estate migrations used to be in Region store +# here they will do nothing (bad) if the tables are already there, +# just update the store version. + +BEGIN; + +CREATE TABLE IF NOT EXISTS `estate_managers` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_groups` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_users` ( + `EstateID` int(10) unsigned NOT NULL, + `uuid` char(36) NOT NULL, + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estateban` ( + `EstateID` int(10) unsigned NOT NULL, + `bannedUUID` varchar(36) NOT NULL, + `bannedIp` varchar(16) NOT NULL, + `bannedIpHostMask` varchar(16) NOT NULL, + `bannedNameMask` varchar(64) default NULL, + KEY `estateban_EstateID` (`EstateID`) +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS `estate_settings` ( + `EstateID` int(10) unsigned NOT NULL auto_increment, + `EstateName` varchar(64) default NULL, + `AbuseEmailToEstateOwner` tinyint(4) NOT NULL, + `DenyAnonymous` tinyint(4) NOT NULL, + `ResetHomeOnTeleport` tinyint(4) NOT NULL, + `FixedSun` tinyint(4) NOT NULL, + `DenyTransacted` tinyint(4) NOT NULL, + `BlockDwell` tinyint(4) NOT NULL, + `DenyIdentified` tinyint(4) NOT NULL, + `AllowVoice` tinyint(4) NOT NULL, + `UseGlobalTime` tinyint(4) NOT NULL, + `PricePerMeter` int(11) NOT NULL, + `TaxFree` tinyint(4) NOT NULL, + `AllowDirectTeleport` tinyint(4) NOT NULL, + `RedirectGridX` int(11) NOT NULL, + `RedirectGridY` int(11) NOT NULL, + `ParentEstateID` int(10) unsigned NOT NULL, + `SunPosition` double NOT NULL, + `EstateSkipScripts` tinyint(4) NOT NULL, + `BillableFactor` float NOT NULL, + `PublicAccess` tinyint(4) NOT NULL, + `AbuseEmail` varchar(255) not null, + `EstateOwner` varchar(36) not null, + `DenyMinors` tinyint not null, + + PRIMARY KEY (`EstateID`) +) ENGINE=InnoDB AUTO_INCREMENT=100; + +CREATE TABLE IF NOT EXISTS `estate_map` ( + `RegionID` char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `EstateID` int(11) NOT NULL, + PRIMARY KEY (`RegionID`), + KEY `EstateID` (`EstateID`) +) ENGINE=InnoDB; + +COMMIT; + +:VERSION 32 #--------------------- (moved from RegionStore migr, just in case) + +BEGIN; +ALTER TABLE estate_settings AUTO_INCREMENT = 100; +COMMIT; + +:VERSION 33 #--------------------- + +BEGIN; +ALTER TABLE estate_settings ADD COLUMN `AllowLandmark` tinyint(4) NOT NULL default '1'; +ALTER TABLE estate_settings ADD COLUMN `AllowParcelChanges` tinyint(4) NOT NULL default '1'; +ALTER TABLE estate_settings ADD COLUMN `AllowSetHome` tinyint(4) NOT NULL default '1'; +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations b/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations new file mode 100644 index 0000000000..87d08c6c23 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/FSAssetStore.migrations @@ -0,0 +1,18 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `fsassets` ( + `id` char(36) NOT NULL, + `name` varchar(64) NOT NULL DEFAULT '', + `description` varchar(64) NOT NULL DEFAULT '', + `type` int(11) NOT NULL, + `hash` char(80) NOT NULL, + `create_time` int(11) NOT NULL DEFAULT '0', + `access_time` int(11) NOT NULL DEFAULT '0', + `asset_flags` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/MySQL/Resources/FriendsStore.migrations b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..5faf95625f --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/FriendsStore.migrations @@ -0,0 +1,32 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `Friends` ( + `PrincipalID` CHAR(36) NOT NULL, + `Friend` VARCHAR(255) NOT NULL, + `Flags` VARCHAR(16) NOT NULL DEFAULT 0, + `Offered` VARCHAR(32) NOT NULL DEFAULT 0, + PRIMARY KEY(`PrincipalID`, `Friend`), + KEY(`PrincipalID`) +) ENGINE=InnoDB; + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; + +:VERSION 3 # ------------------------- + +BEGIN; + +ALTER TABLE `Friends` MODIFY COLUMN PrincipalID varchar(255) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE `Friends` DROP PRIMARY KEY; +ALTER TABLE `Friends` ADD PRIMARY KEY(PrincipalID(36), Friend(36)); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/GridStore.migrations b/OpenSim/Data/MySQL/Resources/GridStore.migrations new file mode 100644 index 0000000000..98ba8c5604 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/GridStore.migrations @@ -0,0 +1,105 @@ +:VERSION 1 + +CREATE TABLE `regions` ( + `uuid` varchar(36) NOT NULL, + `regionHandle` bigint(20) unsigned NOT NULL, + `regionName` varchar(32) default NULL, + `regionRecvKey` varchar(128) default NULL, + `regionSendKey` varchar(128) default NULL, + `regionSecret` varchar(128) default NULL, + `regionDataURI` varchar(255) default NULL, + `serverIP` varchar(64) default NULL, + `serverPort` int(10) unsigned default NULL, + `serverURI` varchar(255) default NULL, + `locX` int(10) unsigned default NULL, + `locY` int(10) unsigned default NULL, + `locZ` int(10) unsigned default NULL, + `eastOverrideHandle` bigint(20) unsigned default NULL, + `westOverrideHandle` bigint(20) unsigned default NULL, + `southOverrideHandle` bigint(20) unsigned default NULL, + `northOverrideHandle` bigint(20) unsigned default NULL, + `regionAssetURI` varchar(255) default NULL, + `regionAssetRecvKey` varchar(128) default NULL, + `regionAssetSendKey` varchar(128) default NULL, + `regionUserURI` varchar(255) default NULL, + `regionUserRecvKey` varchar(128) default NULL, + `regionUserSendKey` varchar(128) default NULL, `regionMapTexture` varchar(36) default NULL, + `serverHttpPort` int(10) default NULL, `serverRemotingPort` int(10) default NULL, + `owner_uuid` varchar(36) default '00000000-0000-0000-0000-000000000000' not null, + `originUUID` varchar(36), + PRIMARY KEY (`uuid`), + KEY `regionName` (`regionName`), + KEY `regionHandle` (`regionHandle`), + KEY `overrideHandles` (`eastOverrideHandle`,`westOverrideHandle`,`southOverrideHandle`,`northOverrideHandle`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Rev. 3'; + +:VERSION 2 + +BEGIN; + +ALTER TABLE regions add column access integer unsigned default 1; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE regions add column ScopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +create index ScopeID on regions(ScopeID); + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE regions add column sizeX integer not null default 0; +ALTER TABLE regions add column sizeY integer not null default 0; + +COMMIT; + +:VERSION 5 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `flags` integer NOT NULL DEFAULT 0; +CREATE INDEX flags ON regions(flags); + +COMMIT; + +:VERSION 6 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `last_seen` integer NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 7 + +BEGIN; + +ALTER TABLE `regions` ADD COLUMN `PrincipalID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE `regions` ADD COLUMN `Token` varchar(255) NOT NULL; + +COMMIT; + + +:VERSION 8 # ------------ + +BEGIN; + +alter table regions modify column regionName varchar(128) default NULL; + +COMMIT; + +:VERSION 9 # ------------ + +BEGIN; + +alter table regions add column `parcelMapTexture` varchar(36) default NULL; + +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/GridUserStore.migrations b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations new file mode 100644 index 0000000000..d08e096364 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/GridUserStore.migrations @@ -0,0 +1,24 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `GridUser` ( + `UserID` VARCHAR(255) NOT NULL, + `HomeRegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `HomePosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `HomeLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LastRegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `LastPosition` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `LastLookAt` CHAR(64) NOT NULL DEFAULT '<0,0,0>', + `Online` CHAR(5) NOT NULL DEFAULT 'false', + `Login` CHAR(16) NOT NULL DEFAULT '0', + `Logout` CHAR(16) NOT NULL DEFAULT '0', + PRIMARY KEY (`UserID`) +) ENGINE=InnoDB; + +COMMIT; + +:VERSION 2 # -------------------------- +BEGIN; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations b/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..b4e4422871 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/HGTravelStore.migrations @@ -0,0 +1,18 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `hg_traveling_data` ( + `SessionID` VARCHAR(36) NOT NULL, + `UserID` VARCHAR(36) NOT NULL, + `GridExternalName` VARCHAR(255) NOT NULL DEFAULT '', + `ServiceToken` VARCHAR(255) NOT NULL DEFAULT '', + `ClientIPAddress` VARCHAR(16) NOT NULL DEFAULT '', + `MyIPAddress` VARCHAR(16) NOT NULL DEFAULT '', + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`SessionID`), + KEY (`UserID`) +) ENGINE=InnoDB; + +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/IM_Store.migrations b/OpenSim/Data/MySQL/Resources/IM_Store.migrations new file mode 100644 index 0000000000..79ead98cce --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/IM_Store.migrations @@ -0,0 +1,42 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `im_offline` ( + `ID` MEDIUMINT NOT NULL AUTO_INCREMENT, + `PrincipalID` char(36) NOT NULL default '', + `Message` text NOT NULL, + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`ID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +INSERT INTO `im_offline` SELECT * from `diva_im_offline`; +DROP TABLE `diva_im_offline`; +DELETE FROM `migrations` WHERE name='diva_im_Store'; + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +ALTER TABLE `im_offline` + ADD `FromID` char(36) NOT NULL default '' AFTER `PrincipalID`, + ADD KEY `FromID` (`FromID`); + +COMMIT; + +:VERSION 4 # -------------------------- + +BEGIN; + +ALTER TABLE im_offline CONVERT TO CHARACTER SET utf8; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/InventoryStore.migrations b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..993a5a0f1f --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/InventoryStore.migrations @@ -0,0 +1,109 @@ +:VERSION 1 # ------------ +BEGIN; + +CREATE TABLE `inventoryfolders` ( + `folderID` varchar(36) NOT NULL default '', + `agentID` varchar(36) default NULL, + `parentFolderID` varchar(36) default NULL, + `folderName` varchar(64) default NULL, + `type` smallint NOT NULL default 0, + `version` int NOT NULL default 0, + PRIMARY KEY (`folderID`), + KEY `owner` (`agentID`), + KEY `parent` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `inventoryitems` ( + `inventoryID` varchar(36) NOT NULL default '', + `assetID` varchar(36) default NULL, + `assetType` int(11) default NULL, + `parentFolderID` varchar(36) default NULL, + `avatarID` varchar(36) default NULL, + `inventoryName` varchar(64) default NULL, + `inventoryDescription` varchar(128) default NULL, + `inventoryNextPermissions` int(10) unsigned default NULL, + `inventoryCurrentPermissions` int(10) unsigned default NULL, + `invType` int(11) default NULL, + `creatorID` varchar(36) default NULL, + `inventoryBasePermissions` int(10) unsigned NOT NULL default 0, + `inventoryEveryOnePermissions` int(10) unsigned NOT NULL default 0, + `salePrice` int(11) NOT NULL default 0, + `saleType` tinyint(4) NOT NULL default 0, + `creationDate` int(11) NOT NULL default 0, + `groupID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + `groupOwned` tinyint(4) NOT NULL default 0, + `flags` int(11) unsigned NOT NULL default 0, + PRIMARY KEY (`inventoryID`), + KEY `owner` (`avatarID`), + KEY `folder` (`parentFolderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------ + +BEGIN; + +ALTER TABLE inventoryfolders change folderID folderIDold varchar(36); +ALTER TABLE inventoryfolders change agentID agentIDold varchar(36); +ALTER TABLE inventoryfolders change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryfolders add folderID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryfolders add agentID char(36) default NULL; +ALTER TABLE inventoryfolders add parentFolderID char(36) default NULL; +UPDATE inventoryfolders set folderID = folderIDold, agentID = agentIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryfolders drop folderIDold; +ALTER TABLE inventoryfolders drop agentIDold; +ALTER TABLE inventoryfolders drop parentFolderIDold; +ALTER TABLE inventoryfolders add constraint primary key(folderID); +ALTER TABLE inventoryfolders add index inventoryfolders_agentid(agentID); +ALTER TABLE inventoryfolders add index inventoryfolders_parentFolderid(parentFolderID); + +ALTER TABLE inventoryitems change inventoryID inventoryIDold varchar(36); +ALTER TABLE inventoryitems change avatarID avatarIDold varchar(36); +ALTER TABLE inventoryitems change parentFolderID parentFolderIDold varchar(36); +ALTER TABLE inventoryitems add inventoryID char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE inventoryitems add avatarID char(36) default NULL; +ALTER TABLE inventoryitems add parentFolderID char(36) default NULL; +UPDATE inventoryitems set inventoryID = inventoryIDold, avatarID = avatarIDold, parentFolderID = parentFolderIDold; +ALTER TABLE inventoryitems drop inventoryIDold; +ALTER TABLE inventoryitems drop avatarIDold; +ALTER TABLE inventoryitems drop parentFolderIDold; +ALTER TABLE inventoryitems add constraint primary key(inventoryID); +ALTER TABLE inventoryitems add index inventoryitems_avatarid(avatarID); +ALTER TABLE inventoryitems add index inventoryitems_parentFolderid(parentFolderID); + +COMMIT; + +:VERSION 3 # ------------ + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 # ------------ + +BEGIN; + +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID is NULL; +update inventoryitems set creatorID = '00000000-0000-0000-0000-000000000000' where creatorID = ''; +alter table inventoryitems modify column creatorID varchar(36) not NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 # ------------ + +BEGIN; + +alter table inventoryitems modify column creatorID varchar(128) not NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 6 # ------------ + +BEGIN; + +alter table inventoryitems modify column creatorID varchar(255) not NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/LogStore.migrations b/OpenSim/Data/MySQL/Resources/LogStore.migrations new file mode 100644 index 0000000000..9ac26ac722 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/LogStore.migrations @@ -0,0 +1,13 @@ + +:VERSION 1 + +CREATE TABLE `logs` ( + `logID` int(10) unsigned NOT NULL auto_increment, + `target` varchar(36) default NULL, + `server` varchar(64) default NULL, + `method` varchar(64) default NULL, + `arguments` varchar(255) default NULL, + `priority` int(11) default NULL, + `message` text, + PRIMARY KEY (`logID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/OpenSim/Data/MySQL/Resources/Presence.migrations b/OpenSim/Data/MySQL/Resources/Presence.migrations new file mode 100644 index 0000000000..c4e40fa8d1 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/Presence.migrations @@ -0,0 +1,31 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `Presence` ( + `UserID` VARCHAR(255) NOT NULL, + `RegionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `SessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + `SecureSessionID` CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' +) ENGINE=InnoDB; + +CREATE UNIQUE INDEX SessionID ON Presence(SessionID); +CREATE INDEX UserID ON Presence(UserID); + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +ALTER TABLE `Presence` ADD COLUMN LastSeen timestamp; + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +CREATE INDEX RegionID ON Presence(RegionID); + +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..ac31380191 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -0,0 +1,950 @@ + +:VERSION 1 #--------------------- + +BEGIN; + +CREATE TABLE `prims` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `ParentID` int(11) default NULL, + `CreationDate` int(11) default NULL, + `Name` varchar(255) default NULL, + `SceneGroupID` varchar(255) default NULL, + `Text` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `SitName` varchar(255) default NULL, + `TouchName` varchar(255) default NULL, + `ObjectFlags` int(11) default NULL, + `CreatorID` varchar(255) default NULL, + `OwnerID` varchar(255) default NULL, + `GroupID` varchar(255) default NULL, + `LastOwnerID` varchar(255) default NULL, + `OwnerMask` int(11) default NULL, + `NextOwnerMask` int(11) default NULL, + `GroupMask` int(11) default NULL, + `EveryoneMask` int(11) default NULL, + `BaseMask` int(11) default NULL, + `PositionX` float default NULL, + `PositionY` float default NULL, + `PositionZ` float default NULL, + `GroupPositionX` float default NULL, + `GroupPositionY` float default NULL, + `GroupPositionZ` float default NULL, + `VelocityX` float default NULL, + `VelocityY` float default NULL, + `VelocityZ` float default NULL, + `AngularVelocityX` float default NULL, + `AngularVelocityY` float default NULL, + `AngularVelocityZ` float default NULL, + `AccelerationX` float default NULL, + `AccelerationY` float default NULL, + `AccelerationZ` float default NULL, + `RotationX` float default NULL, + `RotationY` float default NULL, + `RotationZ` float default NULL, + `RotationW` float default NULL, + `SitTargetOffsetX` float default NULL, + `SitTargetOffsetY` float default NULL, + `SitTargetOffsetZ` float default NULL, + `SitTargetOrientW` float default NULL, + `SitTargetOrientX` float default NULL, + `SitTargetOrientY` float default NULL, + `SitTargetOrientZ` float default NULL, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primshapes` ( + `UUID` varchar(255) NOT NULL, + `Shape` int(11) default NULL, + `ScaleX` float default NULL, + `ScaleY` float default NULL, + `ScaleZ` float default NULL, + `PCode` int(11) default NULL, + `PathBegin` int(11) default NULL, + `PathEnd` int(11) default NULL, + `PathScaleX` int(11) default NULL, + `PathScaleY` int(11) default NULL, + `PathShearX` int(11) default NULL, + `PathShearY` int(11) default NULL, + `PathSkew` int(11) default NULL, + `PathCurve` int(11) default NULL, + `PathRadiusOffset` int(11) default NULL, + `PathRevolutions` int(11) default NULL, + `PathTaperX` int(11) default NULL, + `PathTaperY` int(11) default NULL, + `PathTwist` int(11) default NULL, + `PathTwistBegin` int(11) default NULL, + `ProfileBegin` int(11) default NULL, + `ProfileEnd` int(11) default NULL, + `ProfileCurve` int(11) default NULL, + `ProfileHollow` int(11) default NULL, + `State` int(11) default NULL, + `Texture` longblob, + `ExtraParams` longblob, + PRIMARY KEY (`UUID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `primitems` ( + `itemID` varchar(255) NOT NULL, + `primID` varchar(255) default NULL, + `assetID` varchar(255) default NULL, + `parentFolderID` varchar(255) default NULL, + `invType` int(11) default NULL, + `assetType` int(11) default NULL, + `name` varchar(255) default NULL, + `description` varchar(255) default NULL, + `creationDate` bigint(20) default NULL, + `creatorID` varchar(255) default NULL, + `ownerID` varchar(255) default NULL, + `lastOwnerID` varchar(255) default NULL, + `groupID` varchar(255) default NULL, + `nextPermissions` int(11) default NULL, + `currentPermissions` int(11) default NULL, + `basePermissions` int(11) default NULL, + `everyonePermissions` int(11) default NULL, + `groupPermissions` int(11) default NULL, + PRIMARY KEY (`itemID`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `terrain` ( + `RegionUUID` varchar(255) default NULL, + `Revision` int(11) default NULL, + `Heightfield` longblob +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +CREATE TABLE `land` ( + `UUID` varchar(255) NOT NULL, + `RegionUUID` varchar(255) default NULL, + `LocalLandID` int(11) default NULL, + `Bitmap` longblob, + `Name` varchar(255) default NULL, + `Description` varchar(255) default NULL, + `OwnerUUID` varchar(255) default NULL, + `IsGroupOwned` int(11) default NULL, + `Area` int(11) default NULL, + `AuctionID` int(11) default NULL, + `Category` int(11) default NULL, + `ClaimDate` int(11) default NULL, + `ClaimPrice` int(11) default NULL, + `GroupUUID` varchar(255) default NULL, + `SalePrice` int(11) default NULL, + `LandStatus` int(11) default NULL, + `LandFlags` int(11) default NULL, + `LandingType` int(11) default NULL, + `MediaAutoScale` int(11) default NULL, + `MediaTextureUUID` varchar(255) default NULL, + `MediaURL` varchar(255) default NULL, + `MusicURL` varchar(255) default NULL, + `PassHours` float default NULL, + `PassPrice` int(11) default NULL, + `SnapshotUUID` varchar(255) default NULL, + `UserLocationX` float default NULL, + `UserLocationY` float default NULL, + `UserLocationZ` float default NULL, + `UserLookAtX` float default NULL, + `UserLookAtY` float default NULL, + `UserLookAtZ` float default NULL, + `AuthbuyerID` varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + PRIMARY KEY (`UUID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +CREATE TABLE `landaccesslist` ( + `LandUUID` varchar(255) default NULL, + `AccessUUID` varchar(255) default NULL, + `Flags` int(11) default NULL +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +COMMIT; + +:VERSION 2 #--------------------- + +BEGIN; + +CREATE index prims_regionuuid on prims(RegionUUID); +CREATE index primitems_primid on primitems(primID); + +COMMIT; + +:VERSION 3 #--------------------- + +BEGIN; + CREATE TABLE regionban (regionUUID VARCHAR(36) NOT NULL, bannedUUID VARCHAR(36) NOT NULL, bannedIp VARCHAR(16) NOT NULL, bannedIpHostMask VARCHAR(16) NOT NULL) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT='Rev. 1'; +COMMIT; + +:VERSION 4 #--------------------- + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 5 #--------------------- +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36), + primary key(regionUUID) +); + +COMMIT; + + +:VERSION 6 #--------------------- + +BEGIN; + +alter table landaccesslist ENGINE = InnoDB; +alter table migrations ENGINE = InnoDB; +alter table primitems ENGINE = InnoDB; +alter table prims ENGINE = InnoDB; +alter table primshapes ENGINE = InnoDB; +alter table regionsettings ENGINE = InnoDB; +alter table terrain ENGINE = InnoDB; + +COMMIT; + +:VERSION 7 #--------------------- + +BEGIN; + +ALTER TABLE prims change UUID UUIDold varchar(255); +ALTER TABLE prims change RegionUUID RegionUUIDold varchar(255); +ALTER TABLE prims change CreatorID CreatorIDold varchar(255); +ALTER TABLE prims change OwnerID OwnerIDold varchar(255); +ALTER TABLE prims change GroupID GroupIDold varchar(255); +ALTER TABLE prims change LastOwnerID LastOwnerIDold varchar(255); +ALTER TABLE prims add UUID char(36); +ALTER TABLE prims add RegionUUID char(36); +ALTER TABLE prims add CreatorID char(36); +ALTER TABLE prims add OwnerID char(36); +ALTER TABLE prims add GroupID char(36); +ALTER TABLE prims add LastOwnerID char(36); +UPDATE prims set UUID = UUIDold, RegionUUID = RegionUUIDold, CreatorID = CreatorIDold, OwnerID = OwnerIDold, GroupID = GroupIDold, LastOwnerID = LastOwnerIDold; +ALTER TABLE prims drop UUIDold; +ALTER TABLE prims drop RegionUUIDold; +ALTER TABLE prims drop CreatorIDold; +ALTER TABLE prims drop OwnerIDold; +ALTER TABLE prims drop GroupIDold; +ALTER TABLE prims drop LastOwnerIDold; +ALTER TABLE prims add constraint primary key(UUID); +ALTER TABLE prims add index prims_regionuuid(RegionUUID); + +COMMIT; + +:VERSION 8 #--------------------- + +BEGIN; + +ALTER TABLE primshapes change UUID UUIDold varchar(255); +ALTER TABLE primshapes add UUID char(36); +UPDATE primshapes set UUID = UUIDold; +ALTER TABLE primshapes drop UUIDold; +ALTER TABLE primshapes add constraint primary key(UUID); + +COMMIT; + +:VERSION 9 #--------------------- + +BEGIN; + +ALTER TABLE primitems change itemID itemIDold varchar(255); +ALTER TABLE primitems change primID primIDold varchar(255); +ALTER TABLE primitems change assetID assetIDold varchar(255); +ALTER TABLE primitems change parentFolderID parentFolderIDold varchar(255); +ALTER TABLE primitems change creatorID creatorIDold varchar(255); +ALTER TABLE primitems change ownerID ownerIDold varchar(255); +ALTER TABLE primitems change groupID groupIDold varchar(255); +ALTER TABLE primitems change lastOwnerID lastOwnerIDold varchar(255); +ALTER TABLE primitems add itemID char(36); +ALTER TABLE primitems add primID char(36); +ALTER TABLE primitems add assetID char(36); +ALTER TABLE primitems add parentFolderID char(36); +ALTER TABLE primitems add creatorID char(36); +ALTER TABLE primitems add ownerID char(36); +ALTER TABLE primitems add groupID char(36); +ALTER TABLE primitems add lastOwnerID char(36); +UPDATE primitems set itemID = itemIDold, primID = primIDold, assetID = assetIDold, parentFolderID = parentFolderIDold, creatorID = creatorIDold, ownerID = ownerIDold, groupID = groupIDold, lastOwnerID = lastOwnerIDold; +ALTER TABLE primitems drop itemIDold; +ALTER TABLE primitems drop primIDold; +ALTER TABLE primitems drop assetIDold; +ALTER TABLE primitems drop parentFolderIDold; +ALTER TABLE primitems drop creatorIDold; +ALTER TABLE primitems drop ownerIDold; +ALTER TABLE primitems drop groupIDold; +ALTER TABLE primitems drop lastOwnerIDold; +ALTER TABLE primitems add constraint primary key(itemID); +ALTER TABLE primitems add index primitems_primid(primID); + +COMMIT; + +:VERSION 10 #--------------------- + +# 1 "010_RegionStore.sql" +# 1 "" +# 1 "" +# 1 "010_RegionStore.sql" +BEGIN; + +DELETE FROM regionsettings; + +COMMIT; + + +:VERSION 11 #--------------------- + +BEGIN; + +ALTER TABLE prims change SceneGroupID SceneGroupIDold varchar(255); +ALTER TABLE prims add SceneGroupID char(36); +UPDATE prims set SceneGroupID = SceneGroupIDold; +ALTER TABLE prims drop SceneGroupIDold; +ALTER TABLE prims add index prims_scenegroupid(SceneGroupID); + +COMMIT; + +:VERSION 12 #--------------------- + +BEGIN; + +ALTER TABLE prims add index prims_parentid(ParentID); + +COMMIT; + +:VERSION 13 #--------------------- +begin; + +drop table regionsettings; + +CREATE TABLE `regionsettings` ( + `regionUUID` char(36) NOT NULL, + `block_terraform` int(11) NOT NULL, + `block_fly` int(11) NOT NULL, + `allow_damage` int(11) NOT NULL, + `restrict_pushing` int(11) NOT NULL, + `allow_land_resell` int(11) NOT NULL, + `allow_land_join_divide` int(11) NOT NULL, + `block_show_in_search` int(11) NOT NULL, + `agent_limit` int(11) NOT NULL, + `object_bonus` float NOT NULL, + `maturity` int(11) NOT NULL, + `disable_scripts` int(11) NOT NULL, + `disable_collisions` int(11) NOT NULL, + `disable_physics` int(11) NOT NULL, + `terrain_texture_1` char(36) NOT NULL, + `terrain_texture_2` char(36) NOT NULL, + `terrain_texture_3` char(36) NOT NULL, + `terrain_texture_4` char(36) NOT NULL, + `elevation_1_nw` float NOT NULL, + `elevation_2_nw` float NOT NULL, + `elevation_1_ne` float NOT NULL, + `elevation_2_ne` float NOT NULL, + `elevation_1_se` float NOT NULL, + `elevation_2_se` float NOT NULL, + `elevation_1_sw` float NOT NULL, + `elevation_2_sw` float NOT NULL, + `water_height` float NOT NULL, + `terrain_raise_limit` float NOT NULL, + `terrain_lower_limit` float NOT NULL, + `use_estate_sun` int(11) NOT NULL, + `fixed_sun` int(11) NOT NULL, + `sun_position` float NOT NULL, + `covenant` char(36) default NULL, + `Sandbox` tinyint(4) NOT NULL, + PRIMARY KEY (`regionUUID`) +) ENGINE=InnoDB; + +commit; + +:VERSION 16 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton1 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton2 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton3 integer not null default 0; +ALTER TABLE prims ADD COLUMN PayButton4 integer not null default 0; +ALTER TABLE prims ADD COLUMN LoopedSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float not null default 0.0; +ALTER TABLE prims ADD COLUMN TextureAnimation blob; +ALTER TABLE prims ADD COLUMN OmegaX float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaY float not null default 0.0; +ALTER TABLE prims ADD COLUMN OmegaZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float not null default 0.0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float not null default 0.0; +ALTER TABLE prims ADD COLUMN ForceMouselook tinyint not null default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin integer not null default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop tinyint not null default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge tinyint not null default 0; +ALTER TABLE prims ADD COLUMN SalePrice integer not null default 10; +ALTER TABLE prims ADD COLUMN SaleType tinyint not null default 0; + +COMMIT; + + +:VERSION 17 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; +ALTER TABLE prims ADD COLUMN ParticleSystem blob; + +COMMIT; + + +:VERSION 18 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN ClickAction tinyint NOT NULL default 0; + +commit; + +:VERSION 19 #--------------------- + +begin; + +ALTER TABLE prims ADD COLUMN Material tinyint NOT NULL default 3; + +commit; + + +:VERSION 20 #--------------------- + +begin; + +ALTER TABLE land ADD COLUMN OtherCleanTime integer NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell integer NOT NULL default 0; + +commit; + +:VERSION 21 #--------------------- + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + + +:VERSION 22 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float not null default 0.0; + +COMMIT; + +:VERSION 23 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN LinkNumber integer not null default 0; + +COMMIT; + +:VERSION 24 #--------------------- + +BEGIN; + +alter table regionsettings change column `object_bonus` `object_bonus` double NOT NULL; +alter table regionsettings change column `elevation_1_nw` `elevation_1_nw` double NOT NULL; +alter table regionsettings change column `elevation_2_nw` `elevation_2_nw` double NOT NULL; +alter table regionsettings change column `elevation_1_ne` `elevation_1_ne` double NOT NULL; +alter table regionsettings change column `elevation_2_ne` `elevation_2_ne` double NOT NULL; +alter table regionsettings change column `elevation_1_se` `elevation_1_se` double NOT NULL; +alter table regionsettings change column `elevation_2_se` `elevation_2_se` double NOT NULL; +alter table regionsettings change column `elevation_1_sw` `elevation_1_sw` double NOT NULL; +alter table regionsettings change column `elevation_2_sw` `elevation_2_sw` double NOT NULL; +alter table regionsettings change column `water_height` `water_height` double NOT NULL; +alter table regionsettings change column `terrain_raise_limit` `terrain_raise_limit` double NOT NULL; +alter table regionsettings change column `terrain_lower_limit` `terrain_lower_limit` double NOT NULL; +alter table regionsettings change column `sun_position` `sun_position` double NOT NULL; + +COMMIT; + + +:VERSION 25 #--------------------- + +BEGIN; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +alter table primshapes change column `ScaleX` `ScaleX` double NOT NULL default '0'; +alter table primshapes change column `ScaleY` `ScaleY` double NOT NULL default '0'; +alter table primshapes change column `ScaleZ` `ScaleZ` double NOT NULL default '0'; + +COMMIT; + +:VERSION 26 #--------------------- + +begin; + +alter table prims change column `PositionX` `PositionX` double default NULL; +alter table prims change column `PositionY` `PositionY` double default NULL; +alter table prims change column `PositionZ` `PositionZ` double default NULL; +alter table prims change column `GroupPositionX` `GroupPositionX` double default NULL; +alter table prims change column `GroupPositionY` `GroupPositionY` double default NULL; +alter table prims change column `GroupPositionZ` `GroupPositionZ` double default NULL; +alter table prims change column `VelocityX` `VelocityX` double default NULL; +alter table prims change column `VelocityY` `VelocityY` double default NULL; +alter table prims change column `VelocityZ` `VelocityZ` double default NULL; +alter table prims change column `AngularVelocityX` `AngularVelocityX` double default NULL; +alter table prims change column `AngularVelocityY` `AngularVelocityY` double default NULL; +alter table prims change column `AngularVelocityZ` `AngularVelocityZ` double default NULL; +alter table prims change column `AccelerationX` `AccelerationX` double default NULL; +alter table prims change column `AccelerationY` `AccelerationY` double default NULL; +alter table prims change column `AccelerationZ` `AccelerationZ` double default NULL; +alter table prims change column `RotationX` `RotationX` double default NULL; +alter table prims change column `RotationY` `RotationY` double default NULL; +alter table prims change column `RotationZ` `RotationZ` double default NULL; +alter table prims change column `RotationW` `RotationW` double default NULL; +alter table prims change column `SitTargetOffsetX` `SitTargetOffsetX` double default NULL; +alter table prims change column `SitTargetOffsetY` `SitTargetOffsetY` double default NULL; +alter table prims change column `SitTargetOffsetZ` `SitTargetOffsetZ` double default NULL; +alter table prims change column `SitTargetOrientW` `SitTargetOrientW` double default NULL; +alter table prims change column `SitTargetOrientX` `SitTargetOrientX` double default NULL; +alter table prims change column `SitTargetOrientY` `SitTargetOrientY` double default NULL; +alter table prims change column `SitTargetOrientZ` `SitTargetOrientZ` double default NULL; +alter table prims change column `LoopedSoundGain` `LoopedSoundGain` double NOT NULL default '0'; +alter table prims change column `OmegaX` `OmegaX` double NOT NULL default '0'; +alter table prims change column `OmegaY` `OmegaY` double NOT NULL default '0'; +alter table prims change column `OmegaZ` `OmegaZ` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetX` `CameraEyeOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetY` `CameraEyeOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraEyeOffsetZ` `CameraEyeOffsetZ` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetX` `CameraAtOffsetX` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetY` `CameraAtOffsetY` double NOT NULL default '0'; +alter table prims change column `CameraAtOffsetZ` `CameraAtOffsetZ` double NOT NULL default '0'; +alter table prims change column `CollisionSoundVolume` `CollisionSoundVolume` double NOT NULL default '0'; + +commit; + +:VERSION 27 #--------------------- + +BEGIN; + +ALTER TABLE prims DROP COLUMN ParentID; + +COMMIT; + +:VERSION 28 #--------------------- + +BEGIN; + +update terrain + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = concat(substr(LandUUID, 1, 8), "-", substr(LandUUID, 9, 4), "-", substr(LandUUID, 13, 4), "-", substr(LandUUID, 17, 4), "-", substr(LandUUID, 21, 12)) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = concat(substr(AccessUUID, 1, 8), "-", substr(AccessUUID, 9, 4), "-", substr(AccessUUID, 13, 4), "-", substr(AccessUUID, 17, 4), "-", substr(AccessUUID, 21, 12)) + where AccessUUID not like '%-%'; + + +update prims + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update prims + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = concat(substr(SceneGroupID, 1, 8), "-", substr(SceneGroupID, 9, 4), "-", substr(SceneGroupID, 13, 4), "-", substr(SceneGroupID, 17, 4), "-", substr(SceneGroupID, 21, 12)) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = concat(substr(CreatorID, 1, 8), "-", substr(CreatorID, 9, 4), "-", substr(CreatorID, 13, 4), "-", substr(CreatorID, 17, 4), "-", substr(CreatorID, 21, 12)) + where CreatorID not like '%-%'; + +update prims + set OwnerID = concat(substr(OwnerID, 1, 8), "-", substr(OwnerID, 9, 4), "-", substr(OwnerID, 13, 4), "-", substr(OwnerID, 17, 4), "-", substr(OwnerID, 21, 12)) + where OwnerID not like '%-%'; + +update prims + set GroupID = concat(substr(GroupID, 1, 8), "-", substr(GroupID, 9, 4), "-", substr(GroupID, 13, 4), "-", substr(GroupID, 17, 4), "-", substr(GroupID, 21, 12)) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = concat(substr(LastOwnerID, 1, 8), "-", substr(LastOwnerID, 9, 4), "-", substr(LastOwnerID, 13, 4), "-", substr(LastOwnerID, 17, 4), "-", substr(LastOwnerID, 21, 12)) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + + +update land + set UUID = concat(substr(UUID, 1, 8), "-", substr(UUID, 9, 4), "-", substr(UUID, 13, 4), "-", substr(UUID, 17, 4), "-", substr(UUID, 21, 12)) + where UUID not like '%-%'; + +update land + set RegionUUID = concat(substr(RegionUUID, 1, 8), "-", substr(RegionUUID, 9, 4), "-", substr(RegionUUID, 13, 4), "-", substr(RegionUUID, 17, 4), "-", substr(RegionUUID, 21, 12)) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = concat(substr(OwnerUUID, 1, 8), "-", substr(OwnerUUID, 9, 4), "-", substr(OwnerUUID, 13, 4), "-", substr(OwnerUUID, 17, 4), "-", substr(OwnerUUID, 21, 12)) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = concat(substr(GroupUUID, 1, 8), "-", substr(GroupUUID, 9, 4), "-", substr(GroupUUID, 13, 4), "-", substr(GroupUUID, 17, 4), "-", substr(GroupUUID, 21, 12)) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = concat(substr(MediaTextureUUID, 1, 8), "-", substr(MediaTextureUUID, 9, 4), "-", substr(MediaTextureUUID, 13, 4), "-", substr(MediaTextureUUID, 17, 4), "-", substr(MediaTextureUUID, 21, 12)) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = concat(substr(SnapshotUUID, 1, 8), "-", substr(SnapshotUUID, 9, 4), "-", substr(SnapshotUUID, 13, 4), "-", substr(SnapshotUUID, 17, 4), "-", substr(SnapshotUUID, 21, 12)) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = concat(substr(AuthbuyerID, 1, 8), "-", substr(AuthbuyerID, 9, 4), "-", substr(AuthbuyerID, 13, 4), "-", substr(AuthbuyerID, 17, 4), "-", substr(AuthbuyerID, 21, 12)) + where AuthbuyerID not like '%-%'; + +COMMIT; + +:VERSION 29 #--------------------- + +BEGIN; + +ALTER TABLE prims ADD COLUMN PassTouches tinyint not null default 0; + +COMMIT; + +:VERSION 30 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings ADD COLUMN loaded_creation_date varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_time varchar(20) default NULL; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_id varchar(64) default NULL; + +COMMIT; + +:VERSION 31 #--------------------- + +BEGIN; + +ALTER TABLE regionsettings DROP COLUMN loaded_creation_date; +ALTER TABLE regionsettings DROP COLUMN loaded_creation_time; +ALTER TABLE regionsettings ADD COLUMN loaded_creation_datetime int unsigned NOT NULL default 0; + +COMMIT; + +:VERSION 32 + +BEGIN; +CREATE TABLE `regionwindlight` ( + `region_id` varchar(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000', + `water_color_r` float(9,6) unsigned NOT NULL DEFAULT '4.000000', + `water_color_g` float(9,6) unsigned NOT NULL DEFAULT '38.000000', + `water_color_b` float(9,6) unsigned NOT NULL DEFAULT '64.000000', + `water_fog_density_exponent` float(3,1) unsigned NOT NULL DEFAULT '4.0', + `underwater_fog_modifier` float(3,2) unsigned NOT NULL DEFAULT '0.25', + `reflection_wavelet_scale_1` float(3,1) unsigned NOT NULL DEFAULT '2.0', + `reflection_wavelet_scale_2` float(3,1) unsigned NOT NULL DEFAULT '2.0', + `reflection_wavelet_scale_3` float(3,1) unsigned NOT NULL DEFAULT '2.0', + `fresnel_scale` float(3,2) unsigned NOT NULL DEFAULT '0.40', + `fresnel_offset` float(3,2) unsigned NOT NULL DEFAULT '0.50', + `refract_scale_above` float(3,2) unsigned NOT NULL DEFAULT '0.03', + `refract_scale_below` float(3,2) unsigned NOT NULL DEFAULT '0.20', + `blur_multiplier` float(4,3) unsigned NOT NULL DEFAULT '0.040', + `big_wave_direction_x` float(3,2) NOT NULL DEFAULT '1.05', + `big_wave_direction_y` float(3,2) NOT NULL DEFAULT '-0.42', + `little_wave_direction_x` float(3,2) NOT NULL DEFAULT '1.11', + `little_wave_direction_y` float(3,2) NOT NULL DEFAULT '-1.16', + `normal_map_texture` varchar(36) NOT NULL DEFAULT '822ded49-9a6c-f61c-cb89-6df54f42cdf4', + `horizon_r` float(3,2) unsigned NOT NULL DEFAULT '0.25', + `horizon_g` float(3,2) unsigned NOT NULL DEFAULT '0.25', + `horizon_b` float(3,2) unsigned NOT NULL DEFAULT '0.32', + `horizon_i` float(3,2) unsigned NOT NULL DEFAULT '0.32', + `haze_horizon` float(3,2) unsigned NOT NULL DEFAULT '0.19', + `blue_density_r` float(3,2) unsigned NOT NULL DEFAULT '0.12', + `blue_density_g` float(3,2) unsigned NOT NULL DEFAULT '0.22', + `blue_density_b` float(3,2) unsigned NOT NULL DEFAULT '0.38', + `blue_density_i` float(3,2) unsigned NOT NULL DEFAULT '0.38', + `haze_density` float(3,2) unsigned NOT NULL DEFAULT '0.70', + `density_multiplier` float(3,2) unsigned NOT NULL DEFAULT '0.18', + `distance_multiplier` float(4,1) unsigned NOT NULL DEFAULT '0.8', + `max_altitude` int(4) unsigned NOT NULL DEFAULT '1605', + `sun_moon_color_r` float(3,2) unsigned NOT NULL DEFAULT '0.24', + `sun_moon_color_g` float(3,2) unsigned NOT NULL DEFAULT '0.26', + `sun_moon_color_b` float(3,2) unsigned NOT NULL DEFAULT '0.30', + `sun_moon_color_i` float(3,2) unsigned NOT NULL DEFAULT '0.30', + `sun_moon_position` float(4,3) unsigned NOT NULL DEFAULT '0.317', + `ambient_r` float(3,2) unsigned NOT NULL DEFAULT '0.35', + `ambient_g` float(3,2) unsigned NOT NULL DEFAULT '0.35', + `ambient_b` float(3,2) unsigned NOT NULL DEFAULT '0.35', + `ambient_i` float(3,2) unsigned NOT NULL DEFAULT '0.35', + `east_angle` float(3,2) unsigned NOT NULL DEFAULT '0.00', + `sun_glow_focus` float(3,2) unsigned NOT NULL DEFAULT '0.10', + `sun_glow_size` float(3,2) unsigned NOT NULL DEFAULT '1.75', + `scene_gamma` float(4,2) unsigned NOT NULL DEFAULT '1.00', + `star_brightness` float(3,2) unsigned NOT NULL DEFAULT '0.00', + `cloud_color_r` float(3,2) unsigned NOT NULL DEFAULT '0.41', + `cloud_color_g` float(3,2) unsigned NOT NULL DEFAULT '0.41', + `cloud_color_b` float(3,2) unsigned NOT NULL DEFAULT '0.41', + `cloud_color_i` float(3,2) unsigned NOT NULL DEFAULT '0.41', + `cloud_x` float(3,2) unsigned NOT NULL DEFAULT '1.00', + `cloud_y` float(3,2) unsigned NOT NULL DEFAULT '0.53', + `cloud_density` float(3,2) unsigned NOT NULL DEFAULT '1.00', + `cloud_coverage` float(3,2) unsigned NOT NULL DEFAULT '0.27', + `cloud_scale` float(3,2) unsigned NOT NULL DEFAULT '0.42', + `cloud_detail_x` float(3,2) unsigned NOT NULL DEFAULT '1.00', + `cloud_detail_y` float(3,2) unsigned NOT NULL DEFAULT '0.53', + `cloud_detail_density` float(3,2) unsigned NOT NULL DEFAULT '0.12', + `cloud_scroll_x` float(3,2) unsigned NOT NULL DEFAULT '0.20', + `cloud_scroll_x_lock` tinyint(1) unsigned NOT NULL DEFAULT '0', + `cloud_scroll_y` float(3,2) unsigned NOT NULL DEFAULT '0.01', + `cloud_scroll_y_lock` tinyint(1) unsigned NOT NULL DEFAULT '0', + `draw_classic_clouds` tinyint(1) unsigned NOT NULL DEFAULT '1', + PRIMARY KEY (`region_id`) +); + + +:VERSION 33 #--------------------- + +BEGIN; +ALTER TABLE regionsettings ADD map_tile_ID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +COMMIT; + +:VERSION 34 #--------------------- + +BEGIN; +ALTER TABLE `regionwindlight` CHANGE COLUMN `cloud_scroll_x` `cloud_scroll_x` FLOAT(4,2) NOT NULL DEFAULT '0.20' AFTER `cloud_detail_density`, CHANGE COLUMN `cloud_scroll_y` `cloud_scroll_y` FLOAT(4,2) NOT NULL DEFAULT '0.01' AFTER `cloud_scroll_x_lock`; +COMMIT; + +:VERSION 35 #--------------------- + +BEGIN; +ALTER TABLE prims ADD COLUMN MediaURL varchar(255); +ALTER TABLE primshapes ADD COLUMN Media TEXT; +COMMIT; + +:VERSION 36 #--------------------- + +BEGIN; +ALTER TABLE `land` ADD COLUMN `MediaType` VARCHAR(32) NOT NULL DEFAULT 'none/none' ; +ALTER TABLE `land` ADD COLUMN `MediaDescription` VARCHAR(255) NOT NULL DEFAULT ''; +ALTER TABLE `land` ADD COLUMN `MediaSize` VARCHAR(16) NOT NULL DEFAULT '0,0'; +ALTER TABLE `land` ADD COLUMN `MediaLoop` BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE `land` ADD COLUMN `ObscureMusic` BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE `land` ADD COLUMN `ObscureMedia` BOOLEAN NOT NULL DEFAULT FALSE; +COMMIT; + +:VERSION 37 #--------------------- + +BEGIN; + +ALTER TABLE `prims` MODIFY COLUMN `CreatorID` VARCHAR(255) NOT NULL DEFAULT ''; +ALTER TABLE `primitems` MODIFY COLUMN `CreatorID` VARCHAR(255) NOT NULL DEFAULT ''; + +COMMIT; + +:VERSION 38 #--------------------- + +BEGIN; + +alter table land ENGINE = MyISAM; +alter table landaccesslist ENGINE = MyISAM; +alter table migrations ENGINE = MyISAM; +alter table primitems ENGINE = MyISAM; +alter table prims ENGINE = MyISAM; +alter table primshapes ENGINE = MyISAM; +alter table regionban ENGINE = MyISAM; +alter table regionsettings ENGINE = MyISAM; +alter table terrain ENGINE = MyISAM; + +COMMIT; + +:VERSION 39 #--------------- Telehub support + +BEGIN; +CREATE TABLE IF NOT EXISTS `spawn_points` ( + `RegionID` varchar(36) COLLATE utf8_unicode_ci NOT NULL, + `Yaw` float NOT NULL, + `Pitch` float NOT NULL, + `Distance` float NOT NULL, + KEY `RegionID` (`RegionID`) +) ENGINE=Innodb; + +ALTER TABLE `regionsettings` ADD COLUMN `TelehubObject` varchar(36) NOT NULL; +COMMIT; + +:VERSION 40 #---------------- Parcels for sale + +BEGIN; +ALTER TABLE `regionsettings` ADD COLUMN `parcel_tile_ID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +COMMIT; + +:VERSION 41 #---------------- Timed bans/access + +BEGIN; +ALTER TABLE `landaccesslist` ADD COLUMN `Expires` INTEGER NOT NULL DEFAULT 0; +COMMIT; + +:VERSION 42 #--------------------- Region Covenant changed time + +BEGIN; +ALTER TABLE regionsettings ADD COLUMN covenant_datetime int unsigned NOT NULL DEFAULT '0'; +COMMIT; + +:VERSION 43 #--------------------- + +BEGIN; + +ALTER TABLE `regionsettings` MODIFY COLUMN `TelehubObject` VARCHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 44 #--------------------- Environment Settings + +BEGIN; + +CREATE TABLE `regionenvironment` ( + `region_id` varchar(36) NOT NULL, + `llsd_settings` TEXT NOT NULL, + PRIMARY KEY (`region_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 45 + +BEGIN; + +CREATE TABLE `regionextra` (`RegionID` char(36) not null, `Name` varchar(32) not null, `value` text, primary key(`RegionID`, `Name`)); + +COMMIT; + +:VERSION 46 #---------------- Dynamic attributes + +BEGIN; + +ALTER TABLE prims ADD COLUMN DynAttrs TEXT; + +COMMIT; + +:VERSION 47 #---------------- Extra physics params + +BEGIN; + +ALTER TABLE prims ADD COLUMN `PhysicsShapeType` tinyint(4) NOT NULL default '0'; +ALTER TABLE prims ADD COLUMN `Density` double NOT NULL default '1000'; +ALTER TABLE prims ADD COLUMN `GravityModifier` double NOT NULL default '1'; +ALTER TABLE prims ADD COLUMN `Friction` double NOT NULL default '0.6'; +ALTER TABLE prims ADD COLUMN `Restitution` double NOT NULL default '0.5'; + +COMMIT; + +:VERSION 48 #---------------- Keyframes + +BEGIN; + +ALTER TABLE prims ADD COLUMN `KeyframeMotion` blob; + +COMMIT; + +:VERSION 49 #--------------------- Save attachment info + +BEGIN; +ALTER TABLE prims ADD COLUMN AttachedPosX double default 0; +ALTER TABLE prims ADD COLUMN AttachedPosY double default 0; +ALTER TABLE prims ADD COLUMN AttachedPosZ double default 0; +ALTER TABLE primshapes ADD COLUMN LastAttachPoint int(4) not null default '0'; +COMMIT; + +:VERSION 50 #---- Change LandFlags to unsigned + +BEGIN; + +ALTER TABLE land CHANGE COLUMN LandFlags LandFlags int unsigned default null; + +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/UserAccount.migrations b/OpenSim/Data/MySQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..84011e6e49 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserAccount.migrations @@ -0,0 +1,47 @@ +:VERSION 1 # ------------------------- + +BEGIN; + +CREATE TABLE `UserAccounts` ( + `PrincipalID` CHAR(36) NOT NULL, + `ScopeID` CHAR(36) NOT NULL, + `FirstName` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(64) NOT NULL, + `Email` VARCHAR(64), + `ServiceURLs` TEXT, + `Created` INT(11) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +COMMIT; + +:VERSION 2 # ------------------------- + +BEGIN; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, lastname AS LastName, email as Email, CONCAT('AssetServerURI=', userAssetURI, ' InventoryServerURI=', userInventoryURI, ' GatewayURI= HomeURI=') AS ServiceURLs, created as Created FROM users; + +COMMIT; + +:VERSION 3 # ------------------------- + +BEGIN; + +CREATE UNIQUE INDEX PrincipalID ON UserAccounts(PrincipalID); +CREATE INDEX Email ON UserAccounts(Email); +CREATE INDEX FirstName ON UserAccounts(FirstName); +CREATE INDEX LastName ON UserAccounts(LastName); +CREATE INDEX Name ON UserAccounts(FirstName,LastName); + +COMMIT; + +:VERSION 4 # ------------------------- + +BEGIN; + +ALTER TABLE UserAccounts ADD COLUMN UserLevel integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserFlags integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD COLUMN UserTitle varchar(64) NOT NULL DEFAULT ''; + +COMMIT; + + diff --git a/OpenSim/Data/MySQL/Resources/UserProfiles.migrations b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..87e99faac2 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserProfiles.migrations @@ -0,0 +1,98 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS `classifieds` ( + `classifieduuid` char(36) NOT NULL, + `creatoruuid` char(36) NOT NULL, + `creationdate` int(20) NOT NULL, + `expirationdate` int(20) NOT NULL, + `category` varchar(20) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `parceluuid` char(36) NOT NULL, + `parentestate` int(11) NOT NULL, + `snapshotuuid` char(36) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `parcelname` varchar(255) NOT NULL, + `classifiedflags` int(8) NOT NULL, + `priceforlisting` int(5) NOT NULL, + PRIMARY KEY (`classifieduuid`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `usernotes` ( + `useruuid` varchar(36) NOT NULL, + `targetuuid` varchar(36) NOT NULL, + `notes` text NOT NULL, + UNIQUE KEY `useruuid` (`useruuid`,`targetuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userpicks` ( + `pickuuid` varchar(36) NOT NULL, + `creatoruuid` varchar(36) NOT NULL, + `toppick` enum('true','false') NOT NULL, + `parceluuid` varchar(36) NOT NULL, + `name` varchar(255) NOT NULL, + `description` text NOT NULL, + `snapshotuuid` varchar(36) NOT NULL, + `user` varchar(255) NOT NULL, + `originalname` varchar(255) NOT NULL, + `simname` varchar(255) NOT NULL, + `posglobal` varchar(255) NOT NULL, + `sortorder` int(2) NOT NULL, + `enabled` enum('true','false') NOT NULL, + PRIMARY KEY (`pickuuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + + +CREATE TABLE IF NOT EXISTS `userprofile` ( + `useruuid` varchar(36) NOT NULL, + `profilePartner` varchar(36) NOT NULL, + `profileAllowPublish` binary(1) NOT NULL, + `profileMaturePublish` binary(1) NOT NULL, + `profileURL` varchar(255) NOT NULL, + `profileWantToMask` int(3) NOT NULL, + `profileWantToText` text NOT NULL, + `profileSkillsMask` int(3) NOT NULL, + `profileSkillsText` text NOT NULL, + `profileLanguages` text NOT NULL, + `profileImage` varchar(36) NOT NULL, + `profileAboutText` text NOT NULL, + `profileFirstImage` varchar(36) NOT NULL, + `profileFirstText` text NOT NULL, + PRIMARY KEY (`useruuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + +:VERSION 2 # ------------------------------- + +begin; +CREATE TABLE IF NOT EXISTS `userdata` ( + `UserId` char(36) NOT NULL, + `TagId` varchar(64) NOT NULL, + `DataKey` varchar(255), + `DataVal` varchar(255), + PRIMARY KEY (`UserId`,`TagId`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; + +commit; + +:VERSION 3 # ------------------------------- +begin; +CREATE TABLE IF NOT EXISTS `usersettings` ( + `useruuid` varchar(36) NOT NULL, + `imviaemail` enum('true','false') NOT NULL, + `visible` enum('true','false') NOT NULL, + `email` varchar(254) NOT NULL, + PRIMARY KEY (`useruuid`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +commit; + +:VERSION 4 # ------------------------------- +begin; +ALTER TABLE userpicks ADD COLUMN gatekeeper varchar(255); +commit; diff --git a/OpenSim/Data/MySQL/Resources/UserStore.migrations b/OpenSim/Data/MySQL/Resources/UserStore.migrations new file mode 100644 index 0000000000..f054611f04 --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/UserStore.migrations @@ -0,0 +1,168 @@ +:VERSION 1 # ----------------------------- + +BEGIN; + +SET FOREIGN_KEY_CHECKS=0; +-- ---------------------------- +-- Table structure for agents +-- ---------------------------- +CREATE TABLE `agents` ( + `UUID` varchar(36) NOT NULL, + `sessionID` varchar(36) NOT NULL, + `secureSessionID` varchar(36) NOT NULL, + `agentIP` varchar(16) NOT NULL, + `agentPort` int(11) NOT NULL, + `agentOnline` tinyint(4) NOT NULL, + `loginTime` int(11) NOT NULL, + `logoutTime` int(11) NOT NULL, + `currentRegion` varchar(36) NOT NULL, + `currentHandle` bigint(20) unsigned NOT NULL, + `currentPos` varchar(64) NOT NULL, + PRIMARY KEY (`UUID`), + UNIQUE KEY `session` (`sessionID`), + UNIQUE KEY `ssession` (`secureSessionID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- Create schema avatar_appearance +-- + +CREATE TABLE `avatarappearance` ( + Owner char(36) NOT NULL, + Serial int(10) unsigned NOT NULL, + Visual_Params blob NOT NULL, + Texture blob NOT NULL, + Avatar_Height float NOT NULL, + Body_Item char(36) NOT NULL, + Body_Asset char(36) NOT NULL, + Skin_Item char(36) NOT NULL, + Skin_Asset char(36) NOT NULL, + Hair_Item char(36) NOT NULL, + Hair_Asset char(36) NOT NULL, + Eyes_Item char(36) NOT NULL, + Eyes_Asset char(36) NOT NULL, + Shirt_Item char(36) NOT NULL, + Shirt_Asset char(36) NOT NULL, + Pants_Item char(36) NOT NULL, + Pants_Asset char(36) NOT NULL, + Shoes_Item char(36) NOT NULL, + Shoes_Asset char(36) NOT NULL, + Socks_Item char(36) NOT NULL, + Socks_Asset char(36) NOT NULL, + Jacket_Item char(36) NOT NULL, + Jacket_Asset char(36) NOT NULL, + Gloves_Item char(36) NOT NULL, + Gloves_Asset char(36) NOT NULL, + Undershirt_Item char(36) NOT NULL, + Undershirt_Asset char(36) NOT NULL, + Underpants_Item char(36) NOT NULL, + Underpants_Asset char(36) NOT NULL, + Skirt_Item char(36) NOT NULL, + Skirt_Asset char(36) NOT NULL, + PRIMARY KEY (`Owner`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +SET FOREIGN_KEY_CHECKS=0; +-- ---------------------------- +-- Table structure for users +-- ---------------------------- +CREATE TABLE `userfriends` ( + `ownerID` VARCHAR(37) NOT NULL, + `friendID` VARCHAR(37) NOT NULL, + `friendPerms` INT NOT NULL, + `datetimestamp` INT NOT NULL, + UNIQUE KEY (`ownerID`, `friendID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +-- ---------------------------- +-- Table structure for users +-- ---------------------------- +CREATE TABLE `users` ( + `UUID` varchar(36) NOT NULL default '', + `username` varchar(32) NOT NULL, + `lastname` varchar(32) NOT NULL, + `passwordHash` varchar(32) NOT NULL, + `passwordSalt` varchar(32) NOT NULL, + `homeRegion` bigint(20) unsigned default NULL, + `homeLocationX` float default NULL, + `homeLocationY` float default NULL, + `homeLocationZ` float default NULL, + `homeLookAtX` float default NULL, + `homeLookAtY` float default NULL, + `homeLookAtZ` float default NULL, + `created` int(11) NOT NULL, + `lastLogin` int(11) NOT NULL, + `userInventoryURI` varchar(255) default NULL, + `userAssetURI` varchar(255) default NULL, + `profileCanDoMask` int(10) unsigned default NULL, + `profileWantDoMask` int(10) unsigned default NULL, + `profileAboutText` text, + `profileFirstText` text, + `profileImage` varchar(36) default NULL, + `profileFirstImage` varchar(36) default NULL, + `webLoginKey` varchar(36) default NULL, + PRIMARY KEY (`UUID`), + UNIQUE KEY `usernames` (`username`,`lastname`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Records +-- ---------------------------- +COMMIT; + +:VERSION 2 # ----------------------------- + +BEGIN; + +ALTER TABLE users add homeRegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 # ----------------------------- + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 # ----------------------------- + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 # ----------------------------- + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL, `attachpoint` int(11) NOT NULL, `item` char(36) NOT NULL, `asset` char(36) NOT NULL) ENGINE=InnoDB; + +COMMIT; + +:VERSION 6 # ----------------------------- + +BEGIN; + +ALTER TABLE agents add currentLookAt varchar(36) not null default ''; + +COMMIT; + +:VERSION 7 # ----------------------------- + +BEGIN; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 8 # ----------------------------- + +BEGIN; + +ALTER TABLE users add scopeID char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + diff --git a/OpenSim/Data/MySQL/Resources/XAssetStore.migrations b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations new file mode 100644 index 0000000000..9459e3e14e --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/XAssetStore.migrations @@ -0,0 +1,32 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE `XAssetsMeta` ( + `ID` char(36) NOT NULL, + `Hash` binary(32) NOT NULL, + `Name` varchar(64) NOT NULL, + `Description` varchar(64) NOT NULL, + `AssetType` tinyint(4) NOT NULL, + `Local` tinyint(1) NOT NULL, + `Temporary` tinyint(1) NOT NULL, + `CreateTime` int(11) NOT NULL, + `AccessTime` int(11) NOT NULL, + `AssetFlags` int(11) NOT NULL, + `CreatorID` varchar(128) NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; + +CREATE TABLE `XAssetsData` ( + `Hash` binary(32) NOT NULL, + `Data` longblob NOT NULL, + PRIMARY KEY (`hash`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Version 1'; + +COMMIT; + +:VERSION 2 + +BEGIN; +COMMIT; diff --git a/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations b/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations new file mode 100644 index 0000000000..9e6f1c112c --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/os_groups_Store.migrations @@ -0,0 +1,115 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE `os_groups_groups` ( + `GroupID` char(36) NOT NULL default '', + `Location` varchar(255) NOT NULL default '', + `Name` varchar(255) NOT NULL default '', + `Charter` text NOT NULL, + `InsigniaID` char(36) NOT NULL default '', + `FounderID` char(36) NOT NULL default '', + `MembershipFee` int(11) NOT NULL default '0', + `OpenEnrollment` varchar(255) NOT NULL default '', + `ShowInList` int(4) NOT NULL default '0', + `AllowPublish` int(4) NOT NULL default '0', + `MaturePublish` int(4) NOT NULL default '0', + `OwnerRoleID` char(36) NOT NULL default '', + PRIMARY KEY (`GroupID`), + UNIQUE KEY `Name` (`Name`), + FULLTEXT KEY `Name_2` (`Name`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_membership` ( + `GroupID`char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + `SelectedRoleID` char(36) NOT NULL default '', + `Contribution` int(11) NOT NULL default '0', + `ListInProfile` int(4) NOT NULL default '1', + `AcceptNotices` int(4) NOT NULL default '1', + `AccessToken` char(36) NOT NULL default '', + PRIMARY KEY (`GroupID`,`PrincipalID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_roles` ( + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `Name` varchar(255) NOT NULL default '', + `Description` varchar(255) NOT NULL default '', + `Title` varchar(255) NOT NULL default '', + `Powers` bigint(20) unsigned NOT NULL default '0', + PRIMARY KEY (`GroupID`,`RoleID`), + KEY `GroupID` (`GroupID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_rolemembership` ( + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + PRIMARY KEY (`GroupID`,`RoleID`,`PrincipalID`), + KEY `PrincipalID` (`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_invites` ( + `InviteID` char(36) NOT NULL default '', + `GroupID` char(36) NOT NULL default '', + `RoleID` char(36) NOT NULL default '', + `PrincipalID` VARCHAR(255) NOT NULL default '', + `TMStamp` timestamp NOT NULL, + PRIMARY KEY (`InviteID`), + UNIQUE KEY `PrincipalGroup` (`GroupID`,`PrincipalID`) +) ENGINE=MyISAM; + + +CREATE TABLE `os_groups_notices` ( + `GroupID` char(36) NOT NULL default '', + `NoticeID` char(36) NOT NULL default '', + `TMStamp` int(10) unsigned NOT NULL default '0', + `FromName` varchar(255) NOT NULL default '', + `Subject` varchar(255) NOT NULL default '', + `Message` text NOT NULL, + `HasAttachment` int(4) NOT NULL default '0', + `AttachmentType` int(4) NOT NULL default '0', + `AttachmentName` varchar(128) NOT NULL default '', + `AttachmentItemID` char(36) NOT NULL default '', + `AttachmentOwnerID` varchar(255) NOT NULL default '', + PRIMARY KEY (`NoticeID`), + KEY `GroupID` (`GroupID`), + KEY `TMStamp` (`TMStamp`) +) ENGINE=MyISAM; + +CREATE TABLE `os_groups_principals` ( + `PrincipalID` VARCHAR(255) NOT NULL default '', + `ActiveGroupID` char(36) NOT NULL default '', + PRIMARY KEY (`PrincipalID`) +) ENGINE=MyISAM; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +INSERT INTO `os_groups_groups` SELECT * from `diva_groups_groups`; +DROP TABLE `diva_groups_groups`; +INSERT INTO `os_groups_membership` SELECT * from `diva_groups_membership`; +DROP TABLE `diva_groups_membership`; +INSERT INTO `os_groups_roles` SELECT * from `diva_groups_roles`; +DROP TABLE `diva_groups_roles`; +INSERT INTO `os_groups_rolemembership` SELECT * from `diva_groups_rolemembership`; +DROP TABLE `diva_groups_rolemembership`; +INSERT INTO `os_groups_invites` SELECT * from `diva_groups_invites`; +DROP TABLE `diva_groups_invites`; +INSERT INTO `os_groups_notices` SELECT * from `diva_groups_notices`; +DROP TABLE `diva_groups_notices`; +INSERT INTO `os_groups_principals` SELECT * from `diva_groups_principals`; +DROP TABLE `diva_groups_principals`; + +DELETE FROM `migrations` WHERE name='diva_im_Store'; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/Null/NullAuthenticationData.cs b/OpenSim/Data/Null/NullAuthenticationData.cs new file mode 100644 index 0000000000..620deb9786 --- /dev/null +++ b/OpenSim/Data/Null/NullAuthenticationData.cs @@ -0,0 +1,80 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullAuthenticationData : IAuthenticationData + { + private static Dictionary m_DataByUUID = new Dictionary(); + private static Dictionary m_Tokens = new Dictionary(); + + public NullAuthenticationData(string connectionString, string realm) + { + } + + public AuthenticationData Get(UUID principalID) + { + if (m_DataByUUID.ContainsKey(principalID)) + return m_DataByUUID[principalID]; + + return null; + } + + public bool Store(AuthenticationData data) + { + m_DataByUUID[data.PrincipalID] = data; + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + // Not implemented + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + m_Tokens[principalID] = token; + return true; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (m_Tokens.ContainsKey(principalID)) + return m_Tokens[principalID] == token; + + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Null/NullAvatarData.cs b/OpenSim/Data/Null/NullAvatarData.cs new file mode 100644 index 0000000000..c81ba43e6e --- /dev/null +++ b/OpenSim/Data/Null/NullAvatarData.cs @@ -0,0 +1,93 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullAvatarData : IAvatarData + { + private static Dictionary m_DataByUUID = new Dictionary(); + + public NullAvatarData(string connectionString, string realm) + { + } + + public AvatarBaseData[] Get(string field, string val) + { + if (field == "PrincipalID") + { + UUID id = UUID.Zero; + if (UUID.TryParse(val, out id)) + if (m_DataByUUID.ContainsKey(id)) + return new AvatarBaseData[] { m_DataByUUID[id] }; + } + + // Fail + return new AvatarBaseData[0]; + } + + public bool Store(AvatarBaseData data) + { + m_DataByUUID[data.PrincipalID] = data; + return true; + } + + public bool Delete(UUID principalID, string name) + { + if (m_DataByUUID.ContainsKey(principalID) && m_DataByUUID[principalID].Data.ContainsKey(name)) + { + m_DataByUUID[principalID].Data.Remove(name); + return true; + } + + return false; + } + + public bool Delete(string field, string val) + { + if (field == "PrincipalID") + { + UUID id = UUID.Zero; + if (UUID.TryParse(val, out id)) + if (m_DataByUUID.ContainsKey(id)) + { + m_DataByUUID.Remove(id); + return true; + } + } + + return false; + } + + } +} diff --git a/OpenSim/Data/Null/NullEstateData.cs b/OpenSim/Data/Null/NullEstateData.cs new file mode 100755 index 0000000000..57592f180d --- /dev/null +++ b/OpenSim/Data/Null/NullEstateData.cs @@ -0,0 +1,152 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.Null +{ + public class NullEstateStore : IEstateDataStore + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + +// private string m_connectionString; + +// private Dictionary m_knownEstates = new Dictionary(); + private EstateSettings m_estate = null; + + private EstateSettings GetEstate() + { + if (m_estate == null) + { + // This fools the initialization caller into thinking an estate was fetched (a check in OpenSimBase). + // The estate info is pretty empty so don't try banning anyone. + m_estate = new EstateSettings(); + m_estate.EstateID = 1; + m_estate.OnSave += StoreEstateSettings; + } + return m_estate; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public NullEstateStore() + { + } + + public NullEstateStore(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { +// m_connectionString = connectionString; + } + + private string[] FieldList + { + get { return new string[0]; } + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + return GetEstate(); + } + + public void StoreEstateSettings(EstateSettings es) + { + m_estate = es; + return; + } + + public EstateSettings LoadEstateSettings(int estateID) + { + return GetEstate(); + } + + public EstateSettings CreateNewEstate() + { + return new EstateSettings(); + } + + public List LoadEstateSettingsAll() + { + List allEstateSettings = new List(); + allEstateSettings.Add(GetEstate()); + return allEstateSettings; + } + + public List GetEstatesAll() + { + List result = new List(); + result.Add((int)GetEstate().EstateID); + return result; + } + + public List GetEstates(string search) + { + List result = new List(); + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + return false; + } + + public List GetRegions(int estateID) + { + List result = new List(); + return result; + } + + public bool DeleteEstate(int estateID) + { + return false; + } + + #region IEstateDataStore Members + + + public List GetEstatesByOwner(UUID ownerID) + { + return new List(); + } + + #endregion + } +} diff --git a/OpenSim/Data/Null/NullFriendsData.cs b/OpenSim/Data/Null/NullFriendsData.cs new file mode 100644 index 0000000000..473999fdb2 --- /dev/null +++ b/OpenSim/Data/Null/NullFriendsData.cs @@ -0,0 +1,148 @@ +/* + * 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 System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullFriendsData : IFriendsData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static List m_Data = new List(); + + public NullFriendsData(string connectionString, string realm) + { + } + + /// + /// Clear all friends data + /// + /// + /// This is required by unit tests to clear the static data between test runs. + /// + public static void Clear() + { + lock (m_Data) + m_Data.Clear(); + } + + public FriendsData[] GetFriends(UUID principalID) + { + return GetFriends(principalID.ToString()); + } + + /// + /// Tries to implement the Get [] semantics, but it cuts corners. + /// Specifically, it gets all friendships even if they weren't accepted yet. + /// + /// + /// + /// + public FriendsData[] GetFriends(string userID) + { + lock (m_Data) + { + List lst = m_Data.FindAll(fdata => + { + return fdata.PrincipalID == userID.ToString(); + }); + + if (lst != null) + { + lst.ForEach(f => + { + FriendsData f2 = m_Data.Find(candidateF2 => f.Friend == candidateF2.PrincipalID); + if (f2 != null) + f.Data["TheirFlags"] = f2.Data["Flags"]; + + // m_log.DebugFormat( + // "[NULL FRIENDS DATA]: Got {0} {1} {2} for {3}", + // f.Friend, f.Data["Flags"], f2 != null ? f.Data["TheirFlags"] : "not found!", f.PrincipalID); + }); + + // m_log.DebugFormat("[NULL FRIENDS DATA]: Got {0} friends for {1}", lst.Count, userID); + + return lst.ToArray(); + } + } + + return new FriendsData[0]; + } + + public bool Store(FriendsData data) + { + if (data == null) + return false; + +// m_log.DebugFormat( +// "[NULL FRIENDS DATA]: Storing {0} {1} {2}", data.PrincipalID, data.Friend, data.Data["Flags"]); + + lock (m_Data) + m_Data.Add(data); + + return true; + } + + public bool Delete(UUID principalID, string friend) + { + return Delete(principalID.ToString(), friend); + } + + public bool Delete(string userID, string friendID) + { + lock (m_Data) + { + List lst = m_Data.FindAll(delegate(FriendsData fdata) { return fdata.PrincipalID == userID.ToString(); }); + if (lst != null) + { + FriendsData friend = lst.Find(delegate(FriendsData fdata) { return fdata.Friend == friendID; }); + if (friendID != null) + { + // m_log.DebugFormat( + // "[NULL FRIENDS DATA]: Deleting friend {0} {1} for {2}", + // friend.Friend, friend.Data["Flags"], friend.PrincipalID); + + m_Data.Remove(friend); + return true; + } + } + } + + return false; + } + + } +} diff --git a/OpenSim/Data/Null/NullGenericDataHandler.cs b/OpenSim/Data/Null/NullGenericDataHandler.cs new file mode 100644 index 0000000000..dd9d190910 --- /dev/null +++ b/OpenSim/Data/Null/NullGenericDataHandler.cs @@ -0,0 +1,67 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + /// + /// Not a proper generic data handler yet - probably needs to actually store the data as well instead of relying + /// on descendent classes + /// + public class NullGenericDataHandler + { + protected List Get(string[] fields, string[] vals, List inputEntities) + { + List entities = inputEntities; + + for (int i = 0; i < fields.Length; i++) + { + entities + = entities.Where( + e => + { + FieldInfo fi = typeof(T).GetField(fields[i]); + if (fi == null) + throw new NotImplementedException(string.Format("No field {0} for val {1}", fields[i], vals[i])); + + return fi.GetValue(e).ToString() == vals[i]; + } + ).ToList(); + } + + return entities; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Null/NullInventoryData.cs b/OpenSim/Data/Null/NullInventoryData.cs new file mode 100644 index 0000000000..fe9ed017a1 --- /dev/null +++ b/OpenSim/Data/Null/NullInventoryData.cs @@ -0,0 +1,220 @@ +/* + * 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.Data.Null +{ + /// + /// This class is completely null. + /// + public class NullInventoryData : IInventoryDataPlugin + { + public string Version { get { return "1.0.0.0"; } } + + public void Initialise() + { + } + + public void Dispose() + { + // Do nothing. + } + + public string Name + { + get { return "Null Inventory Data Interface"; } + } + + public void Initialise(string connect) + { + } + + + /// + /// Returns all descendent folders of this folder. Does not return the parent folder itself. + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getFolderHierarchy(UUID parentID) + { + return new List(); + } + + /// + /// Returns a list of inventory items contained within the specified folder + /// + /// The UUID of the target folder + /// A List of InventoryItemBase items + public List getInventoryInFolder(UUID folderID) + { + return new List(); + } + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + return new List(); + } + + /// + /// Returns the users inventory root folder. + /// + /// The UUID of the user who is having inventory being returned + /// Root inventory folder, null if no root inventory folder was found + public InventoryFolderBase getUserRootFolder(UUID user) + { + return null; + } + + /// + /// Returns a list of inventory folders contained in the folder 'parentID' + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + return new List(); + } + + /// + /// Returns an inventory item by its UUID + /// + /// The UUID of the item to be returned + /// A class containing item information + public InventoryItemBase getInventoryItem(UUID item) + { + return null; + } + + /// + /// Returns a specified inventory folder by its UUID + /// + /// The UUID of the folder to be returned + /// A class containing folder information + public InventoryFolderBase getInventoryFolder(UUID folder) + { + return null; + } + + /// + /// Creates a new inventory item based on item + /// + /// The item to be created + public void addInventoryItem(InventoryItemBase item) + { + } + + /// + /// Updates an inventory item with item (updates based on ID) + /// + /// The updated item + public void updateInventoryItem(InventoryItemBase item) + { + } + + /// + /// + /// + /// + public void deleteInventoryItem(UUID item) + { + } + + /// + /// + /// + /// + public InventoryItemBase queryInventoryItem(UUID item) + { + return null; + } + + /// + /// + /// + /// + public InventoryFolderBase queryInventoryFolder(UUID folder) + { + return null; + } + + /// + /// Adds a new folder specified by folder + /// + /// The inventory folder + public void addInventoryFolder(InventoryFolderBase folder) + { + } + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + public void updateInventoryFolder(InventoryFolderBase folder) + { + } + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + public void moveInventoryFolder(InventoryFolderBase folder) + { + } + + /// + /// Deletes a folder. Thie will delete both the folder itself and its contents (items and descendent folders) + /// + /// The id of the folder + public void deleteInventoryFolder(UUID folder) + { + } + + /// + /// Returns all activated gesture-items in the inventory of the specified avatar. + /// + /// + /// The of the avatar + /// + /// + /// The list of gestures (s) + /// + public List fetchActiveGestures(UUID avatarID) + { + return new List(); + } + } +} diff --git a/OpenSim/Data/Null/NullPresenceData.cs b/OpenSim/Data/Null/NullPresenceData.cs new file mode 100644 index 0000000000..aff0b0b479 --- /dev/null +++ b/OpenSim/Data/Null/NullPresenceData.cs @@ -0,0 +1,234 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullPresenceData : IPresenceData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static NullPresenceData Instance; + + Dictionary m_presenceData = new Dictionary(); + + public NullPresenceData(string connectionString, string realm) + { + if (Instance == null) + { + Instance = this; + + //Console.WriteLine("[XXX] NullRegionData constructor"); + } + } + + public bool Store(PresenceData data) + { + if (Instance != this) + return Instance.Store(data); + +// m_log.DebugFormat("[NULL PRESENCE DATA]: Storing presence {0}", data.UserID); +// Console.WriteLine("HOME for " + data.UserID + " is " + (data.Data.ContainsKey("HomeRegionID") ? data.Data["HomeRegionID"] : "Not found")); + + m_presenceData[data.SessionID] = data; + return true; + } + + public PresenceData Get(UUID sessionID) + { + if (Instance != this) + return Instance.Get(sessionID); + + if (m_presenceData.ContainsKey(sessionID)) + { + return m_presenceData[sessionID]; + } + + return null; + } + + public void LogoutRegionAgents(UUID regionID) + { + if (Instance != this) + { + Instance.LogoutRegionAgents(regionID); + return; + } + + List toBeDeleted = new List(); + foreach (KeyValuePair kvp in m_presenceData) + if (kvp.Value.RegionID == regionID) + toBeDeleted.Add(kvp.Key); + + foreach (UUID u in toBeDeleted) + m_presenceData.Remove(u); + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + if (Instance != this) + return Instance.ReportAgent(sessionID, regionID); + + if (m_presenceData.ContainsKey(sessionID)) + { + m_presenceData[sessionID].RegionID = regionID; + return true; + } + + return false; + } + + public PresenceData[] Get(string field, string data) + { + if (Instance != this) + return Instance.Get(field, data); + +// m_log.DebugFormat( +// "[NULL PRESENCE DATA]: Getting presence data for field {0} with parameter {1}", field, data); + + List presences = new List(); + if (field == "UserID") + { + foreach (PresenceData p in m_presenceData.Values) + { + if (p.UserID == data) + { + presences.Add(p); +// Console.WriteLine("HOME for " + p.UserID + " is " + (p.Data.ContainsKey("HomeRegionID") ? p.Data["HomeRegionID"] : "Not found")); + } + } + + return presences.ToArray(); + } + else if (field == "SessionID") + { + UUID session = UUID.Zero; + if (!UUID.TryParse(data, out session)) + return presences.ToArray(); + + if (m_presenceData.ContainsKey(session)) + { + presences.Add(m_presenceData[session]); + return presences.ToArray(); + } + } + else if (field == "RegionID") + { + UUID region = UUID.Zero; + if (!UUID.TryParse(data, out region)) + return presences.ToArray(); + foreach (PresenceData p in m_presenceData.Values) + if (p.RegionID == region) + presences.Add(p); + return presences.ToArray(); + } + else + { + foreach (PresenceData p in m_presenceData.Values) + { + if (p.Data.ContainsKey(field) && p.Data[field] == data) + presences.Add(p); + } + return presences.ToArray(); + } + + return presences.ToArray(); + } + + + public bool Delete(string field, string data) + { +// m_log.DebugFormat( +// "[NULL PRESENCE DATA]: Deleting presence data for field {0} with parameter {1}", field, data); + + if (Instance != this) + return Instance.Delete(field, data); + + List presences = new List(); + if (field == "UserID") + { + foreach (KeyValuePair p in m_presenceData) + if (p.Value.UserID == data) + presences.Add(p.Key); + } + else if (field == "SessionID") + { + UUID session = UUID.Zero; + if (UUID.TryParse(data, out session)) + { + if (m_presenceData.ContainsKey(session)) + { + presences.Add(session); + } + } + } + else if (field == "RegionID") + { + UUID region = UUID.Zero; + if (UUID.TryParse(data, out region)) + { + foreach (KeyValuePair p in m_presenceData) + if (p.Value.RegionID == region) + presences.Add(p.Key); + } + } + else + { + foreach (KeyValuePair p in m_presenceData) + { + if (p.Value.Data.ContainsKey(field) && p.Value.Data[field] == data) + presences.Add(p.Key); + } + } + + foreach (UUID u in presences) + m_presenceData.Remove(u); + + if (presences.Count == 0) + return false; + + return true; + } + + public bool VerifyAgent(UUID agentId, UUID secureSessionID) + { + if (Instance != this) + return Instance.VerifyAgent(agentId, secureSessionID); + + return false; + } + + } +} diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs new file mode 100644 index 0000000000..d28cd9933b --- /dev/null +++ b/OpenSim/Data/Null/NullRegionData.cs @@ -0,0 +1,279 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; +using System.Reflection; +using log4net; +using RegionFlags = OpenSim.Framework.RegionFlags; + +namespace OpenSim.Data.Null +{ + public class NullRegionData : IRegionData + { + private static NullRegionData Instance = null; + + /// + /// Should we use the static instance for all invocations? + /// + private bool m_useStaticInstance = true; + +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + Dictionary m_regionData = new Dictionary(); + + public NullRegionData(string connectionString, string realm) + { +// m_log.DebugFormat( +// "[NULL REGION DATA]: Constructor got connectionString {0}, realm {1}", connectionString, realm); + + // The !static connection string is a hack so that regression tests can use this module without a high degree of fragility + // in having to deal with the static reference in the once-loaded NullRegionData class. + // + // In standalone operation, we have to use only one instance of this class since the login service and + // simulator have no other way of using a common data store. + if (connectionString == "!static") + m_useStaticInstance = false; + else if (Instance == null) + Instance = this; + } + + private delegate bool Matcher(string value); + + public List Get(string regionName, UUID scopeID) + { + if (m_useStaticInstance && Instance != this) + return Instance.Get(regionName, scopeID); + +// m_log.DebugFormat("[NULL REGION DATA]: Getting region {0}, scope {1}", regionName, scopeID); + + string cleanName = regionName.ToLower(); + + // Handle SQL wildcards + const string wildcard = "%"; + bool wildcardPrefix = false; + bool wildcardSuffix = false; + if (cleanName.Equals(wildcard)) + { + wildcardPrefix = wildcardSuffix = true; + cleanName = string.Empty; + } + else + { + if (cleanName.StartsWith(wildcard)) + { + wildcardPrefix = true; + cleanName = cleanName.Substring(1); + } + if (regionName.EndsWith(wildcard)) + { + wildcardSuffix = true; + cleanName = cleanName.Remove(cleanName.Length - 1); + } + } + + Matcher queryMatch; + if (wildcardPrefix && wildcardSuffix) + queryMatch = delegate(string s) { return s.Contains(cleanName); }; + else if (wildcardSuffix) + queryMatch = delegate(string s) { return s.StartsWith(cleanName); }; + else if (wildcardPrefix) + queryMatch = delegate(string s) { return s.EndsWith(cleanName); }; + else + queryMatch = delegate(string s) { return s.Equals(cleanName); }; + + // Find region data + List ret = new List(); + + lock (m_regionData) + { + foreach (RegionData r in m_regionData.Values) + { + // m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanName, r.RegionName.ToLower()); + if (queryMatch(r.RegionName.ToLower())) + ret.Add(r); + } + } + + if (ret.Count > 0) + return ret; + + return null; + } + + public RegionData Get(int posX, int posY, UUID scopeID) + { + if (m_useStaticInstance && Instance != this) + return Instance.Get(posX, posY, scopeID); + + List ret = new List(); + + lock (m_regionData) + { + foreach (RegionData r in m_regionData.Values) + { + if (r.posX == posX && r.posY == posY) + ret.Add(r); + } + } + + if (ret.Count > 0) + return ret[0]; + + return null; + } + + public RegionData Get(UUID regionID, UUID scopeID) + { + if (m_useStaticInstance && Instance != this) + return Instance.Get(regionID, scopeID); + + lock (m_regionData) + { + if (m_regionData.ContainsKey(regionID)) + return m_regionData[regionID]; + } + + return null; + } + + public List Get(int startX, int startY, int endX, int endY, UUID scopeID) + { + if (m_useStaticInstance && Instance != this) + return Instance.Get(startX, startY, endX, endY, scopeID); + + List ret = new List(); + + lock (m_regionData) + { + foreach (RegionData r in m_regionData.Values) + { + if (r.posX >= startX && r.posX <= endX && r.posY >= startY && r.posY <= endY) + ret.Add(r); + } + } + + return ret; + } + + public bool Store(RegionData data) + { + if (m_useStaticInstance && Instance != this) + return Instance.Store(data); + +// m_log.DebugFormat( +// "[NULL REGION DATA]: Storing region {0} {1}, scope {2}", data.RegionName, data.RegionID, data.ScopeID); + + lock (m_regionData) + { + m_regionData[data.RegionID] = data; + } + + return true; + } + + public bool SetDataItem(UUID regionID, string item, string value) + { + if (m_useStaticInstance && Instance != this) + return Instance.SetDataItem(regionID, item, value); + + lock (m_regionData) + { + if (!m_regionData.ContainsKey(regionID)) + return false; + + m_regionData[regionID].Data[item] = value; + } + + return true; + } + + public bool Delete(UUID regionID) + { + if (m_useStaticInstance && Instance != this) + return Instance.Delete(regionID); + +// m_log.DebugFormat("[NULL REGION DATA]: Deleting region {0}", regionID); + + lock (m_regionData) + { + if (!m_regionData.ContainsKey(regionID)) + return false; + + m_regionData.Remove(regionID); + } + + return true; + } + + public List GetDefaultRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultRegion, scopeID); + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + List regions = Get((int)RegionFlags.FallbackRegion, scopeID); + RegionDataDistanceCompare distanceComparer = new RegionDataDistanceCompare(x, y); + regions.Sort(distanceComparer); + return regions; + } + + public List GetHyperlinks(UUID scopeID) + { + return Get((int)RegionFlags.Hyperlink, scopeID); + } + + private List Get(int regionFlags, UUID scopeID) + { + if (Instance != this) + return Instance.Get(regionFlags, scopeID); + + List ret = new List(); + + lock (m_regionData) + { + foreach (RegionData r in m_regionData.Values) + { + if ((Convert.ToInt32(r.Data["flags"]) & regionFlags) != 0) + ret.Add(r); + } + } + + return ret; + } + } +} diff --git a/OpenSim/Data/Null/NullSimulationData.cs b/OpenSim/Data/Null/NullSimulationData.cs new file mode 100644 index 0000000000..deeaced294 --- /dev/null +++ b/OpenSim/Data/Null/NullSimulationData.cs @@ -0,0 +1,200 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Data.Null +{ + /// + /// NULL DataStore, do not store anything + /// + public class NullSimulationData : ISimulationDataStore + { + public NullSimulationData() + { + } + + public NullSimulationData(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string dbfile) + { + return; + } + + public void Dispose() + { + } + + public void StoreRegionSettings(RegionSettings rs) + { + } + + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + //This connector doesn't support the windlight module yet + //Return default LL windlight settings + return new RegionLightShareData(); + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + //This connector doesn't support the windlight module yet + } + + #region Environment Settings + + private Dictionary EnvironmentSettings = new Dictionary(); + + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + return EnvironmentSettings[regionUUID]; + } + return string.Empty; + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + lock (EnvironmentSettings) + { + EnvironmentSettings[regionUUID] = settings; + } + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + lock (EnvironmentSettings) + { + if (EnvironmentSettings.ContainsKey(regionUUID)) + EnvironmentSettings.Remove(regionUUID); + } + } + #endregion + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + RegionSettings rs = new RegionSettings(); + rs.RegionUUID = regionUUID; + return rs; + } + + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + } + + public void RemoveObject(UUID obj, UUID regionUUID) + { + } + + public void StorePrimInventory(UUID primID, ICollection items) + { + } + + public List LoadObjects(UUID regionUUID) + { + return new List(); + } + + Dictionary m_terrains = new Dictionary(); + public void StoreTerrain(TerrainData ter, UUID regionID) + { + if (m_terrains.ContainsKey(regionID)) + m_terrains.Remove(regionID); + m_terrains.Add(regionID, ter); + } + + // Legacy. Just don't do this. + public void StoreTerrain(double[,] ter, UUID regionID) + { + TerrainData terrData = new HeightmapTerrainData(ter); + StoreTerrain(terrData, regionID); + } + + // Legacy. Just don't do this. + // Returns 'null' if region not found + public double[,] LoadTerrain(UUID regionID) + { + if (m_terrains.ContainsKey(regionID)) + { + return m_terrains[regionID].GetDoubles(); + } + return null; + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + if (m_terrains.ContainsKey(regionID)) + { + return m_terrains[regionID]; + } + return null; + } + + public void RemoveLandObject(UUID globalID) + { + } + + public void StoreLandObject(ILandObject land) + { + } + + public List LoadLandObjects(UUID regionUUID) + { + return new List(); + } + + public void Shutdown() + { + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } +} diff --git a/OpenSim/Data/Null/NullUserAccountData.cs b/OpenSim/Data/Null/NullUserAccountData.cs new file mode 100644 index 0000000000..ec54dba2e8 --- /dev/null +++ b/OpenSim/Data/Null/NullUserAccountData.cs @@ -0,0 +1,197 @@ +/* + * 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 System.Text; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullUserAccountData : IUserAccountData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary m_DataByUUID = new Dictionary(); + private Dictionary m_DataByName = new Dictionary(); + private Dictionary m_DataByEmail = new Dictionary(); + + public NullUserAccountData(string connectionString, string realm) + { +// m_log.DebugFormat( +// "[NULL USER ACCOUNT DATA]: Initializing new NullUserAccountData with connectionString [{0}], realm [{1}]", +// connectionString, realm); + } + + /// + /// Tries to implement the Get [] semantics, but it cuts corners like crazy. + /// Specifically, it relies on the knowledge that the only Gets used are + /// keyed on PrincipalID, Email, and FirstName+LastName. + /// + /// + /// + /// + public UserAccountData[] Get(string[] fields, string[] values) + { +// if (m_log.IsDebugEnabled) +// { +// m_log.DebugFormat( +// "[NULL USER ACCOUNT DATA]: Called Get with fields [{0}], values [{1}]", +// string.Join(", ", fields), string.Join(", ", values)); +// } + + UserAccountData[] userAccounts = new UserAccountData[0]; + + List fieldsLst = new List(fields); + if (fieldsLst.Contains("PrincipalID")) + { + int i = fieldsLst.IndexOf("PrincipalID"); + UUID id = UUID.Zero; + if (UUID.TryParse(values[i], out id)) + if (m_DataByUUID.ContainsKey(id)) + userAccounts = new UserAccountData[] { m_DataByUUID[id] }; + } + else if (fieldsLst.Contains("FirstName") && fieldsLst.Contains("LastName")) + { + int findex = fieldsLst.IndexOf("FirstName"); + int lindex = fieldsLst.IndexOf("LastName"); + if (m_DataByName.ContainsKey(values[findex] + " " + values[lindex])) + { + userAccounts = new UserAccountData[] { m_DataByName[values[findex] + " " + values[lindex]] }; + } + } + else if (fieldsLst.Contains("Email")) + { + int i = fieldsLst.IndexOf("Email"); + if (m_DataByEmail.ContainsKey(values[i])) + userAccounts = new UserAccountData[] { m_DataByEmail[values[i]] }; + } + +// if (m_log.IsDebugEnabled) +// { +// StringBuilder sb = new StringBuilder(); +// foreach (UserAccountData uad in userAccounts) +// sb.AppendFormat("({0} {1} {2}) ", uad.FirstName, uad.LastName, uad.PrincipalID); +// +// m_log.DebugFormat( +// "[NULL USER ACCOUNT DATA]: Returning {0} user accounts out of {1}: [{2}]", userAccounts.Length, m_DataByName.Count, sb); +// } + + return userAccounts; + } + + public bool Store(UserAccountData data) + { + if (data == null) + return false; + + m_log.DebugFormat( + "[NULL USER ACCOUNT DATA]: Storing user account {0} {1} {2} {3}", + data.FirstName, data.LastName, data.PrincipalID, this.GetHashCode()); + + m_DataByUUID[data.PrincipalID] = data; + m_DataByName[data.FirstName + " " + data.LastName] = data; + if (data.Data.ContainsKey("Email") && data.Data["Email"] != null && data.Data["Email"] != string.Empty) + m_DataByEmail[data.Data["Email"]] = data; + +// m_log.DebugFormat("m_DataByUUID count is {0}, m_DataByName count is {1}", m_DataByUUID.Count, m_DataByName.Count); + + return true; + } + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { +// m_log.DebugFormat( +// "[NULL USER ACCOUNT DATA]: Called GetUsers with scope [{0}], query [{1}]", scopeID, query); + + string[] words = query.Split(new char[] { ' ' }); + + for (int i = 0; i < words.Length; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + List lst = new List(m_DataByName.Keys); + if (words.Length == 1) + { + lst = lst.FindAll(delegate(string s) { return s.StartsWith(words[0]); }); + } + else + { + lst = lst.FindAll(delegate(string s) { return s.Contains(words[0]) || s.Contains(words[1]); }); + } + + if (lst == null || (lst != null && lst.Count == 0)) + return new UserAccountData[0]; + + UserAccountData[] result = new UserAccountData[lst.Count]; + int n = 0; + foreach (string key in lst) + result[n++] = m_DataByName[key]; + + return result; + } + + public bool Delete(string field, string val) + { + // Only delete by PrincipalID + if (field.Equals("PrincipalID")) + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(val, out uuid) && m_DataByUUID.ContainsKey(uuid)) + { + UserAccountData account = m_DataByUUID[uuid]; + m_DataByUUID.Remove(uuid); + if (m_DataByName.ContainsKey(account.FirstName + " " + account.LastName)) + m_DataByName.Remove(account.FirstName + " " + account.LastName); + if (account.Data.ContainsKey("Email") && account.Data["Email"] != string.Empty && m_DataByEmail.ContainsKey(account.Data["Email"])) + m_DataByEmail.Remove(account.Data["Email"]); + + return true; + } + } + + return false; + } + } +} diff --git a/OpenSim/Data/Null/NullXGroupData.cs b/OpenSim/Data/Null/NullXGroupData.cs new file mode 100644 index 0000000000..3fa9451f27 --- /dev/null +++ b/OpenSim/Data/Null/NullXGroupData.cs @@ -0,0 +1,86 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Data.Null +{ + public class NullXGroupData : IXGroupData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary m_groups = new Dictionary(); + + public NullXGroupData(string connectionString, string realm) {} + + public bool StoreGroup(XGroup group) + { + lock (m_groups) + { + m_groups[group.groupID] = group.Clone(); + } + + return true; + } + + public XGroup GetGroup(UUID groupID) + { + XGroup group = null; + + lock (m_groups) + m_groups.TryGetValue(groupID, out group); + + return group; + } + + public Dictionary GetGroups() + { + Dictionary groupsClone = new Dictionary(); + + lock (m_groups) + foreach (XGroup group in m_groups.Values) + groupsClone[group.groupID] = group.Clone(); + + return groupsClone; + } + + public bool DeleteGroup(UUID groupID) + { + lock (m_groups) + return m_groups.Remove(groupID); + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Null/Properties/AssemblyInfo.cs b/OpenSim/Data/Null/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..a827bd0f83 --- /dev/null +++ b/OpenSim/Data/Null/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Data.Null")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.Null")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("b4a1656d-de22-4080-a970-fd8166acbf16")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Data/PGSQL/PGSQLAgentPreferencesData.cs b/OpenSim/Data/PGSQL/PGSQLAgentPreferencesData.cs new file mode 100644 index 0000000000..20612fecb3 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAgentPreferencesData.cs @@ -0,0 +1,64 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLAgentPreferencesData : PGSQLGenericTableHandler, IAgentPreferencesData + { + public PGSQLAgentPreferencesData(string connectionString, string realm) + : base(connectionString, realm, "AgentPrefs") + { + } + + public AgentPreferencesData GetPrefs(UUID agentID) + { + // Until someone sends in a table that works + return null; + //AgentPreferencesData[] ret = Get("PrincipalID", agentID.ToString()); + + //if (ret.Length == 0) + // return null; + + //return ret[0]; + } + + public override bool Store(AgentPreferencesData row) + { + // Until someone sends in a table that works + return false; + } + + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLAssetData.cs b/OpenSim/Data/PGSQL/PGSQLAssetData.cs new file mode 100644 index 0000000000..5d8b0a2c18 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAssetData.cs @@ -0,0 +1,316 @@ +/* + * 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.Data; +using System.Reflection; +using System.Collections.Generic; +using OpenMetaverse; +using log4net; +using OpenSim.Framework; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Asset server + /// + public class PGSQLAssetData : AssetDataBase + { + private const string _migrationStore = "AssetStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private long m_ticksToEpoch; + /// + /// Database manager + /// + private PGSQLManager m_database; + private string m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #region IPlugin Members + + override public void Dispose() { } + + /// + /// Initialises asset interface + /// + // [Obsolete("Cannot be default-initialized!")] + override public void Initialise() + { + m_log.Info("[PGSQLAssetData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// Initialises asset interface + /// + /// + /// a string instead of file, if someone writes the support + /// + /// connect string + override public void Initialise(string connectionString) + { + m_ticksToEpoch = new System.DateTime(1970, 1, 1).Ticks; + + m_database = new PGSQLManager(connectionString); + m_connectionString = connectionString; + + //New migration to check for DB changes + m_database.CheckMigration(_migrationStore); + } + + /// + /// Database provider version. + /// + override public string Version + { + get { return m_database.getVersion(); } + } + + /// + /// The name of this DB provider. + /// + override public string Name + { + get { return "PGSQL Asset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from m_database + /// + /// the asset UUID + /// + override public AssetBase GetAsset(UUID assetID) + { + string sql = "SELECT * FROM assets WHERE id = :id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("id", assetID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + AssetBase asset = new AssetBase( + DBGuid.FromDB(reader["id"]), + (string)reader["name"], + Convert.ToSByte(reader["assetType"]), + reader["creatorid"].ToString() + ); + // Region Main + asset.Description = (string)reader["description"]; + asset.Local = Convert.ToBoolean(reader["local"]); + asset.Temporary = Convert.ToBoolean(reader["temporary"]); + asset.Flags = (AssetFlags)(Convert.ToInt32(reader["asset_flags"])); + asset.Data = (byte[])reader["data"]; + return asset; + } + return null; // throw new Exception("No rows to return"); + } + } + } + + /// + /// Create asset in m_database + /// + /// the asset + override public void StoreAsset(AssetBase asset) + { + + string sql = + @"UPDATE assets set name = :name, description = :description, " + "\"assetType\" " + @" = :assetType, + local = :local, temporary = :temporary, creatorid = :creatorid, data = :data + WHERE id=:id; + + INSERT INTO assets + (id, name, description, " + "\"assetType\" " + @", local, + temporary, create_time, access_time, creatorid, asset_flags, data) + Select :id, :name, :description, :assetType, :local, + :temporary, :create_time, :access_time, :creatorid, :asset_flags, :data + Where not EXISTS(SELECT * FROM assets WHERE id=:id) + "; + + string assetName = asset.Name; + if (asset.Name.Length > AssetBase.MAX_ASSET_NAME) + { + assetName = asset.Name.Substring(0, AssetBase.MAX_ASSET_NAME); + m_log.WarnFormat( + "[ASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > AssetBase.MAX_ASSET_DESC) + { + assetDescription = asset.Description.Substring(0, AssetBase.MAX_ASSET_DESC); + m_log.WarnFormat( + "[ASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); + command.Parameters.Add(m_database.CreateParameter("id", asset.FullID)); + command.Parameters.Add(m_database.CreateParameter("name", assetName)); + command.Parameters.Add(m_database.CreateParameter("description", assetDescription)); + command.Parameters.Add(m_database.CreateParameter("assetType", asset.Type)); + command.Parameters.Add(m_database.CreateParameter("local", asset.Local)); + command.Parameters.Add(m_database.CreateParameter("temporary", asset.Temporary)); + command.Parameters.Add(m_database.CreateParameter("access_time", now)); + command.Parameters.Add(m_database.CreateParameter("create_time", now)); + command.Parameters.Add(m_database.CreateParameter("asset_flags", (int)asset.Flags)); + command.Parameters.Add(m_database.CreateParameter("creatorid", asset.Metadata.CreatorID)); + command.Parameters.Add(m_database.CreateParameter("data", asset.Data)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch(Exception e) + { + m_log.Error("[ASSET DB]: Error storing item :" + e.Message + " sql "+sql); + } + } + } + + +// Commented out since currently unused - this probably should be called in GetAsset() +// private void UpdateAccessTime(AssetBase asset) +// { +// using (AutoClosingSqlCommand cmd = m_database.Query("UPDATE assets SET access_time = :access_time WHERE id=:id")) +// { +// int now = (int)((System.DateTime.Now.Ticks - m_ticksToEpoch) / 10000000); +// cmd.Parameters.AddWithValue(":id", asset.FullID.ToString()); +// cmd.Parameters.AddWithValue(":access_time", now); +// try +// { +// cmd.ExecuteNonQuery(); +// } +// catch (Exception e) +// { +// m_log.Error(e.ToString()); +// } +// } +// } + + /// + /// Check if the assets exist in the database. + /// + /// The assets' IDs + /// For each asset: true if it exists, false otherwise + public override bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exist = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format("SELECT id FROM assets WHERE id IN ({0})", ids); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + UUID id = DBGuid.FromDB(reader["id"]); + exist.Add(id); + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exist.Contains(uuids[i]); + return results; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public override List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + string sql = @" SELECT id, name, description, " + "\"assetType\"" + @", temporary, creatorid + FROM assets + order by id + limit :stop + offset :start;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("start", start)); + cmd.Parameters.Add(m_database.CreateParameter("stop", start + count - 1)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.FullID = DBGuid.FromDB(reader["id"]); + metadata.Name = (string)reader["name"]; + metadata.Description = (string)reader["description"]; + metadata.Type = Convert.ToSByte(reader["assetType"]); + metadata.Temporary = Convert.ToBoolean(reader["temporary"]); + metadata.CreatorID = (string)reader["creatorid"]; + retList.Add(metadata); + } + } + } + + return retList; + } + + public override bool Delete(string id) + { + return false; + } + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs b/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs new file mode 100644 index 0000000000..d174112f0f --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAuthenticationData.cs @@ -0,0 +1,254 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using System.Data; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLAuthenticationData : IAuthenticationData + { + private string m_Realm; + private List m_ColumnNames = null; + private int m_LastExpire = 0; + private string m_ConnectionString; + private PGSQLManager m_database; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLAuthenticationData(string connectionString, string realm) + { + m_Realm = realm; + m_ConnectionString = connectionString; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "AuthStore"); + m_database = new PGSQLManager(m_ConnectionString); + m.Update(); + } + } + + public AuthenticationData Get(UUID principalID) + { + AuthenticationData ret = new AuthenticationData(); + ret.Data = new Dictionary(); + + string sql = string.Format("select * from {0} where uuid = :principalID", m_Realm); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (result.Read()) + { + ret.PrincipalID = principalID; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "UUID"||s == "uuid") + continue; + + ret.Data[s] = result[s].ToString(); + } + return ret; + } + } + } + return null; + } + + public bool Store(AuthenticationData data) + { + if (data.Data.ContainsKey("UUID")) + data.Data.Remove("UUID"); + if (data.Data.ContainsKey("uuid")) + data.Data.Remove("uuid"); + + /* + Dictionary oAuth = new Dictionary(); + + foreach (KeyValuePair oDado in data.Data) + { + if (oDado.Key != oDado.Key.ToLower()) + { + oAuth.Add(oDado.Key.ToLower(), oDado.Value); + } + } + foreach (KeyValuePair oDado in data.Data) + { + if (!oAuth.ContainsKey(oDado.Key.ToLower())) { + oAuth.Add(oDado.Key.ToLower(), oDado.Value); + } + } + */ + string[] fields = new List(data.Data.Keys).ToArray(); + StringBuilder updateBuilder = new StringBuilder(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + updateBuilder.AppendFormat("update {0} set ", m_Realm); + + bool first = true; + foreach (string field in fields) + { + if (!first) + updateBuilder.Append(", "); + updateBuilder.AppendFormat("\"{0}\" = :{0}",field); + + first = false; + + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field])); + } + + updateBuilder.Append(" where uuid = :principalID"); + + cmd.CommandText = updateBuilder.ToString(); + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("principalID", data.PrincipalID)); + + conn.Open(); + if (cmd.ExecuteNonQuery() < 1) + { + StringBuilder insertBuilder = new StringBuilder(); + + insertBuilder.AppendFormat("insert into {0} (uuid, \"", m_Realm); + insertBuilder.Append(String.Join("\", \"", fields)); + insertBuilder.Append("\") values (:principalID, :"); + insertBuilder.Append(String.Join(", :", fields)); + insertBuilder.Append(")"); + + cmd.CommandText = insertBuilder.ToString(); + + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + } + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + string sql = string.Format("update {0} set {1} = :{1} where uuid = :UUID", m_Realm, item); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + string sql = "insert into tokens (uuid, token, validity) values (:principalID, :token, :lifetime)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("token", token)); + cmd.Parameters.Add(m_database.CreateParameter("lifetime", DateTime.Now.AddMinutes(lifetime))); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + return true; + } + } + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + DateTime validDate = DateTime.Now.AddMinutes(lifetime); + string sql = "update tokens set validity = :validDate where uuid = :principalID and token = :token and validity > (CURRENT_DATE + CURRENT_TIME)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("token", token)); + cmd.Parameters.Add(m_database.CreateParameter("validDate", validDate)); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + return true; + } + } + return false; + } + + private void DoExpire() + { + DateTime currentDateTime = DateTime.Now; + string sql = "delete from tokens where validity < :currentDateTime"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + cmd.Parameters.Add(m_database.CreateParameter("currentDateTime", currentDateTime)); + cmd.ExecuteNonQuery(); + } + m_LastExpire = System.Environment.TickCount; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLAvatarData.cs b/OpenSim/Data/PGSQL/PGSQLAvatarData.cs new file mode 100644 index 0000000000..d9c49052d4 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLAvatarData.cs @@ -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; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; +using NpgsqlTypes; + + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for Avatar Storage + /// + public class PGSQLAvatarData : PGSQLGenericTableHandler, + IAvatarData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLAvatarData(string connectionString, string realm) : + base(connectionString, realm, "Avatar") + { + } + + public bool Delete(UUID principalID, string name) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("DELETE FROM {0} where \"PrincipalID\" = :PrincipalID and \"Name\" = :Name", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("Name", name)); + cmd.Connection = conn; + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + + return false; + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLEstateData.cs b/OpenSim/Data/PGSQL/PGSQLEstateData.cs new file mode 100644 index 0000000000..b5ca235200 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLEstateData.cs @@ -0,0 +1,602 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using System.Data; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLEstateStore : IEstateDataStore + { + private const string _migrationStore = "EstateStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private PGSQLManager _Database; + private string m_connectionString; + private FieldInfo[] _Fields; + private Dictionary _FieldMap = new Dictionary(); + + #region Public methods + + public PGSQLEstateStore() + { + } + + public PGSQLEstateStore(string connectionString) + { + Initialise(connectionString); + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + /// + /// Initialises the estatedata class. + /// + /// connectionString. + public void Initialise(string connectionString) + { + if (!string.IsNullOrEmpty(connectionString)) + { + m_connectionString = connectionString; + _Database = new PGSQLManager(connectionString); + } + + //Migration settings + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "EstateStore"); + m.Update(); + } + + //Interesting way to get parameters! Maybe implement that also with other types + Type t = typeof(EstateSettings); + _Fields = t.GetFields(BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + foreach (FieldInfo f in _Fields) + { + if (f.Name.Substring(0, 2) == "m_") + _FieldMap[f.Name.Substring(2)] = f; + } + } + + /// + /// Loads the estate settings. + /// + /// region ID. + /// + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + EstateSettings es = new EstateSettings(); + + string sql = "select estate_settings.\"" + String.Join("\",estate_settings.\"", FieldList) + + "\" from estate_map left join estate_settings on estate_map.\"EstateID\" = estate_settings.\"EstateID\" " + + " where estate_settings.\"EstateID\" is not null and \"RegionID\" = :RegionID"; + + bool insertEstate = false; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionID", regionID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + foreach (string name in FieldList) + { + FieldInfo f = _FieldMap[name]; + object v = reader[name]; + if (f.FieldType == typeof(bool)) + { + f.SetValue(es, v); + } + else if (f.FieldType == typeof(UUID)) + { + UUID estUUID = UUID.Zero; + + UUID.TryParse(v.ToString(), out estUUID); + + f.SetValue(es, estUUID); + } + else if (f.FieldType == typeof(string)) + { + f.SetValue(es, v.ToString()); + } + else if (f.FieldType == typeof(UInt32)) + { + f.SetValue(es, Convert.ToUInt32(v)); + } + else if (f.FieldType == typeof(Single)) + { + f.SetValue(es, Convert.ToSingle(v)); + } + else + f.SetValue(es, v); + } + } + else + { + insertEstate = true; + } + } + } + + if (insertEstate && create) + { + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); + } + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + //Set event + es.OnSave += StoreEstateSettings; + return es; + } + + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private void DoCreate(EstateSettings es) + { + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = string.Format("insert into estate_settings (\"{0}\") values ( :{1} )", String.Join("\",\"", names.ToArray()), String.Join(", :", names.ToArray())); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand insertCommand = new NpgsqlCommand(sql, conn)) + { + insertCommand.CommandText = sql; + + foreach (string name in names) + { + insertCommand.Parameters.Add(_Database.CreateParameter("" + name, _FieldMap[name].GetValue(es))); + } + //NpgsqlParameter idParameter = new NpgsqlParameter("ID", SqlDbType.Int); + //idParameter.Direction = ParameterDirection.Output; + //insertCommand.Parameters.Add(idParameter); + conn.Open(); + + es.EstateID = 100; + + if (insertCommand.ExecuteNonQuery() > 0) + { + insertCommand.CommandText = "Select cast(lastval() as int) as ID ;"; + + using (NpgsqlDataReader result = insertCommand.ExecuteReader()) + { + if (result.Read()) + { + es.EstateID = (uint)result.GetInt32(0); + } + } + } + + } + + //TODO check if this is needed?? + es.Save(); + } + + /// + /// Stores the estate settings. + /// + /// estate settings + public void StoreEstateSettings(EstateSettings es) + { + List names = new List(FieldList); + + names.Remove("EstateID"); + + string sql = string.Format("UPDATE estate_settings SET "); + foreach (string name in names) + { + sql += "\"" + name + "\" = :" + name + ", "; + } + sql = sql.Remove(sql.LastIndexOf(",")); + sql += " WHERE \"EstateID\" = :EstateID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + foreach (string name in names) + { + cmd.Parameters.Add(_Database.CreateParameter("" + name, _FieldMap[name].GetValue(es))); + } + + cmd.Parameters.Add(_Database.CreateParameter("EstateID", es.EstateID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + SaveBanList(es); + SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers); + SaveUUIDList(es.EstateID, "estate_users", es.EstateAccess); + SaveUUIDList(es.EstateID, "estate_groups", es.EstateGroups); + } + + #endregion + + #region Private methods + + private string[] FieldList + { + get { return new List(_FieldMap.Keys).ToArray(); } + } + + private void LoadBanList(EstateSettings es) + { + es.ClearBans(); + + string sql = "select \"bannedUUID\" from estateban where \"EstateID\" = :EstateID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + NpgsqlParameter idParameter = new NpgsqlParameter("EstateID", DbType.Int32); + idParameter.Value = es.EstateID; + cmd.Parameters.Add(idParameter); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + EstateBan eb = new EstateBan(); + + eb.BannedUserID = new UUID((Guid)reader["bannedUUID"]); //uuid; + eb.BannedHostAddress = "0.0.0.0"; + eb.BannedHostIPMask = "0.0.0.0"; + es.AddBan(eb); + } + } + } + } + + private UUID[] LoadUUIDList(uint estateID, string table) + { + List uuids = new List(); + + string sql = string.Format("select uuid from {0} where \"EstateID\" = :EstateID", table); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("EstateID", estateID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + uuids.Add(new UUID((Guid)reader["uuid"])); //uuid); + } + } + } + + return uuids.ToArray(); + } + + private void SaveBanList(EstateSettings es) + { + //Delete first + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = conn.CreateCommand()) + { + cmd.CommandText = "delete from estateban where \"EstateID\" = :EstateID"; + cmd.Parameters.AddWithValue("EstateID", (int)es.EstateID); + cmd.ExecuteNonQuery(); + + //Insert after + cmd.CommandText = "insert into estateban (\"EstateID\", \"bannedUUID\",\"bannedIp\", \"bannedIpHostMask\", \"bannedNameMask\") values ( :EstateID, :bannedUUID, '','','' )"; + cmd.Parameters.AddWithValue("bannedUUID", Guid.Empty); + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters["bannedUUID"].Value = b.BannedUserID.Guid; + cmd.ExecuteNonQuery(); + } + } + } + } + + private void SaveUUIDList(uint estateID, string table, UUID[] data) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = conn.CreateCommand()) + { + cmd.Parameters.AddWithValue("EstateID", (int)estateID); + cmd.CommandText = string.Format("delete from {0} where \"EstateID\" = :EstateID", table); + cmd.ExecuteNonQuery(); + + cmd.CommandText = string.Format("insert into {0} (\"EstateID\", uuid) values ( :EstateID, :uuid )", table); + cmd.Parameters.AddWithValue("uuid", Guid.Empty); + foreach (UUID uuid in data) + { + cmd.Parameters["uuid"].Value = uuid.Guid; //.ToString(); //TODO check if this works + cmd.ExecuteNonQuery(); + } + } + } + } + + public EstateSettings LoadEstateSettings(int estateID) + { + EstateSettings es = new EstateSettings(); + string sql = "select estate_settings.\"" + String.Join("\",estate_settings.\"", FieldList) + "\" from estate_settings where \"EstateID\" = :EstateID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateID", (int)estateID); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + foreach (string name in FieldList) + { + FieldInfo f = _FieldMap[name]; + object v = reader[name]; + if (f.FieldType == typeof(bool)) + { + f.SetValue(es, Convert.ToInt32(v) != 0); + } + else if (f.FieldType == typeof(UUID)) + { + f.SetValue(es, new UUID((Guid)v)); // uuid); + } + else if (f.FieldType == typeof(string)) + { + f.SetValue(es, v.ToString()); + } + else if (f.FieldType == typeof(UInt32)) + { + f.SetValue(es, Convert.ToUInt32(v)); + } + else if (f.FieldType == typeof(Single)) + { + f.SetValue(es, Convert.ToSingle(v)); + } + else + f.SetValue(es, v); + } + } + + } + } + } + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + //Set event + es.OnSave += StoreEstateSettings; + return es; + + } + + public List LoadEstateSettingsAll() + { + List allEstateSettings = new List(); + + List allEstateIds = GetEstatesAll(); + + foreach (int estateId in allEstateIds) + allEstateSettings.Add(LoadEstateSettings(estateId)); + + return allEstateSettings; + } + + public List GetEstates(string search) + { + List result = new List(); + string sql = "select \"EstateID\" from estate_settings where lower(\"EstateName\") = lower(:EstateName)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateName", search); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public List GetEstatesAll() + { + List result = new List(); + string sql = "select \"EstateID\" from estate_settings"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public List GetEstatesByOwner(UUID ownerID) + { + List result = new List(); + string sql = "select \"EstateID\" from estate_settings where \"EstateOwner\" = :EstateOwner"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateOwner", ownerID); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(Convert.ToInt32(reader["EstateID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + string deleteSQL = "delete from estate_map where \"RegionID\" = :RegionID"; + string insertSQL = "insert into estate_map values (:RegionID, :EstateID)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + + NpgsqlTransaction transaction = conn.BeginTransaction(); + + try + { + using (NpgsqlCommand cmd = new NpgsqlCommand(deleteSQL, conn)) + { + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("RegionID", regionID.Guid); + + cmd.ExecuteNonQuery(); + } + + using (NpgsqlCommand cmd = new NpgsqlCommand(insertSQL, conn)) + { + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue("RegionID", regionID.Guid); + cmd.Parameters.AddWithValue("EstateID", estateID); + + int ret = cmd.ExecuteNonQuery(); + + if (ret != 0) + transaction.Commit(); + else + transaction.Rollback(); + + return (ret != 0); + } + } + catch (Exception ex) + { + m_log.Error("[REGION DB]: LinkRegion failed: " + ex.Message); + transaction.Rollback(); + } + } + return false; + } + + public List GetRegions(int estateID) + { + List result = new List(); + string sql = "select \"RegionID\" from estate_map where \"EstateID\" = :EstateID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddWithValue("EstateID", estateID); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + result.Add(DBGuid.FromDB(reader["RegionID"])); + } + reader.Close(); + } + } + } + + return result; + } + + public bool DeleteEstate(int estateID) + { + // TODO: Implementation! + return false; + } + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLFramework.cs b/OpenSim/Data/PGSQL/PGSQLFramework.cs new file mode 100644 index 0000000000..1028e4ea86 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLFramework.cs @@ -0,0 +1,111 @@ +/* + * 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.Data; +using System.Reflection; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A database interface class to a user profile storage system + /// + public class PGSqlFramework + { + private static readonly log4net.ILog m_log = + log4net.LogManager.GetLogger( + System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_connectionString; + protected object m_dbLock = new object(); + + protected PGSqlFramework(string connectionString) + { + m_connectionString = connectionString; + InitializeMonoSecurity(); + } + + public void InitializeMonoSecurity() + { + if (!Util.IsPlatformMono) + { + + if (AppDomain.CurrentDomain.GetData("MonoSecurityPostgresAdded") == null) + { + AppDomain.CurrentDomain.SetData("MonoSecurityPostgresAdded", "true"); + + AppDomain currentDomain = AppDomain.CurrentDomain; + currentDomain.AssemblyResolve += new ResolveEventHandler(ResolveEventHandlerMonoSec); + } + } + } + + private System.Reflection.Assembly ResolveEventHandlerMonoSec(object sender, ResolveEventArgs args) + { + Assembly MyAssembly = null; + + if (args.Name.Substring(0, args.Name.IndexOf(",")) == "Mono.Security") + { + MyAssembly = Assembly.LoadFrom("lib/NET/Mono.Security.dll"); + } + + //Return the loaded assembly. + return MyAssembly; + } + ////////////////////////////////////////////////////////////// + // + // All non queries are funneled through one connection + // to increase performance a little + // + protected int ExecuteNonQuery(NpgsqlCommand cmd) + { + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + try + { + return cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error(e.Message, e); + return 0; + } + } + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLFriendsData.cs b/OpenSim/Data/PGSQL/PGSQLFriendsData.cs new file mode 100644 index 0000000000..a8413531fd --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLFriendsData.cs @@ -0,0 +1,116 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLFriendsData : PGSQLGenericTableHandler, IFriendsData + { + public PGSQLFriendsData(string connectionString, string realm) + : base(connectionString, realm, "FriendsStore") + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "FriendsStore"); + m.Update(); + } + } + + + public override bool Delete(string principalID, string friend) + { + UUID princUUID = UUID.Zero; + + bool ret = UUID.TryParse(principalID, out princUUID); + + if (ret) + return Delete(princUUID, friend); + else + return false; + } + + public bool Delete(UUID principalID, string friend) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"PrincipalID\" = :PrincipalID and \"Friend\" = :Friend", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("Friend", friend)); + cmd.Connection = conn; + conn.Open(); + cmd.ExecuteNonQuery(); + + return true; + } + } + + public FriendsData[] GetFriends(string principalID) + { + UUID princUUID = UUID.Zero; + + bool ret = UUID.TryParse(principalID, out princUUID); + + if (ret) + return GetFriends(princUUID); + else + return new FriendsData[0]; + } + + public FriendsData[] GetFriends(UUID principalID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("select a.*,case when b.\"Flags\" is null then '-1' else b.\"Flags\" end as \"TheirFlags\" from {0} as a " + + " left join {0} as b on a.\"PrincipalID\" = b.\"Friend\" and a.\"Friend\" = b.\"PrincipalID\" " + + " where a.\"PrincipalID\" = :PrincipalID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID.ToString())); + cmd.Connection = conn; + conn.Open(); + return DoQuery(cmd); + } + } + + public FriendsData[] GetFriends(Guid principalID) + { + return GetFriends(principalID); + } + + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs b/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs new file mode 100644 index 0000000000..826c6fc11a --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGenericTableHandler.cs @@ -0,0 +1,537 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using System.Text; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLGenericTableHandler : PGSqlFramework where T : class, new() + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_ConnectionString; + protected PGSQLManager m_database; //used for parameter type translation + protected Dictionary m_Fields = + new Dictionary(); + + protected Dictionary m_FieldTypes = new Dictionary(); + + protected List m_ColumnNames = null; + protected string m_Realm; + protected FieldInfo m_DataField = null; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLGenericTableHandler(string connectionString, + string realm, string storeName) + : base(connectionString) + { + m_Realm = realm; + + m_ConnectionString = connectionString; + + if (storeName != String.Empty) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, storeName); + m.Update(); + } + + } + m_database = new PGSQLManager(m_ConnectionString); + + Type t = typeof(T); + FieldInfo[] fields = t.GetFields(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + LoadFieldTypes(); + + if (fields.Length == 0) + return; + + foreach (FieldInfo f in fields) + { + if (f.Name != "Data") + m_Fields[f.Name] = f; + else + m_DataField = f; + } + + } + + private void LoadFieldTypes() + { + m_FieldTypes = new Dictionary(); + + string query = string.Format(@"select column_name,data_type + from INFORMATION_SCHEMA.COLUMNS + where table_name = lower('{0}'); + + ", m_Realm); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + m_FieldTypes.Add((string)rdr[0], (string)rdr[1]); + } + } + } + } + + private void CheckColumnNames(NpgsqlDataReader reader) + { + if (m_ColumnNames != null) + return; + + m_ColumnNames = new List(); + + DataTable schemaTable = reader.GetSchemaTable(); + + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null && + (!m_Fields.ContainsKey(row["ColumnName"].ToString()))) + m_ColumnNames.Add(row["ColumnName"].ToString()); + + } + } + + // TODO GET CONSTRAINTS FROM POSTGRESQL + private List GetConstraints() + { + List constraints = new List(); + string query = string.Format(@"SELECT kcu.column_name + FROM information_schema.table_constraints tc + LEFT JOIN information_schema.key_column_usage kcu + ON tc.constraint_catalog = kcu.constraint_catalog + AND tc.constraint_schema = kcu.constraint_schema + AND tc.constraint_name = kcu.constraint_name + + LEFT JOIN information_schema.referential_constraints rc + ON tc.constraint_catalog = rc.constraint_catalog + AND tc.constraint_schema = rc.constraint_schema + AND tc.constraint_name = rc.constraint_name + + LEFT JOIN information_schema.constraint_column_usage ccu + ON rc.unique_constraint_catalog = ccu.constraint_catalog + AND rc.unique_constraint_schema = ccu.constraint_schema + AND rc.unique_constraint_name = ccu.constraint_name + + where tc.table_name = lower('{0}') + and lower(tc.constraint_type) in ('primary key') + and kcu.column_name is not null + ;", m_Realm); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + constraints.Add((string)rdr[0]); + } + } + return constraints; + } + } + + public virtual T[] Get(string field, string key) + { + return Get(new string[] { field }, new string[] { key }); + } + + public virtual T[] Get(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return new T[0]; + + List terms = new List(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + for (int i = 0; i < fields.Length; i++) + { + if ( m_FieldTypes.ContainsKey(fields[i]) ) + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i], m_FieldTypes[fields[i]])); + else + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i])); + + terms.Add(" \"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" AND ", terms.ToArray()); + + string query = String.Format("SELECT * FROM {0} WHERE {1}", + m_Realm, where); + + cmd.Connection = conn; + cmd.CommandText = query; + conn.Open(); + return DoQuery(cmd); + } + } + + protected T[] DoQuery(NpgsqlCommand cmd) + { + List result = new List(); + if (cmd.Connection == null) + { + cmd.Connection = new NpgsqlConnection(m_connectionString); + } + if (cmd.Connection.State == ConnectionState.Closed) + { + cmd.Connection.Open(); + } + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader == null) + return new T[0]; + + CheckColumnNames(reader); + + while (reader.Read()) + { + T row = new T(); + + foreach (string name in m_Fields.Keys) + { + if (m_Fields[name].GetValue(row) is bool) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].GetValue(row) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(reader[name].ToString(), out uuid); + m_Fields[name].SetValue(row, uuid); + } + else if (m_Fields[name].GetValue(row) is int) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else + { + m_Fields[name].SetValue(row, reader[name]); + } + } + + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); + + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + + if (data[col] == null) + data[col] = String.Empty; + } + + m_DataField.SetValue(row, data); + } + + result.Add(row); + } + return result.ToArray(); + } + } + + public virtual T[] Get(string where) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + string query = String.Format("SELECT * FROM {0} WHERE {1}", + m_Realm, where); + cmd.Connection = conn; + cmd.CommandText = query; + //m_log.WarnFormat("[PGSQLGenericTable]: SELECT {0} WHERE {1}", m_Realm, where); + + conn.Open(); + return DoQuery(cmd); + } + } + + public virtual T[] Get(string where, NpgsqlParameter parameter) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + string query = String.Format("SELECT * FROM {0} WHERE {1}", + m_Realm, where); + cmd.Connection = conn; + cmd.CommandText = query; + //m_log.WarnFormat("[PGSQLGenericTable]: SELECT {0} WHERE {1}", m_Realm, where); + + cmd.Parameters.Add(parameter); + + conn.Open(); + return DoQuery(cmd); + } + } + + public virtual bool Store(T row) + { + List constraintFields = GetConstraints(); + List> constraints = new List>(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + StringBuilder query = new StringBuilder(); + List names = new List(); + List values = new List(); + + foreach (FieldInfo fi in m_Fields.Values) + { + names.Add(fi.Name); + values.Add(":" + fi.Name); + // Temporarily return more information about what field is unexpectedly null for + // http://opensimulator.org/mantis/view.php?id=5403. This might be due to a bug in the + // InventoryTransferModule or we may be required to substitute a DBNull here. + if (fi.GetValue(row) == null) + throw new NullReferenceException( + string.Format( + "[PGSQL GENERIC TABLE HANDLER]: Trying to store field {0} for {1} which is unexpectedly null", + fi.Name, row)); + + if (constraintFields.Count > 0 && constraintFields.Contains(fi.Name)) + { + constraints.Add(new KeyValuePair(fi.Name, fi.GetValue(row).ToString() )); + } + if (m_FieldTypes.ContainsKey(fi.Name)) + cmd.Parameters.Add(m_database.CreateParameter(fi.Name, fi.GetValue(row), m_FieldTypes[fi.Name])); + else + cmd.Parameters.Add(m_database.CreateParameter(fi.Name, fi.GetValue(row))); + } + + if (m_DataField != null) + { + Dictionary data = + (Dictionary)m_DataField.GetValue(row); + + foreach (KeyValuePair kvp in data) + { + if (constraintFields.Count > 0 && constraintFields.Contains(kvp.Key)) + { + constraints.Add(new KeyValuePair(kvp.Key, kvp.Key)); + } + names.Add(kvp.Key); + values.Add(":" + kvp.Key); + + if (m_FieldTypes.ContainsKey(kvp.Key)) + cmd.Parameters.Add(m_database.CreateParameter("" + kvp.Key, kvp.Value, m_FieldTypes[kvp.Key])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + kvp.Key, kvp.Value)); + } + + } + + query.AppendFormat("UPDATE {0} SET ", m_Realm); + int i = 0; + for (i = 0; i < names.Count - 1; i++) + { + query.AppendFormat("\"{0}\" = {1}, ", names[i], values[i]); + } + query.AppendFormat("\"{0}\" = {1} ", names[i], values[i]); + if (constraints.Count > 0) + { + List terms = new List(); + for (int j = 0; j < constraints.Count; j++) + { + terms.Add(String.Format(" \"{0}\" = :{0}", constraints[j].Key)); + } + string where = String.Join(" AND ", terms.ToArray()); + query.AppendFormat(" WHERE {0} ", where); + + } + cmd.Connection = conn; + cmd.CommandText = query.ToString(); + + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + { + //m_log.WarnFormat("[PGSQLGenericTable]: Updating {0}", m_Realm); + return true; + } + else + { + // assume record has not yet been inserted + + query = new StringBuilder(); + query.AppendFormat("INSERT INTO {0} (\"", m_Realm); + query.Append(String.Join("\",\"", names.ToArray())); + query.Append("\") values (" + String.Join(",", values.ToArray()) + ")"); + cmd.Connection = conn; + cmd.CommandText = query.ToString(); + + // m_log.WarnFormat("[PGSQLGenericTable]: Inserting into {0} sql {1}", m_Realm, cmd.CommandText); + + if (conn.State != ConnectionState.Open) + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + + return false; + } + } + + public virtual bool Delete(string field, string key) + { + return Delete(new string[] { field }, new string[] { key }); + } + + public virtual bool Delete(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return false; + + List terms = new List(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + if (m_FieldTypes.ContainsKey(fields[i])) + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i], m_FieldTypes[fields[i]])); + else + cmd.Parameters.Add(m_database.CreateParameter(fields[i], keys[i])); + + terms.Add(" \"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" AND ", terms.ToArray()); + + string query = String.Format("DELETE FROM {0} WHERE {1}", m_Realm, where); + + cmd.Connection = conn; + cmd.CommandText = query; + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + { + //m_log.Warn("[PGSQLGenericTable]: " + deleteCommand); + return true; + } + return false; + } + } + public long GetCount(string field, string key) + { + return GetCount(new string[] { field }, new string[] { key }); + } + + public long GetCount(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return 0; + + List terms = new List(); + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + for (int i = 0; i < fields.Length; i++) + { + cmd.Parameters.AddWithValue(fields[i], keys[i]); + terms.Add("\"" + fields[i] + "\" = :" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + Object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public long GetCount(string where) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + string query = String.Format("select count(*) from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + object result = DoQueryScalar(cmd); + + return Convert.ToInt64(result); + } + } + + public object DoQueryScalar(NpgsqlCommand cmd) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_ConnectionString)) + { + dbcon.Open(); + cmd.Connection = dbcon; + + return cmd.ExecuteScalar(); + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGridUserData.cs b/OpenSim/Data/PGSQL/PGSQLGridUserData.cs new file mode 100644 index 0000000000..89319f3a58 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGridUserData.cs @@ -0,0 +1,68 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for Avatar Storage + /// + public class PGSQLGridUserData : PGSQLGenericTableHandler, + IGridUserData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLGridUserData(string connectionString, string realm) : + base(connectionString, realm, "GridUserStore") + { + } + + public new GridUserData Get(string userID) + { + GridUserData[] ret = Get("UserID", userID); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("\"UserID\" LIKE '{0}%'", userID)); + } + + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLGroupsData.cs b/OpenSim/Data/PGSQL/PGSQLGroupsData.cs new file mode 100755 index 0000000000..e257e7c723 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLGroupsData.cs @@ -0,0 +1,485 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; +using log4net; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLGroupsData : IGroupsData + { + private PGSqlGroupsGroupsHandler m_Groups; + private PGSqlGroupsMembershipHandler m_Membership; + private PGSqlGroupsRolesHandler m_Roles; + private PGSqlGroupsRoleMembershipHandler m_RoleMembership; + private PGSqlGroupsInvitesHandler m_Invites; + private PGSqlGroupsNoticesHandler m_Notices; + private PGSqlGroupsPrincipalsHandler m_Principals; + + public PGSQLGroupsData(string connectionString, string realm) + { + m_Groups = new PGSqlGroupsGroupsHandler(connectionString, realm + "_groups", realm + "_Store"); + m_Membership = new PGSqlGroupsMembershipHandler(connectionString, realm + "_membership"); + m_Roles = new PGSqlGroupsRolesHandler(connectionString, realm + "_roles"); + m_RoleMembership = new PGSqlGroupsRoleMembershipHandler(connectionString, realm + "_rolemembership"); + m_Invites = new PGSqlGroupsInvitesHandler(connectionString, realm + "_invites"); + m_Notices = new PGSqlGroupsNoticesHandler(connectionString, realm + "_notices"); + m_Principals = new PGSqlGroupsPrincipalsHandler(connectionString, realm + "_principals"); + } + + #region groups table + public bool StoreGroup(GroupData data) + { + return m_Groups.Store(data); + } + + public GroupData RetrieveGroup(UUID groupID) + { + GroupData[] groups = m_Groups.Get("GroupID", groupID.ToString()); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData RetrieveGroup(string name) + { + GroupData[] groups = m_Groups.Get("Name", name); + if (groups.Length > 0) + return groups[0]; + + return null; + } + + public GroupData[] RetrieveGroups(string pattern) + { + + if (string.IsNullOrEmpty(pattern)) // True for where clause + { + pattern = " 1 ORDER BY lower(\"Name\") LIMIT 100"; + + return m_Groups.Get(pattern); + } + else + { + pattern = " \"ShowInList\" = 1 AND lower(\"Name\") LIKE lower('%" + pattern + "%') ORDER BY lower(\"Name\") LIMIT 100"; + + return m_Groups.Get(pattern, new NpgsqlParameter("pattern", pattern)); + } + } + + public bool DeleteGroup(UUID groupID) + { + return m_Groups.Delete("GroupID", groupID.ToString()); + } + + public int GroupsCount() + { + return (int)m_Groups.GetCount(" \"Location\" = \"\""); + } + + #endregion + + #region membership table + public MembershipData[] RetrieveMembers(UUID groupID) + { + return m_Membership.Get("GroupID", groupID.ToString()); + } + + public MembershipData RetrieveMember(UUID groupID, string pricipalID) + { + MembershipData[] m = m_Membership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + if (m != null && m.Length > 0) + return m[0]; + + return null; + } + + public MembershipData[] RetrieveMemberships(string pricipalID) + { + return m_Membership.Get("PrincipalID", pricipalID.ToString()); + } + + public bool StoreMember(MembershipData data) + { + return m_Membership.Store(data); + } + + public bool DeleteMember(UUID groupID, string pricipalID) + { + return m_Membership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), pricipalID }); + } + + public int MemberCount(UUID groupID) + { + return (int)m_Membership.GetCount("GroupID", groupID.ToString()); + } + #endregion + + #region roles table + public bool StoreRole(RoleData data) + { + return m_Roles.Store(data); + } + + public RoleData RetrieveRole(UUID groupID, UUID roleID) + { + RoleData[] data = m_Roles.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public RoleData[] RetrieveRoles(UUID groupID) + { + //return m_Roles.RetrieveRoles(groupID); + return m_Roles.Get("GroupID", groupID.ToString()); + } + + public bool DeleteRole(UUID groupID, UUID roleID) + { + return m_Roles.Delete(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public int RoleCount(UUID groupID) + { + return (int)m_Roles.GetCount("GroupID", groupID.ToString()); + } + + + #endregion + + #region rolememberhip table + public RoleMembershipData[] RetrieveRolesMembers(UUID groupID) + { + RoleMembershipData[] data = m_RoleMembership.Get("GroupID", groupID.ToString()); + + return data; + } + + public RoleMembershipData[] RetrieveRoleMembers(UUID groupID, UUID roleID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + + return data; + } + + public RoleMembershipData[] RetrieveMemberRoles(UUID groupID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID.ToString() }); + + return data; + } + + public RoleMembershipData RetrieveRoleMember(UUID groupID, UUID roleID, string principalID) + { + RoleMembershipData[] data = m_RoleMembership.Get(new string[] { "GroupID", "RoleID", "PrincipalID" }, + new string[] { groupID.ToString(), roleID.ToString(), principalID.ToString() }); + + if (data != null && data.Length > 0) + return data[0]; + + return null; + } + + public int RoleMemberCount(UUID groupID, UUID roleID) + { + return (int)m_RoleMembership.GetCount(new string[] { "GroupID", "RoleID" }, + new string[] { groupID.ToString(), roleID.ToString() }); + } + + public bool StoreRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Store(data); + } + + public bool DeleteRoleMember(RoleMembershipData data) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "RoleID", "PrincipalID"}, + new string[] { data.GroupID.ToString(), data.RoleID.ToString(), data.PrincipalID }); + } + + public bool DeleteMemberAllRoles(UUID groupID, string principalID) + { + return m_RoleMembership.Delete(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + } + + #endregion + + #region principals table + public bool StorePrincipal(PrincipalData data) + { + return m_Principals.Store(data); + } + + public PrincipalData RetrievePrincipal(string principalID) + { + PrincipalData[] p = m_Principals.Get("PrincipalID", principalID); + if (p != null && p.Length > 0) + return p[0]; + + return null; + } + + public bool DeletePrincipal(string principalID) + { + return m_Principals.Delete("PrincipalID", principalID); + } + #endregion + + #region invites table + + public bool StoreInvitation(InvitationData data) + { + return m_Invites.Store(data); + } + + public InvitationData RetrieveInvitation(UUID inviteID) + { + InvitationData[] invites = m_Invites.Get("InviteID", inviteID.ToString()); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public InvitationData RetrieveInvitation(UUID groupID, string principalID) + { + InvitationData[] invites = m_Invites.Get(new string[] { "GroupID", "PrincipalID" }, + new string[] { groupID.ToString(), principalID }); + + if (invites != null && invites.Length > 0) + return invites[0]; + + return null; + } + + public bool DeleteInvite(UUID inviteID) + { + return m_Invites.Delete("InviteID", inviteID.ToString()); + } + + public void DeleteOldInvites() + { + m_Invites.DeleteOld(); + } + + #endregion + + #region notices table + + public bool StoreNotice(NoticeData data) + { + return m_Notices.Store(data); + } + + public NoticeData RetrieveNotice(UUID noticeID) + { + NoticeData[] notices = m_Notices.Get("NoticeID", noticeID.ToString()); + + if (notices != null && notices.Length > 0) + return notices[0]; + + return null; + } + + public NoticeData[] RetrieveNotices(UUID groupID) + { + NoticeData[] notices = m_Notices.Get("GroupID", groupID.ToString()); + + return notices; + } + + public bool DeleteNotice(UUID noticeID) + { + return m_Notices.Delete("NoticeID", noticeID.ToString()); + } + + public void DeleteOldNotices() + { + m_Notices.DeleteOld(); + } + + #endregion + + #region combinations + public MembershipData RetrievePrincipalGroupMembership(string principalID, UUID groupID) + { + // TODO + return null; + } + public MembershipData[] RetrievePrincipalGroupMemberships(string principalID) + { + // TODO + return null; + } + + #endregion + } + + public class PGSqlGroupsGroupsHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsGroupsHandler(string connectionString, string realm, string store) + : base(connectionString, realm, store) + { + } + + } + + public class PGSqlGroupsMembershipHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsRolesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsRolesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsRoleMembershipHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsRoleMembershipHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + } + + public class PGSqlGroupsInvitesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsInvitesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < CURRENT_DATE - INTERVAL '2 week'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } + + public class PGSqlGroupsNoticesHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsNoticesHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + + public void DeleteOld() + { + + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < CURRENT_DATE - INTERVAL '2 week'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } + + public class PGSqlGroupsPrincipalsHandler : PGSQLGenericTableHandler + { + protected override Assembly Assembly + { + // WARNING! Moving migrations to this assembly!!! + get { return GetType().Assembly; } + } + + public PGSqlGroupsPrincipalsHandler(string connectionString, string realm) + : base(connectionString, realm, string.Empty) + { + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs b/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs new file mode 100644 index 0000000000..c71b15f474 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLHGTravelData.cs @@ -0,0 +1,80 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for user grid data + /// + public class PGSQLHGTravelData : PGSQLGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLHGTravelData(string connectionString, string realm) : base(connectionString, realm, "HGTravelStore") { } + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"delete from {0} where ""TMStamp"" < CURRENT_DATE - INTERVAL '2 day'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLInventoryData.cs b/OpenSim/Data/PGSQL/PGSQLInventoryData.cs new file mode 100644 index 0000000000..c9994332fa --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLInventoryData.cs @@ -0,0 +1,831 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL interface for the inventory server + /// + public class PGSQLInventoryData : IInventoryDataPlugin + { + private const string _migrationStore = "InventoryStore"; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The database manager + /// + private PGSQLManager database; + private string m_connectionString; + + #region IPlugin members + + [Obsolete("Cannot be default-initialized!")] + public void Initialise() + { + m_log.Info("[PGSQLInventoryData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// Loads and initialises the PGSQL inventory storage interface + /// + /// connect string + /// use PGSQL_connection.ini + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + database = new PGSQLManager(connectionString); + + //New migrations check of store + database.CheckMigration(_migrationStore); + } + + /// + /// The name of this DB provider + /// + /// A string containing the name of the DB provider + public string Name + { + get { return "PGSQL Inventory Data Interface"; } + } + + /// + /// Closes this DB provider + /// + public void Dispose() + { + database = null; + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider + public string Version + { + get { return database.getVersion(); } + } + + #endregion + + #region Folder methods + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + if (user == UUID.Zero) + return new List(); + + return getInventoryFolders(UUID.Zero, user); + } + + /// + /// see InventoryItemBase.getUserRootFolder + /// + /// the User UUID + /// + public InventoryFolderBase getUserRootFolder(UUID user) + { + List items = getUserRootFolders(user); + + InventoryFolderBase rootFolder = null; + + // There should only ever be one root folder for a user. However, if there's more + // than one we'll simply use the first one rather than failing. It would be even + // nicer to print some message to this effect, but this feels like it's too low a + // to put such a message out, and it's too minor right now to spare the time to + // suitably refactor. + if (items.Count > 0) + { + rootFolder = items[0]; + } + + return rootFolder; + } + + /// + /// Returns a list of folders in a users inventory contained within the specified folder + /// + /// The folder to search + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + return getInventoryFolders(parentID, UUID.Zero); + } + + /// + /// Returns a specified inventory folder + /// + /// The folder to return + /// A folder class + public InventoryFolderBase getInventoryFolder(UUID folderID) + { + string sql = "SELECT * FROM inventoryfolders WHERE \"folderID\" = :folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folderID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + return readInventoryFolder(reader); + } + } + } + m_log.InfoFormat("[INVENTORY DB] : Found no inventory folder with ID : {0}", folderID); + return null; + } + + /// + /// Returns all child folders in the hierarchy from the parent folder and down. + /// Does not return the parent folder itself. + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getFolderHierarchy(UUID parentID) + { + //Note maybe change this to use a Dataset that loading in all folders of a user and then go throw it that way. + //Note this is changed so it opens only one connection to the database and not everytime it wants to get data. + + /* NOTE: the implementation below is very inefficient (makes a separate request to get subfolders for + * every found folder, recursively). Inventory code for other DBs has been already rewritten to get ALL + * inventory for a specific user at once. + * + * Meanwhile, one little thing is corrected: getFolderHierarchy(UUID.Zero) doesn't make sense and should never + * be used, so check for that and return an empty list. + */ + + List folders = new List(); + + if (parentID == UUID.Zero) + return folders; + + string sql = "SELECT * FROM inventoryfolders WHERE \"parentFolderID\" = :parentID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentID", parentID)); + conn.Open(); + folders.AddRange(getInventoryFolders(cmd)); + + List tempFolders = new List(); + + foreach (InventoryFolderBase folderBase in folders) + { + tempFolders.AddRange(getFolderHierarchy(folderBase.ID, cmd)); + } + if (tempFolders.Count > 0) + { + folders.AddRange(tempFolders); + } + } + return folders; + } + + /// + /// Creates a new inventory folder + /// + /// Folder to create + public void addInventoryFolder(InventoryFolderBase folder) + { + string sql = "INSERT INTO inventoryfolders (\"folderID\", \"agentID\", \"parentFolderID\", \"folderName\", type, version) " + + " VALUES (:folderID, :agentID, :parentFolderID, :folderName, :type, :version);"; + + string folderName = folder.Name; + if (folderName.Length > 64) + { + folderName = folderName.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + folder.Name.Length.ToString() + " to " + folderName.Length + " characters on add"); + } + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + cmd.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderName", folderName)); + cmd.Parameters.Add(database.CreateParameter("type", folder.Type)); + cmd.Parameters.Add(database.CreateParameter("version", folder.Version)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Updates an inventory folder + /// + /// Folder to update + public void updateInventoryFolder(InventoryFolderBase folder) + { + string sql = @"UPDATE inventoryfolders SET ""agentID"" = :agentID, + ""parentFolderID"" = :parentFolderID, + ""folderName"" = :folderName, + type = :type, + version = :version + WHERE folderID = :folderID"; + + string folderName = folder.Name; + if (folderName.Length > 64) + { + folderName = folderName.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + folder.Name.Length.ToString() + " to " + folderName.Length + " characters on update"); + } + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + cmd.Parameters.Add(database.CreateParameter("agentID", folder.Owner)); + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderName", folderName)); + cmd.Parameters.Add(database.CreateParameter("type", folder.Type)); + cmd.Parameters.Add(database.CreateParameter("version", folder.Version)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Updates an inventory folder + /// + /// Folder to update + public void moveInventoryFolder(InventoryFolderBase folder) + { + string sql = @"UPDATE inventoryfolders SET ""parentFolderID"" = :parentFolderID WHERE ""folderID"" = :folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folder.ParentID)); + cmd.Parameters.Add(database.CreateParameter("folderID", folder.ID)); + conn.Open(); + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.ErrorFormat("[INVENTORY DB]: Error : {0}", e.Message); + } + } + } + + /// + /// Delete an inventory folder + /// + /// Id of folder to delete + public void deleteInventoryFolder(UUID folderID) + { + string sql = @"SELECT * FROM inventoryfolders WHERE ""parentFolderID"" = :parentID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + List subFolders; + cmd.Parameters.Add(database.CreateParameter("parentID", UUID.Zero)); + conn.Open(); + subFolders = getFolderHierarchy(folderID, cmd); + + + //Delete all sub-folders + foreach (InventoryFolderBase f in subFolders) + { + DeleteOneFolder(f.ID, conn); + DeleteItemsInFolder(f.ID, conn); + } + + //Delete the actual row + DeleteOneFolder(folderID, conn); + DeleteItemsInFolder(folderID, conn); + } + } + + #endregion + + #region Item Methods + + /// + /// Returns a list of items in a specified folder + /// + /// The folder to search + /// A list containing inventory items + public List getInventoryInFolder(UUID folderID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""parentFolderID"" = :parentFolderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("parentFolderID", folderID)); + conn.Open(); + List items = new List(); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + items.Add(readInventoryItem(reader)); + } + } + return items; + } + } + + /// + /// Returns a specified inventory item + /// + /// The item ID + /// An inventory item + public InventoryItemBase getInventoryItem(UUID itemID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""inventoryID"" = :inventoryID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("inventoryID", itemID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + return readInventoryItem(reader); + } + } + } + + m_log.InfoFormat("[INVENTORY DB]: Found no inventory item with ID : {0}", itemID); + return null; + } + + /// + /// Adds a specified item to the database + /// + /// The inventory item + public void addInventoryItem(InventoryItemBase item) + { + if (getInventoryItem(item.ID) != null) + { + updateInventoryItem(item); + return; + } + + string sql = @"INSERT INTO inventoryitems + (""inventoryID"", ""assetID"", ""assetType"", ""parentFolderID"", ""avatarID"", ""inventoryName"", + ""inventoryDescription"", ""inventoryNextPermissions"", ""inventoryCurrentPermissions"", + ""invType"", ""creatorID"", ""inventoryBasePermissions"", ""inventoryEveryOnePermissions"", ""inventoryGroupPermissions"", + ""salePrice"", ""SaleType"", ""creationDate"", ""groupID"", ""groupOwned"", flags) + VALUES + (:inventoryID, :assetID, :assetType, :parentFolderID, :avatarID, :inventoryName, :inventoryDescription, + :inventoryNextPermissions, :inventoryCurrentPermissions, :invType, :creatorID, + :inventoryBasePermissions, :inventoryEveryOnePermissions, :inventoryGroupPermissions, :SalePrice, :SaleType, + :creationDate, :groupID, :groupOwned, :flags)"; + + string itemName = item.Name; + if (item.Name.Length > 64) + { + itemName = item.Name.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length.ToString() + " to " + itemName.Length.ToString() + " characters"); + } + + string itemDesc = item.Description; + if (item.Description.Length > 128) + { + itemDesc = item.Description.Substring(0, 128); + m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length.ToString() + " to " + itemDesc.Length.ToString() + " characters"); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); + command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); + command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); + command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); + command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); + command.Parameters.Add(database.CreateParameter("inventoryName", itemName)); + command.Parameters.Add(database.CreateParameter("inventoryDescription", itemDesc)); + command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); + command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); + command.Parameters.Add(database.CreateParameter("invType", item.InvType)); + command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); + command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); + command.Parameters.Add(database.CreateParameter("SalePrice", item.SalePrice)); + command.Parameters.Add(database.CreateParameter("SaleType", item.SaleType)); + command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); + command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); + command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); + command.Parameters.Add(database.CreateParameter("flags", item.Flags)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error inserting item :" + e.Message); + } + } + + sql = @"UPDATE inventoryfolders SET version = version + 1 WHERE ""folderID"" = @folderID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("folderID", item.Folder.ToString())); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error updating inventory folder for new item :" + e.Message); + } + } + } + + /// + /// Updates the specified inventory item + /// + /// Inventory item to update + public void updateInventoryItem(InventoryItemBase item) + { + string sql = @"UPDATE inventoryitems SET ""assetID"" = :assetID, + ""assetType"" = :assetType, + ""parentFolderID"" = :parentFolderID, + ""avatarID"" = :avatarID, + ""inventoryName"" = :inventoryName, + ""inventoryDescription"" = :inventoryDescription, + ""inventoryNextPermissions"" = :inventoryNextPermissions, + ""inventoryCurrentPermissions"" = :inventoryCurrentPermissions, + ""invType"" = :invType, + ""creatorID"" = :creatorID, + ""inventoryBasePermissions"" = :inventoryBasePermissions, + ""inventoryEveryOnePermissions"" = :inventoryEveryOnePermissions, + ""inventoryGroupPermissions"" = :inventoryGroupPermissions, + ""salePrice"" = :SalePrice, + ""saleType"" = :SaleType, + ""creationDate"" = :creationDate, + ""groupID"" = :groupID, + ""groupOwned"" = :groupOwned, + flags = :flags + WHERE ""inventoryID"" = :inventoryID"; + + string itemName = item.Name; + if (item.Name.Length > 64) + { + itemName = item.Name.Substring(0, 64); + m_log.Warn("[INVENTORY DB]: Name field truncated from " + item.Name.Length.ToString() + " to " + itemName.Length.ToString() + " characters on update"); + } + + string itemDesc = item.Description; + if (item.Description.Length > 128) + { + itemDesc = item.Description.Substring(0, 128); + m_log.Warn("[INVENTORY DB]: Description field truncated from " + item.Description.Length.ToString() + " to " + itemDesc.Length.ToString() + " characters on update"); + } + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(database.CreateParameter("inventoryID", item.ID)); + command.Parameters.Add(database.CreateParameter("assetID", item.AssetID)); + command.Parameters.Add(database.CreateParameter("assetType", item.AssetType)); + command.Parameters.Add(database.CreateParameter("parentFolderID", item.Folder)); + command.Parameters.Add(database.CreateParameter("avatarID", item.Owner)); + command.Parameters.Add(database.CreateParameter("inventoryName", itemName)); + command.Parameters.Add(database.CreateParameter("inventoryDescription", itemDesc)); + command.Parameters.Add(database.CreateParameter("inventoryNextPermissions", item.NextPermissions)); + command.Parameters.Add(database.CreateParameter("inventoryCurrentPermissions", item.CurrentPermissions)); + command.Parameters.Add(database.CreateParameter("invType", item.InvType)); + command.Parameters.Add(database.CreateParameter("creatorID", item.CreatorId)); + command.Parameters.Add(database.CreateParameter("inventoryBasePermissions", item.BasePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryEveryOnePermissions", item.EveryOnePermissions)); + command.Parameters.Add(database.CreateParameter("inventoryGroupPermissions", item.GroupPermissions)); + command.Parameters.Add(database.CreateParameter("SalePrice", item.SalePrice)); + command.Parameters.Add(database.CreateParameter("SaleType", item.SaleType)); + command.Parameters.Add(database.CreateParameter("creationDate", item.CreationDate)); + command.Parameters.Add(database.CreateParameter("groupID", item.GroupID)); + command.Parameters.Add(database.CreateParameter("groupOwned", item.GroupOwned)); + command.Parameters.Add(database.CreateParameter("flags", item.Flags)); + conn.Open(); + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error updating item :" + e.Message); + } + } + } + + // See IInventoryDataPlugin + + /// + /// Delete an item in inventory database + /// + /// the item UUID + public void deleteInventoryItem(UUID itemID) + { + string sql = @"DELETE FROM inventoryitems WHERE ""inventoryID""=:inventoryID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("inventoryID", itemID)); + try + { + conn.Open(); + cmd.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB]: Error deleting item :" + e.Message); + } + } + } + + public InventoryItemBase queryInventoryItem(UUID itemID) + { + return getInventoryItem(itemID); + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + /// + /// Returns all activated gesture-items in the inventory of the specified avatar. + /// + /// The of the avatar + /// + /// The list of gestures (s) + /// + public List fetchActiveGestures(UUID avatarID) + { + string sql = @"SELECT * FROM inventoryitems WHERE ""avatarID"" = :uuid AND ""assetType"" = :assetType and flags = 1"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(database.CreateParameter("uuid", avatarID)); + cmd.Parameters.Add(database.CreateParameter("assetType", (int)AssetType.Gesture)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + List gestureList = new List(); + while (reader.Read()) + { + gestureList.Add(readInventoryItem(reader)); + } + return gestureList; + } + } + } + + #endregion + + #region Private methods + + /// + /// Delete an item in inventory database + /// + /// the item ID + /// connection to the database + private void DeleteItemsInFolder(UUID folderID, NpgsqlConnection connection) + { + using (NpgsqlCommand command = new NpgsqlCommand(@"DELETE FROM inventoryitems WHERE ""folderID""=:folderID", connection)) + { + command.Parameters.Add(database.CreateParameter("folderID", folderID)); + + try + { + command.ExecuteNonQuery(); + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error deleting item :" + e.Message); + } + } + } + + /// + /// Gets the folder hierarchy in a loop. + /// + /// parent ID. + /// SQL command/connection to database + /// + private static List getFolderHierarchy(UUID parentID, NpgsqlCommand command) + { + command.Parameters["parentID"].Value = parentID.Guid; //.ToString(); + + List folders = getInventoryFolders(command); + + if (folders.Count > 0) + { + List tempFolders = new List(); + + foreach (InventoryFolderBase folderBase in folders) + { + tempFolders.AddRange(getFolderHierarchy(folderBase.ID, command)); + } + + if (tempFolders.Count > 0) + { + folders.AddRange(tempFolders); + } + } + return folders; + } + + /// + /// Gets the inventory folders. + /// + /// parentID, use UUID.Zero to get root + /// user id, use UUID.Zero, if you want all folders from a parentID. + /// + private List getInventoryFolders(UUID parentID, UUID user) + { + string sql = @"SELECT * FROM inventoryfolders WHERE ""parentFolderID"" = :parentID AND ""agentID"" = :uuid"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + if (user == UUID.Zero) + { + command.Parameters.Add(database.CreateParameter("uuid", "%")); + } + else + { + command.Parameters.Add(database.CreateParameter("uuid", user)); + } + command.Parameters.Add(database.CreateParameter("parentID", parentID)); + conn.Open(); + return getInventoryFolders(command); + } + } + + /// + /// Gets the inventory folders. + /// + /// SQLcommand. + /// + private static List getInventoryFolders(NpgsqlCommand command) + { + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + + List items = new List(); + while (reader.Read()) + { + items.Add(readInventoryFolder(reader)); + } + return items; + } + } + + /// + /// Reads a list of inventory folders returned by a query. + /// + /// A PGSQL Data Reader + /// A List containing inventory folders + protected static InventoryFolderBase readInventoryFolder(NpgsqlDataReader reader) + { + try + { + InventoryFolderBase folder = new InventoryFolderBase(); + folder.Owner = DBGuid.FromDB(reader["agentID"]); + folder.ParentID = DBGuid.FromDB(reader["parentFolderID"]); + folder.ID = DBGuid.FromDB(reader["folderID"]); + folder.Name = (string)reader["folderName"]; + folder.Type = (short)reader["type"]; + folder.Version = Convert.ToUInt16(reader["version"]); + + return folder; + } + catch (Exception e) + { + m_log.Error("[INVENTORY DB] Error reading inventory folder :" + e.Message); + } + + return null; + } + + /// + /// Reads a one item from an SQL result + /// + /// The SQL Result + /// the item read + private static InventoryItemBase readInventoryItem(IDataRecord reader) + { + try + { + InventoryItemBase item = new InventoryItemBase(); + + item.ID = DBGuid.FromDB(reader["inventoryID"]); + item.AssetID = DBGuid.FromDB(reader["assetID"]); + item.AssetType = Convert.ToInt32(reader["assetType"].ToString()); + item.Folder = DBGuid.FromDB(reader["parentFolderID"]); + item.Owner = DBGuid.FromDB(reader["avatarID"]); + item.Name = reader["inventoryName"].ToString(); + item.Description = reader["inventoryDescription"].ToString(); + item.NextPermissions = Convert.ToUInt32(reader["inventoryNextPermissions"]); + item.CurrentPermissions = Convert.ToUInt32(reader["inventoryCurrentPermissions"]); + item.InvType = Convert.ToInt32(reader["invType"].ToString()); + item.CreatorId = reader["creatorID"].ToString(); + item.BasePermissions = Convert.ToUInt32(reader["inventoryBasePermissions"]); + item.EveryOnePermissions = Convert.ToUInt32(reader["inventoryEveryOnePermissions"]); + item.GroupPermissions = Convert.ToUInt32(reader["inventoryGroupPermissions"]); + item.SalePrice = Convert.ToInt32(reader["salePrice"]); + item.SaleType = Convert.ToByte(reader["saleType"]); + item.CreationDate = Convert.ToInt32(reader["creationDate"]); + item.GroupID = DBGuid.FromDB(reader["groupID"]); + item.GroupOwned = Convert.ToBoolean(reader["groupOwned"]); + item.Flags = Convert.ToUInt32(reader["flags"]); + + return item; + } + catch (NpgsqlException e) + { + m_log.Error("[INVENTORY DB]: Error reading inventory item :" + e.Message); + } + + return null; + } + + /// + /// Delete a folder in inventory databasae + /// + /// the folder UUID + /// connection to database + private void DeleteOneFolder(UUID folderID, NpgsqlConnection connection) + { + try + { + using (NpgsqlCommand command = new NpgsqlCommand(@"DELETE FROM inventoryfolders WHERE ""folderID""=:folderID and type=-1", connection)) + { + command.Parameters.Add(database.CreateParameter("folderID", folderID)); + + command.ExecuteNonQuery(); + } + } + catch (NpgsqlException e) + { + m_log.Error("[INVENTORY DB]: Error deleting folder :" + e.Message); + } + } + + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLManager.cs b/OpenSim/Data/PGSQL/PGSQLManager.cs new file mode 100644 index 0000000000..46f835ab7e --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLManager.cs @@ -0,0 +1,354 @@ +/* + * 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.Data; +using System.IO; +using System.Reflection; +using OpenSim.Framework; +using log4net; +using OpenMetaverse; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A management class for the MS SQL Storage Engine + /// + public class PGSQLManager + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Connection string for ADO.net + /// + private readonly string connectionString; + + /// + /// Initialize the manager and set the connectionstring + /// + /// + public PGSQLManager(string connection) + { + connectionString = connection; + InitializeMonoSecurity(); + } + + public void InitializeMonoSecurity() + { + if (!Util.IsPlatformMono) + { + if (AppDomain.CurrentDomain.GetData("MonoSecurityPostgresAdded") == null) + { + AppDomain.CurrentDomain.SetData("MonoSecurityPostgresAdded", "true"); + + AppDomain currentDomain = AppDomain.CurrentDomain; + currentDomain.AssemblyResolve += new ResolveEventHandler(ResolveEventHandlerMonoSec); + } + } + } + + private System.Reflection.Assembly ResolveEventHandlerMonoSec(object sender, ResolveEventArgs args) + { + Assembly MyAssembly = null; + + if (args.Name.Substring(0, args.Name.IndexOf(",")) == "Mono.Security") + { + MyAssembly = Assembly.LoadFrom("lib/NET/Mono.Security.dll"); + } + + //Return the loaded assembly. + return MyAssembly; + } + + /// + /// Type conversion to a SQLDbType functions + /// + /// + /// + internal NpgsqlDbType DbtypeFromType(Type type) + { + if (type == typeof(string)) + { + return NpgsqlDbType.Varchar; + } + if (type == typeof(double)) + { + return NpgsqlDbType.Double; + } + if (type == typeof(Single)) + { + return NpgsqlDbType.Double; + } + if (type == typeof(int)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(bool)) + { + return NpgsqlDbType.Boolean; + } + if (type == typeof(UUID)) + { + return NpgsqlDbType.Uuid; + } + if (type == typeof(byte)) + { + return NpgsqlDbType.Smallint; + } + if (type == typeof(sbyte)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(Byte[])) + { + return NpgsqlDbType.Bytea; + } + if (type == typeof(uint) || type == typeof(ushort)) + { + return NpgsqlDbType.Integer; + } + if (type == typeof(ulong)) + { + return NpgsqlDbType.Bigint; + } + if (type == typeof(DateTime)) + { + return NpgsqlDbType.Timestamp; + } + + return NpgsqlDbType.Varchar; + } + + internal NpgsqlDbType DbtypeFromString(Type type, string PGFieldType) + { + if (PGFieldType == "") + { + return DbtypeFromType(type); + } + + if (PGFieldType == "character varying") + { + return NpgsqlDbType.Varchar; + } + if (PGFieldType == "double precision") + { + return NpgsqlDbType.Double; + } + if (PGFieldType == "integer") + { + return NpgsqlDbType.Integer; + } + if (PGFieldType == "smallint") + { + return NpgsqlDbType.Smallint; + } + if (PGFieldType == "boolean") + { + return NpgsqlDbType.Boolean; + } + if (PGFieldType == "uuid") + { + return NpgsqlDbType.Uuid; + } + if (PGFieldType == "bytea") + { + return NpgsqlDbType.Bytea; + } + + return DbtypeFromType(type); + } + + /// + /// Creates value for parameter. + /// + /// The value. + /// + private static object CreateParameterValue(object value) + { + Type valueType = value.GetType(); + + if (valueType == typeof(UUID)) //TODO check if this works + { + return ((UUID) value).Guid; + } + if (valueType == typeof(UUID)) + { + return ((UUID)value).Guid; + } + if (valueType == typeof(bool)) + { + return (bool)value; + } + if (valueType == typeof(Byte[])) + { + return value; + } + if (valueType == typeof(int)) + { + return value; + } + return value; + } + + /// + /// Create value for parameter based on PGSQL Schema + /// + /// + /// + /// + internal static object CreateParameterValue(object value, string PGFieldType) + { + if (PGFieldType == "uuid") + { + UUID uidout; + UUID.TryParse(value.ToString(), out uidout); + return uidout; + } + if (PGFieldType == "integer") + { + int intout; + int.TryParse(value.ToString(), out intout); + return intout; + } + if (PGFieldType == "boolean") + { + return (value.ToString() == "true"); + } + if (PGFieldType == "timestamp with time zone") + { + return (DateTime)value; + } + if (PGFieldType == "timestamp without time zone") + { + return (DateTime)value; + } + if (PGFieldType == "double precision") + { + return (Double)value; + } + return CreateParameterValue(value); + } + + /// + /// Create a parameter for a command + /// + /// Name of the parameter. + /// parameter object. + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject) + { + return CreateParameter(parameterName, parameterObject, false); + } + + /// + /// Creates the parameter for a command. + /// + /// Name of the parameter. + /// parameter object. + /// if set to true parameter is a output parameter + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject, bool parameterOut) + { + //Tweak so we dont always have to add : sign + if (parameterName.StartsWith(":")) parameterName = parameterName.Replace(":",""); + + //HACK if object is null, it is turned into a string, there are no nullable type till now + if (parameterObject == null) parameterObject = ""; + + NpgsqlParameter parameter = new NpgsqlParameter(parameterName, DbtypeFromType(parameterObject.GetType())); + + if (parameterOut) + { + parameter.Direction = ParameterDirection.Output; + } + else + { + parameter.Direction = ParameterDirection.Input; + parameter.Value = CreateParameterValue(parameterObject); + } + + return parameter; + } + + /// + /// Create a parameter with PGSQL schema type + /// + /// + /// + /// + /// + internal NpgsqlParameter CreateParameter(string parameterName, object parameterObject, string PGFieldType) + { + //Tweak so we dont always have to add : sign + if (parameterName.StartsWith(":")) parameterName = parameterName.Replace(":", ""); + + //HACK if object is null, it is turned into a string, there are no nullable type till now + if (parameterObject == null) parameterObject = ""; + + NpgsqlParameter parameter = new NpgsqlParameter(parameterName, DbtypeFromString(parameterObject.GetType(), PGFieldType)); + + parameter.Direction = ParameterDirection.Input; + parameter.Value = CreateParameterValue(parameterObject, PGFieldType); + + return parameter; + } + + /// + /// Checks if we need to do some migrations to the database + /// + /// migrationStore. + public void CheckMigration(string migrationStore) + { + using (NpgsqlConnection connection = new NpgsqlConnection(connectionString)) + { + connection.Open(); + Assembly assem = GetType().Assembly; + PGSQLMigration migration = new PGSQLMigration(connection, assem, migrationStore); + + migration.Update(); + } + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider + public string getVersion() + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLMigration.cs b/OpenSim/Data/PGSQL/PGSQLMigration.cs new file mode 100644 index 0000000000..709fde04f2 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLMigration.cs @@ -0,0 +1,102 @@ +/* + * 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 Npgsql; +using System; +using System.Data; +using System.Data.Common; +using System.Reflection; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLMigration : Migration + { + public PGSQLMigration(NpgsqlConnection conn, Assembly assem, string type) + : base(conn, assem, type) + { + } + + public PGSQLMigration(NpgsqlConnection conn, Assembly assem, string subtype, string type) + : base(conn, assem, subtype, type) + { + } + + protected override int FindVersion(DbConnection conn, string type) + { + int version = 0; + NpgsqlConnection lcConn = (NpgsqlConnection)conn; + + using (NpgsqlCommand cmd = lcConn.CreateCommand()) + { + try + { + cmd.CommandText = "select version from migrations where name = '" + type + "' " + + " order by version desc limit 1"; //Must be + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + version = Convert.ToInt32(reader["version"]); + } + reader.Close(); + } + } + catch + { + // Return -1 to indicate table does not exist + return -1; + } + } + return version; + } + + protected override void ExecuteScript(DbConnection conn, string[] script) + { + if (!(conn is NpgsqlConnection)) + { + base.ExecuteScript(conn, script); + return; + } + + foreach (string sql in script) + { + try + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, (NpgsqlConnection)conn)) + { + cmd.ExecuteNonQuery(); + } + } + catch (Exception) + { + throw new Exception(sql); + + } + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs b/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs new file mode 100644 index 0000000000..82e5ed829e --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLOfflineIMData.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLOfflineIMData : PGSQLGenericTableHandler, IOfflineIMData + { + public PGSQLOfflineIMData(string connectionString, string realm) + : base(connectionString, realm, "IM_Store") + { + } + + public void DeleteOld() + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format("delete from {0} where \"TMStamp\" < CURRENT_DATE - INTERVAL '2 week'", m_Realm); + + ExecuteNonQuery(cmd); + } + + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLPresenceData.cs b/OpenSim/Data/PGSQL/PGSQLPresenceData.cs new file mode 100755 index 0000000000..0376585863 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLPresenceData.cs @@ -0,0 +1,116 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Presence Server + /// + public class PGSQLPresenceData : PGSQLGenericTableHandler, + IPresenceData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PGSQLPresenceData(string connectionString, string realm) : + base(connectionString, realm, "Presence") + { + } + + public PresenceData Get(UUID sessionID) + { + PresenceData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public void LogoutRegionAgents(UUID regionID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("DELETE FROM {0} WHERE \"RegionID\" = :regionID", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("RegionID", regionID)); + cmd.Connection = conn; + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + PresenceData[] pd = Get("SessionID", sessionID.ToString()); + if (pd.Length == 0) + return false; + + if (regionID == UUID.Zero) + return false; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + cmd.CommandText = String.Format("UPDATE {0} SET \"RegionID\" = :regionID, \"LastSeen\" = now() WHERE \"SessionID\" = :sessionID", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("SessionID", sessionID)); + cmd.Parameters.Add(m_database.CreateParameter("RegionID", regionID)); + cmd.Connection = conn; + conn.Open(); + if (cmd.ExecuteNonQuery() == 0) + return false; + } + return true; + } + + public bool VerifyAgent(UUID agentId, UUID secureSessionID) + { + PresenceData[] ret = Get("SecureSessionID", secureSessionID.ToString()); + + if (ret.Length == 0) + return false; + + if(ret[0].UserID != agentId.ToString()) + return false; + + return true; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLRegionData.cs b/OpenSim/Data/PGSQL/PGSQLRegionData.cs new file mode 100644 index 0000000000..b3076f0756 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLRegionData.cs @@ -0,0 +1,392 @@ +/* + * 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.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using RegionFlags = OpenSim.Framework.RegionFlags; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Region Server. + /// + public class PGSQLRegionData : IRegionData + { + private string m_Realm; + private List m_ColumnNames = null; + private string m_ConnectionString; + private PGSQLManager m_database; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_FieldTypes = new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLRegionData(string connectionString, string realm) + { + m_Realm = realm; + m_ConnectionString = connectionString; + m_database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "GridStore"); + m.Update(); + } + LoadFieldTypes(); + } + + private void LoadFieldTypes() + { + m_FieldTypes = new Dictionary(); + + string query = string.Format(@"select column_name,data_type + from INFORMATION_SCHEMA.COLUMNS + where table_name = lower('{0}'); + + ", m_Realm); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(query, conn)) + { + conn.Open(); + using (NpgsqlDataReader rdr = cmd.ExecuteReader()) + { + while (rdr.Read()) + { + // query produces 0 to many rows of single column, so always add the first item in each row + m_FieldTypes.Add((string)rdr[0], (string)rdr[1]); + } + } + } + } + + public List Get(string regionName, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where lower(\"regionName\") like lower(:regionName) "; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + sql += " order by lower(\"regionName\")"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("regionName", regionName)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + + public RegionData Get(int posX, int posY, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where \"locX\" = :posX and \"locY\" = :posY"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("posX", posX)); + cmd.Parameters.Add(m_database.CreateParameter("posY", posY)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public RegionData Get(UUID regionID, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where uuid = :regionID"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("regionID", regionID)); + if (scopeID != UUID.Zero) + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + List ret = RunCommand(cmd); + if (ret.Count == 0) + return null; + + return ret[0]; + } + } + + public List Get(int startX, int startY, int endX, int endY, UUID scopeID) + { + string sql = "select * from "+m_Realm+" where \"locX\" between :startX and :endX and \"locY\" between :startY and :endY"; + if (scopeID != UUID.Zero) + sql += " and \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("startX", startX)); + cmd.Parameters.Add(m_database.CreateParameter("startY", startY)); + cmd.Parameters.Add(m_database.CreateParameter("endX", endX)); + cmd.Parameters.Add(m_database.CreateParameter("endY", endY)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + + public List RunCommand(NpgsqlCommand cmd) + { + List retList = new List(); + + NpgsqlDataReader result = cmd.ExecuteReader(); + + while (result.Read()) + { + RegionData ret = new RegionData(); + ret.Data = new Dictionary(); + + UUID regionID; + UUID.TryParse(result["uuid"].ToString(), out regionID); + ret.RegionID = regionID; + UUID scope; + UUID.TryParse(result["ScopeID"].ToString(), out scope); + ret.ScopeID = scope; + ret.RegionName = result["regionName"].ToString(); + ret.posX = Convert.ToInt32(result["locX"]); + ret.posY = Convert.ToInt32(result["locY"]); + ret.sizeX = Convert.ToInt32(result["sizeX"]); + ret.sizeY = Convert.ToInt32(result["sizeY"]); + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "uuid") + continue; + if (s == "ScopeID") + continue; + if (s == "regionName") + continue; + if (s == "locX") + continue; + if (s == "locY") + continue; + + ret.Data[s] = result[s].ToString(); + } + + retList.Add(ret); + } + return retList; + } + + public bool Store(RegionData data) + { + if (data.Data.ContainsKey("uuid")) + data.Data.Remove("uuid"); + if (data.Data.ContainsKey("ScopeID")) + data.Data.Remove("ScopeID"); + if (data.Data.ContainsKey("regionName")) + data.Data.Remove("regionName"); + if (data.Data.ContainsKey("posX")) + data.Data.Remove("posX"); + if (data.Data.ContainsKey("posY")) + data.Data.Remove("posY"); + if (data.Data.ContainsKey("sizeX")) + data.Data.Remove("sizeX"); + if (data.Data.ContainsKey("sizeY")) + data.Data.Remove("sizeY"); + if (data.Data.ContainsKey("locX")) + data.Data.Remove("locX"); + if (data.Data.ContainsKey("locY")) + data.Data.Remove("locY"); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + + string update = "update " + m_Realm + " set \"locX\"=:posX, \"locY\"=:posY, \"sizeX\"=:sizeX, \"sizeY\"=:sizeY "; + + foreach (string field in fields) + { + + update += ", "; + update += " \"" + field + "\" = :" + field; + + if (m_FieldTypes.ContainsKey(field)) + cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field], m_FieldTypes[field])); + else + cmd.Parameters.Add(m_database.CreateParameter(field, data.Data[field])); + } + + update += " where uuid = :regionID"; + + if (data.ScopeID != UUID.Zero) + update += " and \"ScopeID\" = :scopeID"; + + cmd.CommandText = update; + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("regionID", data.RegionID)); + cmd.Parameters.Add(m_database.CreateParameter("regionName", data.RegionName)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", data.ScopeID)); + cmd.Parameters.Add(m_database.CreateParameter("posX", data.posX)); + cmd.Parameters.Add(m_database.CreateParameter("posY", data.posY)); + cmd.Parameters.Add(m_database.CreateParameter("sizeX", data.sizeX)); + cmd.Parameters.Add(m_database.CreateParameter("sizeY", data.sizeY)); + conn.Open(); + try + { + if (cmd.ExecuteNonQuery() < 1) + { + string insert = "insert into " + m_Realm + " (uuid, \"ScopeID\", \"locX\", \"locY\", \"sizeX\", \"sizeY\", \"regionName\", \"" + + String.Join("\", \"", fields) + + "\") values (:regionID, :scopeID, :posX, :posY, :sizeX, :sizeY, :regionName, :" + String.Join(", :", fields) + ")"; + + cmd.CommandText = insert; + + try + { + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + catch (Exception ex) + { + m_log.Warn("[PGSQL Grid]: Error inserting into Regions table: " + ex.Message + ", INSERT sql: " + insert); + } + } + } + catch (Exception ex) + { + m_log.Warn("[PGSQL Grid]: Error updating Regions table: " + ex.Message + ", UPDATE sql: " + update); + } + } + + return true; + } + + public bool SetDataItem(UUID regionID, string item, string value) + { + string sql = "update " + m_Realm + + " set \"" + item + "\" = :" + item + " where uuid = :UUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public bool Delete(UUID regionID) + { + string sql = "delete from " + m_Realm + + " where uuid = :UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("UUID", regionID)); + conn.Open(); + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + + public List GetDefaultRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultRegion, scopeID); + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + return Get((int)RegionFlags.DefaultHGRegion, scopeID); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + List regions = Get((int)RegionFlags.FallbackRegion, scopeID); + RegionDataDistanceCompare distanceComparer = new RegionDataDistanceCompare(x, y); + regions.Sort(distanceComparer); + + return regions; + } + + public List GetHyperlinks(UUID scopeID) + { + return Get((int)RegionFlags.Hyperlink, scopeID); + } + + private List Get(int regionFlags, UUID scopeID) + { + string sql = "SELECT * FROM " + m_Realm + " WHERE (\"flags\" & " + regionFlags.ToString() + ") <> 0"; + if (scopeID != UUID.Zero) + sql += " AND \"ScopeID\" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + conn.Open(); + return RunCommand(cmd); + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs new file mode 100644 index 0000000000..77d87d43e9 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs @@ -0,0 +1,2243 @@ +/* + * 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.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + /// + /// A PGSQL Interface for the Region Server. + /// + public class PGSQLSimulationData : ISimulationDataStore + { + private const string _migrationStore = "RegionStore"; + private const string LogHeader = "[REGION DB PGSQL]"; + + // private static FileSystemDataStore Instance = new FileSystemDataStore(); + private static readonly ILog _Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The database manager + /// + private PGSQLManager _Database; + private string m_connectionString; + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public PGSQLSimulationData() + { + } + + public PGSQLSimulationData(string connectionString) + { + Initialise(connectionString); + } + + /// + /// Initialises the region datastore + /// + /// The connection string. + public void Initialise(string connectionString) + { + m_connectionString = connectionString; + _Database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(connectionString)) + { + conn.Open(); + //New Migration settings + Migration m = new Migration(conn, Assembly, "RegionStore"); + m.Update(); + } + } + + /// + /// Dispose the database + /// + public void Dispose() { } + + #region SceneObjectGroup region for loading and Store of the scene. + + /// + /// Loads the objects present in the region. + /// + /// The region UUID. + /// + public List LoadObjects(UUID regionUUID) + { + UUID lastGroupID = UUID.Zero; + + Dictionary prims = new Dictionary(); + Dictionary objects = new Dictionary(); + SceneObjectGroup grp = null; + + string sql = @"SELECT *, + CASE WHEN prims.""UUID"" = prims.""SceneGroupID"" THEN 0 ELSE 1 END as sort + FROM prims + LEFT JOIN primshapes ON prims.""UUID"" = primshapes.""UUID"" + WHERE ""RegionUUID"" = :RegionUUID + ORDER BY ""SceneGroupID"" asc, sort asc, ""LinkNumber"" asc"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + command.Parameters.Add(_Database.CreateParameter("regionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + SceneObjectPart sceneObjectPart = BuildPrim(reader); + if (reader["Shape"] is DBNull) + sceneObjectPart.Shape = PrimitiveBaseShape.Default; + else + sceneObjectPart.Shape = BuildShape(reader); + + prims[sceneObjectPart.UUID] = sceneObjectPart; + + UUID groupID = new UUID((Guid)reader["SceneGroupID"]); + + if (groupID != lastGroupID) // New SOG + { + if (grp != null) + objects[grp.UUID] = grp; + + lastGroupID = groupID; + + // There sometimes exist OpenSim bugs that 'orphan groups' so that none of the prims are + // recorded as the root prim (for which the UUID must equal the persisted group UUID). In + // this case, force the UUID to be the same as the group UUID so that at least these can be + // deleted (we need to change the UUID so that any other prims in the linkset can also be + // deleted). + if (sceneObjectPart.UUID != groupID && groupID != UUID.Zero) + { + _Log.WarnFormat( + "[REGION DB]: Found root prim {0} {1} at {2} where group was actually {3}. Forcing UUID to group UUID", + sceneObjectPart.Name, sceneObjectPart.UUID, sceneObjectPart.GroupPosition, groupID); + + sceneObjectPart.UUID = groupID; + } + + grp = new SceneObjectGroup(sceneObjectPart); + } + else + { + // Black magic to preserve link numbers + // Why is this needed, fix this in AddPart method. + int link = sceneObjectPart.LinkNum; + + grp.AddPart(sceneObjectPart); + + if (link != 0) + sceneObjectPart.LinkNum = link; + } + } + } + } + + if (grp != null) + objects[grp.UUID] = grp; + + // Instead of attempting to LoadItems on every prim, + // most of which probably have no items... get a + // list from DB of all prims which have items and + // LoadItems only on those + List primsWithInventory = new List(); + string qry = "select distinct \"primID\" from primitems"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(qry, conn)) + { + conn.Open(); + using (NpgsqlDataReader itemReader = command.ExecuteReader()) + { + while (itemReader.Read()) + { + if (!(itemReader["primID"] is DBNull)) + { + UUID primID = new UUID(itemReader["primID"].ToString()); + if (prims.ContainsKey(primID)) + { + primsWithInventory.Add(prims[primID]); + } + } + } + } + } + + LoadItems(primsWithInventory); + + _Log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + + return new List(objects.Values); + } + + /// + /// Load in the prim's persisted inventory. + /// + /// all prims with inventory on a region + private void LoadItems(List allPrimsWithInventory) + { + string sql = @"SELECT * FROM primitems WHERE ""primID"" = :PrimID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand command = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (SceneObjectPart objectPart in allPrimsWithInventory) + { + command.Parameters.Clear(); + command.Parameters.Add(_Database.CreateParameter("PrimID", objectPart.UUID)); + + List inventory = new List(); + + using (NpgsqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + TaskInventoryItem item = BuildItem(reader); + + item.ParentID = objectPart.UUID; // Values in database are + // often wrong + inventory.Add(item); + } + } + + objectPart.Inventory.RestoreInventoryItems(inventory); + } + } + } + + /// + /// Stores all object's details apart from inventory + /// + /// + /// + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + uint flags = obj.RootPart.GetEffectiveObjectFlags(); + // Eligibility check + // + if ((flags & (uint)PrimFlags.Temporary) != 0) + return; + if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) + return; + + //_Log.DebugFormat("[PGSQL]: Adding/Changing SceneObjectGroup: {0} to region: {1}, object has {2} prims.", obj.UUID, regionUUID, obj.Parts.Length); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + NpgsqlTransaction transaction = conn.BeginTransaction(); + + try + { + foreach (SceneObjectPart sceneObjectPart in obj.Parts) + { + //Update prim + using (NpgsqlCommand sqlCommand = conn.CreateCommand()) + { + sqlCommand.Transaction = transaction; + try + { + StoreSceneObjectPrim(sceneObjectPart, sqlCommand, obj.UUID, regionUUID); + } + catch (NpgsqlException sqlEx) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectPrim SQL error: {0} at line {1}", sqlEx.Message, sqlEx.Line); + throw; + } + } + + //Update primshapes + using (NpgsqlCommand sqlCommand = conn.CreateCommand()) + { + sqlCommand.Transaction = transaction; + try + { + StoreSceneObjectPrimShapes(sceneObjectPart, sqlCommand, obj.UUID, regionUUID); + } + catch (NpgsqlException sqlEx) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectPrimShapes SQL error: {0} at line {1}", sqlEx.Message, sqlEx.Line); + throw; + } + } + } + + transaction.Commit(); + } + catch (Exception ex) + { + _Log.ErrorFormat("[REGION DB]: Store SceneObjectGroup error: {0}, Rolling back...", ex.Message); + try + { + transaction.Rollback(); + } + catch (Exception ex2) + { + //Show error + _Log.InfoFormat("[REGION DB]: Rollback of SceneObjectGroup store transaction failed with error: {0}", ex2.Message); + + } + } + } + } + + /// + /// Stores the prim of the sceneobjectpart. + /// + /// The sceneobjectpart or prim. + /// The SQL command with the transaction. + /// The scenegroup UUID. + /// The region UUID. + private void StoreSceneObjectPrim(SceneObjectPart sceneObjectPart, NpgsqlCommand sqlCommand, UUID sceneGroupID, UUID regionUUID) + { + //Big query to update or insert a new prim. + + string queryPrims = @" + UPDATE prims SET + ""CreationDate"" = :CreationDate, ""Name"" = :Name, ""Text"" = :Text, ""Description"" = :Description, ""SitName"" = :SitName, + ""TouchName"" = :TouchName, ""ObjectFlags"" = :ObjectFlags, ""OwnerMask"" = :OwnerMask, ""NextOwnerMask"" = :NextOwnerMask, ""GroupMask"" = :GroupMask, + ""EveryoneMask"" = :EveryoneMask, ""BaseMask"" = :BaseMask, ""PositionX"" = :PositionX, ""PositionY"" = :PositionY, ""PositionZ"" = :PositionZ, + ""GroupPositionX"" = :GroupPositionX, ""GroupPositionY"" = :GroupPositionY, ""GroupPositionZ"" = :GroupPositionZ, ""VelocityX"" = :VelocityX, + ""VelocityY"" = :VelocityY, ""VelocityZ"" = :VelocityZ, ""AngularVelocityX"" = :AngularVelocityX, ""AngularVelocityY"" = :AngularVelocityY, + ""AngularVelocityZ"" = :AngularVelocityZ, ""AccelerationX"" = :AccelerationX, ""AccelerationY"" = :AccelerationY, + ""AccelerationZ"" = :AccelerationZ, ""RotationX"" = :RotationX, ""RotationY"" = :RotationY, ""RotationZ"" = :RotationZ, ""RotationW"" = :RotationW, + ""SitTargetOffsetX"" = :SitTargetOffsetX, ""SitTargetOffsetY"" = :SitTargetOffsetY, ""SitTargetOffsetZ"" = :SitTargetOffsetZ, + ""SitTargetOrientW"" = :SitTargetOrientW, ""SitTargetOrientX"" = :SitTargetOrientX, ""SitTargetOrientY"" = :SitTargetOrientY, + ""SitTargetOrientZ"" = :SitTargetOrientZ, ""RegionUUID"" = :RegionUUID, ""CreatorID"" = :CreatorID, ""OwnerID"" = :OwnerID, ""GroupID"" = :GroupID, + ""LastOwnerID"" = :LastOwnerID, ""SceneGroupID"" = :SceneGroupID, ""PayPrice"" = :PayPrice, ""PayButton1"" = :PayButton1, ""PayButton2"" = :PayButton2, + ""PayButton3"" = :PayButton3, ""PayButton4"" = :PayButton4, ""LoopedSound"" = :LoopedSound, ""LoopedSoundGain"" = :LoopedSoundGain, + ""TextureAnimation"" = :TextureAnimation, ""OmegaX"" = :OmegaX, ""OmegaY"" = :OmegaY, ""OmegaZ"" = :OmegaZ, ""CameraEyeOffsetX"" = :CameraEyeOffsetX, + ""CameraEyeOffsetY"" = :CameraEyeOffsetY, ""CameraEyeOffsetZ"" = :CameraEyeOffsetZ, ""CameraAtOffsetX"" = :CameraAtOffsetX, + ""CameraAtOffsetY"" = :CameraAtOffsetY, ""CameraAtOffsetZ"" = :CameraAtOffsetZ, ""ForceMouselook"" = :ForceMouselook, + ""ScriptAccessPin"" = :ScriptAccessPin, ""AllowedDrop"" = :AllowedDrop, ""DieAtEdge"" = :DieAtEdge, ""SalePrice"" = :SalePrice, + ""SaleType"" = :SaleType, ""ColorR"" = :ColorR, ""ColorG"" = :ColorG, ""ColorB"" = :ColorB, ""ColorA"" = :ColorA, ""ParticleSystem"" = :ParticleSystem, + ""ClickAction"" = :ClickAction, ""Material"" = :Material, ""CollisionSound"" = :CollisionSound, ""CollisionSoundVolume"" = :CollisionSoundVolume, ""PassTouches"" = :PassTouches, + ""LinkNumber"" = :LinkNumber, ""MediaURL"" = :MediaURL, ""DynAttrs"" = :DynAttrs, + ""PhysicsShapeType"" = :PhysicsShapeType, ""Density"" = :Density, ""GravityModifier"" = :GravityModifier, ""Friction"" = :Friction, ""Restitution"" = :Restitution + WHERE ""UUID"" = :UUID ; + + INSERT INTO + prims ( + ""UUID"", ""CreationDate"", ""Name"", ""Text"", ""Description"", ""SitName"", ""TouchName"", ""ObjectFlags"", ""OwnerMask"", ""NextOwnerMask"", ""GroupMask"", + ""EveryoneMask"", ""BaseMask"", ""PositionX"", ""PositionY"", ""PositionZ"", ""GroupPositionX"", ""GroupPositionY"", ""GroupPositionZ"", ""VelocityX"", + ""VelocityY"", ""VelocityZ"", ""AngularVelocityX"", ""AngularVelocityY"", ""AngularVelocityZ"", ""AccelerationX"", ""AccelerationY"", ""AccelerationZ"", + ""RotationX"", ""RotationY"", ""RotationZ"", ""RotationW"", ""SitTargetOffsetX"", ""SitTargetOffsetY"", ""SitTargetOffsetZ"", ""SitTargetOrientW"", + ""SitTargetOrientX"", ""SitTargetOrientY"", ""SitTargetOrientZ"", ""RegionUUID"", ""CreatorID"", ""OwnerID"", ""GroupID"", ""LastOwnerID"", ""SceneGroupID"", + ""PayPrice"", ""PayButton1"", ""PayButton2"", ""PayButton3"", ""PayButton4"", ""LoopedSound"", ""LoopedSoundGain"", ""TextureAnimation"", ""OmegaX"", + ""OmegaY"", ""OmegaZ"", ""CameraEyeOffsetX"", ""CameraEyeOffsetY"", ""CameraEyeOffsetZ"", ""CameraAtOffsetX"", ""CameraAtOffsetY"", ""CameraAtOffsetZ"", + ""ForceMouselook"", ""ScriptAccessPin"", ""AllowedDrop"", ""DieAtEdge"", ""SalePrice"", ""SaleType"", ""ColorR"", ""ColorG"", ""ColorB"", ""ColorA"", + ""ParticleSystem"", ""ClickAction"", ""Material"", ""CollisionSound"", ""CollisionSoundVolume"", ""PassTouches"", ""LinkNumber"", ""MediaURL"", ""DynAttrs"", + ""PhysicsShapeType"", ""Density"", ""GravityModifier"", ""Friction"", ""Restitution"" + ) Select + :UUID, :CreationDate, :Name, :Text, :Description, :SitName, :TouchName, :ObjectFlags, :OwnerMask, :NextOwnerMask, :GroupMask, + :EveryoneMask, :BaseMask, :PositionX, :PositionY, :PositionZ, :GroupPositionX, :GroupPositionY, :GroupPositionZ, :VelocityX, + :VelocityY, :VelocityZ, :AngularVelocityX, :AngularVelocityY, :AngularVelocityZ, :AccelerationX, :AccelerationY, :AccelerationZ, + :RotationX, :RotationY, :RotationZ, :RotationW, :SitTargetOffsetX, :SitTargetOffsetY, :SitTargetOffsetZ, :SitTargetOrientW, + :SitTargetOrientX, :SitTargetOrientY, :SitTargetOrientZ, :RegionUUID, :CreatorID, :OwnerID, :GroupID, :LastOwnerID, :SceneGroupID, + :PayPrice, :PayButton1, :PayButton2, :PayButton3, :PayButton4, :LoopedSound, :LoopedSoundGain, :TextureAnimation, :OmegaX, + :OmegaY, :OmegaZ, :CameraEyeOffsetX, :CameraEyeOffsetY, :CameraEyeOffsetZ, :CameraAtOffsetX, :CameraAtOffsetY, :CameraAtOffsetZ, + :ForceMouselook, :ScriptAccessPin, :AllowedDrop, :DieAtEdge, :SalePrice, :SaleType, :ColorR, :ColorG, :ColorB, :ColorA, + :ParticleSystem, :ClickAction, :Material, :CollisionSound, :CollisionSoundVolume, :PassTouches, :LinkNumber, :MediaURL, :DynAttrs, + :PhysicsShapeType, :Density, :GravityModifier, :Friction, :Restitution + where not EXISTS (SELECT ""UUID"" FROM prims WHERE ""UUID"" = :UUID); + "; + + //Set commandtext. + sqlCommand.CommandText = queryPrims; + //Add parameters + sqlCommand.Parameters.AddRange(CreatePrimParameters(sceneObjectPart, sceneGroupID, regionUUID)); + + //Execute the query. If it fails then error is trapped in calling function + sqlCommand.ExecuteNonQuery(); + } + + /// + /// Stores the scene object prim shapes. + /// + /// The sceneobjectpart containing prim shape. + /// The SQL command with the transaction. + /// The scenegroup UUID. + /// The region UUID. + private void StoreSceneObjectPrimShapes(SceneObjectPart sceneObjectPart, NpgsqlCommand sqlCommand, UUID sceneGroupID, UUID regionUUID) + { + //Big query to or insert or update primshapes + + string queryPrimShapes = @" + UPDATE primshapes SET + ""Shape"" = :Shape, ""ScaleX"" = :ScaleX, ""ScaleY"" = :ScaleY, ""ScaleZ"" = :ScaleZ, ""PCode"" = :PCode, ""PathBegin"" = :PathBegin, + ""PathEnd"" = :PathEnd, ""PathScaleX"" = :PathScaleX, ""PathScaleY"" = :PathScaleY, ""PathShearX"" = :PathShearX, ""PathShearY"" = :PathShearY, + ""PathSkew"" = :PathSkew, ""PathCurve"" = :PathCurve, ""PathRadiusOffset"" = :PathRadiusOffset, ""PathRevolutions"" = :PathRevolutions, + ""PathTaperX"" = :PathTaperX, ""PathTaperY"" = :PathTaperY, ""PathTwist"" = :PathTwist, ""PathTwistBegin"" = :PathTwistBegin, + ""ProfileBegin"" = :ProfileBegin, ""ProfileEnd"" = :ProfileEnd, ""ProfileCurve"" = :ProfileCurve, ""ProfileHollow"" = :ProfileHollow, + ""Texture"" = :Texture, ""ExtraParams"" = :ExtraParams, ""State"" = :State, ""Media"" = :Media + WHERE ""UUID"" = :UUID ; + + INSERT INTO + primshapes ( + ""UUID"", ""Shape"", ""ScaleX"", ""ScaleY"", ""ScaleZ"", ""PCode"", ""PathBegin"", ""PathEnd"", ""PathScaleX"", ""PathScaleY"", ""PathShearX"", ""PathShearY"", + ""PathSkew"", ""PathCurve"", ""PathRadiusOffset"", ""PathRevolutions"", ""PathTaperX"", ""PathTaperY"", ""PathTwist"", ""PathTwistBegin"", ""ProfileBegin"", + ""ProfileEnd"", ""ProfileCurve"", ""ProfileHollow"", ""Texture"", ""ExtraParams"", ""State"", ""Media"" + ) + Select + :UUID, :Shape, :ScaleX, :ScaleY, :ScaleZ, :PCode, :PathBegin, :PathEnd, :PathScaleX, :PathScaleY, :PathShearX, :PathShearY, + :PathSkew, :PathCurve, :PathRadiusOffset, :PathRevolutions, :PathTaperX, :PathTaperY, :PathTwist, :PathTwistBegin, :ProfileBegin, + :ProfileEnd, :ProfileCurve, :ProfileHollow, :Texture, :ExtraParams, :State, :Media + where not EXISTS (SELECT ""UUID"" FROM primshapes WHERE ""UUID"" = :UUID); + "; + + //Set commandtext. + sqlCommand.CommandText = queryPrimShapes; + + //Add parameters + sqlCommand.Parameters.AddRange(CreatePrimShapeParameters(sceneObjectPart, sceneGroupID, regionUUID)); + + //Execute the query. If it fails then error is trapped in calling function + sqlCommand.ExecuteNonQuery(); + + } + + /// + /// Removes a object from the database. + /// Meaning removing it from tables Prims, PrimShapes and PrimItems + /// + /// id of scenegroup + /// regionUUID (is this used anyway + public void RemoveObject(UUID objectID, UUID regionUUID) + { + //_Log.InfoFormat("[PGSQL]: Removing obj: {0} from region: {1}", objectID, regionUUID); + + //Remove from prims and primsitem table + string sqlPrims = @"DELETE FROM PRIMS WHERE ""SceneGroupID"" = :objectID"; + string sqlPrimItems = @"DELETE FROM PRIMITEMS WHERE ""primID"" in (SELECT ""UUID"" FROM PRIMS WHERE ""SceneGroupID"" = :objectID)"; + string sqlPrimShapes = @"DELETE FROM PRIMSHAPES WHERE ""UUID"" in (SELECT ""UUID"" FROM PRIMS WHERE ""SceneGroupID"" = :objectID)"; + + lock (_Database) + { + //Using the non transaction mode. + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.Connection = conn; + cmd.CommandText = sqlPrimShapes; + conn.Open(); + cmd.Parameters.Add(_Database.CreateParameter("objectID", objectID)); + cmd.ExecuteNonQuery(); + + cmd.CommandText = sqlPrimItems; + cmd.ExecuteNonQuery(); + + cmd.CommandText = sqlPrims; + cmd.ExecuteNonQuery(); + } + } + } + + /// + /// Store the inventory of a prim. Warning deletes everything first and then adds all again. + /// + /// + /// + public void StorePrimInventory(UUID primID, ICollection items) + { + //_Log.InfoFormat("[REGION DB: Persisting Prim Inventory with prim ID {0}", primID); + + //Statement from PGSQL section! + // For now, we're just going to crudely remove all the previous inventory items + // no matter whether they have changed or not, and replace them with the current set. + + //Delete everything from PrimID + //TODO add index on PrimID in DB, if not already exist + + string sql = @"delete from primitems where ""primID"" = :primID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("primID", primID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = + @"INSERT INTO primitems ( + ""itemID"",""primID"",""assetID"",""parentFolderID"",""invType"",""assetType"",""name"",""description"",""creationDate"",""creatorID"",""ownerID"",""lastOwnerID"",""groupID"", + ""nextPermissions"",""currentPermissions"",""basePermissions"",""everyonePermissions"",""groupPermissions"",""flags"") + VALUES (:itemID,:primID,:assetID,:parentFolderID,:invType,:assetType,:name,:description,:creationDate,:creatorID,:ownerID, + :lastOwnerID,:groupID,:nextPermissions,:currentPermissions,:basePermissions,:everyonePermissions,:groupPermissions,:flags)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (TaskInventoryItem taskItem in items) + { + cmd.Parameters.AddRange(CreatePrimInventoryParameters(taskItem)); + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + #endregion + + /// + /// Loads the terrain map. + /// + /// regionID. + /// + public double[,] LoadTerrain(UUID regionID) + { + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; + + string sql = @"select ""RegionUUID"", ""Revision"", ""Heightfield"" from terrain + where ""RegionUUID"" = :RegionUUID order by ""Revision"" desc limit 1; "; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + // PGSqlParameter param = new PGSqlParameter(); + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + int rev; + if (reader.Read()) + { + rev = Convert.ToInt32(reader["Revision"]); + byte[] blob = (byte[])reader["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); + } + else + { + _Log.Info("[REGION DB]: No terrain found for region"); + return null; + } + _Log.Info("[REGION DB]: Loaded terrain revision r" + rev); + } + } + } + + return terrData; + } + + // Legacy entry point for when terrain was always a 256x256 heightmap + public void StoreTerrain(double[,] terrain, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(terrain), regionID); + } + + /// + /// Stores the terrain map to DB. + /// + /// terrain map data. + /// regionID. + public void StoreTerrain(TerrainData terrData, UUID regionID) + { + //Delete old terrain map + string sql = @"delete from terrain where ""RegionUUID""=:RegionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + conn.Open(); + cmd.ExecuteNonQuery(); + + _Log.InfoFormat("{0} Deleted terrain revision id = {1}", LogHeader, regionID); + } + } + + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + + sql = @"insert into terrain(""RegionUUID"", ""Revision"", ""Heightfield"") values(:RegionUUID, :Revision, :Heightfield)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionID)); + cmd.Parameters.Add(_Database.CreateParameter("Revision", terrainDBRevision)); + cmd.Parameters.Add(_Database.CreateParameter("Heightfield", terrainDBblob)); + conn.Open(); + cmd.ExecuteNonQuery(); + + _Log.InfoFormat("{0} Stored terrain id = {1}, terrainSize = <{2},{3}>", + LogHeader, regionID, terrData.SizeX, terrData.SizeY); + } + } + + } + + /// + /// Loads all the land objects of a region. + /// + /// The region UUID. + /// + public List LoadLandObjects(UUID regionUUID) + { + List LandDataForRegion = new List(); + + string sql = @"select * from land where ""RegionUUID"" = :RegionUUID"; + + //Retrieve all land data from region + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader readerLandData = cmd.ExecuteReader()) + { + while (readerLandData.Read()) + { + LandDataForRegion.Add(BuildLandData(readerLandData)); + } + } + } + + //Retrieve all accesslist data for all landdata + foreach (LandData LandData in LandDataForRegion) + { + sql = @"select * from landaccesslist where ""LandUUID"" = :LandUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("LandUUID", LandData.GlobalID)); + conn.Open(); + using (NpgsqlDataReader readerAccessList = cmd.ExecuteReader()) + { + while (readerAccessList.Read()) + { + LandData.ParcelAccessList.Add(BuildLandAccessData(readerAccessList)); + } + } + } + } + + //Return data + return LandDataForRegion; + } + + /// + /// Stores land object with landaccess list. + /// + /// parcel data. + public void StoreLandObject(ILandObject parcel) + { + //As this is only one record in land table I just delete all and then add a new record. + //As the delete landaccess is already in the pgsql code + + //Delete old values + RemoveLandObject(parcel.LandData.GlobalID); + + //Insert new values + string sql = @"INSERT INTO land + (""UUID"",""RegionUUID"",""LocalLandID"",""Bitmap"",""Name"",""Description"",""OwnerUUID"",""IsGroupOwned"",""Area"",""AuctionID"",""Category"",""ClaimDate"",""ClaimPrice"", + ""GroupUUID"",""SalePrice"",""LandStatus"",""LandFlags"",""LandingType"",""MediaAutoScale"",""MediaTextureUUID"",""MediaURL"",""MusicURL"",""PassHours"",""PassPrice"", + ""SnapshotUUID"",""UserLocationX"",""UserLocationY"",""UserLocationZ"",""UserLookAtX"",""UserLookAtY"",""UserLookAtZ"",""AuthbuyerID"",""OtherCleanTime"") + VALUES + (:UUID,:RegionUUID,:LocalLandID,:Bitmap,:Name,:Description,:OwnerUUID,:IsGroupOwned,:Area,:AuctionID,:Category,:ClaimDate,:ClaimPrice, + :GroupUUID,:SalePrice,:LandStatus,:LandFlags,:LandingType,:MediaAutoScale,:MediaTextureUUID,:MediaURL,:MusicURL,:PassHours,:PassPrice, + :SnapshotUUID,:UserLocationX,:UserLocationY,:UserLocationZ,:UserLookAtX,:UserLookAtY,:UserLookAtZ,:AuthbuyerID,:OtherCleanTime)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateLandParameters(parcel.LandData, parcel.RegionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = @"INSERT INTO landaccesslist (""LandUUID"",""AccessUUID"",""LandFlags"",""Expires"") VALUES (:LandUUID,:AccessUUID,:Flags,:Expires)"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + foreach (LandAccessEntry parcelAccessEntry in parcel.LandData.ParcelAccessList) + { + cmd.Parameters.AddRange(CreateLandAccessParameters(parcelAccessEntry, parcel.RegionUUID)); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + /// + /// Removes a land object from DB. + /// + /// UUID of landobject + public void RemoveLandObject(UUID globalID) + { + string sql = @"delete from land where ""UUID""=:UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("UUID", globalID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + sql = @"delete from landaccesslist where ""LandUUID""=:UUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("UUID", globalID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + RegionLightShareData nWP = new RegionLightShareData(); + nWP.OnSave += StoreRegionWindlightSettings; + + string sql = @"select * from regionwindlight where ""region_id"" = :regionID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionID", regionUUID.ToString() )); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (!result.Read()) + { + //No result, so store our default windlight profile and return it + nWP.regionID = regionUUID; + StoreRegionWindlightSettings(nWP); + return nWP; + } + else + { + nWP.regionID = DBGuid.FromDB(result["region_id"]); + nWP.waterColor.X = Convert.ToSingle(result["water_color_r"]); + nWP.waterColor.Y = Convert.ToSingle(result["water_color_g"]); + nWP.waterColor.Z = Convert.ToSingle(result["water_color_b"]); + nWP.waterFogDensityExponent = Convert.ToSingle(result["water_fog_density_exponent"]); + nWP.underwaterFogModifier = Convert.ToSingle(result["underwater_fog_modifier"]); + nWP.reflectionWaveletScale.X = Convert.ToSingle(result["reflection_wavelet_scale_1"]); + nWP.reflectionWaveletScale.Y = Convert.ToSingle(result["reflection_wavelet_scale_2"]); + nWP.reflectionWaveletScale.Z = Convert.ToSingle(result["reflection_wavelet_scale_3"]); + nWP.fresnelScale = Convert.ToSingle(result["fresnel_scale"]); + nWP.fresnelOffset = Convert.ToSingle(result["fresnel_offset"]); + nWP.refractScaleAbove = Convert.ToSingle(result["refract_scale_above"]); + nWP.refractScaleBelow = Convert.ToSingle(result["refract_scale_below"]); + nWP.blurMultiplier = Convert.ToSingle(result["blur_multiplier"]); + nWP.bigWaveDirection.X = Convert.ToSingle(result["big_wave_direction_x"]); + nWP.bigWaveDirection.Y = Convert.ToSingle(result["big_wave_direction_y"]); + nWP.littleWaveDirection.X = Convert.ToSingle(result["little_wave_direction_x"]); + nWP.littleWaveDirection.Y = Convert.ToSingle(result["little_wave_direction_y"]); + UUID.TryParse(result["normal_map_texture"].ToString(), out nWP.normalMapTexture); + nWP.horizon.X = Convert.ToSingle(result["horizon_r"]); + nWP.horizon.Y = Convert.ToSingle(result["horizon_g"]); + nWP.horizon.Z = Convert.ToSingle(result["horizon_b"]); + nWP.horizon.W = Convert.ToSingle(result["horizon_i"]); + nWP.hazeHorizon = Convert.ToSingle(result["haze_horizon"]); + nWP.blueDensity.X = Convert.ToSingle(result["blue_density_r"]); + nWP.blueDensity.Y = Convert.ToSingle(result["blue_density_g"]); + nWP.blueDensity.Z = Convert.ToSingle(result["blue_density_b"]); + nWP.blueDensity.W = Convert.ToSingle(result["blue_density_i"]); + nWP.hazeDensity = Convert.ToSingle(result["haze_density"]); + nWP.densityMultiplier = Convert.ToSingle(result["density_multiplier"]); + nWP.distanceMultiplier = Convert.ToSingle(result["distance_multiplier"]); + nWP.maxAltitude = Convert.ToUInt16(result["max_altitude"]); + nWP.sunMoonColor.X = Convert.ToSingle(result["sun_moon_color_r"]); + nWP.sunMoonColor.Y = Convert.ToSingle(result["sun_moon_color_g"]); + nWP.sunMoonColor.Z = Convert.ToSingle(result["sun_moon_color_b"]); + nWP.sunMoonColor.W = Convert.ToSingle(result["sun_moon_color_i"]); + nWP.sunMoonPosition = Convert.ToSingle(result["sun_moon_position"]); + nWP.ambient.X = Convert.ToSingle(result["ambient_r"]); + nWP.ambient.Y = Convert.ToSingle(result["ambient_g"]); + nWP.ambient.Z = Convert.ToSingle(result["ambient_b"]); + nWP.ambient.W = Convert.ToSingle(result["ambient_i"]); + nWP.eastAngle = Convert.ToSingle(result["east_angle"]); + nWP.sunGlowFocus = Convert.ToSingle(result["sun_glow_focus"]); + nWP.sunGlowSize = Convert.ToSingle(result["sun_glow_size"]); + nWP.sceneGamma = Convert.ToSingle(result["scene_gamma"]); + nWP.starBrightness = Convert.ToSingle(result["star_brightness"]); + nWP.cloudColor.X = Convert.ToSingle(result["cloud_color_r"]); + nWP.cloudColor.Y = Convert.ToSingle(result["cloud_color_g"]); + nWP.cloudColor.Z = Convert.ToSingle(result["cloud_color_b"]); + nWP.cloudColor.W = Convert.ToSingle(result["cloud_color_i"]); + nWP.cloudXYDensity.X = Convert.ToSingle(result["cloud_x"]); + nWP.cloudXYDensity.Y = Convert.ToSingle(result["cloud_y"]); + nWP.cloudXYDensity.Z = Convert.ToSingle(result["cloud_density"]); + nWP.cloudCoverage = Convert.ToSingle(result["cloud_coverage"]); + nWP.cloudScale = Convert.ToSingle(result["cloud_scale"]); + nWP.cloudDetailXYDensity.X = Convert.ToSingle(result["cloud_detail_x"]); + nWP.cloudDetailXYDensity.Y = Convert.ToSingle(result["cloud_detail_y"]); + nWP.cloudDetailXYDensity.Z = Convert.ToSingle(result["cloud_detail_density"]); + nWP.cloudScrollX = Convert.ToSingle(result["cloud_scroll_x"]); + nWP.cloudScrollXLock = Convert.ToBoolean(result["cloud_scroll_x_lock"]); + nWP.cloudScrollY = Convert.ToSingle(result["cloud_scroll_y"]); + nWP.cloudScrollYLock = Convert.ToBoolean(result["cloud_scroll_y_lock"]); + nWP.drawClassicClouds = Convert.ToBoolean(result["draw_classic_clouds"]); + nWP.valid = true; + } + } + } + return nWP; + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + string sql = @"delete from regionwindlight where ""region_id"" = :region_id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + conn.Open(); + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionID.ToString())); + cmd.ExecuteNonQuery(); + } + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + string sql = @"select region_id from regionwindlight where ""region_id"" = :region_id limit 1;"; + bool exists = false; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", wl.regionID.ToString() )); + NpgsqlDataReader dr = cmd.ExecuteReader(); + exists = dr.Read(); + } + } + if (exists) + { + RemoveRegionWindlightSettings(wl.regionID); + } + + // sql insert + sql = @"INSERT INTO regionwindlight + (region_id + ,water_color_r + ,water_color_g + ,water_color_b + ,water_fog_density_exponent + ,underwater_fog_modifier + ,reflection_wavelet_scale_1 + ,reflection_wavelet_scale_2 + ,reflection_wavelet_scale_3 + ,fresnel_scale + ,fresnel_offset + ,refract_scale_above + ,refract_scale_below + ,blur_multiplier + ,big_wave_direction_x + ,big_wave_direction_y + ,little_wave_direction_x + ,little_wave_direction_y + ,normal_map_texture + ,horizon_r + ,horizon_g + ,horizon_b + ,horizon_i + ,haze_horizon + ,blue_density_r + ,blue_density_g + ,blue_density_b + ,blue_density_i + ,haze_density + ,density_multiplier + ,distance_multiplier + ,max_altitude + ,sun_moon_color_r + ,sun_moon_color_g + ,sun_moon_color_b + ,sun_moon_color_i + ,sun_moon_position + ,ambient_r + ,ambient_g + ,ambient_b + ,ambient_i + ,east_angle + ,sun_glow_focus + ,sun_glow_size + ,scene_gamma + ,star_brightness + ,cloud_color_r + ,cloud_color_g + ,cloud_color_b + ,cloud_color_i + ,cloud_x + ,cloud_y + ,cloud_density + ,cloud_coverage + ,cloud_scale + ,cloud_detail_x + ,cloud_detail_y + ,cloud_detail_density + ,cloud_scroll_x + ,cloud_scroll_x_lock + ,cloud_scroll_y + ,cloud_scroll_y_lock + ,draw_classic_clouds) + VALUES + (:region_id + ,:water_color_r + ,:water_color_g + ,:water_color_b + ,:water_fog_density_exponent + ,:underwater_fog_modifier + ,:reflection_wavelet_scale_1 + ,:reflection_wavelet_scale_2 + ,:reflection_wavelet_scale_3 + ,:fresnel_scale + ,:fresnel_offset + ,:refract_scale_above + ,:refract_scale_below + ,:blur_multiplier + ,:big_wave_direction_x + ,:big_wave_direction_y + ,:little_wave_direction_x + ,:little_wave_direction_y + ,:normal_map_texture + ,:horizon_r + ,:horizon_g + ,:horizon_b + ,:horizon_i + ,:haze_horizon + ,:blue_density_r + ,:blue_density_g + ,:blue_density_b + ,:blue_density_i + ,:haze_density + ,:density_multiplier + ,:distance_multiplier + ,:max_altitude + ,:sun_moon_color_r + ,:sun_moon_color_g + ,:sun_moon_color_b + ,:sun_moon_color_i + ,:sun_moon_position + ,:ambient_r + ,:ambient_g + ,:ambient_b + ,:ambient_i + ,:east_angle + ,:sun_glow_focus + ,:sun_glow_size + ,:scene_gamma + ,:star_brightness + ,:cloud_color_r + ,:cloud_color_g + ,:cloud_color_b + ,:cloud_color_i + ,:cloud_x + ,:cloud_y + ,:cloud_density + ,:cloud_coverage + ,:cloud_scale + ,:cloud_detail_x + ,:cloud_detail_y + ,:cloud_detail_density + ,:cloud_scroll_x + ,:cloud_scroll_x_lock + ,:cloud_scroll_y + ,:cloud_scroll_y_lock + ,:draw_classic_clouds);"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", wl.regionID.ToString())); + cmd.Parameters.Add(_Database.CreateParameter("water_color_r", wl.waterColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("water_color_g", wl.waterColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("water_color_b", wl.waterColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("water_fog_density_exponent", wl.waterFogDensityExponent)); + cmd.Parameters.Add(_Database.CreateParameter("underwater_fog_modifier", wl.underwaterFogModifier)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y)); + cmd.Parameters.Add(_Database.CreateParameter("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z)); + cmd.Parameters.Add(_Database.CreateParameter("fresnel_scale", wl.fresnelScale)); + cmd.Parameters.Add(_Database.CreateParameter("fresnel_offset", wl.fresnelOffset)); + cmd.Parameters.Add(_Database.CreateParameter("refract_scale_above", wl.refractScaleAbove)); + cmd.Parameters.Add(_Database.CreateParameter("refract_scale_below", wl.refractScaleBelow)); + cmd.Parameters.Add(_Database.CreateParameter("blur_multiplier", wl.blurMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("big_wave_direction_x", wl.bigWaveDirection.X)); + cmd.Parameters.Add(_Database.CreateParameter("big_wave_direction_y", wl.bigWaveDirection.Y)); + cmd.Parameters.Add(_Database.CreateParameter("little_wave_direction_x", wl.littleWaveDirection.X)); + cmd.Parameters.Add(_Database.CreateParameter("little_wave_direction_y", wl.littleWaveDirection.Y)); + cmd.Parameters.Add(_Database.CreateParameter("normal_map_texture", wl.normalMapTexture.ToString())); + cmd.Parameters.Add(_Database.CreateParameter("horizon_r", wl.horizon.X)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_g", wl.horizon.Y)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_b", wl.horizon.Z)); + cmd.Parameters.Add(_Database.CreateParameter("horizon_i", wl.horizon.W)); + cmd.Parameters.Add(_Database.CreateParameter("haze_horizon", wl.hazeHorizon)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_r", wl.blueDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_g", wl.blueDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_b", wl.blueDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("blue_density_i", wl.blueDensity.W)); + cmd.Parameters.Add(_Database.CreateParameter("haze_density", wl.hazeDensity)); + cmd.Parameters.Add(_Database.CreateParameter("density_multiplier", wl.densityMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("distance_multiplier", wl.distanceMultiplier)); + cmd.Parameters.Add(_Database.CreateParameter("max_altitude", wl.maxAltitude)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_r", wl.sunMoonColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_g", wl.sunMoonColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_b", wl.sunMoonColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_color_i", wl.sunMoonColor.W)); + cmd.Parameters.Add(_Database.CreateParameter("sun_moon_position", wl.sunMoonPosition)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_r", wl.ambient.X)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_g", wl.ambient.Y)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_b", wl.ambient.Z)); + cmd.Parameters.Add(_Database.CreateParameter("ambient_i", wl.ambient.W)); + cmd.Parameters.Add(_Database.CreateParameter("east_angle", wl.eastAngle)); + cmd.Parameters.Add(_Database.CreateParameter("sun_glow_focus", wl.sunGlowFocus)); + cmd.Parameters.Add(_Database.CreateParameter("sun_glow_size", wl.sunGlowSize)); + cmd.Parameters.Add(_Database.CreateParameter("scene_gamma", wl.sceneGamma)); + cmd.Parameters.Add(_Database.CreateParameter("star_brightness", wl.starBrightness)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_r", wl.cloudColor.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_g", wl.cloudColor.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_b", wl.cloudColor.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_color_i", wl.cloudColor.W)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_x", wl.cloudXYDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_y", wl.cloudXYDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_density", wl.cloudXYDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_coverage", wl.cloudCoverage)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scale", wl.cloudScale)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_x", wl.cloudDetailXYDensity.X)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_y", wl.cloudDetailXYDensity.Y)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_detail_density", wl.cloudDetailXYDensity.Z)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_x", wl.cloudScrollX)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_x_lock", wl.cloudScrollXLock)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_y", wl.cloudScrollY)); + cmd.Parameters.Add(_Database.CreateParameter("cloud_scroll_y_lock", wl.cloudScrollYLock)); + cmd.Parameters.Add(_Database.CreateParameter("draw_classic_clouds", wl.drawClassicClouds)); + + cmd.ExecuteNonQuery(); + } + } + #region update + // } + // else + // { + // // sql update + // sql = @"UPDATE [OpenSim].[dbo].[regionwindlight] + // SET [region_id] = @region_id + // ,[water_color_r] = @water_color_r + // ,[water_color_g] = @water_color_g + // ,[water_color_b] = @water_color_b + // ,[water_fog_density_exponent] = @water_fog_density_exponent + // ,[underwater_fog_modifier] = @underwater_fog_modifier + // ,[reflection_wavelet_scale_1] = @reflection_wavelet_scale_1 + // ,[reflection_wavelet_scale_2] = @reflection_wavelet_scale_2 + // ,[reflection_wavelet_scale_3] = @reflection_wavelet_scale_3 + // ,[fresnel_scale] = @fresnel_scale + // ,[fresnel_offset] = @fresnel_offset + // ,[refract_scale_above] = @refract_scale_above + // ,[refract_scale_below] = @refract_scale_below + // ,[blur_multiplier] = @blur_multiplier + // ,[big_wave_direction_x] = @big_wave_direction_x + // ,[big_wave_direction_y] = @big_wave_direction_y + // ,[little_wave_direction_x] = @little_wave_direction_x + // ,[little_wave_direction_y] = @little_wave_direction_y + // ,[normal_map_texture] = @normal_map_texture + // ,[horizon_r] = @horizon_r + // ,[horizon_g] = @horizon_g + // ,[horizon_b] = @horizon_b + // ,[horizon_i] = @horizon_i + // ,[haze_horizon] = @haze_horizon + // ,[blue_density_r] = @blue_density_r + // ,[blue_density_g] = @blue_density_g + // ,[blue_density_b] = @blue_density_b + // ,[blue_density_i] = @blue_density_i + // ,[haze_density] = @haze_density + // ,[density_multiplier] = @density_multiplier + // ,[distance_multiplier] = @distance_multiplier + // ,[max_altitude] = @max_altitude + // ,[sun_moon_color_r] = @sun_moon_color_r + // ,[sun_moon_color_g] = @sun_moon_color_g + // ,[sun_moon_color_b] = @sun_moon_color_b + // ,[sun_moon_color_i] = @sun_moon_color_i + // ,[sun_moon_position] = @sun_moon_position + // ,[ambient_r] = @ambient_r + // ,[ambient_g] = @ambient_g + // ,[ambient_b] = @ambient_b + // ,[ambient_i] = @ambient_i + // ,[east_angle] = @east_angle + // ,[sun_glow_focus] = @sun_glow_focus + // ,[sun_glow_size] = @sun_glow_size + // ,[scene_gamma] = @scene_gamma + // ,[star_brightness] = @star_brightness + // ,[cloud_color_r] = @cloud_color_r + // ,[cloud_color_g] = @cloud_color_g + // ,[cloud_color_b] = @cloud_color_b + // ,[cloud_color_i] = @cloud_color_i + // ,[cloud_x] = @cloud_x + // ,[cloud_y] = @cloud_y + // ,[cloud_density] = @cloud_density + // ,[cloud_coverage] = @cloud_coverage + // ,[cloud_scale] = @cloud_scale + // ,[cloud_detail_x] = @cloud_detail_x + // ,[cloud_detail_y] = @cloud_detail_y + // ,[cloud_detail_density] = @cloud_detail_density + // ,[cloud_scroll_x] = @cloud_scroll_x + // ,[cloud_scroll_x_lock] = @cloud_scroll_x_lock + // ,[cloud_scroll_y] = @cloud_scroll_y + // ,[cloud_scroll_y_lock] = @cloud_scroll_y_lock + // ,[draw_classic_clouds] = @draw_classic_clouds + // WHERE region_id = @region_id"; + // using (SqlConnection conn = new SqlConnection(m_connectionString)) + // { + // conn.Open(); + // using (SqlCommand cmd = new SqlCommand(sql, conn)) + // { + // cmd.Parameters.AddWithValue("region_id", wl.regionID); + // cmd.Parameters.AddWithValue("water_color_r", wl.waterColor.X); + // cmd.Parameters.AddWithValue("water_color_g", wl.waterColor.Y); + // cmd.Parameters.AddWithValue("water_color_b", wl.waterColor.Z); + // cmd.Parameters.AddWithValue("water_fog_density_exponent", wl.waterFogDensityExponent); + // cmd.Parameters.AddWithValue("underwater_fog_modifier", wl.underwaterFogModifier); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_1", wl.reflectionWaveletScale.X); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_2", wl.reflectionWaveletScale.Y); + // cmd.Parameters.AddWithValue("reflection_wavelet_scale_3", wl.reflectionWaveletScale.Z); + // cmd.Parameters.AddWithValue("fresnel_scale", wl.fresnelScale); + // cmd.Parameters.AddWithValue("fresnel_offset", wl.fresnelOffset); + // cmd.Parameters.AddWithValue("refract_scale_above", wl.refractScaleAbove); + // cmd.Parameters.AddWithValue("refract_scale_below", wl.refractScaleBelow); + // cmd.Parameters.AddWithValue("blur_multiplier", wl.blurMultiplier); + // cmd.Parameters.AddWithValue("big_wave_direction_x", wl.bigWaveDirection.X); + // cmd.Parameters.AddWithValue("big_wave_direction_y", wl.bigWaveDirection.Y); + // cmd.Parameters.AddWithValue("little_wave_direction_x", wl.littleWaveDirection.X); + // cmd.Parameters.AddWithValue("little_wave_direction_y", wl.littleWaveDirection.Y); + // cmd.Parameters.AddWithValue("normal_map_texture", wl.normalMapTexture); + // cmd.Parameters.AddWithValue("horizon_r", wl.horizon.X); + // cmd.Parameters.AddWithValue("horizon_g", wl.horizon.Y); + // cmd.Parameters.AddWithValue("horizon_b", wl.horizon.Z); + // cmd.Parameters.AddWithValue("horizon_i", wl.horizon.W); + // cmd.Parameters.AddWithValue("haze_horizon", wl.hazeHorizon); + // cmd.Parameters.AddWithValue("blue_density_r", wl.blueDensity.X); + // cmd.Parameters.AddWithValue("blue_density_g", wl.blueDensity.Y); + // cmd.Parameters.AddWithValue("blue_density_b", wl.blueDensity.Z); + // cmd.Parameters.AddWithValue("blue_density_i", wl.blueDensity.W); + // cmd.Parameters.AddWithValue("haze_density", wl.hazeDensity); + // cmd.Parameters.AddWithValue("density_multiplier", wl.densityMultiplier); + // cmd.Parameters.AddWithValue("distance_multiplier", wl.distanceMultiplier); + // cmd.Parameters.AddWithValue("max_altitude", wl.maxAltitude); + // cmd.Parameters.AddWithValue("sun_moon_color_r", wl.sunMoonColor.X); + // cmd.Parameters.AddWithValue("sun_moon_color_g", wl.sunMoonColor.Y); + // cmd.Parameters.AddWithValue("sun_moon_color_b", wl.sunMoonColor.Z); + // cmd.Parameters.AddWithValue("sun_moon_color_i", wl.sunMoonColor.W); + // cmd.Parameters.AddWithValue("sun_moon_position", wl.sunMoonPosition); + // cmd.Parameters.AddWithValue("ambient_r", wl.ambient.X); + // cmd.Parameters.AddWithValue("ambient_g", wl.ambient.Y); + // cmd.Parameters.AddWithValue("ambient_b", wl.ambient.Z); + // cmd.Parameters.AddWithValue("ambient_i", wl.ambient.W); + // cmd.Parameters.AddWithValue("east_angle", wl.eastAngle); + // cmd.Parameters.AddWithValue("sun_glow_focus", wl.sunGlowFocus); + // cmd.Parameters.AddWithValue("sun_glow_size", wl.sunGlowSize); + // cmd.Parameters.AddWithValue("scene_gamma", wl.sceneGamma); + // cmd.Parameters.AddWithValue("star_brightness", wl.starBrightness); + // cmd.Parameters.AddWithValue("cloud_color_r", wl.cloudColor.X); + // cmd.Parameters.AddWithValue("cloud_color_g", wl.cloudColor.Y); + // cmd.Parameters.AddWithValue("cloud_color_b", wl.cloudColor.Z); + // cmd.Parameters.AddWithValue("cloud_color_i", wl.cloudColor.W); + // cmd.Parameters.AddWithValue("cloud_x", wl.cloudXYDensity.X); + // cmd.Parameters.AddWithValue("cloud_y", wl.cloudXYDensity.Y); + // cmd.Parameters.AddWithValue("cloud_density", wl.cloudXYDensity.Z); + // cmd.Parameters.AddWithValue("cloud_coverage", wl.cloudCoverage); + // cmd.Parameters.AddWithValue("cloud_scale", wl.cloudScale); + // cmd.Parameters.AddWithValue("cloud_detail_x", wl.cloudDetailXYDensity.X); + // cmd.Parameters.AddWithValue("cloud_detail_y", wl.cloudDetailXYDensity.Y); + // cmd.Parameters.AddWithValue("cloud_detail_density", wl.cloudDetailXYDensity.Z); + // cmd.Parameters.AddWithValue("cloud_scroll_x", wl.cloudScrollX); + // cmd.Parameters.AddWithValue("cloud_scroll_x_lock", wl.cloudScrollXLock); + // cmd.Parameters.AddWithValue("cloud_scroll_y", wl.cloudScrollY); + // cmd.Parameters.AddWithValue("cloud_scroll_y_lock", wl.cloudScrollYLock); + // cmd.Parameters.AddWithValue("draw_classic_clouds", wl.drawClassicClouds); + + // cmd.ExecuteNonQuery(); + // } + // } + // } + #endregion + } + + #region Environment Settings + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + string sql = "select * from regionenvironment where region_id = :region_id"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (!result.Read()) + { + return String.Empty; + } + else + { + return Convert.ToString(result["llsd_settings"]); + } + } + } + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + { + string sql = "DELETE FROM regionenvironment WHERE region_id = :region_id ;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + + sql = "INSERT INTO regionenvironment (region_id, llsd_settings) VALUES (:region_id, :llsd_settings) ;"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + cmd.Parameters.Add(_Database.CreateParameter("llsd_settings", settings)); + + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + string sql = "delete from regionenvironment where region_id = :region_id ;"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("region_id", regionUUID)); + + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + #endregion + + /// + /// Loads the settings of a region. + /// + /// The region UUID. + /// + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + string sql = @"select * from regionsettings where ""regionUUID"" = :regionUUID"; + RegionSettings regionSettings; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionUUID", regionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + regionSettings = BuildRegionSettings(reader); + regionSettings.OnSave += StoreRegionSettings; + + return regionSettings; + } + } + } + + //If we reach this point then there are new region settings for that region + regionSettings = new RegionSettings(); + regionSettings.RegionUUID = regionUUID; + regionSettings.OnSave += StoreRegionSettings; + + //Store new values + StoreNewRegionSettings(regionSettings); + + LoadSpawnPoints(regionSettings); + + return regionSettings; + } + + /// + /// Store region settings, need to check if the check is really necesary. If we can make something for creating new region. + /// + /// region settings. + public void StoreRegionSettings(RegionSettings regionSettings) + { + //Little check if regionUUID already exist in DB + string regionUUID; + string sql = @"SELECT ""regionUUID"" FROM regionsettings WHERE ""regionUUID"" = :regionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("regionUUID", regionSettings.RegionUUID)); + conn.Open(); + regionUUID = cmd.ExecuteScalar().ToString(); + } + + if (string.IsNullOrEmpty(regionUUID)) + { + StoreNewRegionSettings(regionSettings); + } + else + { + //This method only updates region settings!!! First call LoadRegionSettings to create new region settings in DB + sql = + @"UPDATE regionsettings SET block_terraform = :block_terraform ,block_fly = :block_fly ,allow_damage = :allow_damage +,restrict_pushing = :restrict_pushing ,allow_land_resell = :allow_land_resell ,allow_land_join_divide = :allow_land_join_divide +,block_show_in_search = :block_show_in_search ,agent_limit = :agent_limit ,object_bonus = :object_bonus ,maturity = :maturity +,disable_scripts = :disable_scripts ,disable_collisions = :disable_collisions ,disable_physics = :disable_physics +,terrain_texture_1 = :terrain_texture_1 ,terrain_texture_2 = :terrain_texture_2 ,terrain_texture_3 = :terrain_texture_3 +,terrain_texture_4 = :terrain_texture_4 ,elevation_1_nw = :elevation_1_nw ,elevation_2_nw = :elevation_2_nw +,elevation_1_ne = :elevation_1_ne ,elevation_2_ne = :elevation_2_ne ,elevation_1_se = :elevation_1_se ,elevation_2_se = :elevation_2_se +,elevation_1_sw = :elevation_1_sw ,elevation_2_sw = :elevation_2_sw ,water_height = :water_height ,terrain_raise_limit = :terrain_raise_limit +,terrain_lower_limit = :terrain_lower_limit ,use_estate_sun = :use_estate_sun ,fixed_sun = :fixed_sun ,sun_position = :sun_position +,covenant = :covenant ,covenant_datetime = :covenant_datetime, sunvectorx = :sunvectorx, sunvectory = :sunvectory, sunvectorz = :sunvectorz, +""Sandbox"" = :Sandbox, loaded_creation_datetime = :loaded_creation_datetime, loaded_creation_id = :loaded_creation_id, ""map_tile_ID"" = :TerrainImageID, +""TelehubObject"" = :telehubobject, ""parcel_tile_ID"" = :ParcelImageID + WHERE ""regionUUID"" = :regionUUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateRegionSettingParameters(regionSettings)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + SaveSpawnPoints(regionSettings); + } + + public void Shutdown() + { + //Not used?? + } + + #region Private Methods + + /// + /// Stores new regionsettings. + /// + /// The region settings. + private void StoreNewRegionSettings(RegionSettings regionSettings) + { + string sql = @"INSERT INTO regionsettings + (""regionUUID"",block_terraform,block_fly,allow_damage,restrict_pushing,allow_land_resell,allow_land_join_divide, + block_show_in_search,agent_limit,object_bonus,maturity,disable_scripts,disable_collisions,disable_physics, + terrain_texture_1,terrain_texture_2,terrain_texture_3,terrain_texture_4,elevation_1_nw,elevation_2_nw,elevation_1_ne, + elevation_2_ne,elevation_1_se,elevation_2_se,elevation_1_sw,elevation_2_sw,water_height,terrain_raise_limit, + terrain_lower_limit,use_estate_sun,fixed_sun,sun_position,covenant,covenant_datetime,sunvectorx, sunvectory, sunvectorz, + ""Sandbox"", loaded_creation_datetime, loaded_creation_id + ) + VALUES + (:regionUUID,:block_terraform,:block_fly,:allow_damage,:restrict_pushing,:allow_land_resell,:allow_land_join_divide, + :block_show_in_search,:agent_limit,:object_bonus,:maturity,:disable_scripts,:disable_collisions,:disable_physics, + :terrain_texture_1,:terrain_texture_2,:terrain_texture_3,:terrain_texture_4,:elevation_1_nw,:elevation_2_nw,:elevation_1_ne, + :elevation_2_ne,:elevation_1_se,:elevation_2_se,:elevation_1_sw,:elevation_2_sw,:water_height,:terrain_raise_limit, + :terrain_lower_limit,:use_estate_sun,:fixed_sun,:sun_position,:covenant, :covenant_datetime, :sunvectorx,:sunvectory, + :sunvectorz, :Sandbox, :loaded_creation_datetime, :loaded_creation_id )"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.AddRange(CreateRegionSettingParameters(regionSettings)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + + #region Private DataRecord conversion methods + + /// + /// Builds the region settings from a datarecod. + /// + /// datarecord with regionsettings. + /// + private static RegionSettings BuildRegionSettings(IDataRecord row) + { + //TODO change this is some more generic code so we doesnt have to change it every time a new field is added? + RegionSettings newSettings = new RegionSettings(); + + newSettings.RegionUUID = new UUID((Guid)row["regionUUID"]); + newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); + newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); + newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); + newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]); + newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]); + newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]); + newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]); + newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]); + newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]); + newSettings.Maturity = Convert.ToInt32(row["maturity"]); + newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); + newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); + newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); + newSettings.TerrainTexture1 = new UUID((Guid)row["terrain_texture_1"]); + newSettings.TerrainTexture2 = new UUID((Guid)row["terrain_texture_2"]); + newSettings.TerrainTexture3 = new UUID((Guid)row["terrain_texture_3"]); + newSettings.TerrainTexture4 = new UUID((Guid)row["terrain_texture_4"]); + newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); + newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); + newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); + newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]); + newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]); + newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]); + newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]); + newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]); + newSettings.WaterHeight = Convert.ToDouble(row["water_height"]); + newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]); + newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]); + newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]); + newSettings.Sandbox = Convert.ToBoolean(row["Sandbox"]); + newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); + newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); + newSettings.SunVector = new Vector3( + Convert.ToSingle(row["sunvectorx"]), + Convert.ToSingle(row["sunvectory"]), + Convert.ToSingle(row["sunvectorz"]) + ); + newSettings.Covenant = new UUID((Guid)row["covenant"]); + newSettings.CovenantChangedDateTime = Convert.ToInt32(row["covenant_datetime"]); + newSettings.LoadedCreationDateTime = Convert.ToInt32(row["loaded_creation_datetime"]); + + if (row["loaded_creation_id"] is DBNull) + newSettings.LoadedCreationID = ""; + else + newSettings.LoadedCreationID = (String)row["loaded_creation_id"]; + + newSettings.TerrainImageID = new UUID((string)row["map_tile_ID"]); + newSettings.ParcelImageID = new UUID((Guid)row["parcel_tile_ID"]); + newSettings.TelehubObject = new UUID((Guid)row["TelehubObject"]); + + return newSettings; + } + + /// + /// Builds the land data from a datarecord. + /// + /// datarecord with land data + /// + private static LandData BuildLandData(IDataRecord row) + { + LandData newData = new LandData(); + + newData.GlobalID = new UUID((Guid)row["UUID"]); + newData.LocalID = Convert.ToInt32(row["LocalLandID"]); + + // Bitmap is a byte[512] + newData.Bitmap = (Byte[])row["Bitmap"]; + + newData.Name = (string)row["Name"]; + newData.Description = (string)row["Description"]; + newData.OwnerID = new UUID((Guid)row["OwnerUUID"]); + newData.IsGroupOwned = Convert.ToBoolean(row["IsGroupOwned"]); + newData.Area = Convert.ToInt32(row["Area"]); + newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented + newData.Category = (ParcelCategory)Convert.ToInt32(row["Category"]); + //Enum libsecondlife.Parcel.ParcelCategory + newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); + newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); + newData.GroupID = new UUID((Guid)row["GroupUUID"]); + newData.SalePrice = Convert.ToInt32(row["SalePrice"]); + newData.Status = (ParcelStatus)Convert.ToInt32(row["LandStatus"]); + //Enum. libsecondlife.Parcel.ParcelStatus + newData.Flags = Convert.ToUInt32(row["LandFlags"]); + newData.LandingType = Convert.ToByte(row["LandingType"]); + newData.MediaAutoScale = Convert.ToByte(row["MediaAutoScale"]); + newData.MediaID = new UUID((Guid)row["MediaTextureUUID"]); + newData.MediaURL = (string)row["MediaURL"]; + newData.MusicURL = (string)row["MusicURL"]; + newData.PassHours = Convert.ToSingle(row["PassHours"]); + newData.PassPrice = Convert.ToInt32(row["PassPrice"]); + + // UUID authedbuyer; + // UUID snapshotID; + // + // if (UUID.TryParse((string)row["AuthBuyerID"], out authedbuyer)) + // newData.AuthBuyerID = authedbuyer; + // + // if (UUID.TryParse((string)row["SnapshotUUID"], out snapshotID)) + // newData.SnapshotID = snapshotID; + newData.AuthBuyerID = new UUID((Guid)row["AuthBuyerID"]); + newData.SnapshotID = new UUID((Guid)row["SnapshotUUID"]); + + newData.OtherCleanTime = Convert.ToInt32(row["OtherCleanTime"]); + + try + { + newData.UserLocation = + new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]), + Convert.ToSingle(row["UserLocationZ"])); + newData.UserLookAt = + new Vector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]), + Convert.ToSingle(row["UserLookAtZ"])); + } + catch (InvalidCastException) + { + newData.UserLocation = Vector3.Zero; + newData.UserLookAt = Vector3.Zero; + _Log.ErrorFormat("[PARCEL]: unable to get parcel telehub settings for {1}", newData.Name); + } + + newData.ParcelAccessList = new List(); + newData.MediaDescription = (string)row["MediaDescription"]; + newData.MediaType = (string)row["MediaType"]; + newData.MediaWidth = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[0]); + newData.MediaHeight = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[1]); + newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]); + newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]); + newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]); + + return newData; + } + + /// + /// Builds the landaccess data from a data record. + /// + /// datarecord with landaccess data + /// + private static LandAccessEntry BuildLandAccessData(IDataRecord row) + { + LandAccessEntry entry = new LandAccessEntry(); + entry.AgentID = new UUID((Guid)row["AccessUUID"]); + entry.Flags = (AccessList)Convert.ToInt32(row["Flags"]); + entry.Expires = Convert.ToInt32(row["Expires"]); + return entry; + } + + /// + /// Builds the prim from a datarecord. + /// + /// datarecord + /// + private static SceneObjectPart BuildPrim(IDataRecord primRow) + { + SceneObjectPart prim = new SceneObjectPart(); + + prim.UUID = new UUID((Guid)primRow["UUID"]); + // explicit conversion of integers is required, which sort + // of sucks. No idea if there is a shortcut here or not. + prim.CreationDate = Convert.ToInt32(primRow["CreationDate"]); + prim.Name = (string)primRow["Name"]; + // various text fields + prim.Text = (string)primRow["Text"]; + prim.Color = Color.FromArgb(Convert.ToInt32(primRow["ColorA"]), + Convert.ToInt32(primRow["ColorR"]), + Convert.ToInt32(primRow["ColorG"]), + Convert.ToInt32(primRow["ColorB"])); + prim.Description = (string)primRow["Description"]; + prim.SitName = (string)primRow["SitName"]; + prim.TouchName = (string)primRow["TouchName"]; + // permissions + prim.Flags = (PrimFlags)Convert.ToUInt32(primRow["ObjectFlags"]); + //prim.creatorID = new UUID((Guid)primRow["creatorID"]); + prim.CreatorIdentification = (string)primRow["CreatorID"].ToString(); + prim.OwnerID = new UUID((Guid)primRow["OwnerID"]); + prim.GroupID = new UUID((Guid)primRow["GroupID"]); + prim.LastOwnerID = new UUID((Guid)primRow["LastOwnerID"]); + prim.OwnerMask = Convert.ToUInt32(primRow["OwnerMask"]); + prim.NextOwnerMask = Convert.ToUInt32(primRow["NextOwnerMask"]); + prim.GroupMask = Convert.ToUInt32(primRow["GroupMask"]); + prim.EveryoneMask = Convert.ToUInt32(primRow["EveryoneMask"]); + prim.BaseMask = Convert.ToUInt32(primRow["BaseMask"]); + // vectors + prim.OffsetPosition = new Vector3( + Convert.ToSingle(primRow["PositionX"]), + Convert.ToSingle(primRow["PositionY"]), + Convert.ToSingle(primRow["PositionZ"])); + + prim.GroupPosition = new Vector3( + Convert.ToSingle(primRow["GroupPositionX"]), + Convert.ToSingle(primRow["GroupPositionY"]), + Convert.ToSingle(primRow["GroupPositionZ"])); + + prim.Velocity = new Vector3( + Convert.ToSingle(primRow["VelocityX"]), + Convert.ToSingle(primRow["VelocityY"]), + Convert.ToSingle(primRow["VelocityZ"])); + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(primRow["AngularVelocityX"]), + Convert.ToSingle(primRow["AngularVelocityY"]), + Convert.ToSingle(primRow["AngularVelocityZ"])); + + prim.Acceleration = new Vector3( + Convert.ToSingle(primRow["AccelerationX"]), + Convert.ToSingle(primRow["AccelerationY"]), + Convert.ToSingle(primRow["AccelerationZ"])); + + // quaternions + prim.RotationOffset = new Quaternion( + Convert.ToSingle(primRow["RotationX"]), + Convert.ToSingle(primRow["RotationY"]), + Convert.ToSingle(primRow["RotationZ"]), + Convert.ToSingle(primRow["RotationW"])); + + prim.SitTargetPositionLL = new Vector3( + Convert.ToSingle(primRow["SitTargetOffsetX"]), + Convert.ToSingle(primRow["SitTargetOffsetY"]), + Convert.ToSingle(primRow["SitTargetOffsetZ"])); + + prim.SitTargetOrientationLL = new Quaternion( + Convert.ToSingle(primRow["SitTargetOrientX"]), + Convert.ToSingle(primRow["SitTargetOrientY"]), + Convert.ToSingle(primRow["SitTargetOrientZ"]), + Convert.ToSingle(primRow["SitTargetOrientW"])); + + prim.PayPrice[0] = Convert.ToInt32(primRow["PayPrice"]); + prim.PayPrice[1] = Convert.ToInt32(primRow["PayButton1"]); + prim.PayPrice[2] = Convert.ToInt32(primRow["PayButton2"]); + prim.PayPrice[3] = Convert.ToInt32(primRow["PayButton3"]); + prim.PayPrice[4] = Convert.ToInt32(primRow["PayButton4"]); + + prim.Sound = new UUID((Guid)primRow["LoopedSound"]); + prim.SoundGain = Convert.ToSingle(primRow["LoopedSoundGain"]); + prim.SoundFlags = 1; // If it's persisted at all, it's looped + + if (!(primRow["TextureAnimation"] is DBNull)) + prim.TextureAnimation = (Byte[])primRow["TextureAnimation"]; + if (!(primRow["ParticleSystem"] is DBNull)) + prim.ParticleSystem = (Byte[])primRow["ParticleSystem"]; + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(primRow["OmegaX"]), + Convert.ToSingle(primRow["OmegaY"]), + Convert.ToSingle(primRow["OmegaZ"])); + + prim.SetCameraEyeOffset(new Vector3( + Convert.ToSingle(primRow["CameraEyeOffsetX"]), + Convert.ToSingle(primRow["CameraEyeOffsetY"]), + Convert.ToSingle(primRow["CameraEyeOffsetZ"]) + )); + + prim.SetCameraAtOffset(new Vector3( + Convert.ToSingle(primRow["CameraAtOffsetX"]), + Convert.ToSingle(primRow["CameraAtOffsetY"]), + Convert.ToSingle(primRow["CameraAtOffsetZ"]) + )); + + if (Convert.ToInt16(primRow["ForceMouselook"]) != 0) + prim.SetForceMouselook(true); + + prim.ScriptAccessPin = Convert.ToInt32(primRow["ScriptAccessPin"]); + + if (Convert.ToInt16(primRow["AllowedDrop"]) != 0) + prim.AllowedDrop = true; + + if (Convert.ToInt16(primRow["DieAtEdge"]) != 0) + prim.DIE_AT_EDGE = true; + + prim.SalePrice = Convert.ToInt32(primRow["SalePrice"]); + prim.ObjectSaleType = Convert.ToByte(primRow["SaleType"]); + + prim.Material = Convert.ToByte(primRow["Material"]); + + if (!(primRow["ClickAction"] is DBNull)) + prim.ClickAction = Convert.ToByte(primRow["ClickAction"]); + + prim.CollisionSound = new UUID((Guid)primRow["CollisionSound"]); + prim.CollisionSoundVolume = Convert.ToSingle(primRow["CollisionSoundVolume"]); + + prim.PassTouches = (bool)primRow["PassTouches"]; + + if (!(primRow["MediaURL"] is System.DBNull)) + prim.MediaUrl = (string)primRow["MediaURL"]; + + if (!(primRow["DynAttrs"] is System.DBNull) && (string)primRow["DynAttrs"] != "") + prim.DynAttrs = DAMap.FromXml((string)primRow["DynAttrs"]); + else + prim.DynAttrs = new DAMap(); + + prim.PhysicsShapeType = Convert.ToByte(primRow["PhysicsShapeType"]); + prim.Density = Convert.ToSingle(primRow["Density"]); + prim.GravityModifier = Convert.ToSingle(primRow["GravityModifier"]); + prim.Friction = Convert.ToSingle(primRow["Friction"]); + prim.Restitution = Convert.ToSingle(primRow["Restitution"]); + + return prim; + } + + /// + /// Builds the prim shape from a datarecord. + /// + /// The row. + /// + private static PrimitiveBaseShape BuildShape(IDataRecord shapeRow) + { + PrimitiveBaseShape baseShape = new PrimitiveBaseShape(); + + baseShape.Scale = new Vector3( + (float)Convert.ToDouble(shapeRow["ScaleX"]), + (float)Convert.ToDouble(shapeRow["ScaleY"]), + (float)Convert.ToDouble(shapeRow["ScaleZ"])); + + // paths + baseShape.PCode = Convert.ToByte(shapeRow["PCode"]); + baseShape.PathBegin = Convert.ToUInt16(shapeRow["PathBegin"]); + baseShape.PathEnd = Convert.ToUInt16(shapeRow["PathEnd"]); + baseShape.PathScaleX = Convert.ToByte(shapeRow["PathScaleX"]); + baseShape.PathScaleY = Convert.ToByte(shapeRow["PathScaleY"]); + baseShape.PathShearX = Convert.ToByte(shapeRow["PathShearX"]); + baseShape.PathShearY = Convert.ToByte(shapeRow["PathShearY"]); + baseShape.PathSkew = Convert.ToSByte(shapeRow["PathSkew"]); + baseShape.PathCurve = Convert.ToByte(shapeRow["PathCurve"]); + baseShape.PathRadiusOffset = Convert.ToSByte(shapeRow["PathRadiusOffset"]); + baseShape.PathRevolutions = Convert.ToByte(shapeRow["PathRevolutions"]); + baseShape.PathTaperX = Convert.ToSByte(shapeRow["PathTaperX"]); + baseShape.PathTaperY = Convert.ToSByte(shapeRow["PathTaperY"]); + baseShape.PathTwist = Convert.ToSByte(shapeRow["PathTwist"]); + baseShape.PathTwistBegin = Convert.ToSByte(shapeRow["PathTwistBegin"]); + // profile + baseShape.ProfileBegin = Convert.ToUInt16(shapeRow["ProfileBegin"]); + baseShape.ProfileEnd = Convert.ToUInt16(shapeRow["ProfileEnd"]); + baseShape.ProfileCurve = Convert.ToByte(shapeRow["ProfileCurve"]); + baseShape.ProfileHollow = Convert.ToUInt16(shapeRow["ProfileHollow"]); + + byte[] textureEntry = (byte[])shapeRow["Texture"]; + baseShape.TextureEntry = textureEntry; + + baseShape.ExtraParams = (byte[])shapeRow["ExtraParams"]; + + try + { + baseShape.State = Convert.ToByte(shapeRow["State"]); + } + catch (InvalidCastException) + { + } + + if (!(shapeRow["Media"] is System.DBNull)) + { + baseShape.Media = PrimitiveBaseShape.MediaList.FromXml((string)shapeRow["Media"]); + } + + return baseShape; + } + + /// + /// Build a prim inventory item from the persisted data. + /// + /// + /// + private static TaskInventoryItem BuildItem(IDataRecord inventoryRow) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = new UUID((Guid)inventoryRow["itemID"]); + taskItem.ParentPartID = new UUID((Guid)inventoryRow["primID"]); + taskItem.AssetID = new UUID((Guid)inventoryRow["assetID"]); + taskItem.ParentID = new UUID((Guid)inventoryRow["parentFolderID"]); + + taskItem.InvType = Convert.ToInt32(inventoryRow["invType"]); + taskItem.Type = Convert.ToInt32(inventoryRow["assetType"]); + + taskItem.Name = (string)inventoryRow["name"]; + taskItem.Description = (string)inventoryRow["description"]; + taskItem.CreationDate = Convert.ToUInt32(inventoryRow["creationDate"]); + //taskItem.creatorID = new UUID((Guid)inventoryRow["creatorID"]); + taskItem.CreatorIdentification = (string)inventoryRow["creatorID"].ToString(); + taskItem.OwnerID = new UUID((Guid)inventoryRow["ownerID"]); + taskItem.LastOwnerID = new UUID((Guid)inventoryRow["lastOwnerID"]); + taskItem.GroupID = new UUID((Guid)inventoryRow["groupID"]); + + taskItem.NextPermissions = Convert.ToUInt32(inventoryRow["nextPermissions"]); + taskItem.CurrentPermissions = Convert.ToUInt32(inventoryRow["currentPermissions"]); + taskItem.BasePermissions = Convert.ToUInt32(inventoryRow["basePermissions"]); + taskItem.EveryonePermissions = Convert.ToUInt32(inventoryRow["everyonePermissions"]); + taskItem.GroupPermissions = Convert.ToUInt32(inventoryRow["groupPermissions"]); + taskItem.Flags = Convert.ToUInt32(inventoryRow["flags"]); + + return taskItem; + } + + #endregion + + #region Create parameters methods + + /// + /// Creates the prim inventory parameters. + /// + /// item in inventory. + /// + private NpgsqlParameter[] CreatePrimInventoryParameters(TaskInventoryItem taskItem) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("itemID", taskItem.ItemID)); + parameters.Add(_Database.CreateParameter("primID", taskItem.ParentPartID)); + parameters.Add(_Database.CreateParameter("assetID", taskItem.AssetID)); + parameters.Add(_Database.CreateParameter("parentFolderID", taskItem.ParentID)); + parameters.Add(_Database.CreateParameter("invType", taskItem.InvType)); + parameters.Add(_Database.CreateParameter("assetType", taskItem.Type)); + + parameters.Add(_Database.CreateParameter("name", taskItem.Name)); + parameters.Add(_Database.CreateParameter("description", taskItem.Description)); + parameters.Add(_Database.CreateParameter("creationDate", taskItem.CreationDate)); + parameters.Add(_Database.CreateParameter("creatorID", taskItem.CreatorID)); + parameters.Add(_Database.CreateParameter("ownerID", taskItem.OwnerID)); + parameters.Add(_Database.CreateParameter("lastOwnerID", taskItem.LastOwnerID)); + parameters.Add(_Database.CreateParameter("groupID", taskItem.GroupID)); + parameters.Add(_Database.CreateParameter("nextPermissions", taskItem.NextPermissions)); + parameters.Add(_Database.CreateParameter("currentPermissions", taskItem.CurrentPermissions)); + parameters.Add(_Database.CreateParameter("basePermissions", taskItem.BasePermissions)); + parameters.Add(_Database.CreateParameter("everyonePermissions", taskItem.EveryonePermissions)); + parameters.Add(_Database.CreateParameter("groupPermissions", taskItem.GroupPermissions)); + parameters.Add(_Database.CreateParameter("flags", taskItem.Flags)); + + return parameters.ToArray(); + } + + /// + /// Creates the region setting parameters. + /// + /// regionsettings. + /// + private NpgsqlParameter[] CreateRegionSettingParameters(RegionSettings settings) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("regionUUID", settings.RegionUUID)); + parameters.Add(_Database.CreateParameter("block_terraform", settings.BlockTerraform)); + parameters.Add(_Database.CreateParameter("block_fly", settings.BlockFly)); + parameters.Add(_Database.CreateParameter("allow_damage", settings.AllowDamage)); + parameters.Add(_Database.CreateParameter("restrict_pushing", settings.RestrictPushing)); + parameters.Add(_Database.CreateParameter("allow_land_resell", settings.AllowLandResell)); + parameters.Add(_Database.CreateParameter("allow_land_join_divide", settings.AllowLandJoinDivide)); + parameters.Add(_Database.CreateParameter("block_show_in_search", settings.BlockShowInSearch)); + parameters.Add(_Database.CreateParameter("agent_limit", settings.AgentLimit)); + parameters.Add(_Database.CreateParameter("object_bonus", settings.ObjectBonus)); + parameters.Add(_Database.CreateParameter("maturity", settings.Maturity)); + parameters.Add(_Database.CreateParameter("disable_scripts", settings.DisableScripts)); + parameters.Add(_Database.CreateParameter("disable_collisions", settings.DisableCollisions)); + parameters.Add(_Database.CreateParameter("disable_physics", settings.DisablePhysics)); + parameters.Add(_Database.CreateParameter("terrain_texture_1", settings.TerrainTexture1)); + parameters.Add(_Database.CreateParameter("terrain_texture_2", settings.TerrainTexture2)); + parameters.Add(_Database.CreateParameter("terrain_texture_3", settings.TerrainTexture3)); + parameters.Add(_Database.CreateParameter("terrain_texture_4", settings.TerrainTexture4)); + parameters.Add(_Database.CreateParameter("elevation_1_nw", settings.Elevation1NW)); + parameters.Add(_Database.CreateParameter("elevation_2_nw", settings.Elevation2NW)); + parameters.Add(_Database.CreateParameter("elevation_1_ne", settings.Elevation1NE)); + parameters.Add(_Database.CreateParameter("elevation_2_ne", settings.Elevation2NE)); + parameters.Add(_Database.CreateParameter("elevation_1_se", settings.Elevation1SE)); + parameters.Add(_Database.CreateParameter("elevation_2_se", settings.Elevation2SE)); + parameters.Add(_Database.CreateParameter("elevation_1_sw", settings.Elevation1SW)); + parameters.Add(_Database.CreateParameter("elevation_2_sw", settings.Elevation2SW)); + parameters.Add(_Database.CreateParameter("water_height", settings.WaterHeight)); + parameters.Add(_Database.CreateParameter("terrain_raise_limit", settings.TerrainRaiseLimit)); + parameters.Add(_Database.CreateParameter("terrain_lower_limit", settings.TerrainLowerLimit)); + parameters.Add(_Database.CreateParameter("use_estate_sun", settings.UseEstateSun)); + parameters.Add(_Database.CreateParameter("Sandbox", settings.Sandbox)); + parameters.Add(_Database.CreateParameter("fixed_sun", settings.FixedSun)); + parameters.Add(_Database.CreateParameter("sun_position", settings.SunPosition)); + parameters.Add(_Database.CreateParameter("sunvectorx", settings.SunVector.X)); + parameters.Add(_Database.CreateParameter("sunvectory", settings.SunVector.Y)); + parameters.Add(_Database.CreateParameter("sunvectorz", settings.SunVector.Z)); + parameters.Add(_Database.CreateParameter("covenant", settings.Covenant)); + parameters.Add(_Database.CreateParameter("covenant_datetime", settings.CovenantChangedDateTime)); + parameters.Add(_Database.CreateParameter("Loaded_Creation_DateTime", settings.LoadedCreationDateTime)); + parameters.Add(_Database.CreateParameter("Loaded_Creation_ID", settings.LoadedCreationID)); + parameters.Add(_Database.CreateParameter("TerrainImageID", settings.TerrainImageID)); + parameters.Add(_Database.CreateParameter("ParcelImageID", settings.ParcelImageID)); + parameters.Add(_Database.CreateParameter("TelehubObject", settings.TelehubObject)); + + return parameters.ToArray(); + } + + /// + /// Creates the land parameters. + /// + /// land parameters. + /// region UUID. + /// + private NpgsqlParameter[] CreateLandParameters(LandData land, UUID regionUUID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("UUID", land.GlobalID)); + parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + parameters.Add(_Database.CreateParameter("LocalLandID", land.LocalID)); + + // Bitmap is a byte[512] + parameters.Add(_Database.CreateParameter("Bitmap", land.Bitmap)); + + parameters.Add(_Database.CreateParameter("Name", land.Name)); + parameters.Add(_Database.CreateParameter("Description", land.Description)); + parameters.Add(_Database.CreateParameter("OwnerUUID", land.OwnerID)); + parameters.Add(_Database.CreateParameter("IsGroupOwned", land.IsGroupOwned)); + parameters.Add(_Database.CreateParameter("Area", land.Area)); + parameters.Add(_Database.CreateParameter("AuctionID", land.AuctionID)); //Unemplemented + parameters.Add(_Database.CreateParameter("Category", (int)land.Category)); //Enum libsecondlife.Parcel.ParcelCategory + parameters.Add(_Database.CreateParameter("ClaimDate", land.ClaimDate)); + parameters.Add(_Database.CreateParameter("ClaimPrice", land.ClaimPrice)); + parameters.Add(_Database.CreateParameter("GroupUUID", land.GroupID)); + parameters.Add(_Database.CreateParameter("SalePrice", land.SalePrice)); + parameters.Add(_Database.CreateParameter("LandStatus", (int)land.Status)); //Enum. libsecondlife.Parcel.ParcelStatus + parameters.Add(_Database.CreateParameter("LandFlags", land.Flags)); + parameters.Add(_Database.CreateParameter("LandingType", Convert.ToInt32( land.LandingType) )); + parameters.Add(_Database.CreateParameter("MediaAutoScale", Convert.ToInt32( land.MediaAutoScale ))); + parameters.Add(_Database.CreateParameter("MediaTextureUUID", land.MediaID)); + parameters.Add(_Database.CreateParameter("MediaURL", land.MediaURL)); + parameters.Add(_Database.CreateParameter("MusicURL", land.MusicURL)); + parameters.Add(_Database.CreateParameter("PassHours", land.PassHours)); + parameters.Add(_Database.CreateParameter("PassPrice", land.PassPrice)); + parameters.Add(_Database.CreateParameter("SnapshotUUID", land.SnapshotID)); + parameters.Add(_Database.CreateParameter("UserLocationX", land.UserLocation.X)); + parameters.Add(_Database.CreateParameter("UserLocationY", land.UserLocation.Y)); + parameters.Add(_Database.CreateParameter("UserLocationZ", land.UserLocation.Z)); + parameters.Add(_Database.CreateParameter("UserLookAtX", land.UserLookAt.X)); + parameters.Add(_Database.CreateParameter("UserLookAtY", land.UserLookAt.Y)); + parameters.Add(_Database.CreateParameter("UserLookAtZ", land.UserLookAt.Z)); + parameters.Add(_Database.CreateParameter("AuthBuyerID", land.AuthBuyerID)); + parameters.Add(_Database.CreateParameter("OtherCleanTime", land.OtherCleanTime)); + + return parameters.ToArray(); + } + + /// + /// Creates the land access parameters. + /// + /// parcel access entry. + /// parcel ID. + /// + private NpgsqlParameter[] CreateLandAccessParameters(LandAccessEntry parcelAccessEntry, UUID parcelID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("LandUUID", parcelID)); + parameters.Add(_Database.CreateParameter("AccessUUID", parcelAccessEntry.AgentID)); + parameters.Add(_Database.CreateParameter("Flags", parcelAccessEntry.Flags)); + parameters.Add(_Database.CreateParameter("Expires", parcelAccessEntry.Expires)); + + return parameters.ToArray(); + } + + /// + /// Creates the prim parameters for storing in DB. + /// + /// Basic data of SceneObjectpart prim. + /// The scenegroup ID. + /// The region ID. + /// + private NpgsqlParameter[] CreatePrimParameters(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + List parameters = new List(); + + parameters.Add(_Database.CreateParameter("UUID", prim.UUID)); + parameters.Add(_Database.CreateParameter("RegionUUID", regionUUID)); + parameters.Add(_Database.CreateParameter("CreationDate", prim.CreationDate)); + parameters.Add(_Database.CreateParameter("Name", prim.Name)); + parameters.Add(_Database.CreateParameter("SceneGroupID", sceneGroupID)); + // the UUID of the root part for this SceneObjectGroup + // various text fields + parameters.Add(_Database.CreateParameter("Text", prim.Text)); + parameters.Add(_Database.CreateParameter("ColorR", prim.Color.R)); + parameters.Add(_Database.CreateParameter("ColorG", prim.Color.G)); + parameters.Add(_Database.CreateParameter("ColorB", prim.Color.B)); + parameters.Add(_Database.CreateParameter("ColorA", prim.Color.A)); + parameters.Add(_Database.CreateParameter("Description", prim.Description)); + parameters.Add(_Database.CreateParameter("SitName", prim.SitName)); + parameters.Add(_Database.CreateParameter("TouchName", prim.TouchName)); + // permissions + parameters.Add(_Database.CreateParameter("ObjectFlags", (uint)prim.Flags)); + parameters.Add(_Database.CreateParameter("CreatorID", prim.CreatorID)); + parameters.Add(_Database.CreateParameter("OwnerID", prim.OwnerID)); + parameters.Add(_Database.CreateParameter("GroupID", prim.GroupID)); + parameters.Add(_Database.CreateParameter("LastOwnerID", prim.LastOwnerID)); + parameters.Add(_Database.CreateParameter("OwnerMask", prim.OwnerMask)); + parameters.Add(_Database.CreateParameter("NextOwnerMask", prim.NextOwnerMask)); + parameters.Add(_Database.CreateParameter("GroupMask", prim.GroupMask)); + parameters.Add(_Database.CreateParameter("EveryoneMask", prim.EveryoneMask)); + parameters.Add(_Database.CreateParameter("BaseMask", prim.BaseMask)); + // vectors + parameters.Add(_Database.CreateParameter("PositionX", prim.OffsetPosition.X)); + parameters.Add(_Database.CreateParameter("PositionY", prim.OffsetPosition.Y)); + parameters.Add(_Database.CreateParameter("PositionZ", prim.OffsetPosition.Z)); + parameters.Add(_Database.CreateParameter("GroupPositionX", prim.GroupPosition.X)); + parameters.Add(_Database.CreateParameter("GroupPositionY", prim.GroupPosition.Y)); + parameters.Add(_Database.CreateParameter("GroupPositionZ", prim.GroupPosition.Z)); + parameters.Add(_Database.CreateParameter("VelocityX", prim.Velocity.X)); + parameters.Add(_Database.CreateParameter("VelocityY", prim.Velocity.Y)); + parameters.Add(_Database.CreateParameter("VelocityZ", prim.Velocity.Z)); + parameters.Add(_Database.CreateParameter("AngularVelocityX", prim.AngularVelocity.X)); + parameters.Add(_Database.CreateParameter("AngularVelocityY", prim.AngularVelocity.Y)); + parameters.Add(_Database.CreateParameter("AngularVelocityZ", prim.AngularVelocity.Z)); + parameters.Add(_Database.CreateParameter("AccelerationX", prim.Acceleration.X)); + parameters.Add(_Database.CreateParameter("AccelerationY", prim.Acceleration.Y)); + parameters.Add(_Database.CreateParameter("AccelerationZ", prim.Acceleration.Z)); + // quaternions + parameters.Add(_Database.CreateParameter("RotationX", prim.RotationOffset.X)); + parameters.Add(_Database.CreateParameter("RotationY", prim.RotationOffset.Y)); + parameters.Add(_Database.CreateParameter("RotationZ", prim.RotationOffset.Z)); + parameters.Add(_Database.CreateParameter("RotationW", prim.RotationOffset.W)); + + // Sit target + Vector3 sitTargetPos = prim.SitTargetPositionLL; + parameters.Add(_Database.CreateParameter("SitTargetOffsetX", sitTargetPos.X)); + parameters.Add(_Database.CreateParameter("SitTargetOffsetY", sitTargetPos.Y)); + parameters.Add(_Database.CreateParameter("SitTargetOffsetZ", sitTargetPos.Z)); + + Quaternion sitTargetOrient = prim.SitTargetOrientationLL; + parameters.Add(_Database.CreateParameter("SitTargetOrientW", sitTargetOrient.W)); + parameters.Add(_Database.CreateParameter("SitTargetOrientX", sitTargetOrient.X)); + parameters.Add(_Database.CreateParameter("SitTargetOrientY", sitTargetOrient.Y)); + parameters.Add(_Database.CreateParameter("SitTargetOrientZ", sitTargetOrient.Z)); + + parameters.Add(_Database.CreateParameter("PayPrice", prim.PayPrice[0])); + parameters.Add(_Database.CreateParameter("PayButton1", prim.PayPrice[1])); + parameters.Add(_Database.CreateParameter("PayButton2", prim.PayPrice[2])); + parameters.Add(_Database.CreateParameter("PayButton3", prim.PayPrice[3])); + parameters.Add(_Database.CreateParameter("PayButton4", prim.PayPrice[4])); + + if ((prim.SoundFlags & 1) != 0) // Looped + { + parameters.Add(_Database.CreateParameter("LoopedSound", prim.Sound)); + parameters.Add(_Database.CreateParameter("LoopedSoundGain", prim.SoundGain)); + } + else + { + parameters.Add(_Database.CreateParameter("LoopedSound", UUID.Zero)); + parameters.Add(_Database.CreateParameter("LoopedSoundGain", 0.0f)); + } + + parameters.Add(_Database.CreateParameter("TextureAnimation", prim.TextureAnimation)); + parameters.Add(_Database.CreateParameter("ParticleSystem", prim.ParticleSystem)); + + parameters.Add(_Database.CreateParameter("OmegaX", prim.AngularVelocity.X)); + parameters.Add(_Database.CreateParameter("OmegaY", prim.AngularVelocity.Y)); + parameters.Add(_Database.CreateParameter("OmegaZ", prim.AngularVelocity.Z)); + + parameters.Add(_Database.CreateParameter("CameraEyeOffsetX", prim.GetCameraEyeOffset().X)); + parameters.Add(_Database.CreateParameter("CameraEyeOffsetY", prim.GetCameraEyeOffset().Y)); + parameters.Add(_Database.CreateParameter("CameraEyeOffsetZ", prim.GetCameraEyeOffset().Z)); + + parameters.Add(_Database.CreateParameter("CameraAtOffsetX", prim.GetCameraAtOffset().X)); + parameters.Add(_Database.CreateParameter("CameraAtOffsetY", prim.GetCameraAtOffset().Y)); + parameters.Add(_Database.CreateParameter("CameraAtOffsetZ", prim.GetCameraAtOffset().Z)); + + if (prim.GetForceMouselook()) + parameters.Add(_Database.CreateParameter("ForceMouselook", 1)); + else + parameters.Add(_Database.CreateParameter("ForceMouselook", 0)); + + parameters.Add(_Database.CreateParameter("ScriptAccessPin", prim.ScriptAccessPin)); + + if (prim.AllowedDrop) + parameters.Add(_Database.CreateParameter("AllowedDrop", 1)); + else + parameters.Add(_Database.CreateParameter("AllowedDrop", 0)); + + if (prim.DIE_AT_EDGE) + parameters.Add(_Database.CreateParameter("DieAtEdge", 1)); + else + parameters.Add(_Database.CreateParameter("DieAtEdge", 0)); + + parameters.Add(_Database.CreateParameter("SalePrice", prim.SalePrice)); + parameters.Add(_Database.CreateParameter("SaleType", prim.ObjectSaleType)); + + byte clickAction = prim.ClickAction; + parameters.Add(_Database.CreateParameter("ClickAction", clickAction)); + + parameters.Add(_Database.CreateParameter("Material", prim.Material)); + + parameters.Add(_Database.CreateParameter("CollisionSound", prim.CollisionSound)); + parameters.Add(_Database.CreateParameter("CollisionSoundVolume", prim.CollisionSoundVolume)); + + parameters.Add(_Database.CreateParameter("PassTouches", prim.PassTouches)); + + parameters.Add(_Database.CreateParameter("LinkNumber", prim.LinkNum)); + parameters.Add(_Database.CreateParameter("MediaURL", prim.MediaUrl)); + + if (prim.DynAttrs.CountNamespaces > 0) + parameters.Add(_Database.CreateParameter("DynAttrs", prim.DynAttrs.ToXml())); + else + parameters.Add(_Database.CreateParameter("DynAttrs", null)); + + parameters.Add(_Database.CreateParameter("PhysicsShapeType", prim.PhysicsShapeType)); + parameters.Add(_Database.CreateParameter("Density", (double)prim.Density)); + parameters.Add(_Database.CreateParameter("GravityModifier", (double)prim.GravityModifier)); + parameters.Add(_Database.CreateParameter("Friction", (double)prim.Friction)); + parameters.Add(_Database.CreateParameter("Restitution", (double)prim.Restitution)); + + return parameters.ToArray(); + } + + /// + /// Creates the primshape parameters for stroing in DB. + /// + /// Basic data of SceneObjectpart prim. + /// The scene group ID. + /// The region UUID. + /// + private NpgsqlParameter[] CreatePrimShapeParameters(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + List parameters = new List(); + + PrimitiveBaseShape s = prim.Shape; + parameters.Add(_Database.CreateParameter("UUID", prim.UUID)); + // shape is an enum + parameters.Add(_Database.CreateParameter("Shape", 0)); + // vectors + parameters.Add(_Database.CreateParameter("ScaleX", s.Scale.X)); + parameters.Add(_Database.CreateParameter("ScaleY", s.Scale.Y)); + parameters.Add(_Database.CreateParameter("ScaleZ", s.Scale.Z)); + // paths + parameters.Add(_Database.CreateParameter("PCode", s.PCode)); + parameters.Add(_Database.CreateParameter("PathBegin", s.PathBegin)); + parameters.Add(_Database.CreateParameter("PathEnd", s.PathEnd)); + parameters.Add(_Database.CreateParameter("PathScaleX", s.PathScaleX)); + parameters.Add(_Database.CreateParameter("PathScaleY", s.PathScaleY)); + parameters.Add(_Database.CreateParameter("PathShearX", s.PathShearX)); + parameters.Add(_Database.CreateParameter("PathShearY", s.PathShearY)); + parameters.Add(_Database.CreateParameter("PathSkew", s.PathSkew)); + parameters.Add(_Database.CreateParameter("PathCurve", s.PathCurve)); + parameters.Add(_Database.CreateParameter("PathRadiusOffset", s.PathRadiusOffset)); + parameters.Add(_Database.CreateParameter("PathRevolutions", s.PathRevolutions)); + parameters.Add(_Database.CreateParameter("PathTaperX", s.PathTaperX)); + parameters.Add(_Database.CreateParameter("PathTaperY", s.PathTaperY)); + parameters.Add(_Database.CreateParameter("PathTwist", s.PathTwist)); + parameters.Add(_Database.CreateParameter("PathTwistBegin", s.PathTwistBegin)); + // profile + parameters.Add(_Database.CreateParameter("ProfileBegin", s.ProfileBegin)); + parameters.Add(_Database.CreateParameter("ProfileEnd", s.ProfileEnd)); + parameters.Add(_Database.CreateParameter("ProfileCurve", s.ProfileCurve)); + parameters.Add(_Database.CreateParameter("ProfileHollow", s.ProfileHollow)); + parameters.Add(_Database.CreateParameter("Texture", s.TextureEntry)); + parameters.Add(_Database.CreateParameter("ExtraParams", s.ExtraParams)); + parameters.Add(_Database.CreateParameter("State", s.State)); + + if (null == s.Media) + { + parameters.Add(_Database.CreateParameter("Media", DBNull.Value)); + } + else + { + parameters.Add(_Database.CreateParameter("Media", s.Media.ToXml())); + } + + return parameters.ToArray(); + } + + #endregion + + #endregion + + private void LoadSpawnPoints(RegionSettings rs) + { + rs.ClearSpawnPoints(); + + string sql = @"SELECT ""Yaw"", ""Pitch"", ""Distance"" FROM spawn_points WHERE ""RegionUUID"" = :RegionUUID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + SpawnPoint sp = new SpawnPoint(); + + sp.Yaw = (float)reader["Yaw"]; + sp.Pitch = (float)reader["Pitch"]; + sp.Distance = (float)reader["Distance"]; + + rs.AddSpawnPoint(sp); + } + } + } + } + + private void SaveSpawnPoints(RegionSettings rs) + { + string sql = @"DELETE FROM spawn_points WHERE ""RegionUUID"" = :RegionUUID"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + foreach (SpawnPoint p in rs.SpawnPoints()) + { + sql = @"INSERT INTO spawn_points (""RegionUUID"", ""Yaw"", ""Pitch"", ""Distance"") VALUES (:RegionUUID, :Yaw, :Pitch, :Distance)"; + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(_Database.CreateParameter("RegionUUID", rs.RegionUUID)); + cmd.Parameters.Add(_Database.CreateParameter("Yaw", p.Yaw)); + cmd.Parameters.Add(_Database.CreateParameter("Pitch", p.Pitch)); + cmd.Parameters.Add(_Database.CreateParameter("Distance", p.Distance)); + conn.Open(); + cmd.ExecuteNonQuery(); + } + } + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs b/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs new file mode 100644 index 0000000000..0a68b23384 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLUserAccountData.cs @@ -0,0 +1,329 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Text; +using Npgsql; +using log4net; +using System.Reflection; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLUserAccountData : PGSQLGenericTableHandler,IUserAccountData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + + public PGSQLUserAccountData(string connectionString, string realm) : + base(connectionString, realm, "UserAccount") + { + } + + /* + private string m_Realm; + private List m_ColumnNames = null; + private PGSQLManager m_database; + + public PGSQLUserAccountData(string connectionString, string realm) : + base(connectionString, realm, "UserAccount") + { + m_Realm = realm; + m_ConnectionString = connectionString; + m_database = new PGSQLManager(connectionString); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + conn.Open(); + Migration m = new Migration(conn, GetType().Assembly, "UserAccount"); + m.Update(); + } + } + */ + /* + public List Query(UUID principalID, UUID scopeID, string query) + { + return null; + } + */ + /* + public override UserAccountData[] Get(string[] fields, string[] keys) + { + UserAccountData[] retUA = base.Get(fields,keys); + + if (retUA.Length > 0) + { + Dictionary data = retUA[0].Data; + Dictionary data2 = new Dictionary(); + + foreach (KeyValuePair chave in data) + { + string s2 = chave.Key; + + data2[s2] = chave.Value; + + if (!m_FieldTypes.ContainsKey(chave.Key)) + { + string tipo = ""; + m_FieldTypes.TryGetValue(chave.Key, out tipo); + m_FieldTypes.Add(s2, tipo); + } + } + foreach (KeyValuePair chave in data2) + { + if (!retUA[0].Data.ContainsKey(chave.Key)) + retUA[0].Data.Add(chave.Key, chave.Value); + } + } + + return retUA; + } + */ + /* + public UserAccountData Get(UUID principalID, UUID scopeID) + { + UserAccountData ret = new UserAccountData(); + ret.Data = new Dictionary(); + + string sql = string.Format(@"select * from {0} where ""PrincipalID"" = :principalID", m_Realm); + if (scopeID != UUID.Zero) + sql += @" and ""ScopeID"" = :scopeID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + cmd.Parameters.Add(m_database.CreateParameter("principalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", scopeID)); + + conn.Open(); + using (NpgsqlDataReader result = cmd.ExecuteReader()) + { + if (result.Read()) + { + ret.PrincipalID = principalID; + UUID scope; + UUID.TryParse(result["scopeid"].ToString(), out scope); + ret.ScopeID = scope; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + string s2 = s; + if (s2 == "uuid") + continue; + if (s2 == "scopeid") + continue; + + ret.Data[s] = result[s].ToString(); + } + return ret; + } + } + } + return null; + } + + + public override bool Store(UserAccountData data) + { + if (data.Data.ContainsKey("PrincipalID")) + data.Data.Remove("PrincipalID"); + if (data.Data.ContainsKey("ScopeID")) + data.Data.Remove("ScopeID"); + + string[] fields = new List(data.Data.Keys).ToArray(); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + m_log.DebugFormat("[USER]: Try to update user {0} {1}", data.FirstName, data.LastName); + + StringBuilder updateBuilder = new StringBuilder(); + updateBuilder.AppendFormat("update {0} set ", m_Realm); + bool first = true; + foreach (string field in fields) + { + if (!first) + updateBuilder.Append(", "); + updateBuilder.AppendFormat("\"{0}\" = :{0}", field); + + first = false; + if (m_FieldTypes.ContainsKey(field)) + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field], m_FieldTypes[field])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + field, data.Data[field])); + } + + updateBuilder.Append(" where \"PrincipalID\" = :principalID"); + + if (data.ScopeID != UUID.Zero) + updateBuilder.Append(" and \"ScopeID\" = :scopeID"); + + cmd.CommandText = updateBuilder.ToString(); + cmd.Connection = conn; + cmd.Parameters.Add(m_database.CreateParameter("principalID", data.PrincipalID)); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", data.ScopeID)); + + m_log.DebugFormat("[USER]: SQL update user {0} ", cmd.CommandText); + + conn.Open(); + + m_log.DebugFormat("[USER]: CON opened update user {0} ", cmd.CommandText); + + int conta = 0; + try + { + conta = cmd.ExecuteNonQuery(); + } + catch (Exception e){ + m_log.ErrorFormat("[USER]: ERROR opened update user {0} ", e.Message); + } + + + if (conta < 1) + { + m_log.DebugFormat("[USER]: Try to insert user {0} {1}", data.FirstName, data.LastName); + + StringBuilder insertBuilder = new StringBuilder(); + insertBuilder.AppendFormat(@"insert into {0} (""PrincipalID"", ""ScopeID"", ""FirstName"", ""LastName"", """, m_Realm); + insertBuilder.Append(String.Join(@""", """, fields)); + insertBuilder.Append(@""") values (:principalID, :scopeID, :FirstName, :LastName, :"); + insertBuilder.Append(String.Join(", :", fields)); + insertBuilder.Append(");"); + + cmd.Parameters.Add(m_database.CreateParameter("FirstName", data.FirstName)); + cmd.Parameters.Add(m_database.CreateParameter("LastName", data.LastName)); + + cmd.CommandText = insertBuilder.ToString(); + + if (cmd.ExecuteNonQuery() < 1) + { + return false; + } + } + else + m_log.DebugFormat("[USER]: User {0} {1} exists", data.FirstName, data.LastName); + } + return true; + } + + + public bool Store(UserAccountData data, UUID principalID, string token) + { + return false; + } + + + public bool SetDataItem(UUID principalID, string item, string value) + { + string sql = string.Format(@"update {0} set {1} = :{1} where ""UUID"" = :UUID", m_Realm, item); + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + if (m_FieldTypes.ContainsKey(item)) + cmd.Parameters.Add(m_database.CreateParameter("" + item, value, m_FieldTypes[item])); + else + cmd.Parameters.Add(m_database.CreateParameter("" + item, value)); + + cmd.Parameters.Add(m_database.CreateParameter("UUID", principalID)); + conn.Open(); + + if (cmd.ExecuteNonQuery() > 0) + return true; + } + return false; + } + */ + /* + public UserAccountData[] Get(string[] keys, string[] vals) + { + return null; + } + */ + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { + string[] words = query.Split(new char[] { ' ' }); + + for (int i = 0; i < words.Length; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + string sql = ""; + UUID scope_id; + UUID.TryParse(scopeID.ToString(), out scope_id); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + if (words.Length == 1) + { + sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (""FirstName"" ilike :search or ""LastName"" ilike :search)", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("scopeID", (UUID)scope_id)); + cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", (UUID)UUID.Zero)); + cmd.Parameters.Add(m_database.CreateParameter("search", "%" + words[0] + "%")); + } + else + { + sql = String.Format(@"select * from {0} where (""ScopeID""=:ScopeID or ""ScopeID""=:UUIDZero) and (""FirstName"" ilike :searchFirst or ""LastName"" ilike :searchLast)", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("searchFirst", "%" + words[0] + "%")); + cmd.Parameters.Add(m_database.CreateParameter("searchLast", "%" + words[1] + "%")); + cmd.Parameters.Add (m_database.CreateParameter("UUIDZero", (UUID)UUID.Zero)); + cmd.Parameters.Add(m_database.CreateParameter("ScopeID", (UUID)scope_id)); + } + cmd.Connection = conn; + cmd.CommandText = sql; + conn.Open(); + return DoQuery(cmd); + } + } + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs b/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs new file mode 100644 index 0000000000..f1669766af --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLUserProfilesData.cs @@ -0,0 +1,1064 @@ +/* + * 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.Data; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class UserProfilesData : IProfilesData + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected PGSQLManager m_database; + + #region Properites + string ConnectionString + { + get; + set; + } + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + #endregion Properties + + #region class Member Functions + public UserProfilesData(string connectionString) + { + ConnectionString = connectionString; + Init(); + } + + void Init() + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + Migration m = new Migration(dbcon, Assembly, "UserProfiles"); + m.Update(); + m_database = new PGSQLManager(ConnectionString); + } + } + #endregion Member Functions + + #region Classifieds Queries + /// + /// Gets the classified records. + /// + /// + /// Array of classified records + /// + /// + /// Creator identifier. + /// + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + string query = @"SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = :Id"; + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", creatorId)); + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.Default)) + { + if (reader.HasRows) + { + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + + string Name = null; + try + { + Id = DBGuid.FromDB(reader["classifieduuid"]); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: UserAccount exception ", e); + } + + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + } + } + } + } + return data; + } + + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query = @"WITH upsert AS ( + UPDATE classifieds SET + classifieduuid = :ClassifiedId, creatoruuid = :CreatorId, creationdate = :CreatedDate, + expirationdate = :ExpirationDate,category =:Category, name = :Name, description = :Description, + parceluuid = :ParcelId, parentestate = :ParentEstate, snapshotuuid = :SnapshotId, + simname = :SimName, posglobal = :GlobalPos, parcelname = :ParcelName, classifiedflags = :Flags, + priceforlisting = :ListingPrice + RETURNING * ) + INSERT INTO classifieds (classifieduuid,creatoruuid,creationdate,expirationdate,category,name, + description,parceluuid,parentestate,snapshotuuid,simname,posglobal,parcelname,classifiedflags, + priceforlisting) + SELECT + :ClassifiedId,:CreatorId,:CreatedDate,:ExpirationDate,:Category,:Name,:Description, + :ParcelId,:ParentEstate,:SnapshotId,:SimName,:GlobalPos,:ParcelName,:Flags,:ListingPrice + WHERE NOT EXISTS ( + SELECT * FROM upsert )"; + + if (string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if (ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if (string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if (ad.Flags == 2) + { + duration = new TimeSpan(7, 0, 0, 0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365, 0, 0, 0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("ClassifiedId", ad.ClassifiedId)); + cmd.Parameters.Add(m_database.CreateParameter("CreatorId", ad.CreatorId)); + cmd.Parameters.Add(m_database.CreateParameter("CreatedDate", (int)ad.CreationDate)); + cmd.Parameters.Add(m_database.CreateParameter("ExpirationDate", (int)ad.ExpirationDate)); + cmd.Parameters.Add(m_database.CreateParameter("Category", ad.Category.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("Name", ad.Name.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("Description", ad.Description.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("ParcelId", ad.ParcelId)); + cmd.Parameters.Add(m_database.CreateParameter("ParentEstate", (int)ad.ParentEstate)); + cmd.Parameters.Add(m_database.CreateParameter("SnapshotId", ad.SnapshotId)); + cmd.Parameters.Add(m_database.CreateParameter("SimName", ad.SimName.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("GlobalPos", ad.GlobalPos.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("ParcelName", ad.ParcelName.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("Flags", (int)Convert.ToInt32(ad.Flags))); + cmd.Parameters.Add(m_database.CreateParameter("ListingPrice", (int)Convert.ToInt32(ad.Price))); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: ClassifiedsUpdate exception ", e); + result = e.Message; + return false; + } + + return true; + } + + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query = @"DELETE FROM classifieds WHERE classifieduuid = :ClassifiedId ;"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("ClassifiedId", recordId)); + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: DeleteClassifiedRecord exception ", e); + return false; + } + + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = :AdId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("AdId", ad.ClassifiedId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + ad.CreatorId = DBGuid.FromDB(reader["creatoruuid"]); + ad.ParcelId = DBGuid.FromDB(reader["parceluuid"]); + ad.SnapshotId = DBGuid.FromDB(reader["snapshotuuid"]); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte)Convert.ToInt16(reader["classifiedflags"]); + ad.Category = Convert.ToInt32(reader["category"]); + ad.Price = Convert.ToInt16(reader["priceforlisting"]); + ad.Name = reader["name"].ToString(); + ad.Description = reader["description"].ToString(); + ad.SimName = reader["simname"].ToString(); + ad.GlobalPos = reader["posglobal"].ToString(); + ad.ParcelName = reader["parcelname"].ToString(); + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetClassifiedInfo exception ", e); + } + + return true; + } + + public static UUID GetUUID(object uuidValue) + { + + UUID ret = UUID.Zero; + + UUID.TryParse(uuidValue.ToString(), out ret); + + return ret; + } + + #endregion Classifieds Queries + + #region Picks Queries + public OSDArray GetAvatarPicks(UUID avatarId) + { + string query = string.Empty; + + query += "SELECT pickuuid, name FROM userpicks WHERE "; + query += "creatoruuid = :Id"; + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", avatarId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.HasRows) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid", OSD.FromUUID(DBGuid.FromDB(reader["pickuuid"]))); + record.Add("name", OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetAvatarPicks exception ", e); + } + + return data; + } + + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = :CreatorId AND "; + query += "pickuuid = :PickId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("CreatorId", avatarId)); + cmd.Parameters.Add(m_database.CreateParameter("PickId", pickId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.HasRows) + { + reader.Read(); + + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + pick.PickId = DBGuid.FromDB(reader["pickuuid"]); + pick.CreatorId = DBGuid.FromDB(reader["creatoruuid"]); + pick.ParcelId = DBGuid.FromDB(reader["parceluuid"]); + pick.SnapshotId = DBGuid.FromDB(reader["snapshotuuid"]); + pick.GlobalPos = (string)reader["posglobal"].ToString(); + pick.TopPick = Convert.ToBoolean(reader["toppick"]); + pick.Enabled = Convert.ToBoolean(reader["enabled"]); + pick.Name = reader["name"].ToString(); + pick.Desc = reader["description"].ToString(); + pick.ParcelName = reader["user"].ToString(); + pick.OriginalName = reader["originalname"].ToString(); + pick.SimName = reader["simname"].ToString(); + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + dbcon.Close(); + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetPickInfo exception ", e); + } + + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + + query = @"WITH upsert AS ( + UPDATE userpicks SET + pickuuid = :PickId, creatoruuid = :CreatorId, toppick = :TopPick, parceluuid = :ParcelId, + name = :Name, description = :Desc, snapshotuuid = :SnapshotId, ""user"" = :User, + originalname = :Original, simname = :SimName, posglobal = :GlobalPos, + sortorder = :SortOrder, enabled = :Enabled + RETURNING * ) + INSERT INTO userpicks (pickuuid,creatoruuid,toppick,parceluuid,name,description, + snapshotuuid,""user"",originalname,simname,posglobal,sortorder,enabled) + SELECT + :PickId,:CreatorId,:TopPick,:ParcelId,:Name,:Desc,:SnapshotId,:User, + :Original,:SimName,:GlobalPos,:SortOrder,:Enabled + WHERE NOT EXISTS ( + SELECT * FROM upsert )"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("PickId", pick.PickId)); + cmd.Parameters.Add(m_database.CreateParameter("CreatorId", pick.CreatorId)); + cmd.Parameters.Add(m_database.CreateParameter("TopPick", pick.TopPick)); + cmd.Parameters.Add(m_database.CreateParameter("ParcelId", pick.ParcelId)); + cmd.Parameters.Add(m_database.CreateParameter("Name", pick.Name)); + cmd.Parameters.Add(m_database.CreateParameter("Desc", pick.Desc)); + cmd.Parameters.Add(m_database.CreateParameter("SnapshotId", pick.SnapshotId)); + cmd.Parameters.Add(m_database.CreateParameter("User", pick.ParcelName)); + cmd.Parameters.Add(m_database.CreateParameter("Original", pick.OriginalName)); + cmd.Parameters.Add(m_database.CreateParameter("SimName", pick.SimName)); + cmd.Parameters.Add(m_database.CreateParameter("GlobalPos", pick.GlobalPos)); + cmd.Parameters.Add(m_database.CreateParameter("SortOrder", pick.SortOrder)); + cmd.Parameters.Add(m_database.CreateParameter("Enabled", pick.Enabled)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: UpdateAvatarNotes exception ", e); + return false; + } + + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = :PickId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("PickId", pickId)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: DeleteUserPickRecord exception ", e); + return false; + } + + return true; + } + + #endregion Picks Queries + + #region Avatar Notes Queries + + public bool GetAvatarNotes(ref UserProfileNotes notes) + { // WIP + string query = string.Empty; + + query += "SELECT notes FROM usernotes WHERE "; + query += "useruuid = :Id AND "; + query += "targetuuid = :TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", notes.UserId)); + cmd.Parameters.Add(m_database.CreateParameter("TargetId", notes.TargetId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + reader.Read(); + notes.Notes = OSD.FromString((string)reader["notes"]); + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetAvatarNotes exception ", e); + } + + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if (string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=:UserId AND "; + query += "targetuuid=:TargetId"; + } + else + { + remove = false; + + query = @"WITH upsert AS ( + UPDATE usernotes SET notes = :Notes, useruuid = :UserId, targetuuid = :TargetId RETURNING * ) + INSERT INTO usernotes (notes,useruuid,targetuuid) + SELECT :Notes,:UserId,:TargetId + WHERE NOT EXISTS ( + SELECT * FROM upsert + )"; + } + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + if (!remove) + cmd.Parameters.Add(m_database.CreateParameter("Notes", note.Notes)); + + cmd.Parameters.Add(m_database.CreateParameter("TargetId", note.TargetId)); + cmd.Parameters.Add(m_database.CreateParameter("UserId", note.UserId)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: UpdateAvatarNotes exception ", e); + return false; + } + + return true; + } + + #endregion Avatar Notes Queries + + #region Avatar Properties + + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = :Id"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", props.UserId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + // m_log.DebugFormat("[PROFILES_DATA]" + + // ": Getting data for {0}.", props.UserId); + reader.Read(); + props.WebUrl = (string)reader["profileURL"].ToString(); + props.ImageId = DBGuid.FromDB(reader["profileImage"]); + props.AboutText = (string)reader["profileAboutText"]; + props.FirstLifeImageId = DBGuid.FromDB(reader["profileFirstImage"]); + props.FirstLifeText = (string)reader["profileFirstText"]; + props.PartnerId = DBGuid.FromDB(reader["profilePartner"]); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + //m_log.DebugFormat("[PROFILES_DATA]" + + // ": No data for {0}", props.UserId); + + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "\"profilePartner\", "; + query += "\"profileAllowPublish\", "; + query += "\"profileMaturePublish\", "; + query += "\"profileURL\", "; + query += "\"profileWantToMask\", "; + query += "\"profileWantToText\", "; + query += "\"profileSkillsMask\", "; + query += "\"profileSkillsText\", "; + query += "\"profileLanguages\", "; + query += "\"profileImage\", "; + query += "\"profileAboutText\", "; + query += "\"profileFirstImage\", "; + query += "\"profileFirstText\") VALUES ("; + query += ":userId, "; + query += ":profilePartner, "; + query += ":profileAllowPublish, "; + query += ":profileMaturePublish, "; + query += ":profileURL, "; + query += ":profileWantToMask, "; + query += ":profileWantToText, "; + query += ":profileSkillsMask, "; + query += ":profileSkillsText, "; + query += ":profileLanguages, "; + query += ":profileImage, "; + query += ":profileAboutText, "; + query += ":profileFirstImage, "; + query += ":profileFirstText)"; + + dbcon.Close(); + dbcon.Open(); + + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + //m_log.DebugFormat("[PROFILES_DATA]" + + // ": Adding new data for {0}", props.UserId); + + put.Parameters.Add(m_database.CreateParameter("userId", props.UserId)); + put.Parameters.Add(m_database.CreateParameter("profilePartner", props.PartnerId)); + put.Parameters.Add(m_database.CreateParameter("profileAllowPublish", props.PublishProfile)); + put.Parameters.Add(m_database.CreateParameter("profileMaturePublish", props.PublishMature)); + put.Parameters.Add(m_database.CreateParameter("profileURL", props.WebUrl)); + put.Parameters.Add(m_database.CreateParameter("profileWantToMask", props.WantToMask)); + put.Parameters.Add(m_database.CreateParameter("profileWantToText", props.WantToText)); + put.Parameters.Add(m_database.CreateParameter("profileSkillsMask", props.SkillsMask)); + put.Parameters.Add(m_database.CreateParameter("profileSkillsText", props.SkillsText)); + put.Parameters.Add(m_database.CreateParameter("profileLanguages", props.Language)); + put.Parameters.Add(m_database.CreateParameter("profileImage", props.ImageId)); + put.Parameters.Add(m_database.CreateParameter("profileAboutText", props.AboutText)); + put.Parameters.Add(m_database.CreateParameter("profileFirstImage", props.FirstLifeImageId)); + put.Parameters.Add(m_database.CreateParameter("profileFirstText", props.FirstLifeText)); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetAvatarProperties exception ", e); + result = e.Message; + return false; + } + + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "\"profileURL\"=:profileURL, "; + query += "\"profileImage\"=:image, "; + query += "\"profileAboutText\"=:abouttext,"; + query += "\"profileFirstImage\"=:firstlifeimage,"; + query += "\"profileFirstText\"=:firstlifetext "; + query += "WHERE \"useruuid\"=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("profileURL", props.WebUrl)); + cmd.Parameters.Add(m_database.CreateParameter("image", props.ImageId)); + cmd.Parameters.Add(m_database.CreateParameter("abouttext", props.AboutText)); + cmd.Parameters.Add(m_database.CreateParameter("firstlifeimage", props.FirstLifeImageId)); + cmd.Parameters.Add(m_database.CreateParameter("firstlifetext", props.FirstLifeText)); + cmd.Parameters.Add(m_database.CreateParameter("uuid", props.UserId)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: AgentPropertiesUpdate exception ", e); + return false; + } + + return true; + } + + #endregion Avatar Properties + + #region Avatar Interests + + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "\"profileWantToMask\"=:WantMask, "; + query += "\"profileWantToText\"=:WantText,"; + query += "\"profileSkillsMask\"=:SkillsMask,"; + query += "\"profileSkillsText\"=:SkillsText, "; + query += "\"profileLanguages\"=:Languages "; + query += "WHERE \"useruuid\"=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("WantMask", up.WantToMask)); + cmd.Parameters.Add(m_database.CreateParameter("WantText", up.WantToText)); + cmd.Parameters.Add(m_database.CreateParameter("SkillsMask", up.SkillsMask)); + cmd.Parameters.Add(m_database.CreateParameter("SkillsText", up.SkillsText)); + cmd.Parameters.Add(m_database.CreateParameter("Languages", up.Language)); + cmd.Parameters.Add(m_database.CreateParameter("uuid", up.UserId)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: UpdateAvatarInterests exception ", e); + result = e.Message; + return false; + } + + return true; + } + + #endregion Avatar Interests + + public OSDArray GetUserImageAssets(UUID avatarId) + { + OSDArray data = new OSDArray(); + string query = "SELECT \"snapshotuuid\" FROM {0} WHERE \"creatoruuid\" = :Id"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format(query, "\"classifieds\""), dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", avatarId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString(reader["snapshotuuid"].ToString())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format(query, "\"userpicks\""), dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", avatarId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString(reader["snapshotuuid"].ToString())); + } + } + } + } + + dbcon.Close(); + dbcon.Open(); + + query = "SELECT \"profileImage\", \"profileFirstImage\" FROM \"userprofile\" WHERE \"useruuid\" = :Id"; + + using (NpgsqlCommand cmd = new NpgsqlCommand(string.Format(query, "\"userpicks\""), dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", avatarId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + while (reader.Read()) + { + data.Add(new OSDString(reader["profileImage"].ToString())); + data.Add(new OSDString(reader["profileFirstImage"].ToString())); + } + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetUserImageAssets exception ", e); + } + + return data; + } + + #region User Preferences + + public bool GetUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "SELECT imviaemail::VARCHAR,visible::VARCHAR,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = :Id"; + + OSDArray data = new OSDArray(); + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", pref.UserId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + if (reader.HasRows) + { + reader.Read(); + bool.TryParse((string)reader["imviaemail"], out pref.IMViaEmail); + bool.TryParse((string)reader["visible"], out pref.Visible); + pref.EMail = (string)reader["email"]; + } + else + { + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + put.Parameters.Add(m_database.CreateParameter("Id", pref.UserId)); + query = "INSERT INTO usersettings VALUES "; + query += "(:Id,'false','false', '')"; + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetUserPreferences exception ", e); + result = e.Message; + } + + return true; + } + + public bool UpdateUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "UPDATE usersettings SET "; + query += "imviaemail=:ImViaEmail, "; + query += "visible=:Visible, "; + query += "email=:Email "; + query += "WHERE useruuid=:uuid"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("ImViaEmail", pref.IMViaEmail)); + cmd.Parameters.Add(m_database.CreateParameter("Visible", pref.Visible)); + cmd.Parameters.Add(m_database.CreateParameter("EMail", pref.EMail.ToString().ToLower())); + cmd.Parameters.Add(m_database.CreateParameter("uuid", pref.UserId)); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: UpdateUserPreferences exception ", e); + result = e.Message; + return false; + } + + return true; + } + + #endregion User Preferences + + #region Integration + + public bool GetUserAppData(ref UserAppData props, ref string result) + { + string query = string.Empty; + + query += "SELECT * FROM userdata WHERE "; + query += "\"UserId\" = :Id AND "; + query += "\"TagId\" = :TagId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Id", props.UserId)); + cmd.Parameters.Add(m_database.CreateParameter("TagId", props.TagId)); + + using (NpgsqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (reader.HasRows) + { + reader.Read(); + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += ":UserId,"; + query += ":TagId,"; + query += ":DataKey,"; + query += ":DataVal) "; + + using (NpgsqlCommand put = new NpgsqlCommand(query, dbcon)) + { + put.Parameters.Add(m_database.CreateParameter("UserId", props.UserId)); + put.Parameters.Add(m_database.CreateParameter("TagId", props.TagId)); + put.Parameters.Add(m_database.CreateParameter("DataKey", props.DataKey.ToString())); + put.Parameters.Add(m_database.CreateParameter("DataVal", props.DataVal.ToString())); + + put.ExecuteNonQuery(); + } + } + } + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: GetUserAppData exception ", e); + result = e.Message; + return false; + } + + return true; + } + + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "\"TagId\" = :TagId, "; + query += "\"DataKey\" = :DataKey, "; + query += "\"DataVal\" = :DataVal WHERE "; + query += "\"UserId\" = :UserId AND "; + query += "\"TagId\" = :TagId"; + + try + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(ConnectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(query, dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("UserId", props.UserId.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("TagId", props.TagId.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("DataKey", props.DataKey.ToString())); + cmd.Parameters.Add(m_database.CreateParameter("DataVal", props.DataKey.ToString())); + + cmd.ExecuteNonQuery(); + } + } + } + catch (Exception e) + { + m_log.Error("[PROFILES_DATA]: SetUserData exception ", e); + return false; + } + + return true; + } + + #endregion Integration + } +} \ No newline at end of file diff --git a/OpenSim/Data/PGSQL/PGSQLXAssetData.cs b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs new file mode 100644 index 0000000000..4f682f0e54 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLXAssetData.cs @@ -0,0 +1,587 @@ +/* + * 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.Data; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; +using Npgsql; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLXAssetData : IXAssetDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + /// + /// Number of days that must pass before we update the access time on an asset when it has been fetched. + /// + private const int DaysBetweenAccessTimeUpdates = 30; + + private bool m_enableCompression = false; + private PGSQLManager m_database; + private string m_connectionString; + private object m_dbLock = new object(); + + /// + /// We can reuse this for all hashing since all methods are single-threaded through m_dbBLock + /// + private HashAlgorithm hasher = new SHA256CryptoServiceProvider(); + + #region IPlugin Members + + public string Version { get { return "1.0.0.0"; } } + + /// + /// Initialises Asset interface + /// + /// + /// Loads and initialises the PGSQL storage plugin. + /// Warns and uses the obsolete pgsql_connection.ini if connect string is empty. + /// Check for migration + /// + /// + /// + /// connect string + public void Initialise(string connect) + { + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: THIS PLUGIN IS STRICTLY EXPERIMENTAL."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: DO NOT USE FOR ANY DATA THAT YOU DO NOT MIND LOSING."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: DATABASE TABLES CAN CHANGE AT ANY TIME, CAUSING EXISTING DATA TO BE LOST."); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + m_log.ErrorFormat("[PGSQL XASSETDATA]: ***********************************************************"); + + m_connectionString = connect; + m_database = new PGSQLManager(m_connectionString); + + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + Migration m = new Migration(dbcon, Assembly, "XAssetStore"); + m.Update(); + } + } + + public void Initialise() + { + throw new NotImplementedException(); + } + + public void Dispose() { } + + /// + /// The name of this DB provider + /// + public string Name + { + get { return "PGSQL XAsset storage engine"; } + } + + #endregion + + #region IAssetDataPlugin Members + + /// + /// Fetch Asset from database + /// + /// Asset UUID to fetch + /// Return the asset + /// On failure : throw an exception and attempt to reconnect to database + public AssetBase GetAsset(UUID assetID) + { +// m_log.DebugFormat("[PGSQL XASSET DATA]: Looking for asset {0}", assetID); + + AssetBase asset = null; + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand( + @"SELECT name, description, access_time, ""AssetType"", local, temporary, asset_flags, creatorid, data + FROM XAssetsMeta + JOIN XAssetsData ON XAssetsMeta.hash = XAssetsData.Hash WHERE id=:ID", + dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("ID", assetID)); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { + asset = new AssetBase( + assetID, + (string)dbReader["name"], + Convert.ToSByte(dbReader["AssetType"]), + dbReader["creatorid"].ToString()); + + asset.Data = (byte[])dbReader["data"]; + asset.Description = (string)dbReader["description"]; + + string local = dbReader["local"].ToString(); + if (local.Equals("1") || local.Equals("true", StringComparison.InvariantCultureIgnoreCase)) + asset.Local = true; + else + asset.Local = false; + + asset.Temporary = Convert.ToBoolean(dbReader["temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + + if (m_enableCompression) + { + using (GZipStream decompressionStream = new GZipStream(new MemoryStream(asset.Data), CompressionMode.Decompress)) + { + MemoryStream outputStream = new MemoryStream(); + WebUtil.CopyStream(decompressionStream, outputStream, int.MaxValue); + // int compressedLength = asset.Data.Length; + asset.Data = outputStream.ToArray(); + + // m_log.DebugFormat( + // "[XASSET DB]: Decompressed {0} {1} to {2} bytes from {3}", + // asset.ID, asset.Name, asset.Data.Length, compressedLength); + } + } + + UpdateAccessTime(asset.Metadata, (int)dbReader["access_time"]); + } + } + } + catch (Exception e) + { + m_log.Error(string.Format("[PGSQL XASSET DATA]: Failure fetching asset {0}", assetID), e); + } + } + } + } + + return asset; + } + + /// + /// Create an asset in database, or update it if existing. + /// + /// Asset UUID to create + /// On failure : Throw an exception and attempt to reconnect to database + public void StoreAsset(AssetBase asset) + { +// m_log.DebugFormat("[XASSETS DB]: Storing asset {0} {1}", asset.Name, asset.ID); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlTransaction transaction = dbcon.BeginTransaction()) + { + string assetName = asset.Name; + if (asset.Name.Length > 64) + { + assetName = asset.Name.Substring(0, 64); + m_log.WarnFormat( + "[XASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > 64) + { + assetDescription = asset.Description.Substring(0, 64); + m_log.WarnFormat( + "[XASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + if (m_enableCompression) + { + MemoryStream outputStream = new MemoryStream(); + + using (GZipStream compressionStream = new GZipStream(outputStream, CompressionMode.Compress, false)) + { + // Console.WriteLine(WebUtil.CopyTo(new MemoryStream(asset.Data), compressionStream, int.MaxValue)); + // We have to close the compression stream in order to make sure it writes everything out to the underlying memory output stream. + compressionStream.Close(); + byte[] compressedData = outputStream.ToArray(); + asset.Data = compressedData; + } + } + + byte[] hash = hasher.ComputeHash(asset.Data); + + UUID asset_id; + UUID.TryParse(asset.ID, out asset_id); + +// m_log.DebugFormat( +// "[XASSET DB]: Compressed data size for {0} {1}, hash {2} is {3}", +// asset.ID, asset.Name, hash, compressedData.Length); + + try + { + using (NpgsqlCommand cmd = + new NpgsqlCommand( + @"insert INTO XAssetsMeta(id, hash, name, description, ""AssetType"", local, temporary, create_time, access_time, asset_flags, creatorid) + Select :ID, :Hash, :Name, :Description, :AssetType, :Local, :Temporary, :CreateTime, :AccessTime, :AssetFlags, :CreatorID + where not exists( Select id from XAssetsMeta where id = :ID); + + update XAssetsMeta + set id = :ID, hash = :Hash, name = :Name, description = :Description, + ""AssetType"" = :AssetType, local = :Local, temporary = :Temporary, create_time = :CreateTime, + access_time = :AccessTime, asset_flags = :AssetFlags, creatorid = :CreatorID + where id = :ID; + ", + dbcon)) + { + + // create unix epoch time + int now = (int)Utils.DateTimeToUnixTime(DateTime.UtcNow); + cmd.Parameters.Add(m_database.CreateParameter("ID", asset_id)); + cmd.Parameters.Add(m_database.CreateParameter("Hash", hash)); + cmd.Parameters.Add(m_database.CreateParameter("Name", assetName)); + cmd.Parameters.Add(m_database.CreateParameter("Description", assetDescription)); + cmd.Parameters.Add(m_database.CreateParameter("AssetType", asset.Type)); + cmd.Parameters.Add(m_database.CreateParameter("Local", asset.Local)); + cmd.Parameters.Add(m_database.CreateParameter("Temporary", asset.Temporary)); + cmd.Parameters.Add(m_database.CreateParameter("CreateTime", now)); + cmd.Parameters.Add(m_database.CreateParameter("AccessTime", now)); + cmd.Parameters.Add(m_database.CreateParameter("CreatorID", asset.Metadata.CreatorID)); + cmd.Parameters.Add(m_database.CreateParameter("AssetFlags", (int)asset.Flags)); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[ASSET DB]: PGSQL failure creating asset metadata {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + + if (!ExistsData(dbcon, transaction, hash)) + { + try + { + using (NpgsqlCommand cmd = + new NpgsqlCommand( + @"INSERT INTO XAssetsData(hash, data) VALUES(:Hash, :Data)", + dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Hash", hash)); + cmd.Parameters.Add(m_database.CreateParameter("Data", asset.Data)); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[XASSET DB]: PGSQL failure creating asset data {0} with name \"{1}\". Error: {2}", + asset.FullID, asset.Name, e.Message); + + transaction.Rollback(); + + return; + } + } + + transaction.Commit(); + } + } + } + } + + /// + /// Updates the access time of the asset if it was accessed above a given threshhold amount of time. + /// + /// + /// This gives us some insight into assets which haven't ben accessed for a long period. This is only done + /// over the threshold time to avoid excessive database writes as assets are fetched. + /// + /// + /// + private void UpdateAccessTime(AssetMetadata assetMetadata, int accessTime) + { + DateTime now = DateTime.UtcNow; + + if ((now - Utils.UnixTimeToDateTime(accessTime)).TotalDays < DaysBetweenAccessTimeUpdates) + return; + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + NpgsqlCommand cmd = + new NpgsqlCommand(@"update XAssetsMeta set access_time=:AccessTime where id=:ID", dbcon); + + try + { + UUID asset_id; + UUID.TryParse(assetMetadata.ID, out asset_id); + + using (cmd) + { + // create unix epoch time + cmd.Parameters.Add(m_database.CreateParameter("id", asset_id)); + cmd.Parameters.Add(m_database.CreateParameter("access_time", (int)Utils.DateTimeToUnixTime(now))); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSET PGSQL DB]: Failure updating access_time for asset {0} with name {1} : {2}", + assetMetadata.ID, assetMetadata.Name, e.Message); + } + } + } + } + + /// + /// We assume we already have the m_dbLock. + /// + /// TODO: need to actually use the transaction. + /// + /// + /// + /// + private bool ExistsData(NpgsqlConnection dbcon, NpgsqlTransaction transaction, byte[] hash) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool exists = false; + + using (NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT hash FROM XAssetsData WHERE hash=:Hash", dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("Hash", hash)); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + exists = true; + } + } + } + catch (Exception e) + { + m_log.ErrorFormat( + "[XASSETS DB]: PGSql failure in ExistsData fetching hash {0}. Exception {1}{2}", + hash, e.Message, e.StackTrace); + } + } + + return exists; + } + + /// + /// Check if the assets exist in the database. + /// + /// The assets' IDs + /// For each asset: true if it exists, false otherwise + public bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exist = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format(@"SELECT id FROM XAssetsMeta WHERE id IN ({0})", ids); + + using (NpgsqlConnection conn = new NpgsqlConnection(m_connectionString)) + { + conn.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + UUID id = DBGuid.FromDB(reader["id"]); + exist.Add(id); + } + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exist.Contains(uuids[i]); + return results; + } + + /// + /// Check if the asset exists in the database + /// + /// The asset UUID + /// true if it exists, false otherwise. + public bool ExistsAsset(UUID uuid) + { +// m_log.DebugFormat("[ASSETS DB]: Checking for asset {0}", uuid); + + bool assetExists = false; + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + using (NpgsqlCommand cmd = new NpgsqlCommand(@"SELECT id FROM XAssetsMeta WHERE id=:ID", dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter("id", uuid)); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if (dbReader.Read()) + { +// m_log.DebugFormat("[ASSETS DB]: Found asset {0}", uuid); + assetExists = true; + } + } + } + catch (Exception e) + { + m_log.Error(string.Format("[XASSETS DB]: PGSql failure fetching asset {0}", uuid), e); + } + } + } + } + + return assetExists; + } + + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + NpgsqlCommand cmd = new NpgsqlCommand( @"SELECT name, description, access_time, ""AssetType"", temporary, id, asset_flags, creatorid + FROM XAssetsMeta + LIMIT :start, :count", dbcon); + cmd.Parameters.Add(m_database.CreateParameter("start", start)); + cmd.Parameters.Add(m_database.CreateParameter("count", count)); + + try + { + using (NpgsqlDataReader dbReader = cmd.ExecuteReader()) + { + while (dbReader.Read()) + { + AssetMetadata metadata = new AssetMetadata(); + metadata.Name = (string)dbReader["name"]; + metadata.Description = (string)dbReader["description"]; + metadata.Type = Convert.ToSByte(dbReader["AssetType"]); + metadata.Temporary = Convert.ToBoolean(dbReader["temporary"]); + metadata.Flags = (AssetFlags)Convert.ToInt32(dbReader["asset_flags"]); + metadata.FullID = DBGuid.FromDB(dbReader["id"]); + metadata.CreatorID = dbReader["creatorid"].ToString(); + + // We'll ignore this for now - it appears unused! +// metadata.SHA1 = dbReader["hash"]); + + UpdateAccessTime(metadata, (int)dbReader["access_time"]); + + retList.Add(metadata); + } + } + } + catch (Exception e) + { + m_log.Error("[XASSETS DB]: PGSql failure fetching asset set" + Environment.NewLine + e.ToString()); + } + } + } + + return retList; + } + + public bool Delete(string id) + { +// m_log.DebugFormat("[XASSETS DB]: Deleting asset {0}", id); + + lock (m_dbLock) + { + using (NpgsqlConnection dbcon = new NpgsqlConnection(m_connectionString)) + { + dbcon.Open(); + + using (NpgsqlCommand cmd = new NpgsqlCommand(@"delete from XAssetsMeta where id=:ID", dbcon)) + { + cmd.Parameters.Add(m_database.CreateParameter(id, id)); + cmd.ExecuteNonQuery(); + } + + // TODO: How do we deal with data from deleted assets? Probably not easily reapable unless we + // keep a reference count (?) + } + } + + return true; + } + + #endregion + } +} diff --git a/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs b/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs new file mode 100644 index 0000000000..a22b882c01 --- /dev/null +++ b/OpenSim/Data/PGSQL/PGSQLXInventoryData.cs @@ -0,0 +1,330 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +using System.Reflection; +using System.Text; +using log4net; +using Npgsql; +using NpgsqlTypes; + +namespace OpenSim.Data.PGSQL +{ + public class PGSQLXInventoryData : IXInventoryData + { +// private static readonly ILog m_log = LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private PGSQLFolderHandler m_Folders; + private PGSQLItemHandler m_Items; + + public PGSQLXInventoryData(string conn, string realm) + { + m_Folders = new PGSQLFolderHandler( + conn, "inventoryfolders", "InventoryStore"); + m_Items = new PGSQLItemHandler( + conn, "inventoryitems", String.Empty); + } + + public static UUID str2UUID(string strUUID) + { + UUID newUUID = UUID.Zero; + + UUID.TryParse(strUUID, out newUUID); + + return newUUID; + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { + return m_Folders.Get(fields, vals); + } + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { + return m_Items.Get(fields, vals); + } + + public bool StoreFolder(XInventoryFolder folder) + { + if (folder.folderName.Length > 64) + folder.folderName = folder.folderName.Substring(0, 64); + return m_Folders.Store(folder); + } + + public bool StoreItem(XInventoryItem item) + { + if (item.inventoryName.Length > 64) + item.inventoryName = item.inventoryName.Substring(0, 64); + if (item.inventoryDescription.Length > 128) + item.inventoryDescription = item.inventoryDescription.Substring(0, 128); + + return m_Items.Store(item); + } + + public bool DeleteFolders(string field, string val) + { + return m_Folders.Delete(field, val); + } + + public bool DeleteFolders(string[] fields, string[] vals) + { + return m_Folders.Delete(fields, vals); + } + + public bool DeleteItems(string field, string val) + { + return m_Items.Delete(field, val); + } + + public bool DeleteItems(string[] fields, string[] vals) + { + return m_Items.Delete(fields, vals); + } + + public bool MoveItem(string id, string newParent) + { + return m_Items.MoveItem(id, newParent); + } + + public bool MoveFolder(string id, string newParent) + { + return m_Folders.MoveFolder(id, newParent); + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + return m_Items.GetActiveGestures(principalID.ToString()); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Items.GetAssetPermissions(principalID, assetID); + } + } + + public class PGSQLItemHandler : PGSQLInventoryHandler + { + public PGSQLItemHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveItem(string id, string newParent) + { + XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id }); + if (retrievedItems.Length == 0) + return false; + + UUID oldParent = retrievedItems[0].parentFolderID; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""inventoryID"" = :InventoryID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newParent)); + cmd.Parameters.Add(m_database.CreateParameter("InventoryID", id )); + cmd.Connection = conn; + conn.Open(); + + if (cmd.ExecuteNonQuery() == 0) + return false; + } + } + + IncrementFolderVersion(oldParent); + IncrementFolderVersion(newParent); + + return true; + } + + public XInventoryItem[] GetActiveGestures(string principalID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"select * from inventoryitems where ""avatarID"" = :uuid and ""assetType"" = :type and ""flags"" = 1", m_Realm); + + UUID princID = UUID.Zero; + UUID.TryParse(principalID, out princID); + + cmd.Parameters.Add(m_database.CreateParameter("uuid", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("type", (int)AssetType.Gesture)); + cmd.Connection = conn; + conn.Open(); + return DoQuery(cmd); + } + } + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + cmd.CommandText = String.Format(@"select bit_or(""inventoryCurrentPermissions"") as ""inventoryCurrentPermissions"" + from inventoryitems + where ""avatarID"" = :PrincipalID + and ""assetID"" = :AssetID + group by ""assetID"" ", m_Realm); + + cmd.Parameters.Add(m_database.CreateParameter("PrincipalID", principalID)); + cmd.Parameters.Add(m_database.CreateParameter("AssetID", assetID)); + cmd.Connection = conn; + conn.Open(); + using (NpgsqlDataReader reader = cmd.ExecuteReader()) + { + + int perms = 0; + + if (reader.Read()) + { + perms = Convert.ToInt32(reader["inventoryCurrentPermissions"]); + } + + return perms; + } + + } + } + } + + public override bool Store(XInventoryItem item) + { + if (!base.Store(item)) + return false; + + IncrementFolderVersion(item.parentFolderID); + + return true; + } + } + + public class PGSQLFolderHandler : PGSQLInventoryHandler + { + public PGSQLFolderHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public bool MoveFolder(string id, string newParentFolderID) + { + XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id }); + + if (folders.Length == 0) + return false; + + UUID oldParentFolderUUID = folders[0].parentFolderID; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand()) + { + UUID foldID = UUID.Zero; + UUID.TryParse(id, out foldID); + + UUID newPar = UUID.Zero; + UUID.TryParse(newParentFolderID, out newPar); + + cmd.CommandText = String.Format(@"update {0} set ""parentFolderID"" = :ParentFolderID where ""folderID"" = :folderID", m_Realm); + cmd.Parameters.Add(m_database.CreateParameter("ParentFolderID", newPar)); + cmd.Parameters.Add(m_database.CreateParameter("folderID", foldID)); + cmd.Connection = conn; + conn.Open(); + + if (cmd.ExecuteNonQuery() == 0) + return false; + } + } + + IncrementFolderVersion(oldParentFolderUUID); + IncrementFolderVersion(newParentFolderID); + + return true; + } + + public override bool Store(XInventoryFolder folder) + { + if (!base.Store(folder)) + return false; + + IncrementFolderVersion(folder.parentFolderID); + + return true; + } + } + + public class PGSQLInventoryHandler : PGSQLGenericTableHandler where T: class, new() + { + public PGSQLInventoryHandler(string c, string t, string m) : base(c, t, m) {} + + protected bool IncrementFolderVersion(UUID folderID) + { + return IncrementFolderVersion(folderID.ToString()); + } + + protected bool IncrementFolderVersion(string folderID) + { +// m_log.DebugFormat("[PGSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID); +// Util.PrintCallStack(); + + string sql = @"update inventoryfolders set version=version+1 where ""folderID"" = :folderID"; + + using (NpgsqlConnection conn = new NpgsqlConnection(m_ConnectionString)) + { + using (NpgsqlCommand cmd = new NpgsqlCommand(sql, conn)) + { + UUID foldID = UUID.Zero; + UUID.TryParse(folderID, out foldID); + + conn.Open(); + + cmd.Parameters.Add( m_database.CreateParameter("folderID", foldID) ); + + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception) + { + return false; + } + } + } + + return true; + } + } +} diff --git a/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs b/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1e88b2c15a --- /dev/null +++ b/OpenSim/Data/PGSQL/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Data.PGSQL")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.PGSQL")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("0e1c1ca4-2cf2-4315-b0e7-432c02feea8a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Data/PGSQL/Resources/AssetStore.migrations b/OpenSim/Data/PGSQL/Resources/AssetStore.migrations new file mode 100644 index 0000000000..7a858b4100 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/AssetStore.migrations @@ -0,0 +1,99 @@ +:VERSION 1 + +CREATE TABLE assets ( + "id" varchar(36) NOT NULL PRIMARY KEY, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" smallint NOT NULL, + "temporary" smallint NOT NULL, + "data" bytea NOT NULL +) ; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_assets + ( + "id" varchar(36) NOT NULL, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" boolean NOT NULL, + "temporary" boolean NOT NULL, + "data" bytea NOT NULL + ) ; + +INSERT INTO Tmp_assets ("id", "name", "description", "assetType", "local", "temporary", "data") + SELECT "id", "name", "description", "assetType", case when "local" = 1 then true else false end, case when "temporary" = 1 then true else false end, "data" + FROM assets ; + +DROP TABLE assets; + +Alter table Tmp_assets + rename to assets; + +ALTER TABLE assets ADD PRIMARY KEY ("id"); + +COMMIT; + + +:VERSION 3 + +BEGIN TRANSACTION; + +ALTER TABLE assets add "create_time" integer default 0; +ALTER TABLE assets add "access_time" integer default 0; + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_assets + ( + "id" uuid NOT NULL, + "name" varchar(64) NOT NULL, + "description" varchar(64) NOT NULL, + "assetType" smallint NOT NULL, + "local" boolean NOT NULL, + "temporary" boolean NOT NULL, + "data" bytea NOT NULL, + "create_time" int NULL, + "access_time" int NULL + ) ; + + +INSERT INTO Tmp_assets ("id", "name", "description", "assetType", "local", "temporary", "data", "create_time", "access_time") + SELECT cast("id" as uuid), "name", "description", "assetType", "local", "temporary", "data", "create_time", "access_time" + FROM assets ; + +DROP TABLE assets; + +Alter table Tmp_assets + rename to assets; + + ALTER TABLE assets ADD PRIMARY KEY ("id"); + +COMMIT; + + +:VERSION 5 + +DELETE FROM assets WHERE "id" = 'dc4b9f0b-d008-45c6-96a4-01dd947ac621'; + +:VERSION 6 + +ALTER TABLE assets ADD "asset_flags" INTEGER NOT NULL DEFAULT 0; + +:VERSION 7 + +alter table assets add "creatorid" varchar(36) not null default ''; + +:VERSION 8 + +BEGIN TRANSACTION; +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/AuthStore.migrations b/OpenSim/Data/PGSQL/Resources/AuthStore.migrations new file mode 100644 index 0000000000..a1f5b61ef6 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/AuthStore.migrations @@ -0,0 +1,32 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE auth ( + uuid uuid NOT NULL default '00000000-0000-0000-0000-000000000000', + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "webLoginKey" varchar(255) NOT NULL, + "accountType" VARCHAR(32) NOT NULL DEFAULT 'UserAccount' +) ; + +CREATE TABLE tokens ( + uuid uuid NOT NULL default '00000000-0000-0000-0000-000000000000', + token varchar(255) NOT NULL, + validity TIMESTAMP NOT NULL ) + ; + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + + INSERT INTO auth (uuid, "passwordHash", "passwordSalt", "webLoginKey", "accountType") + SELECT uuid AS UUID, passwordHash AS passwordHash, passwordSalt AS passwordSalt, webLoginKey AS webLoginKey, 'UserAccount' as accountType + FROM users + where exists ( Select * from information_schema.tables where table_name = 'users' ) + ; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/Avatar.migrations b/OpenSim/Data/PGSQL/Resources/Avatar.migrations new file mode 100644 index 0000000000..160086d77c --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/Avatar.migrations @@ -0,0 +1,59 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Avatars ( +"PrincipalID" uuid NOT NULL PRIMARY KEY, +"Name" varchar(32) NOT NULL, +"Value" varchar(255) NOT NULL DEFAULT '' +); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Avatars + ( + "PrincipalID" uuid NOT NULL, + "Name" varchar(32) NOT NULL, + "Value" text NOT NULL DEFAULT '' + ) ; + + INSERT INTO Tmp_Avatars ("PrincipalID", "Name", "Value") + SELECT "PrincipalID", cast("Name" as text), "Value" + FROM Avatars ; + +DROP TABLE Avatars; + +Alter table Tmp_Avatars + rename to Avatars; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Avatars + ( + "PrincipalID" uuid NOT NULL, + "Name" varchar(32) NOT NULL, + "Value" text NOT NULL DEFAULT '' +); + +ALTER TABLE Tmp_Avatars ADD PRIMARY KEY ("PrincipalID", "Name"); + + +INSERT INTO Tmp_Avatars ("PrincipalID", "Name", "Value") + SELECT "PrincipalID", "Name", cast("Value" as text) FROM Avatars ; + +DROP TABLE Avatars; + +Alter table Tmp_Avatars + rename to Avatars; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/EstateStore.migrations b/OpenSim/Data/PGSQL/Resources/EstateStore.migrations new file mode 100644 index 0000000000..59270f8b01 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/EstateStore.migrations @@ -0,0 +1,307 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE estate_managers( + "EstateID" int NOT NULL Primary Key, + uuid varchar(36) NOT NULL + ); + +CREATE TABLE estate_groups( + "EstateID" int NOT NULL, + uuid varchar(36) NOT NULL + ); + + +CREATE TABLE estate_users( + "EstateID" int NOT NULL, + uuid varchar(36) NOT NULL + ); + + +CREATE TABLE estateban( + "EstateID" int NOT NULL, + "bannedUUID" varchar(36) NOT NULL, + "bannedIp" varchar(16) NOT NULL, + "bannedIpHostMask" varchar(16) NOT NULL, + "bannedNameMask" varchar(64) NULL DEFAULT NULL + ); + +Create Sequence estate_settings_id increment by 100 start with 100; + +CREATE TABLE estate_settings( + "EstateID" integer DEFAULT nextval('estate_settings_id') NOT NULL, + "EstateName" varchar(64) NULL DEFAULT (NULL), + "AbuseEmailToEstateOwner" boolean NOT NULL, + "DenyAnonymous" boolean NOT NULL, + "ResetHomeOnTeleport" boolean NOT NULL, + "FixedSun" boolean NOT NULL, + "DenyTransacted" boolean NOT NULL, + "BlockDwell" boolean NOT NULL, + "DenyIdentified" boolean NOT NULL, + "AllowVoice" boolean NOT NULL, + "UseGlobalTime" boolean NOT NULL, + "PricePerMeter" int NOT NULL, + "TaxFree" boolean NOT NULL, + "AllowDirectTeleport" boolean NOT NULL, + "RedirectGridX" int NOT NULL, + "RedirectGridY" int NOT NULL, + "ParentEstateID" int NOT NULL, + "SunPosition" double precision NOT NULL, + "EstateSkipScripts" boolean NOT NULL, + "BillableFactor" double precision NOT NULL, + "PublicAccess" boolean NOT NULL, + "AbuseEmail" varchar(255) NOT NULL, + "EstateOwner" varchar(36) NOT NULL, + "DenyMinors" boolean NOT NULL + ); + + +CREATE TABLE estate_map( + "RegionID" varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "EstateID" int NOT NULL + ); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE INDEX IX_estate_managers ON estate_managers + ( + "EstateID" + ); + + +CREATE INDEX IX_estate_groups ON estate_groups + ( + "EstateID" + ); + + +CREATE INDEX IX_estate_users ON estate_users + ( + "EstateID" + ); + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estateban + ( + "EstateID" int NOT NULL, + "bannedUUID" varchar(36) NOT NULL, + "bannedIp" varchar(16) NULL, + "bannedIpHostMask" varchar(16) NULL, + "bannedNameMask" varchar(64) NULL + ); + + INSERT INTO Tmp_estateban ("EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask") + SELECT "EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask" FROM estateban; + +DROP TABLE estateban; + +Alter table Tmp_estateban + rename to estateban; + +CREATE INDEX IX_estateban ON estateban + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_managers + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ); + +INSERT INTO Tmp_estate_managers ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_managers; + +DROP TABLE estate_managers; + +Alter table Tmp_estate_managers + rename to estate_managers; + +CREATE INDEX IX_estate_managers ON estate_managers + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_groups + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ) ; + + INSERT INTO Tmp_estate_groups ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_groups; + +DROP TABLE estate_groups; + +Alter table Tmp_estate_groups + rename to estate_groups; + +CREATE INDEX IX_estate_groups ON estate_groups + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_users + ( + "EstateID" int NOT NULL, + uuid uuid NOT NULL + ); + +INSERT INTO Tmp_estate_users ("EstateID", uuid) + SELECT "EstateID", cast(uuid as uuid) FROM estate_users ; + +DROP TABLE estate_users; + +Alter table Tmp_estate_users + rename to estate_users; + +CREATE INDEX IX_estate_users ON estate_users + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estateban + ( + "EstateID" int NOT NULL, + "bannedUUID" uuid NOT NULL, + "bannedIp" varchar(16) NULL, + "bannedIpHostMask" varchar(16) NULL, + "bannedNameMask" varchar(64) NULL + ); + +INSERT INTO Tmp_estateban ("EstateID", "bannedUUID", "bannedIp", "bannedIpHostMask", "bannedNameMask") + SELECT "EstateID", cast("bannedUUID" as uuid), "bannedIp", "bannedIpHostMask", "bannedNameMask" FROM estateban ; + +DROP TABLE estateban; + +Alter table Tmp_estateban + rename to estateban; + +CREATE INDEX IX_estateban ON estateban + ( + "EstateID" + ); + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_settings + ( + "EstateID" integer default nextval('estate_settings_id') NOT NULL, + "EstateName" varchar(64) NULL DEFAULT (NULL), + "AbuseEmailToEstateOwner" boolean NOT NULL, + "DenyAnonymous" boolean NOT NULL, + "ResetHomeOnTeleport" boolean NOT NULL, + "FixedSun" boolean NOT NULL, + "DenyTransacted" boolean NOT NULL, + "BlockDwell" boolean NOT NULL, + "DenyIdentified" boolean NOT NULL, + "AllowVoice" boolean NOT NULL, + "UseGlobalTime" boolean NOT NULL, + "PricePerMeter" int NOT NULL, + "TaxFree" boolean NOT NULL, + "AllowDirectTeleport" boolean NOT NULL, + "RedirectGridX" int NOT NULL, + "RedirectGridY" int NOT NULL, + "ParentEstateID" int NOT NULL, + "SunPosition" double precision NOT NULL, + "EstateSkipScripts" boolean NOT NULL, + "BillableFactor" double precision NOT NULL, + "PublicAccess" boolean NOT NULL, + "AbuseEmail" varchar(255) NOT NULL, + "EstateOwner" uuid NOT NULL, + "DenyMinors" boolean NOT NULL + ); + +INSERT INTO Tmp_estate_settings ("EstateID", "EstateName", "AbuseEmailToEstateOwner", "DenyAnonymous", "ResetHomeOnTeleport", "FixedSun", "DenyTransacted", "BlockDwell", "DenyIdentified", "AllowVoice", "UseGlobalTime", "PricePerMeter", "TaxFree", "AllowDirectTeleport", "RedirectGridX", "RedirectGridY", "ParentEstateID", "SunPosition", "EstateSkipScripts", "BillableFactor", "PublicAccess", "AbuseEmail", "EstateOwner", "DenyMinors") + SELECT "EstateID", "EstateName", "AbuseEmailToEstateOwner", "DenyAnonymous", "ResetHomeOnTeleport", "FixedSun", "DenyTransacted", "BlockDwell", "DenyIdentified", "AllowVoice", "UseGlobalTime", "PricePerMeter", "TaxFree", "AllowDirectTeleport", "RedirectGridX", "RedirectGridY", "ParentEstateID", "SunPosition", "EstateSkipScripts", "BillableFactor", "PublicAccess", "AbuseEmail", cast("EstateOwner" as uuid), "DenyMinors" FROM estate_settings ; + +DROP TABLE estate_settings; + + +Alter table Tmp_estate_settings + rename to estate_settings; + + +Create index on estate_settings (lower("EstateName")); + +COMMIT; + + +:VERSION 9 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_estate_map + ( + "RegionID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "EstateID" int NOT NULL + ); + +INSERT INTO Tmp_estate_map ("RegionID", "EstateID") + SELECT cast("RegionID" as uuid), "EstateID" FROM estate_map ; + +DROP TABLE estate_map; + +Alter table Tmp_estate_map + rename to estate_map; + +COMMIT; + +:VERSION 10 + +BEGIN TRANSACTION; +ALTER TABLE estate_settings ADD COLUMN "AllowLandmark" boolean NOT NULL default true; +ALTER TABLE estate_settings ADD COLUMN "AllowParcelChanges" boolean NOT NULL default true; +ALTER TABLE estate_settings ADD COLUMN "AllowSetHome" boolean NOT NULL default true; +COMMIT; + +:VERSION 11 + +Begin transaction; + + +Commit; + diff --git a/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations b/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..a87199bfcf --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/FriendsStore.migrations @@ -0,0 +1,44 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Friends ( +"PrincipalID" uuid NOT NULL, +"Friend" varchar(255) NOT NULL, +"Flags" char(16) NOT NULL DEFAULT '0', +"Offered" varchar(32) NOT NULL DEFAULT 0); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO Friends ("PrincipalID", "Friend", "Flags", "Offered") +SELECT "ownerID", "friendID", "friendPerms", 0 FROM userfriends; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_Friends + ("PrincipalID" varchar(255) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "Friend" varchar(255) NOT NULL, + "Flags" char(16) NOT NULL DEFAULT '0', + "Offered" varchar(32) NOT NULL DEFAULT 0) ; + +INSERT INTO Tmp_Friends ("PrincipalID", "Friend", "Flags", "Offered") + SELECT cast("PrincipalID" as varchar(255)), "Friend", "Flags", "Offered" FROM Friends ; + +DROP TABLE Friends; + +Alter table Tmp_Friends + rename to Friends; + +ALTER TABLE Friends ADD PRIMARY KEY("PrincipalID", "Friend"); + + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/GridStore.migrations b/OpenSim/Data/PGSQL/Resources/GridStore.migrations new file mode 100644 index 0000000000..0ab8d2b6d0 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/GridStore.migrations @@ -0,0 +1,242 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE regions( + "regionHandle" varchar(255) NULL, + "regionName" varchar(255) NULL, + uuid varchar(255) NOT NULL PRIMARY KEY, + "regionRecvKey" varchar(255) NULL, + "regionSecret" varchar(255) NULL, + "regionSendKey" varchar(255) NULL, + "regionDataURI" varchar(255) NULL, + "serverIP" varchar(255) NULL, + "serverPort" varchar(255) NULL, + "serverURI" varchar(255) NULL, + "locX" varchar(255) NULL, + "locY" varchar(255) NULL, + "locZ" varchar(255) NULL, + "eastOverrideHandle" varchar(255) NULL, + "westOverrideHandle" varchar(255) NULL, + "southOverrideHandle" varchar(255) NULL, + "northOverrideHandle" varchar(255) NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(255) NULL, + "regionAssetSendKey" varchar(255) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(255) NULL, + "regionUserSendKey" varchar(255) NULL, + "regionMapTexture" varchar(255) NULL, + "serverHttpPort" varchar(255) NULL, + "serverRemotingPort" varchar(255) NULL, + "owner_uuid" varchar(36) NULL +); + +COMMIT; + + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regions + ( + uuid varchar(36) NOT NULL, + "regionHandle" bigint NULL, + "regionName" varchar(20) NULL, + "regionRecvKey" varchar(128) NULL, + "regionSendKey" varchar(128) NULL, + "regionSecret" varchar(128) NULL, + "regionDataURI" varchar(128) NULL, + "serverIP" varchar(64) NULL, + "serverPort" int NULL, + "serverURI" varchar(255) NULL, + "locX" int NULL, + "locY" int NULL, + "locZ" int NULL, + "eastOverrideHandle" bigint NULL, + "westOverrideHandle" bigint NULL, + "southOverrideHandle" bigint NULL, + "northOverrideHandle" bigint NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(128) NULL, + "regionAssetSendKey" varchar(128) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(128) NULL, + "regionUserSendKey" varchar(128) NULL, + "regionMapTexture" varchar(36) NULL, + "serverHttpPort" int NULL, + "serverRemotingPort" int NULL, + "owner_uuid" varchar(36) NULL, + "originUUID" varchar(36) NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ); + +INSERT INTO Tmp_regions (uuid, "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", "regionMapTexture", "serverHttpPort", "serverRemotingPort", "owner_uuid") + SELECT cast(uuid as varchar(36)), cast("regionHandle" as bigint), cast("regionName" as varchar(20)), cast("regionRecvKey" as varchar(128)), cast("regionSendKey" as varchar(128)), cast("regionSecret" as varchar(128)), cast("regionDataURI" as varchar(128)), cast("serverIP" as varchar(64)), cast("serverPort" as int), "serverURI", cast("locX" as int), cast("locY" as int), cast("locZ" as int), cast("eastOverrideHandle" as bigint), cast("westOverrideHandle" as bigint), + cast("southOverrideHandle" as bigint), cast("northOverrideHandle" as bigint), "regionAssetURI", cast("regionAssetRecvKey" as varchar(128)), cast("regionAssetSendKey" as varchar(128)), "regionUserURI", cast("regionUserRecvKey" as varchar(128)), cast("regionUserSendKey" as varchar(128)), cast("regionMapTexture" as varchar(36)), + cast("serverHttpPort" as int), cast("serverRemotingPort" as int), "owner_uuid" + FROM regions; + +DROP TABLE regions; + +alter table Tmp_regions + rename to regions; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +CREATE INDEX IX_regions_handle ON regions + ( + "regionHandle" + ); + + +CREATE INDEX IX_regions_override ON regions + ( + "eastOverrideHandle", + "westOverrideHandle", + "southOverrideHandle", + "northOverrideHandle" + ); + +COMMIT; + + +:VERSION 4 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the cotext of the database designer.*/ +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regions + ( + uuid uuid NOT NULL, + "regionHandle" bigint NULL, + "regionName" varchar(20) NULL, + "regionRecvKey" varchar(128) NULL, + "regionSendKey" varchar(128) NULL, + "regionSecret" varchar(128) NULL, + "regionDataURI" varchar(128) NULL, + "serverIP" varchar(64) NULL, + "serverPort" int NULL, + "serverURI" varchar(255) NULL, + "locX" int NULL, + "locY" int NULL, + "locZ" int NULL, + "eastOverrideHandle" bigint NULL, + "westOverrideHandle" bigint NULL, + "southOverrideHandle" bigint NULL, + "northOverrideHandle" bigint NULL, + "regionAssetURI" varchar(255) NULL, + "regionAssetRecvKey" varchar(128) NULL, + "regionAssetSendKey" varchar(128) NULL, + "regionUserURI" varchar(255) NULL, + "regionUserRecvKey" varchar(128) NULL, + "regionUserSendKey" varchar(128) NULL, + "regionMapTexture" uuid NULL, + "serverHttpPort" int NULL, + "serverRemotingPort" int NULL, + "owner_uuid" uuid NOT NULL, + "originUUID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000') + ); + + +INSERT INTO Tmp_regions (uuid, "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", "regionMapTexture", "serverHttpPort", "serverRemotingPort", "owner_uuid", "originUUID") + SELECT cast(uuid as uuid), "regionHandle", "regionName", "regionRecvKey", "regionSendKey", "regionSecret", "regionDataURI", "serverIP", "serverPort", "serverURI", "locX", "locY", "locZ", "eastOverrideHandle", "westOverrideHandle", "southOverrideHandle", "northOverrideHandle", "regionAssetURI", "regionAssetRecvKey", "regionAssetSendKey", "regionUserURI", "regionUserRecvKey", "regionUserSendKey", cast("regionMapTexture" as uuid), "serverHttpPort", "serverRemotingPort", cast( "owner_uuid" as uuid), cast("originUUID" as uuid) FROM regions ; + + +DROP TABLE regions; + +alter table Tmp_regions rename to regions; + +ALTER TABLE regions ADD CONSTRAINT + PK__regions__uuid PRIMARY KEY + ( + uuid + ); + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +CREATE INDEX IX_regions_handle ON regions + ( + "regionHandle" + ); + +CREATE INDEX IX_regions_override ON regions + ( + "eastOverrideHandle", + "westOverrideHandle", + "southOverrideHandle", + "northOverrideHandle" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD access int default 0; + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "ScopeID" uuid default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions alter column "owner_uuid" set DEFAULT ('00000000-0000-0000-0000-000000000000'); +ALTER TABLE regions ADD "sizeX" integer not null default 0; +ALTER TABLE regions ADD "sizeY" integer not null default 0; + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "flags" integer NOT NULL DEFAULT 0; +CREATE INDEX flags ON regions("flags"); +ALTER TABLE regions ADD "last_seen" integer NOT NULL DEFAULT 0; +ALTER TABLE regions ADD "PrincipalID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +ALTER TABLE regions ADD "Token" varchar(255) NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 8 + +BEGIN TRANSACTION; +ALTER TABLE regions ALTER COLUMN "regionName" type VarChar(128) ; + +DROP INDEX IX_regions_name; +ALTER TABLE regions ALTER COLUMN "regionName" type VarChar(128), + ALTER COLUMN "regionName" SET NOT NULL; + +CREATE INDEX IX_regions_name ON regions + ( + "regionName" + ); + +COMMIT; + +:VERSION 9 + +BEGIN TRANSACTION; + +ALTER TABLE regions ADD "parcelMapTexture" uuid NULL; + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations b/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations new file mode 100644 index 0000000000..d37c4f6d72 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/GridUserStore.migrations @@ -0,0 +1,60 @@ +:VERSION 1 # -------------------------- + +BEGIN TRANSACTION; + +CREATE TABLE GridUser ( + "UserID" VARCHAR(255) NOT NULL Primary Key, + "HomeRegionID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "HomePosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "HomeLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastRegionID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "LastPosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "Online" CHAR(5) NOT NULL DEFAULT 'false', + "Login" CHAR(16) NOT NULL DEFAULT '0', + "Logout" CHAR(16) NOT NULL DEFAULT '0' +) ; + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN TRANSACTION; + +CREATE TABLE GridUser_tmp ( + "UserID" VARCHAR(255) NOT NULL PRIMARY KEY, + "HomeRegionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "HomePosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "HomeLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastRegionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + "LastPosition" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "LastLookAt" CHAR(64) NOT NULL DEFAULT '<0,0,0>', + "Online" CHAR(5) NOT NULL DEFAULT 'false', + "Login" CHAR(16) NOT NULL DEFAULT '0', + "Logout" CHAR(16) NOT NULL DEFAULT '0' + ); + +COMMIT; + + +INSERT INTO GridUser_tmp ("UserID" + ,"HomeRegionID" + ,"HomePosition" + ,"HomeLookAt" + ,"LastRegionID" + ,"LastPosition" + ,"LastLookAt" + ,"Online" + ,"Login" + ,"Logout") + SELECT "UserID", cast("HomeRegionID" as uuid), "HomePosition" ,"HomeLookAt" , cast("LastRegionID" as uuid), + "LastPosition" + ,"LastLookAt" + ,"Online" + ,"Login" + ,"Logout" FROM GridUser; + +DROP TABLE GridUser; + +alter table GridUser_tmp rename to GridUser; + diff --git a/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations b/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..adf126d7c3 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/HGTravelStore.migrations @@ -0,0 +1,17 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE hg_traveling_data ( + "SessionID" VARCHAR(36) NOT NULL Primary Key, + "UserID" VARCHAR(36) NOT NULL, + "GridExternalName" VARCHAR(255) NOT NULL DEFAULT '', + "ServiceToken" VARCHAR(255) NOT NULL DEFAULT '', + "ClientIPAddress" VARCHAR(16) NOT NULL DEFAULT '', + "MyIPAddress" VARCHAR(16) NOT NULL DEFAULT '', + "TMStamp" timestamp NOT NULL default now() +); + + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/IM_Store.migrations b/OpenSim/Data/PGSQL/Resources/IM_Store.migrations new file mode 100644 index 0000000000..eb97824ea3 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/IM_Store.migrations @@ -0,0 +1,45 @@ +:VERSION 1 # -------------------------- + +BEGIN Transaction; + +Create Sequence im_offiline_id increment by 1 start with 1; + +CREATE TABLE im_offline ( + "ID" integer PRIMARY KEY NOT NULL DEFAULT nextval('im_offiline_id') , + "PrincipalID" char(36) NOT NULL default '', + "Message" text NOT NULL, + "TMStamp" timestamp NOT NULL default now() +); + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + +/* +INSERT INTO `im_offline` SELECT * from `diva_im_offline`; +DROP TABLE `diva_im_offline`; +DELETE FROM `migrations` WHERE name='diva_im_Store'; +*/ + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +-- dropping the table here as there most likely is only one record in the table at the time of migration + +DROP TABLE IF EXISTS "public"."im_offline"; +CREATE TABLE "public"."im_offline" ( + "ID" serial, + "PrincipalID" uuid NOT NULL, + "Message" text NOT NULL COLLATE "default", + "TMStamp" timestamp(6) NOT NULL DEFAULT clock_timestamp(), + "FromID" uuid NOT NULL +) +WITH (OIDS=FALSE); +ALTER TABLE "public"."im_offline" ADD PRIMARY KEY ("ID","PrincipalID","FromID") NOT DEFERRABLE INITIALLY IMMEDIATE; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations b/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..8f7982ad35 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/InventoryStore.migrations @@ -0,0 +1,220 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders ( + "folderID" varchar(36) NOT NULL default '' PRIMARY KEY, + "agentID" varchar(36) default NULL, + "parentFolderID" varchar(36) default NULL, + "folderName" varchar(64) default NULL, + "type" smallint NOT NULL default 0, + "version" int NOT NULL default 0 +); + + +CREATE INDEX owner ON inventoryfolders +( + "agentID" ASC +); + +CREATE INDEX parent ON inventoryfolders +( + "parentFolderID" ASC +); + + +CREATE TABLE inventoryitems ( + "inventoryID" varchar(36) NOT NULL default '' Primary Key, + "assetID" varchar(36) default NULL, + "assetType" int default NULL, + "parentFolderID" varchar(36) default NULL, + "avatarID" varchar(36) default NULL, + "inventoryName" varchar(64) default NULL, + "inventoryDescription" varchar(128) default NULL, + "inventoryNextPermissions" int default NULL, + "inventoryCurrentPermissions" int default NULL, + "invType" int default NULL, + "creatorID" varchar(36) default NULL, + "inventoryBasePermissions" int NOT NULL default 0, + "inventoryEveryOnePermissions" int NOT NULL default 0, + "salePrice" int default NULL, + "saleType" smallint default NULL, + "creationDate" int default NULL, + "groupID" varchar(36) default NULL, + "groupOwned" boolean default NULL, + "flags" int default NULL +); + + +CREATE INDEX ii_owner ON inventoryitems +( + "avatarID" ASC +); + +CREATE INDEX ii_folder ON inventoryitems +( + "parentFolderID" ASC +); + +COMMIT; + + +:VERSION 2 + +BEGIN TRANSACTION; + +ALTER TABLE inventoryitems ADD "inventoryGroupPermissions" INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 3 + +/* To prevent any potential data loss issues, you should review this script in detail before running it outside the cotext of the database designer.*/ +BEGIN TRANSACTION; + +CREATE TABLE Tmp_inventoryfolders + ( + "folderID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "agentID" uuid NULL DEFAULT (NULL), + "parentFolderID" uuid NULL DEFAULT (NULL), + "folderName" varchar(64) NULL DEFAULT (NULL), + "type" smallint NOT NULL DEFAULT ((0)), + "version" int NOT NULL DEFAULT ((0)) + ); + + INSERT INTO Tmp_inventoryfolders ("folderID", "agentID", "parentFolderID", "folderName", type, version) + SELECT cast("folderID" as uuid), cast("agentID" as uuid), cast("parentFolderID" as uuid), "folderName", "type", "version" + FROM inventoryfolders; + +DROP TABLE inventoryfolders; + +alter table Tmp_inventoryfolders rename to inventoryfolders; + +ALTER TABLE inventoryfolders ADD CONSTRAINT + PK__inventor__C2FABFB3173876EA PRIMARY KEY + ( + "folderID" + ); + +CREATE INDEX owner ON inventoryfolders + ( + "agentID" + ); + +CREATE INDEX parent ON inventoryfolders + ( + "parentFolderID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_inventoryitems + ( + "inventoryID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "assetID" uuid NULL DEFAULT (NULL), + "assetType" int NULL DEFAULT (NULL), + "parentFolderID" uuid NULL DEFAULT (NULL), + "avatarID" uuid NULL DEFAULT (NULL), + "inventoryName" varchar(64) NULL DEFAULT (NULL), + "inventoryDescription" varchar(128) NULL DEFAULT (NULL), + "inventoryNextPermissions" int NULL DEFAULT (NULL), + "inventoryCurrentPermissions" int NULL DEFAULT (NULL), + "invType" int NULL DEFAULT (NULL), + "creatorID" uuid NULL DEFAULT (NULL), + "inventoryBasePermissions" int NOT NULL DEFAULT ((0)), + "inventoryEveryOnePermissions" int NOT NULL DEFAULT ((0)), + "salePrice" int NULL DEFAULT (NULL), + "SaleType" smallint NULL DEFAULT (NULL), + "creationDate" int NULL DEFAULT (NULL), + "groupID" uuid NULL DEFAULT (NULL), + "groupOwned" boolean NULL DEFAULT (NULL), + "flags" int NULL DEFAULT (NULL), + "inventoryGroupPermissions" int NOT NULL DEFAULT ((0)) + ); + + + INSERT INTO Tmp_inventoryitems ("inventoryID", "assetID", "assetType", "parentFolderID", "avatarID", "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", "creatorID", "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", "groupID", "groupOwned", "flags", "inventoryGroupPermissions") + SELECT cast("inventoryID" as uuid), cast("assetID" as uuid), "assetType", cast("parentFolderID" as uuid), cast("avatarID" as uuid), "inventoryName", "inventoryDescription", "inventoryNextPermissions", "inventoryCurrentPermissions", "invType", cast("creatorID" as uuid), "inventoryBasePermissions", "inventoryEveryOnePermissions", "salePrice", "SaleType", "creationDate", cast("groupID" as uuid), "groupOwned", "flags", "inventoryGroupPermissions" + FROM inventoryitems ; + +DROP TABLE inventoryitems; + +alter table Tmp_inventoryitems rename to inventoryitems; + +ALTER TABLE inventoryitems ADD CONSTRAINT + PK__inventor__C4B7BC2220C1E124 PRIMARY KEY + ( + "inventoryID" + ); + + +CREATE INDEX ii2_owner ON inventoryitems + ( + "avatarID" + ); + +CREATE INDEX ii2_folder ON inventoryitems + ( + "parentFolderID" + ); + +COMMIT; + +:VERSION 5 + + +BEGIN TRANSACTION; + +-- # Restoring defaults: +-- # NOTE: "inventoryID" does NOT need one: it's NOT NULL PK and a unique Guid must be provided every time anyway! + +alter table inventoryitems + alter column "inventoryBasePermissions" set default 0; +alter table inventoryitems + alter column "inventoryEveryOnePermissions" set default 0; +alter table inventoryitems + alter column "inventoryGroupPermissions" set default 0 ; + +COMMIT ; + +:VERSION 7 + +BEGIN TRANSACTION; + +-- # "creatorID" goes back to VARCHAR(36) (???) + +alter table inventoryitems + alter column "creatorID" type varchar(36); + +COMMIT ; + +:VERSION 8 + +ALTER TABLE inventoryitems + alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000'; + + +:VERSION 9 + +BEGIN TRANSACTION; + +--# "creatorID" goes up to VARCHAR(255) + +alter table inventoryitems + alter column "creatorID" type varchar(255); + +Commit; + +:VERSION 10 + +BEGIN TRANSACTION; + +Alter table inventoryitems Rename Column "SaleType" to "saleType"; + +Commit; + diff --git a/OpenSim/Data/PGSQL/Resources/LogStore.migrations b/OpenSim/Data/PGSQL/Resources/LogStore.migrations new file mode 100644 index 0000000000..83727c6fd5 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/LogStore.migrations @@ -0,0 +1,16 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE logs ( + "logID" int NOT NULL Primary Key, + "target" varchar(36) default NULL, + "server" varchar(64) default NULL, + "method" varchar(64) default NULL, + "arguments" varchar(255) default NULL, + "priority" int default NULL, + "message" text +); + +COMMIT; + diff --git a/OpenSim/Data/PGSQL/Resources/Presence.migrations b/OpenSim/Data/PGSQL/Resources/Presence.migrations new file mode 100755 index 0000000000..5184034c9a --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/Presence.migrations @@ -0,0 +1,42 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Presence ( +"UserID" varchar(255) NOT NULL, +"RegionID" uuid NOT NULL, +"SessionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', +"SecureSessionID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000' +); + + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE UNIQUE INDEX SessionID ON Presence("SessionID"); +CREATE INDEX UserID ON Presence("UserID"); + +ALTER TABLE Presence ADD "LastSeen" Timestamp; + +COMMIT; + +:VERSION 3 # -------------------------- + +BEGIN; + +CREATE INDEX RegionID ON Presence("RegionID"); + +COMMIT; + +:VERSION 4 # Making sure LastSeen is actually defined in the table as it most likely erred in the double version 2 migration above + +BEGIN; + +ALTER TABLE Presence +DROP COLUMN IF EXISTS "LastSeen", +ADD COLUMN "LastSeen" Timestamp; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/PGSQL/Resources/RegionStore.migrations b/OpenSim/Data/PGSQL/Resources/RegionStore.migrations new file mode 100644 index 0000000000..1284ce01ae --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/RegionStore.migrations @@ -0,0 +1,1162 @@ +begin transaction ; +:VERSION 1 + +CREATE TABLE prims( + "UUID" varchar(255) NOT NULL Primary key, + "RegionUUID" varchar(255) NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" varchar(255) NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" varchar(255) NULL, + "OwnerID" varchar(255) NULL, + "GroupID" varchar(255) NULL, + "LastOwnerID" varchar(255) NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL + ); + +CREATE TABLE primshapes( + "UUID" varchar(255) NOT NULL primary key, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ); + +CREATE TABLE primitems( + "itemID" varchar(255) NOT NULL primary key, + "primID" varchar(255) NULL, + "assetID" varchar(255) NULL, + "parentFolderID" varchar(255) NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" varchar(255) NULL, + "ownerID" varchar(255) NULL, + "lastOwnerID" varchar(255) NULL, + "groupID" varchar(255) NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL + ); + +CREATE TABLE terrain( + "RegionUUID" varchar(255) NULL, + "Revision" int NULL, + "Heightfield" bytea NULL +); + + +CREATE TABLE land( + "UUID" varchar(255) NOT NULL primary key, + "RegionUUID" varchar(255) NULL, + "LocalLandID" int NULL, + "Bitmap" bytea NULL, + "Name" varchar(255) NULL, + "Description" varchar(255) NULL, + "OwnerUUID" varchar(255) NULL, + "IsGroupOwned" boolean NULL, + "Area" int NULL, + "AuctionID" int NULL, + "Category" int NULL, + "ClaimDate" int NULL, + "ClaimPrice" int NULL, + "GroupUUID" varchar(255) NULL, + "SalePrice" int NULL, + "LandStatus" int NULL, + "LandFlags" int NULL, + "LandingType" int NULL, + "MediaAutoScale" int NULL, + "MediaTextureUUID" varchar(255) NULL, + "MediaURL" varchar(255) NULL, + "MusicURL" varchar(255) NULL, + "PassHours" double precision NULL, + "PassPrice" int NULL, + "SnapshotUUID" varchar(255) NULL, + "UserLocationX" double precision NULL, + "UserLocationY" double precision NULL, + "UserLocationZ" double precision NULL, + "UserLookAtX" double precision NULL, + "UserLookAtY" double precision NULL, + "UserLookAtZ" double precision NULL +); + +Create index on land (lower("Name")); + +CREATE TABLE landaccesslist( + "LandUUID" varchar(255) NULL, + "AccessUUID" varchar(255) NULL, + "Flags" int NULL +); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE regionban ( + "regionUUID" VARCHAR(36) NOT NULL, + "bannedUUID" VARCHAR(36) NOT NULL, + "bannedIp" VARCHAR(16) NOT NULL, + "bannedIpHostMask" VARCHAR(16) NOT NULL + ); + +create table regionsettings ( + "regionUUID" varchar(36) not null primary key, + "block_terraform" boolean not null, + "block_fly" boolean not null, + "allow_damage" boolean not null, + "restrict_pushing" boolean not null, + "allow_land_resell" boolean not null, + "allow_land_join_divide" boolean not null, + "block_show_in_search" boolean not null, + "agent_limit" int not null, + "object_bonus" double precision not null, + "maturity" int not null, + "disable_scripts" boolean not null, + "disable_collisions" boolean not null, + "disable_physics" boolean not null, + "terrain_texture_1" varchar(36) not null, + "terrain_texture_2" varchar(36) not null, + "terrain_texture_3" varchar(36) not null, + "terrain_texture_4" varchar(36) not null, + "elevation_1_nw" double precision not null, + "elevation_2_nw" double precision not null, + "elevation_1_ne" double precision not null, + "elevation_2_ne" double precision not null, + "elevation_1_se" double precision not null, + "elevation_2_se" double precision not null, + "elevation_1_sw" double precision not null, + "elevation_2_sw" double precision not null, + "water_height" double precision not null, + "terrain_raise_limit" double precision not null, + "terrain_lower_limit" double precision not null, + "use_estate_sun" boolean not null, + "fixed_sun" boolean not null, + "sun_position" double precision not null, + "covenant" varchar(36) default NULL, + "Sandbox" boolean NOT NULL + ); + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_prims + ( + "UUID" varchar(36) NOT NULL , + "RegionUUID" varchar(36) NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" varchar(36) NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" varchar(36) NULL, + "OwnerID" varchar(36) NULL, + "GroupID" varchar(36) NULL, + "LastOwnerID" varchar(36) NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL + ); + +INSERT INTO Tmp_prims ("UUID", "RegionUUID", "ParentID", "CreationDate", "Name", "SceneGroupID", "Text", "Description", "SitName", "TouchName", "ObjectFlags", "CreatorID", "OwnerID", "GroupID", "LastOwnerID", "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ") + SELECT cast("UUID" as varchar(36)), cast("RegionUUID" as varchar(36)), "ParentID", "CreationDate", "Name", cast("SceneGroupID" as varchar(36)), "Text", "Description", "SitName", "TouchName", "ObjectFlags", cast("CreatorID" as varchar(36)), cast("OwnerID" as varchar(36)), cast( "GroupID" as varchar(36)), cast("LastOwnerID" as varchar(36)), "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ" + FROM prims ; + +DROP TABLE prims; + +alter table Tmp_prims rename to prims; + + +ALTER TABLE prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY + ( + "UUID" + ); + +COMMIT; + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primitems + ( + "itemID" varchar(36) NOT NULL, + "primID" varchar(36) NULL, + "assetID" varchar(36) NULL, + "parentFolderID" varchar(36) NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" varchar(36) NULL, + "ownerID" varchar(36) NULL, + "lastOwnerID" varchar(36) NULL, + "groupID" varchar(36) NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL + ); + +INSERT INTO Tmp_primitems ("itemID", "primID", "assetID", "parentFolderID", "invType", "assetType", "name", "description", "creationDate", "creatorID", "ownerID", "lastOwnerID", "groupID", "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions") + SELECT cast("itemID" as varchar(36)), cast("primID" as varchar(36)), cast("assetID" as varchar(36)), cast( "parentFolderID" as varchar(36)), "invType", "assetType", "name", "description", "creationDate", cast( "creatorID" as varchar(36)), cast("ownerID" as varchar(36)), cast("lastOwnerID" as varchar(36)), cast("groupID" as varchar(36)), "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions" + from primitems; + +DROP TABLE primitems; + +alter table Tmp_primitems rename to primitems; + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY + ( + "itemID" + ); + + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primshapes + ( + "UUID" varchar(36) NOT NULL, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ) ; + +INSERT INTO Tmp_primshapes ("UUID", "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams") + SELECT cast("UUID" as varchar(36)), "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams" + FROM primshapes; + +DROP TABLE primshapes; + +alter table Tmp_primshapes rename to primshapes; + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY + ( + "UUID" + ) ; + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PayPrice" int not null default 0; +ALTER TABLE prims ADD "PayButton1" int not null default 0; +ALTER TABLE prims ADD "PayButton2" int not null default 0; +ALTER TABLE prims ADD "PayButton3" int not null default 0; +ALTER TABLE prims ADD "PayButton4" int not null default 0; +ALTER TABLE prims ADD "LoopedSound" varchar(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD "LoopedSoundGain" double precision not null default 0.0; +ALTER TABLE prims ADD "TextureAnimation" bytea; +ALTER TABLE prims ADD "OmegaX" double precision not null default 0.0; +ALTER TABLE prims ADD "OmegaY" double precision not null default 0.0; +ALTER TABLE prims ADD "OmegaZ" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetX" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetY" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraEyeOffsetZ" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetX" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetY" double precision not null default 0.0; +ALTER TABLE prims ADD "CameraAtOffsetZ" double precision not null default 0.0; +ALTER TABLE prims ADD "ForceMouselook" smallint not null default 0; +ALTER TABLE prims ADD "ScriptAccessPin" int not null default 0; +ALTER TABLE prims ADD "AllowedDrop" smallint not null default 0; +ALTER TABLE prims ADD "DieAtEdge" smallint not null default 0; +ALTER TABLE prims ADD "SalePrice" int not null default 10; +ALTER TABLE prims ADD "SaleType" smallint not null default 0; + +ALTER TABLE primitems add "flags" integer not null default 0; + +ALTER TABLE land ADD "AuthbuyerID" varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +CREATE index prims_regionuuid on prims("RegionUUID"); +CREATE index prims_parentid on prims("ParentID"); + +CREATE index primitems_primid on primitems("primID"); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "ColorR" int not null default 0; +ALTER TABLE prims ADD "ColorG" int not null default 0; +ALTER TABLE prims ADD "ColorB" int not null default 0; +ALTER TABLE prims ADD "ColorA" int not null default 0; +ALTER TABLE prims ADD "ParticleSystem" bytea; +ALTER TABLE prims ADD "ClickAction" smallint NOT NULL default 0; + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +ALTER TABLE land ADD "OtherCleanTime" integer NOT NULL default 0; +ALTER TABLE land ADD "Dwell" integer NOT NULL default 0; + +COMMIT; + +:VERSION 9 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "Material" smallint NOT NULL default 3; + +COMMIT; + + +:VERSION 10 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "sunvectorx" double precision NOT NULL default 0; +ALTER TABLE regionsettings ADD "sunvectory" double precision NOT NULL default 0; +ALTER TABLE regionsettings ADD "sunvectorz" double precision NOT NULL default 0; + +COMMIT; + + +:VERSION 11 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "CollisionSound" char(36) not null default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD "CollisionSoundVolume" double precision not null default 0.0; + +COMMIT; + + +:VERSION 12 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "LinkNumber" integer not null default 0; + +COMMIT; + + +:VERSION 13 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_prims + ( + "UUID" uuid NOT NULL, + "RegionUUID" uuid NULL, + "ParentID" int NULL, + "CreationDate" int NULL, + "Name" varchar(255) NULL, + "SceneGroupID" uuid NULL, + "Text" varchar(255) NULL, + "Description" varchar(255) NULL, + "SitName" varchar(255) NULL, + "TouchName" varchar(255) NULL, + "ObjectFlags" int NULL, + "CreatorID" uuid NULL, + "OwnerID" uuid NULL, + "GroupID" uuid NULL, + "LastOwnerID" uuid NULL, + "OwnerMask" int NULL, + "NextOwnerMask" int NULL, + "GroupMask" int NULL, + "EveryoneMask" int NULL, + "BaseMask" int NULL, + "PositionX" double precision NULL, + "PositionY" double precision NULL, + "PositionZ" double precision NULL, + "GroupPositionX" double precision NULL, + "GroupPositionY" double precision NULL, + "GroupPositionZ" double precision NULL, + "VelocityX" double precision NULL, + "VelocityY" double precision NULL, + "VelocityZ" double precision NULL, + "AngularVelocityX" double precision NULL, + "AngularVelocityY" double precision NULL, + "AngularVelocityZ" double precision NULL, + "AccelerationX" double precision NULL, + "AccelerationY" double precision NULL, + "AccelerationZ" double precision NULL, + "RotationX" double precision NULL, + "RotationY" double precision NULL, + "RotationZ" double precision NULL, + "RotationW" double precision NULL, + "SitTargetOffsetX" double precision NULL, + "SitTargetOffsetY" double precision NULL, + "SitTargetOffsetZ" double precision NULL, + "SitTargetOrientW" double precision NULL, + "SitTargetOrientX" double precision NULL, + "SitTargetOrientY" double precision NULL, + "SitTargetOrientZ" double precision NULL, + "PayPrice" int NOT NULL DEFAULT ((0)), + "PayButton1" int NOT NULL DEFAULT ((0)), + "PayButton2" int NOT NULL DEFAULT ((0)), + "PayButton3" int NOT NULL DEFAULT ((0)), + "PayButton4" int NOT NULL DEFAULT ((0)), + "LoopedSound" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "LoopedSoundGain" double precision NOT NULL DEFAULT ((0.0)), + "TextureAnimation" bytea NULL, + "OmegaX" double precision NOT NULL DEFAULT ((0.0)), + "OmegaY" double precision NOT NULL DEFAULT ((0.0)), + "OmegaZ" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetX" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetY" double precision NOT NULL DEFAULT ((0.0)), + "CameraEyeOffsetZ" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetX" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetY" double precision NOT NULL DEFAULT ((0.0)), + "CameraAtOffsetZ" double precision NOT NULL DEFAULT ((0.0)), + "ForceMouselook" smallint NOT NULL DEFAULT ((0)), + "ScriptAccessPin" int NOT NULL DEFAULT ((0)), + "AllowedDrop" smallint NOT NULL DEFAULT ((0)), + "DieAtEdge" smallint NOT NULL DEFAULT ((0)), + "SalePrice" int NOT NULL DEFAULT ((10)), + "SaleType" smallint NOT NULL DEFAULT ((0)), + "ColorR" int NOT NULL DEFAULT ((0)), + "ColorG" int NOT NULL DEFAULT ((0)), + "ColorB" int NOT NULL DEFAULT ((0)), + "ColorA" int NOT NULL DEFAULT ((0)), + "ParticleSystem" bytea NULL, + "ClickAction" smallint NOT NULL DEFAULT ((0)), + "Material" smallint NOT NULL DEFAULT ((3)), + "CollisionSound" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "CollisionSoundVolume" double precision NOT NULL DEFAULT ((0.0)), + "LinkNumber" int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_prims ("UUID", "RegionUUID", "ParentID", "CreationDate", "Name", "SceneGroupID", "Text", "Description", "SitName", "TouchName", "ObjectFlags", "CreatorID", "OwnerID", "GroupID", "LastOwnerID", "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ", "PayPrice", "PayButton1", "PayButton2", "PayButton3", "PayButton4", "LoopedSound", "LoopedSoundGain", "TextureAnimation", "OmegaX", "OmegaY", "OmegaZ", "CameraEyeOffsetX", "CameraEyeOffsetY", "CameraEyeOffsetZ", "CameraAtOffsetX", "CameraAtOffsetY", "CameraAtOffsetZ", "ForceMouselook", "ScriptAccessPin", "AllowedDrop", "DieAtEdge", "SalePrice", "SaleType", "ColorR", "ColorG", "ColorB", "ColorA", "ParticleSystem", "ClickAction", "Material", "CollisionSound", "CollisionSoundVolume", "LinkNumber") + SELECT cast("UUID" as uuid), cast("RegionUUID" as uuid), "ParentID", "CreationDate", "Name", cast("SceneGroupID" as uuid), "Text", "Description", "SitName", "TouchName", "ObjectFlags", cast("CreatorID" as uuid), cast("OwnerID" as uuid), cast("GroupID" as uuid), cast("LastOwnerID" as uuid), "OwnerMask", "NextOwnerMask", "GroupMask", "EveryoneMask", "BaseMask", "PositionX", "PositionY", "PositionZ", "GroupPositionX", "GroupPositionY", "GroupPositionZ", "VelocityX", "VelocityY", "VelocityZ", "AngularVelocityX", "AngularVelocityY", "AngularVelocityZ", "AccelerationX", "AccelerationY", "AccelerationZ", "RotationX", "RotationY", "RotationZ", "RotationW", "SitTargetOffsetX", "SitTargetOffsetY", "SitTargetOffsetZ", "SitTargetOrientW", "SitTargetOrientX", "SitTargetOrientY", "SitTargetOrientZ", "PayPrice", "PayButton1", "PayButton2", "PayButton3", "PayButton4", cast("LoopedSound" as uuid), "LoopedSoundGain", "TextureAnimation", "OmegaX", "OmegaY", "OmegaZ", "CameraEyeOffsetX", "CameraEyeOffsetY", "CameraEyeOffsetZ", "CameraAtOffsetX", "CameraAtOffsetY", "CameraAtOffsetZ", "ForceMouselook", "ScriptAccessPin", "AllowedDrop", "DieAtEdge", "SalePrice", "SaleType", "ColorR", "ColorG", "ColorB", "ColorA", "ParticleSystem", "ClickAction", "Material", cast("CollisionSound" as uuid), "CollisionSoundVolume", "LinkNumber" + FROM prims ; + +DROP TABLE prims; + +alter table Tmp_prims rename to prims; + +ALTER TABLE prims ADD CONSTRAINT + PK__prims__10566F31 PRIMARY KEY + ( + "UUID" + ); + + +CREATE INDEX prims_regionuuid ON prims + ( + "RegionUUID" + ); + +CREATE INDEX prims_parentid ON prims + ( + "ParentID" + ); + +COMMIT; + + +:VERSION 14 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primshapes + ( + "UUID" uuid NOT NULL, + "Shape" int NULL, + "ScaleX" double precision NULL, + "ScaleY" double precision NULL, + "ScaleZ" double precision NULL, + "PCode" int NULL, + "PathBegin" int NULL, + "PathEnd" int NULL, + "PathScaleX" int NULL, + "PathScaleY" int NULL, + "PathShearX" int NULL, + "PathShearY" int NULL, + "PathSkew" int NULL, + "PathCurve" int NULL, + "PathRadiusOffset" int NULL, + "PathRevolutions" int NULL, + "PathTaperX" int NULL, + "PathTaperY" int NULL, + "PathTwist" int NULL, + "PathTwistBegin" int NULL, + "ProfileBegin" int NULL, + "ProfileEnd" int NULL, + "ProfileCurve" int NULL, + "ProfileHollow" int NULL, + "State" int NULL, + "Texture" bytea NULL, + "ExtraParams" bytea NULL + ); + +INSERT INTO Tmp_primshapes ("UUID", "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams") + SELECT cast("UUID" as uuid), "Shape", "ScaleX", "ScaleY", "ScaleZ", "PCode", "PathBegin", "PathEnd", "PathScaleX", "PathScaleY", "PathShearX", "PathShearY", "PathSkew", "PathCurve", "PathRadiusOffset", "PathRevolutions", "PathTaperX", "PathTaperY", "PathTwist", "PathTwistBegin", "ProfileBegin", "ProfileEnd", "ProfileCurve", "ProfileHollow", "State", "Texture", "ExtraParams" + FROM primshapes; + +DROP TABLE primshapes; + +alter table Tmp_primshapes rename to primshapes; + +ALTER TABLE primshapes ADD CONSTRAINT + PK__primshapes__0880433F PRIMARY KEY + ( + "UUID" + ); + +COMMIT; + + +:VERSION 15 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_primitems + ( + "itemID" uuid NOT NULL, + "primID" uuid NULL, + "assetID" uuid NULL, + "parentFolderID" uuid NULL, + "invType" int NULL, + "assetType" int NULL, + "name" varchar(255) NULL, + "description" varchar(255) NULL, + "creationDate" varchar(255) NULL, + "creatorID" uuid NULL, + "ownerID" uuid NULL, + "lastOwnerID" uuid NULL, + "groupID" uuid NULL, + "nextPermissions" int NULL, + "currentPermissions" int NULL, + "basePermissions" int NULL, + "everyonePermissions" int NULL, + "groupPermissions" int NULL, + flags int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_primitems ("itemID", "primID", "assetID", "parentFolderID", "invType", "assetType", "name", "description", "creationDate", "creatorID", "ownerID", "lastOwnerID", "groupID", "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions", flags) + SELECT cast("itemID" as uuid), cast("primID" as uuid), cast("assetID" as uuid), cast("parentFolderID" as uuid), "invType", "assetType", "name", "description", "creationDate", cast("creatorID" as uuid), cast("ownerID" as uuid), cast("lastOwnerID" as uuid), cast("groupID" as uuid), "nextPermissions", "currentPermissions", "basePermissions", "everyonePermissions", "groupPermissions", flags + FROM primitems ; + +DROP TABLE primitems; + +alter table Tmp_primitems rename to primitems; + +ALTER TABLE primitems ADD CONSTRAINT + PK__primitems__0A688BB1 PRIMARY KEY + ( + "itemID" + ); + +CREATE INDEX primitems_primid ON primitems + ( + "primID" + ) ; + +COMMIT; + + +:VERSION 16 + + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_terrain + ( + "RegionUUID" uuid NULL, + "Revision" int NULL, + "Heightfield" bytea NULL + ); + +INSERT INTO Tmp_terrain ("RegionUUID", "Revision", "Heightfield") + SELECT cast("RegionUUID" as uuid), "Revision", "Heightfield" + FROM terrain ; + +DROP TABLE terrain; + +alter table Tmp_terrain rename to terrain; + +COMMIT; + + +:VERSION 17 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_land + ( + "UUID" uuid NOT NULL, + "RegionUUID" uuid NULL, + "LocalLandID" int NULL, + "Bitmap" bytea NULL, + "Name" varchar(255) NULL, + "Description" varchar(255) NULL, + "OwnerUUID" uuid NULL, + "IsGroupOwned" boolean NULL, + "Area" int NULL, + "AuctionID" int NULL, + "Category" int NULL, + "ClaimDate" int NULL, + "ClaimPrice" int NULL, + "GroupUUID" uuid NULL, + "SalePrice" int NULL, + "LandStatus" int NULL, + "LandFlags" int NULL, + "LandingType" int NULL, + "MediaAutoScale" int NULL, + "MediaTextureUUID" uuid NULL, + "MediaURL" varchar(255) NULL, + "MusicURL" varchar(255) NULL, + "PassHours" double precision NULL, + "PassPrice" int NULL, + "SnapshotUUID" uuid NULL, + "UserLocationX" double precision NULL, + "UserLocationY" double precision NULL, + "UserLocationZ" double precision NULL, + "UserLookAtX" double precision NULL, + "UserLookAtY" double precision NULL, + "UserLookAtZ" double precision NULL, + "AuthbuyerID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "OtherCleanTime" int NOT NULL DEFAULT ((0)), + "Dwell" int NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_land ("UUID", "RegionUUID", "LocalLandID", "Bitmap", "Name", "Description", "OwnerUUID", "IsGroupOwned", "Area", "AuctionID", "Category", "ClaimDate", "ClaimPrice", "GroupUUID", "SalePrice", "LandStatus", "LandFlags", "LandingType", "MediaAutoScale", "MediaTextureUUID", "MediaURL", "MusicURL", "PassHours", "PassPrice", "SnapshotUUID", "UserLocationX", "UserLocationY", "UserLocationZ", "UserLookAtX", "UserLookAtY", "UserLookAtZ", "AuthbuyerID", "OtherCleanTime", "Dwell") + SELECT cast("UUID" as uuid), cast("RegionUUID" as uuid), "LocalLandID", "Bitmap", "Name", "Description", cast("OwnerUUID" as uuid), "IsGroupOwned", "Area", "AuctionID", "Category", "ClaimDate", "ClaimPrice", cast("GroupUUID" as uuid), "SalePrice", "LandStatus", "LandFlags", "LandingType", "MediaAutoScale", cast("MediaTextureUUID" as uuid), "MediaURL", "MusicURL", "PassHours", "PassPrice", cast("SnapshotUUID" as uuid), "UserLocationX", "UserLocationY", "UserLocationZ", "UserLookAtX", "UserLookAtY", "UserLookAtZ", cast("AuthbuyerID" as uuid), "OtherCleanTime", "Dwell" + FROM land ; + +DROP TABLE land; + +alter table Tmp_land rename to land; + +ALTER TABLE land ADD CONSTRAINT + PK__land__65A475E71BFD2C07 PRIMARY KEY + ( + "UUID" + ); + +Create index on land (lower("Name")); + +COMMIT; + + + +:VERSION 18 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_landaccesslist + ( + "LandUUID" uuid NULL, + "AccessUUID" uuid NULL, + "Flags" int NULL + ); + +INSERT INTO Tmp_landaccesslist ("LandUUID", "AccessUUID", "Flags") + SELECT cast("LandUUID" as uuid), cast("AccessUUID" as uuid), "Flags" + FROM landaccesslist ; + +DROP TABLE landaccesslist; + +alter table Tmp_landaccesslist rename to landaccesslist; + +COMMIT; + + + +:VERSION 19 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regionban + ( + "regionUUID" uuid NOT NULL, + "bannedUUID" uuid NOT NULL, + "bannedIp" varchar(16) NOT NULL, + "bannedIpHostMask" varchar(16) NOT NULL + ); + +INSERT INTO Tmp_regionban ("regionUUID", "bannedUUID", "bannedIp", "bannedIpHostMask") + SELECT cast("regionUUID" as uuid), cast("bannedUUID" as uuid), "bannedIp", "bannedIpHostMask" + FROM regionban ; + +DROP TABLE regionban; + +alter table Tmp_regionban rename to regionban; + +COMMIT; + + +:VERSION 20 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_regionsettings + ( + "regionUUID" uuid NOT NULL, + "block_terraform" boolean NOT NULL, + "block_fly" boolean NOT NULL, + "allow_damage" boolean NOT NULL, + "restrict_pushing" boolean NOT NULL, + "allow_land_resell" boolean NOT NULL, + "allow_land_join_divide" boolean NOT NULL, + "block_show_in_search" boolean NOT NULL, + "agent_limit" int NOT NULL, + "object_bonus" double precision NOT NULL, + "maturity" int NOT NULL, + "disable_scripts" boolean NOT NULL, + "disable_collisions" boolean NOT NULL, + "disable_physics" boolean NOT NULL, + "terrain_texture_1" uuid NOT NULL, + "terrain_texture_2" uuid NOT NULL, + "terrain_texture_3" uuid NOT NULL, + "terrain_texture_4" uuid NOT NULL, + "elevation_1_nw" double precision NOT NULL, + "elevation_2_nw" double precision NOT NULL, + "elevation_1_ne" double precision NOT NULL, + "elevation_2_ne" double precision NOT NULL, + "elevation_1_se" double precision NOT NULL, + "elevation_2_se" double precision NOT NULL, + "elevation_1_sw" double precision NOT NULL, + "elevation_2_sw" double precision NOT NULL, + "water_height" double precision NOT NULL, + "terrain_raise_limit" double precision NOT NULL, + "terrain_lower_limit" double precision NOT NULL, + "use_estate_sun" boolean NOT NULL, + "fixed_sun" boolean NOT NULL, + "sun_position" double precision NOT NULL, + "covenant" uuid NULL DEFAULT (NULL), + "Sandbox" boolean NOT NULL, + "sunvectorx" double precision NOT NULL DEFAULT ((0)), + "sunvectory" double precision NOT NULL DEFAULT ((0)), + "sunvectorz" double precision NOT NULL DEFAULT ((0)) + ); + +INSERT INTO Tmp_regionsettings ("regionUUID", "block_terraform", "block_fly", "allow_damage", "restrict_pushing", "allow_land_resell", "allow_land_join_divide", "block_show_in_search", "agent_limit", "object_bonus", "maturity", "disable_scripts", "disable_collisions", "disable_physics", "terrain_texture_1", "terrain_texture_2", "terrain_texture_3", "terrain_texture_4", "elevation_1_nw", "elevation_2_nw", "elevation_1_ne", "elevation_2_ne", "elevation_1_se", "elevation_2_se", "elevation_1_sw", "elevation_2_sw", "water_height", "terrain_raise_limit", "terrain_lower_limit", "use_estate_sun", "fixed_sun", "sun_position", "covenant", "Sandbox", "sunvectorx", "sunvectory", "sunvectorz") + SELECT cast("regionUUID" as uuid), "block_terraform", "block_fly", "allow_damage", "restrict_pushing", "allow_land_resell", "allow_land_join_divide", "block_show_in_search", "agent_limit", "object_bonus", "maturity", "disable_scripts", "disable_collisions", "disable_physics", cast("terrain_texture_1" as uuid), cast("terrain_texture_2" as uuid), cast("terrain_texture_3" as uuid), cast("terrain_texture_4" as uuid), "elevation_1_nw", "elevation_2_nw", "elevation_1_ne", "elevation_2_ne", "elevation_1_se", "elevation_2_se", "elevation_1_sw", "elevation_2_sw", "water_height", "terrain_raise_limit", "terrain_lower_limit", "use_estate_sun", "fixed_sun", "sun_position", cast("covenant" as uuid), "Sandbox", "sunvectorx", "sunvectory", "sunvectorz" + FROM regionsettings ; + +DROP TABLE regionsettings; + +alter table Tmp_regionsettings rename to regionsettings; + +ALTER TABLE regionsettings ADD CONSTRAINT + PK__regionse__5B35159D21B6055D PRIMARY KEY + ( + "regionUUID" + ); + +COMMIT; + + +:VERSION 21 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PassTouches" boolean not null default false; + +COMMIT; + + +:VERSION 22 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "loaded_creation_date" varchar(20) ; +ALTER TABLE regionsettings ADD "loaded_creation_time" varchar(20) ; +ALTER TABLE regionsettings ADD "loaded_creation_id" varchar(64) ; + +COMMIT; + +:VERSION 23 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings DROP COLUMN "loaded_creation_date"; +ALTER TABLE regionsettings DROP COLUMN "loaded_creation_time"; +ALTER TABLE regionsettings ADD "loaded_creation_datetime" int NOT NULL default 0; + +COMMIT; + +:VERSION 24 + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "MediaURL" varchar(255); +ALTER TABLE primshapes ADD "Media" TEXT NULL; + +COMMIT; + +:VERSION 25 + +BEGIN TRANSACTION; +CREATE TABLE regionwindlight ( + "region_id" varchar(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000' PRIMARY KEY, + "water_color_r" double precision NOT NULL DEFAULT '4.000000', + water_color_g double precision NOT NULL DEFAULT '38.000000', + water_color_b double precision NOT NULL DEFAULT '64.000000', + water_fog_density_exponent double precision NOT NULL DEFAULT '4.0', + underwater_fog_modifier double precision NOT NULL DEFAULT '0.25', + reflection_wavelet_scale_1 double precision NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_2 double precision NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_3 double precision NOT NULL DEFAULT '2.0', + fresnel_scale double precision NOT NULL DEFAULT '0.40', + fresnel_offset double precision NOT NULL DEFAULT '0.50', + refract_scale_above double precision NOT NULL DEFAULT '0.03', + refract_scale_below double precision NOT NULL DEFAULT '0.20', + blur_multiplier double precision NOT NULL DEFAULT '0.040', + big_wave_direction_x double precision NOT NULL DEFAULT '1.05', + big_wave_direction_y double precision NOT NULL DEFAULT '-0.42', + little_wave_direction_x double precision NOT NULL DEFAULT '1.11', + little_wave_direction_y double precision NOT NULL DEFAULT '-1.16', + normal_map_texture varchar(36) NOT NULL DEFAULT '822ded49-9a6c-f61c-cb89-6df54f42cdf4', + horizon_r double precision NOT NULL DEFAULT '0.25', + horizon_g double precision NOT NULL DEFAULT '0.25', + horizon_b double precision NOT NULL DEFAULT '0.32', + horizon_i double precision NOT NULL DEFAULT '0.32', + haze_horizon double precision NOT NULL DEFAULT '0.19', + blue_density_r double precision NOT NULL DEFAULT '0.12', + blue_density_g double precision NOT NULL DEFAULT '0.22', + blue_density_b double precision NOT NULL DEFAULT '0.38', + blue_density_i double precision NOT NULL DEFAULT '0.38', + haze_density double precision NOT NULL DEFAULT '0.70', + density_multiplier double precision NOT NULL DEFAULT '0.18', + distance_multiplier double precision NOT NULL DEFAULT '0.8', + max_altitude int NOT NULL DEFAULT '1605', + sun_moon_color_r double precision NOT NULL DEFAULT '0.24', + sun_moon_color_g double precision NOT NULL DEFAULT '0.26', + sun_moon_color_b double precision NOT NULL DEFAULT '0.30', + sun_moon_color_i double precision NOT NULL DEFAULT '0.30', + sun_moon_position double precision NOT NULL DEFAULT '0.317', + ambient_r double precision NOT NULL DEFAULT '0.35', + ambient_g double precision NOT NULL DEFAULT '0.35', + ambient_b double precision NOT NULL DEFAULT '0.35', + ambient_i double precision NOT NULL DEFAULT '0.35', + east_angle double precision NOT NULL DEFAULT '0.00', + sun_glow_focus double precision NOT NULL DEFAULT '0.10', + sun_glow_size double precision NOT NULL DEFAULT '1.75', + scene_gamma double precision NOT NULL DEFAULT '1.00', + star_brightness double precision NOT NULL DEFAULT '0.00', + cloud_color_r double precision NOT NULL DEFAULT '0.41', + cloud_color_g double precision NOT NULL DEFAULT '0.41', + cloud_color_b double precision NOT NULL DEFAULT '0.41', + cloud_color_i double precision NOT NULL DEFAULT '0.41', + cloud_x double precision NOT NULL DEFAULT '1.00', + cloud_y double precision NOT NULL DEFAULT '0.53', + cloud_density double precision NOT NULL DEFAULT '1.00', + cloud_coverage double precision NOT NULL DEFAULT '0.27', + cloud_scale double precision NOT NULL DEFAULT '0.42', + cloud_detail_x double precision NOT NULL DEFAULT '1.00', + cloud_detail_y double precision NOT NULL DEFAULT '0.53', + cloud_detail_density double precision NOT NULL DEFAULT '0.12', + cloud_scroll_x double precision NOT NULL DEFAULT '0.20', + cloud_scroll_x_lock smallint NOT NULL DEFAULT '0', + cloud_scroll_y double precision NOT NULL DEFAULT '0.01', + cloud_scroll_y_lock smallint NOT NULL DEFAULT '0', + draw_classic_clouds smallint NOT NULL DEFAULT '1' +); + +COMMIT; + +:VERSION 26 + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "map_tile_ID" CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 27 #--------------------- + +BEGIN TRANSACTION; +ALTER TABLE land ADD "MediaType" VARCHAR(32) NOT NULL DEFAULT 'none/none' ; +ALTER TABLE land ADD "MediaDescription" VARCHAR(255) NOT NULL DEFAULT ''; +ALTER TABLE land ADD "MediaSize" VARCHAR(16) NOT NULL DEFAULT '0,0'; +ALTER TABLE land ADD "MediaLoop" boolean NOT NULL DEFAULT false; +ALTER TABLE land ADD "ObscureMusic" boolean NOT NULL DEFAULT false; +ALTER TABLE land ADD "ObscureMedia" boolean NOT NULL DEFAULT false; +COMMIT; + +:VERSION 28 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims +alter column "CreatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE prims ALTER COLUMN "CreatorID" set NOT NULL; + +ALTER TABLE primitems +alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE primitems ALTER COLUMN "creatorID" set NOT NULL; + +COMMIT; + +:VERSION 29 #----------------- Region Covenant changed time + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "covenant_datetime" int NOT NULL default 0; + +COMMIT; + +:VERSION 30 #------------------Migrate "creatorID" storage to varchars instead of UUIDs for HG support + +BEGIN TRANSACTION; + +alter table prims rename column "CreatorID" to "CreatorIDOld"; +alter table primitems rename column "creatorID" to "creatorIDOld"; + +COMMIT; + +:VERSION 31 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "CreatorID" varchar(255); +ALTER TABLE primitems ADD "creatorID" varchar(255); + +COMMIT; + +:VERSION 32 #--------------------- + +BEGIN TRANSACTION; + +UPDATE prims SET "CreatorID" = cast("CreatorIDOld" as varchar(255)); +UPDATE primitems SET "creatorID" = cast("creatorIDOld" as varchar(255)); + +COMMIT; + +:VERSION 33 #--------------------- + +BEGIN TRANSACTION; + +ALTER TABLE prims alter column "CreatorID" set default '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE prims ALTER COLUMN "CreatorID" set NOT NULL; + +ALTER TABLE primitems alter column "creatorID" set DEFAULT '00000000-0000-0000-0000-000000000000' ; + +ALTER TABLE primitems ALTER COLUMN "creatorID" set NOT NULL; + +COMMIT; + +:VERSION 34 #--------------- Telehub support + +BEGIN TRANSACTION; + +CREATE TABLE spawn_points( + "RegionUUID" uuid NOT NULL PRIMARY KEY, + "Yaw" double precision NOT NULL, + "Pitch" double precision NOT NULL, + "Distance" double precision NOT NULL +); + +ALTER TABLE regionsettings ADD "TelehubObject" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 35 #---------------- Parcels for sale + +BEGIN TRANSACTION; + +ALTER TABLE regionsettings ADD "parcel_tile_ID" uuid NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 36 #---------------- Timed bans/access + +BEGIN TRANSACTION; + +ALTER TABLE landaccesslist ADD "Expires" integer NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 37 #---------------- Environment Settings + +BEGIN TRANSACTION; + +CREATE TABLE regionenvironment( + "region_id" uuid NOT NULL primary key, + "llsd_settings" varchar NOT NULL +); + +COMMIT; + +:VERSION 38 #---------------- Dynamic attributes + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "DynAttrs" TEXT; + +COMMIT; + +:VERSION 39 #---------------- Extra physics params + +BEGIN TRANSACTION; + +ALTER TABLE prims ADD "PhysicsShapeType" smallint NOT NULL default '0'; +ALTER TABLE prims ADD "Density" double precision NOT NULL default '1000'; +ALTER TABLE prims ADD "GravityModifier" double precision NOT NULL default '1'; +ALTER TABLE prims ADD "Friction" double precision NOT NULL default '0.6'; +ALTER TABLE prims ADD "Restitution" double precision NOT NULL default '0.5'; + +COMMIT; + +:VERSION 40 #-- regionwindlight changed type from smallint to bool + +BEGIN TRANSACTION; + +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_x_lock DROP DEFAULT; +ALTER TABLE regionwindlight ALTER cloud_scroll_x_lock TYPE bool USING CASE WHEN cloud_scroll_x_lock=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_x_lock SET DEFAULT FALSE; + +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_y_lock DROP DEFAULT; +ALTER TABLE regionwindlight ALTER cloud_scroll_y_lock TYPE bool USING CASE WHEN cloud_scroll_y_lock=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN cloud_scroll_y_lock SET DEFAULT FALSE; + +ALTER TABLE regionwindlight ALTER COLUMN draw_classic_clouds DROP DEFAULT; +ALTER TABLE regionwindlight ALTER draw_classic_clouds TYPE bool USING CASE WHEN draw_classic_clouds=0 THEN FALSE ELSE TRUE END; +ALTER TABLE regionwindlight ALTER COLUMN draw_classic_clouds SET DEFAULT FALSE; + +COMMIT; + +VERSION 41 #-- Change Landlags to bigint + +BEGIN TRANSACTION; + +ALTER TABLE land ALTER "LandFlags" TYPE bigint; + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/UserAccount.migrations b/OpenSim/Data/PGSQL/Resources/UserAccount.migrations new file mode 100644 index 0000000000..c785463325 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserAccount.migrations @@ -0,0 +1,51 @@ +:VERSION 1 + +CREATE TABLE UserAccounts ( + "PrincipalID" uuid NOT NULL Primary key, + "ScopeID" uuid NOT NULL, + "FirstName" varchar(64) NOT NULL, + "LastName" varchar(64) NOT NULL, + "Email" varchar(64) NULL, + "ServiceURLs" text NULL, + "Created" int default NULL +); + + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO UserAccounts ("PrincipalID", "ScopeID", "FirstName", "LastName", "Email", "ServiceURLs", "Created") +SELECT UUID AS "PrincipalID", '00000000-0000-0000-0000-000000000000' AS "ScopeID", +username AS "FirstName", +lastname AS "LastName", +email as "Email", ( +'AssetServerURI=' + +userAssetURI + ' InventoryServerURI=' + userInventoryURI + ' GatewayURI= HomeURI=') AS "ServiceURLs", +created as "Created" FROM users; + +COMMIT; + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE UNIQUE INDEX "PrincipalID" ON UserAccounts("PrincipalID"); +CREATE INDEX "Email" ON UserAccounts("Email"); +CREATE INDEX "FirstName" ON UserAccounts("FirstName"); +CREATE INDEX "LastName" ON UserAccounts("LastName"); +CREATE INDEX Name ON UserAccounts("FirstName","LastName"); + +COMMIT; + +:VERSION 4 + +BEGIN TRANSACTION; + +ALTER TABLE UserAccounts ADD "UserLevel" integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD "UserFlags" integer NOT NULL DEFAULT 0; +ALTER TABLE UserAccounts ADD "UserTitle" varchar(64) NOT NULL DEFAULT ''; + +COMMIT; + + diff --git a/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations b/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..a6bd8caca8 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserProfiles.migrations @@ -0,0 +1,155 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE classifieds ( + "classifieduuid" char(36) NOT NULL, + "creatoruuid" char(36) NOT NULL, + "creationdate" integer NOT NULL, + "expirationdate" integer NOT NULL, + "category" varchar(20) NOT NULL, + "name" varchar(255) NOT NULL, + "description" text NOT NULL, + "parceluuid" char(36) NOT NULL, + "parentestate" integer NOT NULL, + "snapshotuuid" char(36) NOT NULL, + "simname" varchar(255) NOT NULL, + "posglobal" varchar(255) NOT NULL, + "parcelname" varchar(255) NOT NULL, + "classifiedflags" integer NOT NULL, + "priceforlisting" integer NOT NULL, + constraint classifiedspk PRIMARY KEY ("classifieduuid") +); + + +CREATE TABLE usernotes ( + "useruuid" varchar(36) NOT NULL, + "targetuuid" varchar(36) NOT NULL, + "notes" text NOT NULL, + constraint usernoteuk UNIQUE ("useruuid","targetuuid") +); + + +CREATE TABLE userpicks ( + "pickuuid" varchar(36) NOT NULL, + "creatoruuid" varchar(36) NOT NULL, + "toppick" boolean NOT NULL, + "parceluuid" varchar(36) NOT NULL, + "name" varchar(255) NOT NULL, + "description" text NOT NULL, + "snapshotuuid" varchar(36) NOT NULL, + "user" varchar(255) NOT NULL, + "originalname" varchar(255) NOT NULL, + "simname" varchar(255) NOT NULL, + "posglobal" varchar(255) NOT NULL, + "sortorder" integer NOT NULL, + "enabled" boolean NOT NULL, + PRIMARY KEY ("pickuuid") +); + + +CREATE TABLE userprofile ( + "useruuid" varchar(36) NOT NULL, + "profilePartner" varchar(36) NOT NULL, + "profileAllowPublish" bytea NOT NULL, + "profileMaturePublish" bytea NOT NULL, + "profileURL" varchar(255) NOT NULL, + "profileWantToMask" integer NOT NULL, + "profileWantToText" text NOT NULL, + "profileSkillsMask" integer NOT NULL, + "profileSkillsText" text NOT NULL, + "profileLanguages" text NOT NULL, + "profileImage" varchar(36) NOT NULL, + "profileAboutText" text NOT NULL, + "profileFirstImage" varchar(36) NOT NULL, + "profileFirstText" text NOT NULL, + PRIMARY KEY ("useruuid") +); + +commit; + +:VERSION 2 # ------------------------------- + +begin; +CREATE TABLE userdata ( + "UserId" char(36) NOT NULL, + "TagId" varchar(64) NOT NULL, + "DataKey" varchar(255), + "DataVal" varchar(255), + PRIMARY KEY ("UserId","TagId") +); + +commit; + +:VERSION 3 # ------------------------------- +begin; +CREATE TABLE usersettings ( + "useruuid" char(36) NOT NULL, + "imviaemail" bytea NOT NULL, + "visible" bytea NOT NULL, + PRIMARY KEY ("useruuid") +); +commit; + +:VERSION 4 + +BEGIN; + +-- Classifieds +ALTER TABLE classifieds DROP CONSTRAINT classifiedspk; +ALTER TABLE classifieds ALTER COLUMN classifieduuid SET DATA TYPE uuid using classifieduuid::uuid; +ALTER TABLE classifieds ALTER COLUMN creatoruuid SET DATA TYPE uuid using creatoruuid::uuid; +ALTER TABLE classifieds ALTER COLUMN parceluuid SET DATA TYPE uuid using parceluuid::uuid; +ALTER TABLE classifieds ALTER COLUMN snapshotuuid SET DATA TYPE uuid using snapshotuuid::uuid; +ALTER TABLE classifieds ADD CONSTRAINT classifiedspk PRIMARY KEY (classifieduuid); + +-- Notes +ALTER TABLE usernotes DROP CONSTRAINT usernoteuk; +ALTER TABLE usernotes ALTER COLUMN useruuid SET DATA TYPE uuid USING useruuid::uuid; +ALTER TABLE usernotes ALTER COLUMN targetuuid SET DATA TYPE uuid USING targetuuid::uuid; +ALTER TABLE usernotes ADD CONSTRAINT usernoteuk UNIQUE (useruuid,targetuuid); + + +-- Userpicks +ALTER TABLE userpicks DROP CONSTRAINT userpicks_pkey; +ALTER TABLE userpicks ALTER COLUMN pickuuid SET DATA TYPE uuid USING pickuuid::uuid; +ALTER TABLE userpicks ALTER COLUMN creatoruuid SET DATA TYPE uuid USING creatoruuid::uuid; +ALTER TABLE userpicks ALTER COLUMN parceluuid SET DATA TYPE uuid USING parceluuid::uuid; +ALTER TABLE userpicks ALTER COLUMN parceluuid SET DATA TYPE uuid USING parceluuid::uuid; +ALTER TABLE userpicks ADD PRIMARY KEY (pickuuid); + +-- Userprofile +ALTER TABLE userprofile DROP CONSTRAINT userprofile_pkey; +ALTER TABLE userprofile ALTER COLUMN useruuid SET DATA TYPE uuid USING useruuid::uuid; +ALTER TABLE userprofile ALTER COLUMN "profilePartner" SET DATA TYPE uuid USING "profilePartner"::uuid; +-- Force column conversions +ALTER TABLE userprofile ALTER COLUMN "profileAllowPublish" SET DATA TYPE boolean USING CASE WHEN false THEN false ELSE true END; +ALTER TABLE userprofile ALTER COLUMN "profileMaturePublish" SET DATA TYPE boolean USING CASE WHEN false THEN false ELSE true END; +ALTER TABLE userprofile ALTER COLUMN "profileImage" SET DATA TYPE uuid USING "profileImage"::uuid; +ALTER TABLE userprofile ALTER COLUMN "profileFirstImage" SET DATA TYPE uuid USING "profileFirstImage"::uuid; +ALTER TABLE userprofile ADD PRIMARY KEY (useruuid); + +-- Userdata +ALTER TABLE userdata DROP CONSTRAINT userdata_pkey; +ALTER TABLE userdata ALTER COLUMN "UserId" SET DATA TYPE uuid USING "UserId"::uuid; +ALTER TABLE userdata ALTER COLUMN "UserId" SET DATA TYPE uuid USING "UserId"::uuid; +ALTER TABLE userdata ADD PRIMARY KEY ("UserId","TagId"); + + +-- Usersettings +ALTER TABLE usersettings DROP CONSTRAINT usersettings_pkey; +ALTER TABLE usersettings ALTER COLUMN useruuid SET DATA TYPE uuid USING useruuid::uuid; +ALTER TABLE usersettings ALTER COLUMN visible SET DATA TYPE boolean USING CASE WHEN false THEN false ELSE true END; +ALTER TABLE usersettings ADD COLUMN email varchar(254) NOT NULL; +ALTER TABLE usersettings ADD PRIMARY KEY (useruuid); + +COMMIT; + + +:VERSION 5 # ------------------------------- + +BEGIN; + +ALTER TABLE usersettings ALTER COLUMN imviaemail SET DATA TYPE boolean USING CASE WHEN false THEN false ELSE true END; + +COMMIT; \ No newline at end of file diff --git a/OpenSim/Data/PGSQL/Resources/UserStore.migrations b/OpenSim/Data/PGSQL/Resources/UserStore.migrations new file mode 100644 index 0000000000..974d48995b --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/UserStore.migrations @@ -0,0 +1,404 @@ +:VERSION 1 + +CREATE TABLE users ( + "UUID" varchar(36) NOT NULL default '' Primary Key, + "username" varchar(32) NOT NULL, + "lastname" varchar(32) NOT NULL, + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "homeRegion" bigint default NULL, + "homeLocationX" double precision default NULL, + "homeLocationY" double precision default NULL, + "homeLocationZ" double precision default NULL, + "homeLookAtX" double precision default NULL, + "homeLookAtY" double precision default NULL, + "homeLookAtZ" double precision default NULL, + "created" int NOT NULL, + "lastLogin" int NOT NULL, + "userInventoryURI" varchar(255) default NULL, + "userAssetURI" varchar(255) default NULL, + "profileCanDoMask" int default NULL, + "profileWantDoMask" int default NULL, + "profileAboutText" text, + "profileFirstText" text, + "profileImage" varchar(36) default NULL, + "profileFirstImage" varchar(36) default NULL, + "webLoginKey" varchar(36) default NULL +); + +CREATE INDEX "usernames" ON users +( + "username" ASC, + "lastname" ASC +); + + +CREATE TABLE agents ( + "UUID" varchar(36) NOT NULL Primary Key, + "sessionID" varchar(36) NOT NULL, + "secureSessionID" varchar(36) NOT NULL, + "agentIP" varchar(16) NOT NULL, + "agentPort" int NOT NULL, + "agentOnline" smallint NOT NULL, + "loginTime" int NOT NULL, + "logoutTime" int NOT NULL, + "currentRegion" varchar(36) NOT NULL, + "currentHandle" bigint NOT NULL, + "currentPos" varchar(64) NOT NULL +); + +CREATE INDEX session ON agents +( + "sessionID" ASC +); + +CREATE INDEX ssession ON agents +( + "secureSessionID" ASC +); + + +CREATE TABLE userfriends( + "ownerID" varchar(50) NOT NULL, + "friendID" varchar(50) NOT NULL, + "friendPerms" varchar(50) NOT NULL, + "datetimestamp" varchar(50) NOT NULL +); + +CREATE TABLE avatarappearance ( + "Owner" varchar(36) NOT NULL primary key, + "Serial" int NOT NULL, + "Visual_Params" bytea NOT NULL, + "Texture" bytea NOT NULL, + "Avatar_Height" double precision NOT NULL, + "Body_Item" varchar(36) NOT NULL, + "Body_Asset" varchar(36) NOT NULL, + "Skin_Item" varchar(36) NOT NULL, + "Skin_Asset" varchar(36) NOT NULL, + "Hair_Item" varchar(36) NOT NULL, + "Hair_Asset" varchar(36) NOT NULL, + "Eyes_Item" varchar(36) NOT NULL, + "Eyes_Asset" varchar(36) NOT NULL, + "Shirt_Item" varchar(36) NOT NULL, + "Shirt_Asset" varchar(36) NOT NULL, + "Pants_Item" varchar(36) NOT NULL, + "Pants_Asset" varchar(36) NOT NULL, + "Shoes_Item" varchar(36) NOT NULL, + "Shoes_Asset" varchar(36) NOT NULL, + "Socks_Item" varchar(36) NOT NULL, + "Socks_Asset" varchar(36) NOT NULL, + "Jacket_Item" varchar(36) NOT NULL, + "Jacket_Asset" varchar(36) NOT NULL, + "Gloves_Item" varchar(36) NOT NULL, + "Gloves_Asset" varchar(36) NOT NULL, + "Undershirt_Item" varchar(36) NOT NULL, + "Undershirt_Asset" varchar(36) NOT NULL, + "Underpants_Item" varchar(36) NOT NULL, + "Underpants_Asset" varchar(36) NOT NULL, + "Skirt_Item" varchar(36) NOT NULL, + "Skirt_Asset" varchar(36) NOT NULL +); + +:VERSION 2 + +BEGIN TRANSACTION; + +ALTER TABLE users ADD "homeRegionID" varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE users ADD "userFlags" int NOT NULL default 0; +ALTER TABLE users ADD "godLevel" int NOT NULL default 0; +ALTER TABLE users ADD "customType" varchar(32) not null default ''; +ALTER TABLE users ADD "partner" varchar(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + + +:VERSION 3 + +BEGIN TRANSACTION; + +CREATE TABLE avatarattachments ( + "UUID" varchar(36) NOT NULL + , "attachpoint" int NOT NULL + , item varchar(36) NOT NULL + , asset varchar(36) NOT NULL); + +CREATE INDEX IX_avatarattachments ON avatarattachments + ( + "UUID" + ); + +COMMIT; + + +:VERSION 4 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_userfriends + ( + "ownerID" varchar(36) NOT NULL, + "friendID" varchar(36) NOT NULL, + "friendPerms" int NOT NULL, + "datetimestamp" int NOT NULL + ); + +INSERT INTO Tmp_userfriends ("ownerID", "friendID", "friendPerms", "datetimestamp") + SELECT cast("ownerID" as varchar(36)), cast("friendID" as varchar(36)), cast("friendPerms" as int), cast("datetimestamp" as int) + FROM userfriends; + +DROP TABLE userfriends; + +alter table Tmp_userfriends rename to userfriends; + +CREATE INDEX IX_userfriends_ownerID ON userfriends + ( + "ownerID" + ); + +CREATE INDEX IX_userfriends_friendID ON userfriends + ( + "friendID" + ); + +COMMIT; + + +:VERSION 5 + +BEGIN TRANSACTION; + + ALTER TABLE users add "email" varchar(250); + +COMMIT; + + +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_users + ( + "UUID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "username" varchar(32) NOT NULL, + "lastname" varchar(32) NOT NULL, + "passwordHash" varchar(32) NOT NULL, + "passwordSalt" varchar(32) NOT NULL, + "homeRegion" bigint NULL DEFAULT (NULL), + "homeLocationX" double precision NULL DEFAULT (NULL), + "homeLocationY" double precision NULL DEFAULT (NULL), + "homeLocationZ" double precision NULL DEFAULT (NULL), + "homeLookAtX" double precision NULL DEFAULT (NULL), + "homeLookAtY" double precision NULL DEFAULT (NULL), + "homeLookAtZ" double precision NULL DEFAULT (NULL), + "created" int NOT NULL, + "lastLogin" int NOT NULL, + "userInventoryURI" varchar(255) NULL DEFAULT (NULL), + "userAssetURI" varchar(255) NULL DEFAULT (NULL), + "profileCanDoMask" int NULL DEFAULT (NULL), + "profileWantDoMask" int NULL DEFAULT (NULL), + "profileAboutText" text NULL, + "profileFirstText" text NULL, + "profileImage" uuid NULL DEFAULT (NULL), + "profileFirstImage" uuid NULL DEFAULT (NULL), + "webLoginKey" uuid NULL DEFAULT (NULL), + "homeRegionID" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + "userFlags" int NOT NULL DEFAULT ((0)), + "godLevel" int NOT NULL DEFAULT ((0)), + "customType" varchar(32) NOT NULL DEFAULT (''), + "partner" uuid NOT NULL DEFAULT ('00000000-0000-0000-0000-000000000000'), + email varchar(250) NULL + ); + +INSERT INTO Tmp_users ("UUID", "username", "lastname", "passwordHash", "passwordSalt", "homeRegion", "homeLocationX", "homeLocationY", "homeLocationZ", "homeLookAtX", "homeLookAtY", "homeLookAtZ", "created", "lastLogin", "userInventoryURI", "userAssetURI", "profileCanDoMask", "profileWantDoMask", "profileAboutText", "profileFirstText", "profileImage", "profileFirstImage", "webLoginKey", "homeRegionID", "userFlags", "godLevel", "customType", "partner", email) + SELECT cast("UUID" as uuid), "username", "lastname", "passwordHash", "passwordSalt", "homeRegion", "homeLocationX", "homeLocationY", "homeLocationZ", "homeLookAtX", "homeLookAtY", "homeLookAtZ", "created", "lastLogin", "userInventoryURI", "userAssetURI", "profileCanDoMask", "profileWantDoMask", "profileAboutText", "profileFirstText", cast("profileImage" as uuid), cast("profileFirstImage" as uuid), cast("webLoginKey" as uuid), cast("homeRegionID" as uuid), "userFlags", "godLevel", "customType", cast("partner" as uuid), email + FROM users ; + +DROP TABLE users; + +alter table Tmp_users rename to users; + +ALTER TABLE users ADD CONSTRAINT + PK__users__65A475E737A5467C PRIMARY KEY + ( + "UUID" + ); + +CREATE INDEX "usernames" ON users + ( + "username", + "lastname" + ); + +COMMIT; + + +:VERSION 7 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_agents + ( + "UUID" uuid NOT NULL, + "sessionID" uuid NOT NULL, + "secureSessionID" uuid NOT NULL, + "agentIP" varchar(16) NOT NULL, + "agentPort" int NOT NULL, + "agentOnline" smallint NOT NULL, + "loginTime" int NOT NULL, + "logoutTime" int NOT NULL, + "currentRegion" uuid NOT NULL, + "currentHandle" bigint NOT NULL, + "currentPos" varchar(64) NOT NULL + ); + +INSERT INTO Tmp_agents ("UUID", "sessionID", "secureSessionID", "agentIP", "agentPort", "agentOnline", "loginTime", "logoutTime", "currentRegion", "currentHandle", "currentPos") + SELECT cast("UUID" as uuid), cast("sessionID" as uuid), cast("secureSessionID" as uuid), "agentIP", "agentPort", "agentOnline", "loginTime", "logoutTime", cast("currentRegion" as uuid), "currentHandle", "currentPos" + FROM agents ; + +DROP TABLE agents; + +alter table Tmp_agents rename to agents; + +ALTER TABLE agents ADD CONSTRAINT + PK__agents__65A475E749C3F6B7 PRIMARY KEY + ( + "UUID" + ) ; + +CREATE INDEX session ON agents + ( + "sessionID" + ); + +CREATE INDEX ssession ON agents + ( + "secureSessionID" + ); + +COMMIT; + + +:VERSION 8 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_userfriends + ( + "ownerID" uuid NOT NULL, + "friendID" uuid NOT NULL, + "friendPerms" int NOT NULL, + "datetimestamp" int NOT NULL + ); + +INSERT INTO Tmp_userfriends ("ownerID", "friendID", "friendPerms", "datetimestamp") + SELECT cast("ownerID" as uuid), cast( "friendID" as uuid), "friendPerms", "datetimestamp" + FROM userfriends; + +DROP TABLE userfriends; + +alter table Tmp_userfriends rename to userfriends; + +CREATE INDEX IX_userfriends_ownerID ON userfriends + ( + "ownerID" + ); + +CREATE INDEX IX_userfriends_friendID ON userfriends + ( + "friendID" + ); + +COMMIT; + + +:VERSION 9 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_avatarappearance + ( + "Owner" uuid NOT NULL, + "Serial" int NOT NULL, + "Visual_Params" bytea NOT NULL, + "Texture" bytea NOT NULL, + "Avatar_Height" double precision NOT NULL, + "Body_Item" uuid NOT NULL, + "Body_Asset" uuid NOT NULL, + "Skin_Item" uuid NOT NULL, + "Skin_Asset" uuid NOT NULL, + "Hair_Item" uuid NOT NULL, + "Hair_Asset" uuid NOT NULL, + "Eyes_Item" uuid NOT NULL, + "Eyes_Asset" uuid NOT NULL, + "Shirt_Item" uuid NOT NULL, + "Shirt_Asset" uuid NOT NULL, + "Pants_Item" uuid NOT NULL, + "Pants_Asset" uuid NOT NULL, + "Shoes_Item" uuid NOT NULL, + "Shoes_Asset" uuid NOT NULL, + "Socks_Item" uuid NOT NULL, + "Socks_Asset" uuid NOT NULL, + "Jacket_Item" uuid NOT NULL, + "Jacket_Asset" uuid NOT NULL, + "Gloves_Item" uuid NOT NULL, + "Gloves_Asset" uuid NOT NULL, + "Undershirt_Item" uuid NOT NULL, + "Undershirt_Asset" uuid NOT NULL, + "Underpants_Item" uuid NOT NULL, + "Underpants_Asset" uuid NOT NULL, + "Skirt_Item" uuid NOT NULL, + "Skirt_Asset" uuid NOT NULL + ); + +INSERT INTO Tmp_avatarappearance ("Owner", "Serial", "Visual_Params", "Texture", "Avatar_Height", "Body_Item", "Body_Asset", "Skin_Item", "Skin_Asset", "Hair_Item", "Hair_Asset", "Eyes_Item", "Eyes_Asset", "Shirt_Item", "Shirt_Asset", "Pants_Item", "Pants_Asset", "Shoes_Item", "Shoes_Asset", "Socks_Item", "Socks_Asset", "Jacket_Item", "Jacket_Asset", "Gloves_Item", "Gloves_Asset", "Undershirt_Item", "Undershirt_Asset", "Underpants_Item", "Underpants_Asset", "Skirt_Item", "Skirt_Asset") + SELECT cast("Owner" as uuid), "Serial", "Visual_Params", "Texture", "Avatar_Height", cast("Body_Item" as uuid), cast("Body_Asset" as uuid), cast("Skin_Item" as uuid), cast("Skin_Asset" as uuid), cast("Hair_Item" as uuid), cast("Hair_Asset" as uuid), cast("Eyes_Item" as uuid), cast("Eyes_Asset" as uuid), cast("Shirt_Item" as uuid), cast("Shirt_Asset" as uuid), cast("Pants_Item" as uuid), cast("Pants_Asset" as uuid), cast("Shoes_Item" as uuid), cast("Shoes_Asset" as uuid), cast("Socks_Item" as uuid), cast("Socks_Asset" as uuid), cast("Jacket_Item" as uuid), cast("Jacket_Asset" as uuid), cast("Gloves_Item" as uuid), cast("Gloves_Asset" as uuid), cast("Undershirt_Item" as uuid), cast("Undershirt_Asset" as uuid), cast("Underpants_Item" as uuid), cast("Underpants_Asset" as uuid), cast("Skirt_Item" as uuid), cast("Skirt_Asset" as uuid) + FROM avatarappearance ; + +DROP TABLE avatarappearance; + +alter table Tmp_avatarappearance rename to avatarappearance; + +ALTER TABLE avatarappearance ADD CONSTRAINT + PK__avatarap__7DD115CC4E88ABD4 PRIMARY KEY + ( + "Owner" + ); + +COMMIT; + + +:VERSION 10 + +BEGIN TRANSACTION; + +CREATE TABLE Tmp_avatarattachments + ( + "UUID" uuid NOT NULL, + "attachpoint" int NOT NULL, + item uuid NOT NULL, + asset uuid NOT NULL + ); + +INSERT INTO Tmp_avatarattachments ("UUID", "attachpoint", item, asset) + SELECT cast("UUID" as uuid), "attachpoint", cast(item as uuid), cast(asset as uuid) + FROM avatarattachments ; + +DROP TABLE avatarattachments; + +alter table Tmp_avatarattachments rename to avatarattachments; + +CREATE INDEX IX_avatarattachments ON avatarattachments + ( + "UUID" + ); + +COMMIT; + + +:VERSION 11 + +BEGIN TRANSACTION; + +ALTER TABLE users ADD "scopeID" uuid not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations b/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations new file mode 100644 index 0000000000..df9d821bd2 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/XAssetStore.migrations @@ -0,0 +1,80 @@ +# ----------------- +:VERSION 1 + +BEGIN; + +CREATE TABLE XAssetsMeta ( + "ID" char(36) NOT NULL, + "Hash" char(32) NOT NULL, + "Name" varchar(64) NOT NULL, + "Description" varchar(64) NOT NULL, + "AssetType" smallint NOT NULL, + "Local" smallint NOT NULL, + "Temporary" smallint NOT NULL, + "CreateTime" integer NOT NULL, + "AccessTime" integer NOT NULL, + "AssetFlags" integer NOT NULL, + "CreatorID" varchar(128) NOT NULL, + PRIMARY KEY ("ID") +); + +CREATE TABLE XAssetsData ( + "Hash" char(32) NOT NULL, + "Data" bytea NOT NULL, + PRIMARY KEY ("Hash") +); + +COMMIT; + + +:VERSION 2 + +BEGIN; + +ALTER TABLE xassetsmeta ALTER COLUMN "Local" SET DATA TYPE boolean USING CASE WHEN '0' THEN FALSE ELSE TRUE END; +ALTER TABLE xassetsmeta ALTER COLUMN "Temporary" SET DATA TYPE boolean USING CASE WHEN '0' THEN FALSE ELSE TRUE END; +ALTER TABLE xassetsmeta ALTER COLUMN "Hash" SET DATA TYPE char(66); +ALTER TABLE xassetsdata ALTER COLUMN "Hash" SET DATA TYPE char(66); + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE xassetsmeta RENAME COLUMN "ID" TO id; +ALTER TABLE xassetsmeta RENAME COLUMN "Hash" TO hash; +ALTER TABLE xassetsmeta RENAME COLUMN "Name" TO name; +ALTER TABLE xassetsmeta RENAME COLUMN "Description" TO description; +ALTER TABLE xassetsmeta RENAME COLUMN "Local" to local; +ALTER TABLE xassetsmeta RENAME COLUMN "Temporary" TO temporary; +ALTER TABLE xassetsmeta RENAME COLUMN "CreateTime" TO create_time; +ALTER TABLE xassetsmeta RENAME COLUMN "AccessTime" TO access_time; +ALTER TABLE xassetsmeta RENAME COLUMN "AssetFlags" TO asset_flags; +ALTER TABLE xassetsmeta RENAME COLUMN "CreatorID" TO creatorid; +ALTER TABLE xassetsmeta DROP CONSTRAINT xassetsmeta_pkey; +ALTER TABLE xassetsmeta ADD PRIMARY KEY (id); + + +ALTER TABLE xassetsdata RENAME COLUMN "Hash" TO hash; +ALTER TABLE xassetsdata RENAME COLUMN "Data" TO data; +ALTER TABLE xassetsdata DROP CONSTRAINT xassetsdata_pkey; +ALTER TABLE xassetsdata ADD PRIMARY KEY (hash); + +COMMIT; + + +:VERSION 4 + +BEGIN; + +ALTER TABLE xassetsmeta ALTER COLUMN id SET DATA TYPE uuid USING id::uuid; +ALTER TABLE xassetsmeta ALTER COLUMN hash SET DATA TYPE bytea USING hash::bytea; +ALTER TABLE xassetsdata ALTER COLUMN hash SET DATA TYPE bytea USING hash::bytea; + +COMMIT; + +:VERSION 5 + +BEGIN; +COMMIT; diff --git a/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations b/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations new file mode 100644 index 0000000000..74b07c3141 --- /dev/null +++ b/OpenSim/Data/PGSQL/Resources/os_groups_Store.migrations @@ -0,0 +1,211 @@ +:VERSION 1 # -------------------------- + +BEGIN; + +CREATE TABLE os_groups_groups ( + "GroupID" char(36) Primary Key NOT NULL default '', + "Location" varchar(255) NOT NULL default '', + "Name" varchar(255) NOT NULL default '', + "Charter" text NOT NULL, + "InsigniaID" char(36) NOT NULL default '', + "FounderID" char(36) NOT NULL default '', + "MembershipFee" integer NOT NULL default '0', + "OpenEnrollment" varchar(255) NOT NULL default '', + "ShowInList" integer NOT NULL default '0', + "AllowPublish" integer NOT NULL default '0', + "MaturePublish" integer NOT NULL default '0', + "OwnerRoleID" char(36) NOT NULL default '' +); + + +CREATE TABLE os_groups_membership ( + "GroupID"char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + "SelectedRoleID" char(36) NOT NULL default '', + "Contribution" integer NOT NULL default '0', + "ListInProfile" integer NOT NULL default '1', + "AcceptNotices" integer NOT NULL default '1', + "AccessToken" char(36) NOT NULL default '', + constraint os_groupmemberpk primary key ("GroupID", "PrincipalID") +); + + + +CREATE TABLE os_groups_roles ( + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "Name" varchar(255) NOT NULL default '', + "Description" varchar(255) NOT NULL default '', + "Title" varchar(255) NOT NULL default '', + "Powers" bigint NOT NULL default 0, + constraint os_grouprolepk PRIMARY KEY ("GroupID","RoleID") +); + + +CREATE TABLE os_groups_rolemembership ( + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + constraint os_grouprolememberpk PRIMARY KEY ("GroupID","RoleID","PrincipalID") +); + + +CREATE TABLE os_groups_invites ( + "InviteID" char(36) NOT NULL default '', + "GroupID" char(36) NOT NULL default '', + "RoleID" char(36) NOT NULL default '', + "PrincipalID" VARCHAR(255) NOT NULL default '', + "TMStamp" timestamp NOT NULL default now(), + constraint os_groupinvitespk PRIMARY KEY ("InviteID") +); +-- UNIQUE KEY "PrincipalGroup" ("GroupID","PrincipalID") + + +CREATE TABLE os_groups_notices ( + "GroupID" char(36) NOT NULL default '', + "NoticeID" char(36) NOT NULL default '', + "TMStamp" integer NOT NULL default '0', + "FromName" varchar(255) NOT NULL default '', + "Subject" varchar(255) NOT NULL default '', + "Message" text NOT NULL, + "HasAttachment" integer NOT NULL default '0', + "AttachmentType" integer NOT NULL default '0', + "AttachmentName" varchar(128) NOT NULL default '', + "AttachmentItemID" char(36) NOT NULL default '', + "AttachmentOwnerID" varchar(255) NOT NULL default '', + constraint os_groupsnoticespk PRIMARY KEY ("NoticeID") +); +-- KEY "GroupID" ("GroupID"), +-- KEY "TMStamp" ("TMStamp") + +CREATE TABLE os_groups_principals ( + "PrincipalID" VARCHAR(255) NOT NULL default '', + "ActiveGroupID" char(36) NOT NULL default '', + constraint os_groupprincpk PRIMARY KEY ("PrincipalID") +); + +COMMIT; + +:VERSION 2 # -------------------------- + +BEGIN; + + +COMMIT; + + + +:VERSION 3 + +BEGIN; + +-- Not a pretty way to do this, but it did not work as-is +-- and nothing was found about converting between existing data +-- and the new type. +-- Since there should be nothing to preserve ... + +DROP TABLE IF EXISTS os_groups_groups CASCADE; + +CREATE TABLE os_groups_groups ( + "GroupID" uuid PRIMARY KEY NOT NULL, + "Location" varchar(255) NOT NULL DEFAULT '', + "Name" varchar(255) NOT NULL DEFAULT '', + "Charter" text NOT NULL, + "InsigniaID" uuid NOT NULL, + "FounderID" uuid NOT NULL, + "MembershipFee" integer NOT NULL DEFAULT '0', + "OpenEnrollment" varchar(255) NOT NULL DEFAULT '', + "ShowInList" integer NOT NULL DEFAULT '0', + "AllowPublish" integer NOT NULL DEFAULT '0', + "MaturePublish" integer NOT NULL DEFAULT '0', + "OwnerRoleID" uuid NOT NULL +); + + +DROP TABLE IF EXISTS os_groups_membership; + +CREATE TABLE os_groups_membership ( + "GroupID"uuid NOT NULL, + "PrincipalID" VARCHAR(255) NOT NULL DEFAULT '', + "SelectedRoleID" uuid NOT NULL, + "Contribution" integer NOT NULL DEFAULT '0', + "ListInProfile" integer NOT NULL DEFAULT '1', + "AcceptNotices" integer NOT NULL DEFAULT '1', + "AccessToken" uuid NOT NULL, + constraint os_groupmemberpk PRIMARY KEY ("GroupID", "PrincipalID") +); + + + +DROP TABLE IF EXISTS os_groups_roles; + +CREATE TABLE os_groups_roles ( + "GroupID" uuid NOT NULL, + "RoleID" uuid NOT NULL, + "Name" varchar(255) NOT NULL DEFAULT '', + "Description" varchar(255) NOT NULL DEFAULT '', + "Title" varchar(255) NOT NULL DEFAULT '', + "Powers" varchar(36) NOT NULL DEFAULT '', + constraint os_grouprolepk PRIMARY KEY ("GroupID","RoleID") +); + + +DROP TABLE IF EXISTS os_groups_rolemembership; + +CREATE TABLE os_groups_rolemembership ( + "GroupID" uuid NOT NULL, + "RoleID" uuid NOT NULL, + "PrincipalID" VARCHAR(255) NOT NULL DEFAULT '', + constraint os_grouprolememberpk PRIMARY KEY ("GroupID","RoleID","PrincipalID") +); + + +DROP TABLE IF EXISTS os_groups_invites; + +CREATE TABLE os_groups_invites ( + "InviteID" uuid NOT NULL, + "GroupID" uuid NOT NULL, + "RoleID" uuid NOT NULL, + "PrincipalID" VARCHAR(255) NOT NULL DEFAULT '', + "TMStamp" timestamp NOT NULL DEFAULT now(), + constraint os_groupinvitespk PRIMARY KEY ("InviteID") +); + + +DROP TABLE IF EXISTS os_groups_notices; + +CREATE TABLE os_groups_notices ( + "GroupID" uuid NOT NULL, + "NoticeID" uuid NOT NULL, + "TMStamp" integer NOT NULL DEFAULT '0', + "FromName" varchar(255) NOT NULL DEFAULT '', + "Subject" varchar(255) NOT NULL DEFAULT '', + "Message" text NOT NULL, + "HasAttachment" integer NOT NULL DEFAULT '0', + "AttachmentType" integer NOT NULL DEFAULT '0', + "AttachmentName" varchar(128) NOT NULL DEFAULT '', + "AttachmentItemID" uuid NOT NULL, + "AttachmentOwnerID" varchar(255) NOT NULL DEFAULT '', + constraint os_groupsnoticespk PRIMARY KEY ("NoticeID") +); + + +DROP TABLE IF EXISTS os_groups_principals; + +CREATE TABLE os_groups_principals ( + "PrincipalID" VARCHAR(255) NOT NULL DEFAULT '', + "ActiveGroupID" uuid NOT NULL, + constraint os_groupprincpk PRIMARY KEY ("PrincipalID") +); + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE IF EXISTS os_groups_notices + ALTER COLUMN "AttachmentItemID" SET DEFAULT '00000000-0000-0000-0000-000000000000' +; + +COMMIT; diff --git a/OpenSim/Data/Properties/AssemblyInfo.cs b/OpenSim/Data/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b1f234beaa --- /dev/null +++ b/OpenSim/Data/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Data")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("3a711c34-b0c0-4264-b0fe-f366eabf9d7b")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Data/Resources/OpenSim.Data.addin.xml b/OpenSim/Data/Resources/OpenSim.Data.addin.xml new file mode 100644 index 0000000000..10c9c3cb28 --- /dev/null +++ b/OpenSim/Data/Resources/OpenSim.Data.addin.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d2e62d21bc --- /dev/null +++ b/OpenSim/Data/SQLite/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Data.SQLite")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Data.SQLite")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("6113d5ce-4547-49f4-9236-0dcc503457b1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql b/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql new file mode 100644 index 0000000000..1a24613275 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/001_GridUserStore.sql @@ -0,0 +1,16 @@ +BEGIN TRANSACTION; + +CREATE TABLE GridUser ( + UserID VARCHAR(255) primary key, + HomeRegionID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + HomePosition CHAR(64) NOT NULL DEFAULT '<0,0,0>', + HomeLookAt CHAR(64) NOT NULL DEFAULT '<0,0,0>', + LastRegionID CHAR(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', + LastPosition CHAR(64) NOT NULL DEFAULT '<0,0,0>', + LastLookAt CHAR(64) NOT NULL DEFAULT '<0,0,0>', + Online CHAR(5) NOT NULL DEFAULT 'false', + Login CHAR(16) NOT NULL DEFAULT '0', + Logout CHAR(16) NOT NULL DEFAULT '0' +) ; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/AgentPrefs.migrations b/OpenSim/Data/SQLite/Resources/AgentPrefs.migrations new file mode 100644 index 0000000000..7e0525d8c1 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/AgentPrefs.migrations @@ -0,0 +1,36 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE `AgentPrefs` ( + `PrincipalID` CHAR(36) NOT NULL, + `AccessPrefs` CHAR(2) NOT NULL DEFAULT 'M', + `HoverHeight` DOUBLE(30, 27) NOT NULL DEFAULT 0, + `Language` CHAR(5) NOT NULL DEFAULT 'en-us', + `LanguageIsPublic` BOOLEAN NOT NULL DEFAULT 1, + `PermEveryone` INT(6) NOT NULL DEFAULT 0, + `PermGroup` INT(6) NOT NULL DEFAULT 0, + `PermNextOwner` INT(6) NOT NULL DEFAULT 532480, + UNIQUE KEY `PrincipalID` (`PrincipalID`), + PRIMARY KEY(`PrincipalID`)); + +COMMIT; + +:VERSION 2 + +BEGIN; + +CREATE TABLE AgentPrefs( + PrincipalID CHAR(36) NOT NULL, + AccessPrefs CHAR(2) NOT NULL DEFAULT 'M', + HoverHeight DOUBLE(30, 27) NOT NULL DEFAULT 0, + Language CHAR(5) NOT NULL DEFAULT 'en-us', + LanguageIsPublic BOOLEAN NOT NULL DEFAULT 1, + PermEveryone INT(6) NOT NULL DEFAULT 0, + PermGroup INT(6) NOT NULL DEFAULT 0, + PermNextOwner INT(6) NOT NULL DEFAULT 532480, + UNIQUE(PrincipalID), + PRIMARY KEY(PrincipalID) +); + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/AssetStore.migrations b/OpenSim/Data/SQLite/Resources/AssetStore.migrations new file mode 100644 index 0000000000..f20631cc14 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/AssetStore.migrations @@ -0,0 +1,67 @@ +:VERSION 1 + +BEGIN TRANSACTION; +CREATE TABLE assets( + UUID varchar(255) primary key, + Name varchar(255), + Description varchar(255), + Type integer, + InvType integer, + Local integer, + Temporary integer, + Data blob); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; + +:VERSION 3 + +DELETE FROM assets WHERE UUID = 'dc4b9f0bd00845c696a401dd947ac621' + +:VERSION 4 + +BEGIN; + +update assets + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + +:VERSION 5 + +BEGIN TRANSACTION; + +CREATE TEMPORARY TABLE assets_backup(UUID,Name,Description,Type,Local,Temporary,Data); +INSERT INTO assets_backup SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets; +DROP TABLE assets; +CREATE TABLE assets( + UUID NOT NULL PRIMARY KEY, + Name, + Description, + Type, + Local, + Temporary, + asset_flags INTEGER NOT NULL DEFAULT 0, + CreatorID varchar(128) default '', + Data); + +INSERT INTO assets(UUID,Name,Description,Type,Local,Temporary,Data) +SELECT UUID,Name,Description,Type,Local,Temporary,Data FROM assets_backup; +DROP TABLE assets_backup; + +COMMIT; + + diff --git a/OpenSim/Data/SQLite/Resources/AuthStore.migrations b/OpenSim/Data/SQLite/Resources/AuthStore.migrations new file mode 100644 index 0000000000..ca6bdf55a5 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/AuthStore.migrations @@ -0,0 +1,29 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE auth ( + UUID char(36) NOT NULL, + passwordHash char(32) NOT NULL default '', + passwordSalt char(32) NOT NULL default '', + webLoginKey varchar(255) NOT NULL default '', + accountType VARCHAR(32) NOT NULL DEFAULT 'UserAccount', + PRIMARY KEY (`UUID`) +); + +CREATE TABLE tokens ( + UUID char(36) NOT NULL, + token varchar(255) NOT NULL, + validity datetime NOT NULL +); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO auth (UUID, passwordHash, passwordSalt, webLoginKey) SELECT `UUID` AS UUID, `passwordHash` AS passwordHash, `passwordSalt` AS passwordSalt, `webLoginKey` AS webLoginKey FROM users; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/Avatar.migrations b/OpenSim/Data/SQLite/Resources/Avatar.migrations new file mode 100644 index 0000000000..13b41969ec --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/Avatar.migrations @@ -0,0 +1,11 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE Avatars ( + PrincipalID CHAR(36) NOT NULL, + Name VARCHAR(32) NOT NULL, + Value VARCHAR(255) NOT NULL DEFAULT '', + PRIMARY KEY(PrincipalID, Name)); + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/EstateStore.migrations b/OpenSim/Data/SQLite/Resources/EstateStore.migrations new file mode 100644 index 0000000000..0aec49b3e2 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/EstateStore.migrations @@ -0,0 +1,97 @@ +:VERSION 6 + +BEGIN TRANSACTION; + +CREATE TABLE estate_groups ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_managers ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estate_map ( + RegionID char(36) NOT NULL default '00000000-0000-0000-0000-000000000000', + EstateID int(11) NOT NULL +); + +CREATE TABLE estate_settings ( + EstateID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + EstateName varchar(64) default NULL, + AbuseEmailToEstateOwner tinyint(4) NOT NULL, + DenyAnonymous tinyint(4) NOT NULL, + ResetHomeOnTeleport tinyint(4) NOT NULL, + FixedSun tinyint(4) NOT NULL, + DenyTransacted tinyint(4) NOT NULL, + BlockDwell tinyint(4) NOT NULL, + DenyIdentified tinyint(4) NOT NULL, + AllowVoice tinyint(4) NOT NULL, + UseGlobalTime tinyint(4) NOT NULL, + PricePerMeter int(11) NOT NULL, + TaxFree tinyint(4) NOT NULL, + AllowDirectTeleport tinyint(4) NOT NULL, + RedirectGridX int(11) NOT NULL, + RedirectGridY int(11) NOT NULL, + ParentEstateID int(10) NOT NULL, + SunPosition double NOT NULL, + EstateSkipScripts tinyint(4) NOT NULL, + BillableFactor float NOT NULL, + PublicAccess tinyint(4) NOT NULL +); + +insert into estate_settings ( + EstateID,EstateName,AbuseEmailToEstateOwner,DenyAnonymous,ResetHomeOnTeleport,FixedSun,DenyTransacted,BlockDwell,DenyIdentified,AllowVoice,UseGlobalTime,PricePerMeter,TaxFree,AllowDirectTeleport,RedirectGridX,RedirectGridY,ParentEstateID,SunPosition,PublicAccess,EstateSkipScripts,BillableFactor) + values ( 99, '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''); +delete from estate_settings; + +CREATE TABLE estate_users ( + EstateID int(10) NOT NULL, + uuid char(36) NOT NULL +); + +CREATE TABLE estateban ( + EstateID int(10) NOT NULL, + bannedUUID varchar(36) NOT NULL, + bannedIp varchar(16) NOT NULL, + bannedIpHostMask varchar(16) NOT NULL, + bannedNameMask varchar(64) default NULL +); + +CREATE INDEX estate_ban_estate_id on estateban(EstateID); +CREATE INDEX estate_groups_estate_id on estate_groups(EstateID); +CREATE INDEX estate_managers_estate_id on estate_managers(EstateID); +CREATE INDEX estate_map_estate_id on estate_map(EstateID); +CREATE UNIQUE INDEX estate_map_region_id on estate_map(RegionID); +CREATE INDEX estate_users_estate_id on estate_users(EstateID); + +COMMIT; + + +:VERSION 7 + +begin; + +alter table estate_settings add column AbuseEmail varchar(255) not null default ''; + +alter table estate_settings add column EstateOwner varchar(36) not null default ''; + +commit; + +:VERSION 8 + +begin; + +alter table estate_settings add column DenyMinors tinyint not null default 0; + +commit; + +:VERSION 9 + +begin; +alter table estate_settings add column AllowLandmark tinyint not null default '1'; +alter table estate_settings add column AllowParcelChanges tinyint not null default '1'; +alter table estate_settings add column AllowSetHome tinyint not null default '1'; +commit; + diff --git a/OpenSim/Data/SQLite/Resources/FriendsStore.migrations b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations new file mode 100644 index 0000000000..3eb4352a0c --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/FriendsStore.migrations @@ -0,0 +1,20 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE `Friends` ( + `PrincipalID` CHAR(36) NOT NULL, + `Friend` VARCHAR(255) NOT NULL, + `Flags` VARCHAR(16) NOT NULL DEFAULT 0, + `Offered` VARCHAR(32) NOT NULL DEFAULT 0, + PRIMARY KEY(`PrincipalID`, `Friend`)); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO `Friends` SELECT `ownerID`, `friendID`, `friendPerms`, 0 FROM `userfriends`; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations new file mode 100644 index 0000000000..02612ce13a --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/HGTravelStore.migrations @@ -0,0 +1,18 @@ +:VERSION 2 # -------------------------- + +BEGIN; + +CREATE TABLE hg_traveling_data( + SessionID VARCHAR(36) NOT NULL, + UserID VARCHAR(36) NOT NULL, + GridExternalName VARCHAR(255) NOT NULL DEFAULT "", + ServiceToken VARCHAR(255) NOT NULL DEFAULT "", + ClientIPAddress VARCHAR(16) NOT NULL DEFAULT "", + MyIPAddress VARCHAR(16) NOT NULL DEFAULT "", + TMStamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY(SessionID), + UNIQUE(UserID) +); + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/InventoryStore.migrations b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations new file mode 100644 index 0000000000..585ac498e6 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/InventoryStore.migrations @@ -0,0 +1,92 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + UUID varchar(255) primary key, + name varchar(255), + agentID varchar(255), + parentID varchar(255), + type integer, + version integer); + +CREATE TABLE inventoryitems( + UUID varchar(255) primary key, + assetID varchar(255), + assetType integer, + invType integer, + parentFolderID varchar(255), + avatarID varchar(255), + creatorsID varchar(255), + inventoryName varchar(255), + inventoryDescription varchar(255), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(255) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +create index inventoryfolders_agentid on inventoryfolders(agentid); +create index inventoryfolders_parentid on inventoryfolders(parentid); +create index inventoryitems_parentfolderid on inventoryitems(parentfolderid); +create index inventoryitems_avatarid on inventoryitems(avatarid); + +COMMIT; + +:VERSION 3 + +BEGIN; + +alter table inventoryitems add column inventoryGroupPermissions integer unsigned not null default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +update inventoryitems + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update inventoryitems + set assetID = substr(assetID, 1, 8) || "-" || substr(assetID, 9, 4) || "-" || substr(assetID, 13, 4) || "-" || substr(assetID, 17, 4) || "-" || substr(assetID, 21, 12) + where assetID not like '%-%'; + +update inventoryitems + set parentFolderID = substr(parentFolderID, 1, 8) || "-" || substr(parentFolderID, 9, 4) || "-" || substr(parentFolderID, 13, 4) || "-" || substr(parentFolderID, 17, 4) || "-" || substr(parentFolderID, 21, 12) + where parentFolderID not like '%-%'; + +update inventoryitems + set avatarID = substr(avatarID, 1, 8) || "-" || substr(avatarID, 9, 4) || "-" || substr(avatarID, 13, 4) || "-" || substr(avatarID, 17, 4) || "-" || substr(avatarID, 21, 12) + where avatarID not like '%-%'; + +update inventoryitems + set creatorsID = substr(creatorsID, 1, 8) || "-" || substr(creatorsID, 9, 4) || "-" || substr(creatorsID, 13, 4) || "-" || substr(creatorsID, 17, 4) || "-" || substr(creatorsID, 21, 12) + where creatorsID not like '%-%'; + + +update inventoryfolders + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update inventoryfolders + set agentID = substr(agentID, 1, 8) || "-" || substr(agentID, 9, 4) || "-" || substr(agentID, 13, 4) || "-" || substr(agentID, 17, 4) || "-" || substr(agentID, 21, 12) + where agentID not like '%-%'; + +update inventoryfolders + set parentID = substr(parentID, 1, 8) || "-" || substr(parentID, 9, 4) || "-" || substr(parentID, 13, 4) || "-" || substr(parentID, 17, 4) || "-" || substr(parentID, 21, 12) + where parentID not like '%-%'; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/RegionStore.migrations b/OpenSim/Data/SQLite/Resources/RegionStore.migrations new file mode 100644 index 0000000000..901068ff65 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/RegionStore.migrations @@ -0,0 +1,614 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE prims( + UUID varchar(255) primary key, + RegionUUID varchar(255), + ParentID integer, + CreationDate integer, + Name varchar(255), + SceneGroupID varchar(255), + Text varchar(255), + Description varchar(255), + SitName varchar(255), + TouchName varchar(255), + CreatorID varchar(255), + OwnerID varchar(255), + GroupID varchar(255), + LastOwnerID varchar(255), + OwnerMask integer, + NextOwnerMask integer, + GroupMask integer, + EveryoneMask integer, + BaseMask integer, + PositionX float, + PositionY float, + PositionZ float, + GroupPositionX float, + GroupPositionY float, + GroupPositionZ float, + VelocityX float, + VelocityY float, + VelocityZ float, + AngularVelocityX float, + AngularVelocityY float, + AngularVelocityZ float, + AccelerationX float, + AccelerationY float, + AccelerationZ float, + RotationX float, + RotationY float, + RotationZ float, + RotationW float, + ObjectFlags integer, + SitTargetOffsetX float NOT NULL default 0, + SitTargetOffsetY float NOT NULL default 0, + SitTargetOffsetZ float NOT NULL default 0, + SitTargetOrientW float NOT NULL default 0, + SitTargetOrientX float NOT NULL default 0, + SitTargetOrientY float NOT NULL default 0, + SitTargetOrientZ float NOT NULL default 0); + +CREATE TABLE primshapes( + UUID varchar(255) primary key, + Shape integer, + ScaleX float, + ScaleY float, + ScaleZ float, + PCode integer, + PathBegin integer, + PathEnd integer, + PathScaleX integer, + PathScaleY integer, + PathShearX integer, + PathShearY integer, + PathSkew integer, + PathCurve integer, + PathRadiusOffset integer, + PathRevolutions integer, + PathTaperX integer, + PathTaperY integer, + PathTwist integer, + PathTwistBegin integer, + ProfileBegin integer, + ProfileEnd integer, + ProfileCurve integer, + ProfileHollow integer, + Texture blob, + ExtraParams blob, + State Integer NOT NULL default 0); + +CREATE TABLE primitems( + itemID varchar(255) primary key, + primID varchar(255), + assetID varchar(255), + parentFolderID varchar(255), + invType integer, + assetType integer, + name varchar(255), + description varchar(255), + creationDate integer, + creatorID varchar(255), + ownerID varchar(255), + lastOwnerID varchar(255), + groupID varchar(255), + nextPermissions string, + currentPermissions string, + basePermissions string, + everyonePermissions string, + groupPermissions string); + +CREATE TABLE terrain( + RegionUUID varchar(255), + Revision integer, + Heightfield blob); + +CREATE TABLE land( + UUID varchar(255) primary key, + RegionUUID varchar(255), + LocalLandID string, + Bitmap blob, + Name varchar(255), + Desc varchar(255), + OwnerUUID varchar(255), + IsGroupOwned string, + Area integer, + AuctionID integer, + Category integer, + ClaimDate integer, + ClaimPrice integer, + GroupUUID varchar(255), + SalePrice integer, + LandStatus integer, + LandFlags string, + LandingType string, + MediaAutoScale string, + MediaTextureUUID varchar(255), + MediaURL varchar(255), + MusicURL varchar(255), + PassHours float, + PassPrice string, + SnapshotUUID varchar(255), + UserLocationX float, + UserLocationY float, + UserLocationZ float, + UserLookAtX float, + UserLookAtY float, + UserLookAtZ float, + AuthbuyerID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE landaccesslist( + LandUUID varchar(255), + AccessUUID varchar(255), + Flags string); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +CREATE TABLE regionban( + regionUUID varchar (255), + bannedUUID varchar (255), + bannedIp varchar (255), + bannedIpHostMask varchar (255) + ); + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE primitems add flags integer not null default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +create table regionsettings ( + regionUUID char(36) not null, + block_terraform integer not null, + block_fly integer not null, + allow_damage integer not null, + restrict_pushing integer not null, + allow_land_resell integer not null, + allow_land_join_divide integer not null, + block_show_in_search integer not null, + agent_limit integer not null, + object_bonus float not null, + maturity integer not null, + disable_scripts integer not null, + disable_collisions integer not null, + disable_physics integer not null, + terrain_texture_1 char(36) not null, + terrain_texture_2 char(36) not null, + terrain_texture_3 char(36) not null, + terrain_texture_4 char(36) not null, + elevation_1_nw float not null, + elevation_2_nw float not null, + elevation_1_ne float not null, + elevation_2_ne float not null, + elevation_1_se float not null, + elevation_2_se float not null, + elevation_1_sw float not null, + elevation_2_sw float not null, + water_height float not null, + terrain_raise_limit float not null, + terrain_lower_limit float not null, + use_estate_sun integer not null, + fixed_sun integer not null, + sun_position float not null, + covenant char(36)); + +COMMIT; + +:VERSION 5 + +BEGIN; + +delete from regionsettings; + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +drop table regionsettings; +CREATE TABLE regionsettings ( + regionUUID char(36) NOT NULL, + block_terraform int(11) NOT NULL, + block_fly int(11) NOT NULL, + allow_damage int(11) NOT NULL, + restrict_pushing int(11) NOT NULL, + allow_land_resell int(11) NOT NULL, + allow_land_join_divide int(11) NOT NULL, + block_show_in_search int(11) NOT NULL, + agent_limit int(11) NOT NULL, + object_bonus float NOT NULL, + maturity int(11) NOT NULL, + disable_scripts int(11) NOT NULL, + disable_collisions int(11) NOT NULL, + disable_physics int(11) NOT NULL, + terrain_texture_1 char(36) NOT NULL, + terrain_texture_2 char(36) NOT NULL, + terrain_texture_3 char(36) NOT NULL, + terrain_texture_4 char(36) NOT NULL, + elevation_1_nw float NOT NULL, + elevation_2_nw float NOT NULL, + elevation_1_ne float NOT NULL, + elevation_2_ne float NOT NULL, + elevation_1_se float NOT NULL, + elevation_2_se float NOT NULL, + elevation_1_sw float NOT NULL, + elevation_2_sw float NOT NULL, + water_height float NOT NULL, + terrain_raise_limit float NOT NULL, + terrain_lower_limit float NOT NULL, + use_estate_sun int(11) NOT NULL, + fixed_sun int(11) NOT NULL, + sun_position float NOT NULL, + covenant char(36) default NULL, + sandbox tinyint(4) NOT NULL, + PRIMARY KEY (regionUUID) +); + +COMMIT; + +:VERSION 9 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ColorR integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorG integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorB integer not null default 0; +ALTER TABLE prims ADD COLUMN ColorA integer not null default 0; + +COMMIT; + +:VERSION 10 + +BEGIN; + +ALTER TABLE prims ADD COLUMN ClickAction INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 11 + +BEGIN; + +ALTER TABLE prims ADD COLUMN PayPrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton1 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton2 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton3 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN PayButton4 INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN LoopedSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN LoopedSoundGain float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN TextureAnimation string; +ALTER TABLE prims ADD COLUMN ParticleSystem string; +ALTER TABLE prims ADD COLUMN OmegaX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN OmegaZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraEyeOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetX float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetY float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN CameraAtOffsetZ float NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ForceMouselook string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN ScriptAccessPin INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN AllowedDrop INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN DieAtEdge string NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SalePrice INTEGER NOT NULL default 0; +ALTER TABLE prims ADD COLUMN SaleType string NOT NULL default 0; + +COMMIT; + +:VERSION 12 + +BEGIN; + +ALTER TABLE prims ADD COLUMN Material INTEGER NOT NULL default 3; + +COMMIT; + +:VERSION 13 + +BEGIN; + +ALTER TABLE land ADD COLUMN OtherCleanTime INTEGER NOT NULL default 0; +ALTER TABLE land ADD COLUMN Dwell INTEGER NOT NULL default 0; + +COMMIT; + +:VERSION 14 + +begin; + +ALTER TABLE regionsettings ADD COLUMN sunvectorx double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectory double NOT NULL default 0; +ALTER TABLE regionsettings ADD COLUMN sunvectorz double NOT NULL default 0; + +commit; + +:VERSION 15 + +BEGIN; + +ALTER TABLE prims ADD COLUMN CollisionSound varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +ALTER TABLE prims ADD COLUMN CollisionSoundVolume float NOT NULL default 0; + +COMMIT; + +:VERSION 16 + +BEGIN; + +ALTER TABLE prims ADD COLUMN VolumeDetect INTEGER NOT NULL DEFAULT 0; + +COMMIT; + +:VERSION 17 + +BEGIN; +CREATE TEMPORARY TABLE prims_backup(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims_backup SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims; +DROP TABLE prims; +CREATE TABLE prims(UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect); +INSERT INTO prims SELECT UUID,RegionUUID,CreationDate,Name,SceneGroupID,Text,Description,SitName,TouchName,CreatorID,OwnerID,GroupID,LastOwnerID,OwnerMask,NextOwnerMask,GroupMask,EveryoneMask,BaseMask,PositionX,PositionY,PositionZ,GroupPositionX,GroupPositionY,GroupPositionZ,VelocityX,VelocityY,VelocityZ,AngularVelocityX,AngularVelocityY,AngularVelocityZ,AccelerationX,AccelerationY,AccelerationZ,RotationX,RotationY,RotationZ,RotationW,ObjectFlags,SitTargetOffsetX,SitTargetOffsetY,SitTargetOffsetZ,SitTargetOrientW,SitTargetOrientX,SitTargetOrientY,SitTargetOrientZ,ColorR,ColorG,ColorB,ColorA,ClickAction,PayPrice,PayButton1,PayButton2,PayButton3,PayButton4,LoopedSound,LoopedSoundGain,TextureAnimation,ParticleSystem,OmegaX,OmegaY,OmegaZ,CameraEyeOffsetX,CameraEyeOffsetY,CameraEyeOffsetZ,CameraAtOffsetX,CameraAtOffsetY,CameraAtOffsetZ,ForceMouselook,ScriptAccessPin,AllowedDrop,DieAtEdge,SalePrice,SaleType,Material,CollisionSound,CollisionSoundVolume,VolumeDetect FROM prims_backup; +DROP TABLE prims_backup; +COMMIT; + +:VERSION 18 + +BEGIN; + +update terrain + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + + +update landaccesslist + set LandUUID = substr(LandUUID, 1, 8) || "-" || substr(LandUUID, 9, 4) || "-" || substr(LandUUID, 13, 4) || "-" || substr(LandUUID, 17, 4) || "-" || substr(LandUUID, 21, 12) + where LandUUID not like '%-%'; + +update landaccesslist + set AccessUUID = substr(AccessUUID, 1, 8) || "-" || substr(AccessUUID, 9, 4) || "-" || substr(AccessUUID, 13, 4) || "-" || substr(AccessUUID, 17, 4) || "-" || substr(AccessUUID, 21, 12) + where AccessUUID not like '%-%'; + + +update prims + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update prims + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update prims + set SceneGroupID = substr(SceneGroupID, 1, 8) || "-" || substr(SceneGroupID, 9, 4) || "-" || substr(SceneGroupID, 13, 4) || "-" || substr(SceneGroupID, 17, 4) || "-" || substr(SceneGroupID, 21, 12) + where SceneGroupID not like '%-%'; + +update prims + set CreatorID = substr(CreatorID, 1, 8) || "-" || substr(CreatorID, 9, 4) || "-" || substr(CreatorID, 13, 4) || "-" || substr(CreatorID, 17, 4) || "-" || substr(CreatorID, 21, 12) + where CreatorID not like '%-%'; + +update prims + set OwnerID = substr(OwnerID, 1, 8) || "-" || substr(OwnerID, 9, 4) || "-" || substr(OwnerID, 13, 4) || "-" || substr(OwnerID, 17, 4) || "-" || substr(OwnerID, 21, 12) + where OwnerID not like '%-%'; + +update prims + set GroupID = substr(GroupID, 1, 8) || "-" || substr(GroupID, 9, 4) || "-" || substr(GroupID, 13, 4) || "-" || substr(GroupID, 17, 4) || "-" || substr(GroupID, 21, 12) + where GroupID not like '%-%'; + +update prims + set LastOwnerID = substr(LastOwnerID, 1, 8) || "-" || substr(LastOwnerID, 9, 4) || "-" || substr(LastOwnerID, 13, 4) || "-" || substr(LastOwnerID, 17, 4) || "-" || substr(LastOwnerID, 21, 12) + where LastOwnerID not like '%-%'; + + +update primshapes + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + + +update land + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update land + set RegionUUID = substr(RegionUUID, 1, 8) || "-" || substr(RegionUUID, 9, 4) || "-" || substr(RegionUUID, 13, 4) || "-" || substr(RegionUUID, 17, 4) || "-" || substr(RegionUUID, 21, 12) + where RegionUUID not like '%-%'; + +update land + set OwnerUUID = substr(OwnerUUID, 1, 8) || "-" || substr(OwnerUUID, 9, 4) || "-" || substr(OwnerUUID, 13, 4) || "-" || substr(OwnerUUID, 17, 4) || "-" || substr(OwnerUUID, 21, 12) + where OwnerUUID not like '%-%'; + +update land + set GroupUUID = substr(GroupUUID, 1, 8) || "-" || substr(GroupUUID, 9, 4) || "-" || substr(GroupUUID, 13, 4) || "-" || substr(GroupUUID, 17, 4) || "-" || substr(GroupUUID, 21, 12) + where GroupUUID not like '%-%'; + +update land + set MediaTextureUUID = substr(MediaTextureUUID, 1, 8) || "-" || substr(MediaTextureUUID, 9, 4) || "-" || substr(MediaTextureUUID, 13, 4) || "-" || substr(MediaTextureUUID, 17, 4) || "-" || substr(MediaTextureUUID, 21, 12) + where MediaTextureUUID not like '%-%'; + +update land + set SnapshotUUID = substr(SnapshotUUID, 1, 8) || "-" || substr(SnapshotUUID, 9, 4) || "-" || substr(SnapshotUUID, 13, 4) || "-" || substr(SnapshotUUID, 17, 4) || "-" || substr(SnapshotUUID, 21, 12) + where SnapshotUUID not like '%-%'; + +update land + set AuthbuyerID = substr(AuthbuyerID, 1, 8) || "-" || substr(AuthbuyerID, 9, 4) || "-" || substr(AuthbuyerID, 13, 4) || "-" || substr(AuthbuyerID, 17, 4) || "-" || substr(AuthbuyerID, 21, 12) + where AuthbuyerID not like '%-%'; + +COMMIT; + +:VERSION 19 +BEGIN; +ALTER TABLE regionsettings ADD COLUMN map_tile_ID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; +COMMIT; + +:VERSION 20 +BEGIN; +ALTER TABLE prims ADD COLUMN MediaURL varchar(255); +ALTER TABLE primshapes ADD COLUMN Media TEXT; +COMMIT; + +:VERSION 21 +BEGIN; +ALTER TABLE `land` ADD COLUMN `MediaType` VARCHAR(32) NOT NULL DEFAULT 'none/none'; +ALTER TABLE `land` ADD COLUMN `MediaDescription` VARCHAR(255) NOT NULL DEFAULT ''; +ALTER TABLE `land` ADD COLUMN `MediaSize` VARCHAR(16) NOT NULL DEFAULT '0,0'; +ALTER TABLE `land` ADD COLUMN `MediaLoop` BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE `land` ADD COLUMN `ObscureMusic` BOOLEAN NOT NULL DEFAULT FALSE; +ALTER TABLE `land` ADD COLUMN `ObscureMedia` BOOLEAN NOT NULL DEFAULT FALSE; +COMMIT; + +:VERSION 22 +BEGIN; +ALTER TABLE regionsettings ADD COLUMN covenant_datetime INTEGER NOT NULL default 0; +COMMIT; + +:VERSION 23 +BEGIN; +CREATE TABLE regionwindlight ( + region_id VARCHAR(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000' PRIMARY KEY, + water_color_r FLOAT NOT NULL DEFAULT '4.000000', + water_color_g FLOAT NOT NULL DEFAULT '38.000000', + water_color_b FLOAT NOT NULL DEFAULT '64.000000', + water_color_i FLOAT NOT NULL DEFAULT '1.000000', + water_fog_density_exponent FLOAT NOT NULL DEFAULT '4.0', + underwater_fog_modifier FLOAT NOT NULL DEFAULT '0.25', + reflection_wavelet_scale_1 FLOAT NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_2 FLOAT NOT NULL DEFAULT '2.0', + reflection_wavelet_scale_3 FLOAT NOT NULL DEFAULT '2.0', + fresnel_scale FLOAT NOT NULL DEFAULT '0.40', + fresnel_offset FLOAT NOT NULL DEFAULT '0.50', + refract_scale_above FLOAT NOT NULL DEFAULT '0.03', + refract_scale_below FLOAT NOT NULL DEFAULT '0.20', + blur_multiplier FLOAT NOT NULL DEFAULT '0.040', + big_wave_direction_x FLOAT NOT NULL DEFAULT '1.05', + big_wave_direction_y FLOAT NOT NULL DEFAULT '-0.42', + little_wave_direction_x FLOAT NOT NULL DEFAULT '1.11', + little_wave_direction_y FLOAT NOT NULL DEFAULT '-1.16', + normal_map_texture VARCHAR(36) NOT NULL DEFAULT '822ded49-9a6c-f61c-cb89-6df54f42cdf4', + horizon_r FLOAT NOT NULL DEFAULT '0.25', + horizon_g FLOAT NOT NULL DEFAULT '0.25', + horizon_b FLOAT NOT NULL DEFAULT '0.32', + horizon_i FLOAT NOT NULL DEFAULT '0.32', + haze_horizon FLOAT NOT NULL DEFAULT '0.19', + blue_density_r FLOAT NOT NULL DEFAULT '0.12', + blue_density_g FLOAT NOT NULL DEFAULT '0.22', + blue_density_b FLOAT NOT NULL DEFAULT '0.38', + blue_density_i FLOAT NOT NULL DEFAULT '0.38', + haze_density FLOAT NOT NULL DEFAULT '0.70', + density_multiplier FLOAT NOT NULL DEFAULT '0.18', + distance_multiplier FLOAT NOT NULL DEFAULT '0.8', + max_altitude INTEGER NOT NULL DEFAULT '1605', + sun_moon_color_r FLOAT NOT NULL DEFAULT '0.24', + sun_moon_color_g FLOAT NOT NULL DEFAULT '0.26', + sun_moon_color_b FLOAT NOT NULL DEFAULT '0.30', + sun_moon_color_i FLOAT NOT NULL DEFAULT '0.30', + sun_moon_position FLOAT NOT NULL DEFAULT '0.317', + ambient_r FLOAT NOT NULL DEFAULT '0.35', + ambient_g FLOAT NOT NULL DEFAULT '0.35', + ambient_b FLOAT NOT NULL DEFAULT '0.35', + ambient_i FLOAT NOT NULL DEFAULT '0.35', + east_angle FLOAT NOT NULL DEFAULT '0.00', + sun_glow_focus FLOAT NOT NULL DEFAULT '0.10', + sun_glow_size FLOAT NOT NULL DEFAULT '1.75', + scene_gamma FLOAT NOT NULL DEFAULT '1.00', + star_brightness FLOAT NOT NULL DEFAULT '0.00', + cloud_color_r FLOAT NOT NULL DEFAULT '0.41', + cloud_color_g FLOAT NOT NULL DEFAULT '0.41', + cloud_color_b FLOAT NOT NULL DEFAULT '0.41', + cloud_color_i FLOAT NOT NULL DEFAULT '0.41', + cloud_x FLOAT NOT NULL DEFAULT '1.00', + cloud_y FLOAT NOT NULL DEFAULT '0.53', + cloud_density FLOAT NOT NULL DEFAULT '1.00', + cloud_coverage FLOAT NOT NULL DEFAULT '0.27', + cloud_scale FLOAT NOT NULL DEFAULT '0.42', + cloud_detail_x FLOAT NOT NULL DEFAULT '1.00', + cloud_detail_y FLOAT NOT NULL DEFAULT '0.53', + cloud_detail_density FLOAT NOT NULL DEFAULT '0.12', + cloud_scroll_x FLOAT NOT NULL DEFAULT '0.20', + cloud_scroll_x_lock INTEGER NOT NULL DEFAULT '0', + cloud_scroll_y FLOAT NOT NULL DEFAULT '0.01', + cloud_scroll_y_lock INTEGER NOT NULL DEFAULT '0', + draw_classic_clouds INTEGER NOT NULL DEFAULT '1'); + +COMMIT; + + +:VERSION 24 + +BEGIN; + +CREATE TABLE IF NOT EXISTS `spawn_points` ( + `RegionID` varchar(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000', + `Yaw` float NOT NULL, + `Pitch` float NOT NULL, + `Distance` float NOT NULL +); + +ALTER TABLE `regionsettings` ADD COLUMN `TelehubObject` varchar(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 25 + +BEGIN; +ALTER TABLE `regionsettings` ADD COLUMN `parcel_tile_ID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'; +COMMIT; + +:VERSION 26 + +BEGIN; + +CREATE TABLE `regionenvironment` ( + `region_id` varchar(36) NOT NULL DEFAULT '000000-0000-0000-0000-000000000000' PRIMARY KEY, + `llsd_settings` TEXT NOT NULL +); + +COMMIT; + +:VERSION 27 +BEGIN; +ALTER TABLE prims ADD COLUMN DynAttrs TEXT; +COMMIT; + +:VERSION 28 + +BEGIN; + +ALTER TABLE prims ADD COLUMN `PhysicsShapeType` tinyint(4) NOT NULL default '0'; +ALTER TABLE prims ADD COLUMN `Density` double NOT NULL default '1000'; +ALTER TABLE prims ADD COLUMN `GravityModifier` double NOT NULL default '1'; +ALTER TABLE prims ADD COLUMN `Friction` double NOT NULL default '0.6'; +ALTER TABLE prims ADD COLUMN `Restitution` double NOT NULL default '0.5'; + +COMMIT; + +:VERSION 29 #---------------- Keyframes + +BEGIN; + +ALTER TABLE prims ADD COLUMN `KeyframeMotion` blob; + +COMMIT; + +:VERSION 30 #---------------- Save Attachment info + +BEGIN; + +ALTER TABLE prims ADD COLUMN AttachedPosX double default '0'; +ALTER TABLE prims ADD COLUMN AttachedPosY double default '0'; +ALTER TABLE prims ADD COLUMN AttachedPosZ double default '0'; +ALTER TABLE primshapes ADD COLUMN LastAttachPoint int not null default '0'; + +COMMIT; + diff --git a/OpenSim/Data/SQLite/Resources/UserAccount.migrations b/OpenSim/Data/SQLite/Resources/UserAccount.migrations new file mode 100644 index 0000000000..854fe694c2 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserAccount.migrations @@ -0,0 +1,27 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +-- useraccounts table +CREATE TABLE UserAccounts ( + PrincipalID CHAR(36) primary key, + ScopeID CHAR(36) NOT NULL, + FirstName VARCHAR(64) NOT NULL, + LastName VARCHAR(64) NOT NULL, + Email VARCHAR(64), + ServiceURLs TEXT, + Created INT(11), + UserLevel integer NOT NULL DEFAULT 0, + UserFlags integer NOT NULL DEFAULT 0, + UserTitle varchar(64) NOT NULL DEFAULT '' +); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO UserAccounts (PrincipalID, ScopeID, FirstName, LastName, Email, ServiceURLs, Created) SELECT `UUID` AS PrincipalID, '00000000-0000-0000-0000-000000000000' AS ScopeID, username AS FirstName, surname AS LastName, '' as Email, '' AS ServiceURLs, created as Created FROM users; + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/UserProfiles.migrations b/OpenSim/Data/SQLite/Resources/UserProfiles.migrations new file mode 100644 index 0000000000..86434e8170 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserProfiles.migrations @@ -0,0 +1,102 @@ +:VERSION 1 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS classifieds ( + classifieduuid char(36) NOT NULL PRIMARY KEY, + creatoruuid char(36) NOT NULL, + creationdate int(20) NOT NULL, + expirationdate int(20) NOT NULL, + category varchar(20) NOT NULL, + name varchar(255) NOT NULL, + description text NOT NULL, + parceluuid char(36) NOT NULL, + parentestate int(11) NOT NULL, + snapshotuuid char(36) NOT NULL, + simname varchar(255) NOT NULL, + posglobal varchar(255) NOT NULL, + parcelname varchar(255) NOT NULL, + classifiedflags int(8) NOT NULL, + priceforlisting int(5) NOT NULL +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS usernotes ( + useruuid varchar(36) NOT NULL, + targetuuid varchar(36) NOT NULL, + notes text NOT NULL, + UNIQUE (useruuid,targetuuid) ON CONFLICT REPLACE +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS userpicks ( + pickuuid varchar(36) NOT NULL PRIMARY KEY, + creatoruuid varchar(36) NOT NULL, + toppick int NOT NULL, + parceluuid varchar(36) NOT NULL, + name varchar(255) NOT NULL, + description text NOT NULL, + snapshotuuid varchar(36) NOT NULL, + user varchar(255) NOT NULL, + originalname varchar(255) NOT NULL, + simname varchar(255) NOT NULL, + posglobal varchar(255) NOT NULL, + sortorder int(2) NOT NULL, + enabled int NOT NULL +); + +commit; + +begin; + +CREATE TABLE IF NOT EXISTS userprofile ( + useruuid varchar(36) NOT NULL PRIMARY KEY, + profilePartner varchar(36) NOT NULL, + profileAllowPublish binary(1) NOT NULL, + profileMaturePublish binary(1) NOT NULL, + profileURL varchar(255) NOT NULL, + profileWantToMask int(3) NOT NULL, + profileWantToText text NOT NULL, + profileSkillsMask int(3) NOT NULL, + profileSkillsText text NOT NULL, + profileLanguages text NOT NULL, + profileImage varchar(36) NOT NULL, + profileAboutText text NOT NULL, + profileFirstImage varchar(36) NOT NULL, + profileFirstText text NOT NULL +); + +commit; + +:VERSION 2 # ------------------------------- + +begin; + +CREATE TABLE IF NOT EXISTS userdata ( + UserId char(36) NOT NULL, + TagId varchar(64) NOT NULL, + DataKey varchar(255), + DataVal varchar(255), + PRIMARY KEY (UserId,TagId) +); + +commit; + + +:VERSION 3 # ------------------------------- + +begin; +CREATE TABLE IF NOT EXISTS usersettings ( + useruuid char(36) NOT NULL, + imviaemail binary(1) NOT NULL, + visible binary(1) NOT NULL, + email varchar(254) NOT NULL, + PRIMARY KEY (useruuid) +) +commit; \ No newline at end of file diff --git a/OpenSim/Data/SQLite/Resources/UserStore.migrations b/OpenSim/Data/SQLite/Resources/UserStore.migrations new file mode 100644 index 0000000000..73d35e83c3 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/UserStore.migrations @@ -0,0 +1,169 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +-- users table +CREATE TABLE users( + UUID varchar(255) primary key, + username varchar(255), + surname varchar(255), + passwordHash varchar(255), + passwordSalt varchar(255), + homeRegionX integer, + homeRegionY integer, + homeLocationX float, + homeLocationY float, + homeLocationZ float, + homeLookAtX float, + homeLookAtY float, + homeLookAtZ float, + created integer, + lastLogin integer, + rootInventoryFolderID varchar(255), + userInventoryURI varchar(255), + userAssetURI varchar(255), + profileCanDoMask integer, + profileWantDoMask integer, + profileAboutText varchar(255), + profileFirstText varchar(255), + profileImage varchar(255), + profileFirstImage varchar(255), + webLoginKey text default '00000000-0000-0000-0000-000000000000'); +-- friends table +CREATE TABLE userfriends( + ownerID varchar(255), + friendID varchar(255), + friendPerms integer, + ownerPerms integer, + datetimestamp integer); + +COMMIT; + +:VERSION 2 + +BEGIN; + +ALTER TABLE users add homeRegionID varchar(36) NOT NULL default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 3 + +BEGIN; + +ALTER TABLE users add userFlags integer NOT NULL default 0; +ALTER TABLE users add godLevel integer NOT NULL default 0; + +COMMIT; + +:VERSION 4 + +BEGIN; + +ALTER TABLE users add customType varchar(32) not null default ''; +ALTER TABLE users add partner char(36) not null default '00000000-0000-0000-0000-000000000000'; + +COMMIT; + +:VERSION 5 + +BEGIN; + +CREATE TABLE `avatarattachments` (`UUID` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `attachpoint` int(11) NOT NULL DEFAULT 0, `item` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000', `asset` char(36) NOT NULL DEFAULT '00000000-0000-0000-0000-000000000000'); + +COMMIT; + +:VERSION 6 + +BEGIN TRANSACTION; + +-- usersagents table +CREATE TABLE IF NOT EXISTS useragents( + UUID varchar(255) primary key, + agentIP varchar(255), + agentPort integer, + agentOnline boolean, + sessionID varchar(255), + secureSessionID varchar(255), + regionID varchar(255), + loginTime integer, + logoutTime integer, + currentRegion varchar(255), + currentHandle varchar(255), + currentPosX float, + currentPosY float, + currentPosZ float); + +COMMIT; + +:VERSION 7 + +BEGIN TRANSACTION; + +ALTER TABLE useragents add currentLookAtX float not null default 128; +ALTER TABLE useragents add currentLookAtY float not null default 128; +ALTER TABLE useragents add currentLookAtZ float not null default 70; + +COMMIT; + +:VERSION 8 + +BEGIN TRANSACTION; + +ALTER TABLE users add email varchar(250); + +COMMIT; + +:VERSION 9 + +BEGIN; + +update users + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +update useragents + set UUID = substr(UUID, 1, 8) || "-" || substr(UUID, 9, 4) || "-" || substr(UUID, 13, 4) || "-" || substr(UUID, 17, 4) || "-" || substr(UUID, 21, 12) + where UUID not like '%-%'; + +COMMIT; + +:VERSION 10 + +BEGIN TRANSACTION; + +CREATE TABLE IF NOT EXISTS avatarappearance( + Owner varchar(36) NOT NULL primary key, + BodyItem varchar(36) DEFAULT NULL, + BodyAsset varchar(36) DEFAULT NULL, + SkinItem varchar(36) DEFAULT NULL, + SkinAsset varchar(36) DEFAULT NULL, + HairItem varchar(36) DEFAULT NULL, + HairAsset varchar(36) DEFAULT NULL, + EyesItem varchar(36) DEFAULT NULL, + EyesAsset varchar(36) DEFAULT NULL, + ShirtItem varchar(36) DEFAULT NULL, + ShirtAsset varchar(36) DEFAULT NULL, + PantsItem varchar(36) DEFAULT NULL, + PantsAsset varchar(36) DEFAULT NULL, + ShoesItem varchar(36) DEFAULT NULL, + ShoesAsset varchar(36) DEFAULT NULL, + SocksItem varchar(36) DEFAULT NULL, + SocksAsset varchar(36) DEFAULT NULL, + JacketItem varchar(36) DEFAULT NULL, + JacketAsset varchar(36) DEFAULT NULL, + GlovesItem varchar(36) DEFAULT NULL, + GlovesAsset varchar(36) DEFAULT NULL, + UnderShirtItem varchar(36) DEFAULT NULL, + UnderShirtAsset varchar(36) DEFAULT NULL, + UnderPantsItem varchar(36) DEFAULT NULL, + UnderPantsAsset varchar(36) DEFAULT NULL, + SkirtItem varchar(36) DEFAULT NULL, + SkirtAsset varchar(36) DEFAULT NULL, + Texture blob, + VisualParams blob, + Serial int DEFAULT NULL, + AvatarHeight float DEFAULT NULL +); + +COMMIT; diff --git a/OpenSim/Data/SQLite/Resources/XInventoryStore.migrations b/OpenSim/Data/SQLite/Resources/XInventoryStore.migrations new file mode 100644 index 0000000000..de44982710 --- /dev/null +++ b/OpenSim/Data/SQLite/Resources/XInventoryStore.migrations @@ -0,0 +1,49 @@ +:VERSION 1 + +BEGIN TRANSACTION; + +CREATE TABLE inventoryfolders( + folderName varchar(64), + type integer, + version integer, + folderID varchar(36) primary key, + agentID varchar(36) not null default '00000000-0000-0000-0000-000000000000', + parentFolderID varchar(36) not null default '00000000-0000-0000-0000-000000000000'); + +CREATE TABLE inventoryitems( + assetID varchar(36), + assetType integer, + inventoryName varchar(64), + inventoryDescription varchar(128), + inventoryNextPermissions integer, + inventoryCurrentPermissions integer, + invType integer, + creatorID varchar(128), + inventoryBasePermissions integer, + inventoryEveryOnePermissions integer, + salePrice integer default 99, + saleType integer default 0, + creationDate integer default 2000, + groupID varchar(36) default '00000000-0000-0000-0000-000000000000', + groupOwned integer default 0, + flags integer default 0, + inventoryID varchar(36) primary key, + parentFolderID varchar(36) not null default '00000000-0000-0000-0000-000000000000', + avatarID varchar(36) not null default '00000000-0000-0000-0000-000000000000', + inventoryGroupPermissions integer not null default 0); + +create index inventoryfolders_agentid on inventoryfolders(agentID); +create index inventoryfolders_parentid on inventoryfolders(parentFolderID); +create index inventoryitems_parentfolderid on inventoryitems(parentFolderID); +create index inventoryitems_avatarid on inventoryitems(avatarID); + +COMMIT; + +:VERSION 2 + +BEGIN TRANSACTION; + +INSERT INTO inventoryfolders (folderName, type, version, folderID, agentID, parentFolderID) SELECT `name` AS folderName, `type` AS type, `version` AS version, `UUID` AS folderID, `agentID` AS agentID, `parentID` AS parentFolderID from old.inventoryfolders; +INSERT INTO inventoryitems (assetID, assetType, inventoryName, inventoryDescription, inventoryNextPermissions, inventoryCurrentPermissions, invType, creatorID, inventoryBasePermissions, inventoryEveryOnePermissions, salePrice, saleType, creationDate, groupID, groupOwned, flags, inventoryID, parentFolderID, avatarID, inventoryGroupPermissions) SELECT `assetID`, `assetType` AS assetType, `inventoryName` AS inventoryName, `inventoryDescription` AS inventoryDescription, `inventoryNextPermissions` AS inventoryNextPermissions, `inventoryCurrentPermissions` AS inventoryCurrentPermissions, `invType` AS invType, `creatorsID` AS creatorID, `inventoryBasePermissions` AS inventoryBasePermissions, `inventoryEveryOnePermissions` AS inventoryEveryOnePermissions, `salePrice` AS salePrice, `saleType` AS saleType, `creationDate` AS creationDate, `groupID` AS groupID, `groupOwned` AS groupOwned, `flags` AS flags, `UUID` AS inventoryID, `parentFolderID` AS parentFolderID, `avatarID` AS avatarID, `inventoryGroupPermissions` AS inventoryGroupPermissions FROM old.inventoryitems; + +COMMIT; diff --git a/OpenSim/Data/SQLite/SQLiteAgentPreferencesData.cs b/OpenSim/Data/SQLite/SQLiteAgentPreferencesData.cs new file mode 100644 index 0000000000..d22393d85d --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteAgentPreferencesData.cs @@ -0,0 +1,60 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + public class SQLiteAgentPreferencesData : SQLiteGenericTableHandler, IAgentPreferencesData + { + public SQLiteAgentPreferencesData(string connectionString, string realm) + : base(connectionString, realm, "AgentPrefs") + { + } + + public AgentPreferencesData GetPrefs(UUID agentID) + { + AgentPreferencesData[] ret = Get("PrincipalID", agentID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + } +} diff --git a/OpenSim/Data/SQLite/SQLiteAssetData.cs b/OpenSim/Data/SQLite/SQLiteAssetData.cs new file mode 100644 index 0000000000..f0dda64699 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteAssetData.cs @@ -0,0 +1,404 @@ +/* + * 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.Data; +using System.Reflection; +using System.Collections.Generic; +using log4net; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// An asset storage interface for the SQLite database system + /// + public class SQLiteAssetData : AssetDataBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const string SelectAssetSQL = "select * from assets where UUID=:UUID"; + private const string SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, asset_flags, UUID, CreatorID from assets limit :start, :count"; + private const string DeleteAssetSQL = "delete from assets where UUID=:UUID"; + private const string InsertAssetSQL = "insert into assets(UUID, Name, Description, Type, Local, Temporary, asset_flags, CreatorID, Data) values(:UUID, :Name, :Description, :Type, :Local, :Temporary, :Flags, :CreatorID, :Data)"; + private const string UpdateAssetSQL = "update assets set Name=:Name, Description=:Description, Type=:Type, Local=:Local, Temporary=:Temporary, asset_flags=:Flags, CreatorID=:CreatorID, Data=:Data where UUID=:UUID"; + private const string assetSelect = "select * from assets"; + + private SqliteConnection m_conn; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + override public void Dispose() + { + if (m_conn != null) + { + m_conn.Close(); + m_conn = null; + } + } + + /// + /// + /// Initialises AssetData interface + /// Loads and initialises a new SQLite connection and maintains it. + /// use default URI if connect string is empty. + /// + /// + /// connect string + override public void Initialise(string dbconnect) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + if (dbconnect == string.Empty) + { + dbconnect = "URI=file:Asset.db,version=3"; + } + m_conn = new SqliteConnection(dbconnect); + m_conn.Open(); + + Migration m = new Migration(m_conn, Assembly, "AssetStore"); + m.Update(); + + return; + } + + /// + /// Fetch Asset + /// + /// UUID of ... ? + /// Asset base + override public AssetBase GetAsset(UUID uuid) + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + using (IDataReader reader = cmd.ExecuteReader()) + { + if (reader.Read()) + { + AssetBase asset = buildAsset(reader); + reader.Close(); + return asset; + } + else + { + reader.Close(); + return null; + } + } + } + } + } + + /// + /// Create an asset + /// + /// Asset Base + override public void StoreAsset(AssetBase asset) + { + string assetName = asset.Name; + if (asset.Name.Length > AssetBase.MAX_ASSET_NAME) + { + assetName = asset.Name.Substring(0, AssetBase.MAX_ASSET_NAME); + m_log.WarnFormat( + "[ASSET DB]: Name '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Name, asset.ID, asset.Name.Length, assetName.Length); + } + + string assetDescription = asset.Description; + if (asset.Description.Length > AssetBase.MAX_ASSET_DESC) + { + assetDescription = asset.Description.Substring(0, AssetBase.MAX_ASSET_DESC); + m_log.WarnFormat( + "[ASSET DB]: Description '{0}' for asset {1} truncated from {2} to {3} characters on add", + asset.Description, asset.ID, asset.Description.Length, assetDescription.Length); + } + + //m_log.Info("[ASSET DB]: Creating Asset " + asset.FullID.ToString()); + if (AssetsExist(new[] { asset.FullID })[0]) + { + //LogAssetLoad(asset); + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(UpdateAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Name", assetName)); + cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription)); + cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); + cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); + cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); + cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID)); + cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); + + cmd.ExecuteNonQuery(); + } + } + } + else + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(InsertAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Name", assetName)); + cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription)); + cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type)); + cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local)); + cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary)); + cmd.Parameters.Add(new SqliteParameter(":Flags", asset.Flags)); + cmd.Parameters.Add(new SqliteParameter(":CreatorID", asset.Metadata.CreatorID)); + cmd.Parameters.Add(new SqliteParameter(":Data", asset.Data)); + + cmd.ExecuteNonQuery(); + } + } + } + } + +// /// +// /// Some... logging functionnality +// /// +// /// +// private static void LogAssetLoad(AssetBase asset) +// { +// string temporary = asset.Temporary ? "Temporary" : "Stored"; +// string local = asset.Local ? "Local" : "Remote"; +// +// int assetLength = (asset.Data != null) ? asset.Data.Length : 0; +// +// m_log.Debug("[ASSET DB]: " + +// string.Format("Loaded {5} {4} Asset: [{0}][{3}] \"{1}\":{2} ({6} bytes)", +// asset.FullID, asset.Name, asset.Description, asset.Type, +// temporary, local, assetLength)); +// } + + /// + /// Check if the assets exist in the database. + /// + /// The assets' IDs + /// For each asset: true if it exists, false otherwise + public override bool[] AssetsExist(UUID[] uuids) + { + if (uuids.Length == 0) + return new bool[0]; + + HashSet exist = new HashSet(); + + string ids = "'" + string.Join("','", uuids) + "'"; + string sql = string.Format("select UUID from assets where UUID in ({0})", ids); + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) + { + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + UUID id = new UUID((string)reader["UUID"]); + exist.Add(id); + } + } + } + } + + bool[] results = new bool[uuids.Length]; + for (int i = 0; i < uuids.Length; i++) + results[i] = exist.Contains(uuids[i]); + return results; + } + + /// + /// + /// + /// + /// + private static AssetBase buildAsset(IDataReader row) + { + // TODO: this doesn't work yet because something more + // interesting has to be done to actually get these values + // back out. Not enough time to figure it out yet. + AssetBase asset = new AssetBase( + new UUID((String)row["UUID"]), + (String)row["Name"], + Convert.ToSByte(row["Type"]), + (String)row["CreatorID"] + ); + + asset.Description = (String) row["Description"]; + asset.Local = Convert.ToBoolean(row["Local"]); + asset.Temporary = Convert.ToBoolean(row["Temporary"]); + asset.Flags = (AssetFlags)Convert.ToInt32(row["asset_flags"]); + asset.Data = (byte[])row["Data"]; + return asset; + } + + private static AssetMetadata buildAssetMetadata(IDataReader row) + { + AssetMetadata metadata = new AssetMetadata(); + + metadata.FullID = new UUID((string) row["UUID"]); + metadata.Name = (string) row["Name"]; + metadata.Description = (string) row["Description"]; + metadata.Type = Convert.ToSByte(row["Type"]); + metadata.Temporary = Convert.ToBoolean(row["Temporary"]); // Not sure if this is correct. + metadata.Flags = (AssetFlags)Convert.ToInt32(row["asset_flags"]); + metadata.CreatorID = row["CreatorID"].ToString(); + + // Current SHA1s are not stored/computed. + metadata.SHA1 = new byte[] {}; + + return metadata; + } + + /// + /// Returns a list of AssetMetadata objects. The list is a subset of + /// the entire data set offset by containing + /// elements. + /// + /// The number of results to discard from the total data set. + /// The number of rows the returned list should contain. + /// A list of AssetMetadata objects. + public override List FetchAssetMetadataSet(int start, int count) + { + List retList = new List(count); + + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(SelectAssetMetadataSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":start", start)); + cmd.Parameters.Add(new SqliteParameter(":count", count)); + + using (IDataReader reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + AssetMetadata metadata = buildAssetMetadata(reader); + retList.Add(metadata); + } + } + } + } + + return retList; + } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + #region IPlugin interface + + /// + /// + /// + override public string Version + { + get + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } + + /// + /// Initialise the AssetData interface using default URI + /// + override public void Initialise() + { + Initialise("URI=file:Asset.db,version=3"); + } + + /// + /// Name of this DB provider + /// + override public string Name + { + get { return "SQLite Asset storage engine"; } + } + + // TODO: (AlexRa): one of these is to be removed eventually (?) + + /// + /// Delete an asset from database + /// + /// + public bool DeleteAsset(UUID uuid) + { + lock (this) + { + using (SqliteCommand cmd = new SqliteCommand(DeleteAssetSQL, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString())); + cmd.ExecuteNonQuery(); + } + } + + return true; + } + + public override bool Delete(string id) + { + UUID assetID; + + if (!UUID.TryParse(id, out assetID)) + return false; + + return DeleteAsset(assetID); + } + + #endregion + } +} diff --git a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs new file mode 100644 index 0000000000..0428c11f76 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs @@ -0,0 +1,259 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + public class SQLiteAuthenticationData : SQLiteFramework, IAuthenticationData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_Realm; + private List m_ColumnNames; + private int m_LastExpire; + + protected static SqliteConnection m_Connection; + private static bool m_initialized = false; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteAuthenticationData(string connectionString, string realm) + : base(connectionString) + { + m_Realm = realm; + + if (!m_initialized) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_Connection = new SqliteConnection(connectionString); + m_Connection.Open(); + + Migration m = new Migration(m_Connection, Assembly, "AuthStore"); + m.Update(); + + m_initialized = true; + } + } + + public AuthenticationData Get(UUID principalID) + { + AuthenticationData ret = new AuthenticationData(); + ret.Data = new Dictionary(); + IDataReader result; + + using (SqliteCommand cmd = new SqliteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID")) + { + cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString())); + + result = ExecuteReader(cmd, m_Connection); + } + + try + { + if (result.Read()) + { + ret.PrincipalID = principalID; + + if (m_ColumnNames == null) + { + m_ColumnNames = new List(); + + DataTable schemaTable = result.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + + foreach (string s in m_ColumnNames) + { + if (s == "UUID") + continue; + + ret.Data[s] = result[s].ToString(); + } + + return ret; + } + else + { + return null; + } + } + catch + { + } + + return null; + } + + public bool Store(AuthenticationData data) + { + if (data.Data.ContainsKey("UUID")) + data.Data.Remove("UUID"); + + string[] fields = new List(data.Data.Keys).ToArray(); + string[] values = new string[data.Data.Count]; + int i = 0; + foreach (object o in data.Data.Values) + values[i++] = o.ToString(); + + using (SqliteCommand cmd = new SqliteCommand()) + { + if (Get(data.PrincipalID) != null) + { + + + string update = "update `" + m_Realm + "` set "; + bool first = true; + foreach (string field in fields) + { + if (!first) + update += ", "; + update += "`" + field + "` = :" + field; + cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field])); + + first = false; + } + + update += " where UUID = :UUID"; + cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString())); + + cmd.CommandText = update; + try + { + if (ExecuteNonQuery(cmd, m_Connection) < 1) + { + //CloseCommand(cmd); + return false; + } + } + catch (Exception e) + { + m_log.Error("[SQLITE]: Exception storing authentication data", e); + //CloseCommand(cmd); + return false; + } + } + else + { + string insert = "insert into `" + m_Realm + "` (`UUID`, `" + + String.Join("`, `", fields) + + "`) values (:UUID, :" + String.Join(", :", fields) + ")"; + + cmd.Parameters.Add(new SqliteParameter(":UUID", data.PrincipalID.ToString())); + foreach (string field in fields) + cmd.Parameters.Add(new SqliteParameter(":" + field, data.Data[field])); + + cmd.CommandText = insert; + + try + { + if (ExecuteNonQuery(cmd, m_Connection) < 1) + { + return false; + } + } + catch (Exception e) + { + Console.WriteLine(e.ToString()); + return false; + } + } + } + + return true; + } + + public bool SetDataItem(UUID principalID, string item, string value) + { + using (SqliteCommand cmd = new SqliteCommand("update `" + m_Realm + + "` set `" + item + "` = " + value + " where UUID = '" + principalID.ToString() + "'")) + { + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + } + + return false; + } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + using (SqliteCommand cmd = new SqliteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() + + "', '" + token + "', datetime('now', 'localtime', '+" + lifetime.ToString() + " minutes'))")) + { + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + } + + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + using (SqliteCommand cmd = new SqliteCommand("update tokens set validity = datetime('now', 'localtime', '+" + lifetime.ToString() + + " minutes') where UUID = '" + principalID.ToString() + "' and token = '" + token + "' and validity > datetime('now', 'localtime')")) + { + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + } + + return false; + } + + private void DoExpire() + { + using (SqliteCommand cmd = new SqliteCommand("delete from tokens where validity < datetime('now', 'localtime')")) + ExecuteNonQuery(cmd, m_Connection); + + m_LastExpire = System.Environment.TickCount; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteAvatarData.cs b/OpenSim/Data/SQLite/SQLiteAvatarData.cs new file mode 100644 index 0000000000..c6d615be14 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteAvatarData.cs @@ -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; +using System.Collections.Generic; +using System.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQLite Interface for Avatar Data + /// + public class SQLiteAvatarData : SQLiteGenericTableHandler, + IAvatarData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteAvatarData(string connectionString, string realm) : + base(connectionString, realm, "Avatar") + { + } + + public bool Delete(UUID principalID, string name) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Name", name); + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteEstateData.cs b/OpenSim/Data/SQLite/SQLiteEstateData.cs new file mode 100644 index 0000000000..d51f2d413d --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteEstateData.cs @@ -0,0 +1,516 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteEstateStore : IEstateDataStore + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SqliteConnection m_connection; + private string m_connectionString; + + private FieldInfo[] m_Fields; + private Dictionary m_FieldMap = + new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteEstateStore() + { + } + + public SQLiteEstateStore(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_connectionString = connectionString; + + m_log.Info("[ESTATE DB]: Sqlite - connecting: "+m_connectionString); + + m_connection = new SqliteConnection(m_connectionString); + m_connection.Open(); + + Migration m = new Migration(m_connection, Assembly, "EstateStore"); + m.Update(); + + //m_connection.Close(); + // m_connection.Open(); + + Type t = typeof(EstateSettings); + m_Fields = t.GetFields(BindingFlags.NonPublic | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + foreach (FieldInfo f in m_Fields) + if (f.Name.Substring(0, 2) == "m_") + m_FieldMap[f.Name.Substring(2)] = f; + } + + private string[] FieldList + { + get { return new List(m_FieldMap.Keys).ToArray(); } + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_map left join estate_settings on estate_map.EstateID = estate_settings.EstateID where estate_settings.EstateID is not null and RegionID = :RegionID"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + + return DoLoad(cmd, regionID, create); + } + } + + private EstateSettings DoLoad(SqliteCommand cmd, UUID regionID, bool create) + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + IDataReader r = null; + try + { + r = cmd.ExecuteReader(); + } + catch (SqliteException) + { + m_log.Error("[SQLITE]: There was an issue loading the estate settings. This can happen the first time running OpenSimulator with CSharpSqlite the first time. OpenSimulator will probably crash, restart it and it should be good to go."); + } + + if (r != null && r.Read()) + { + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + int v = Convert.ToInt32(r[name]); + if (v != 0) + m_FieldMap[name].SetValue(es, true); + else + m_FieldMap[name].SetValue(es, false); + } + else if (m_FieldMap[name].GetValue(es) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(r[name].ToString(), out uuid); + m_FieldMap[name].SetValue(es, uuid); + } + else + { + m_FieldMap[name].SetValue(es, Convert.ChangeType(r[name], m_FieldMap[name].FieldType)); + } + } + r.Close(); + } + else if (create) + { + DoCreate(es); + LinkRegion(regionID, (int)es.EstateID); + } + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + return es; + } + + public EstateSettings CreateNewEstate() + { + EstateSettings es = new EstateSettings(); + es.OnSave += StoreEstateSettings; + + DoCreate(es); + + LoadBanList(es); + + es.EstateManagers = LoadUUIDList(es.EstateID, "estate_managers"); + es.EstateAccess = LoadUUIDList(es.EstateID, "estate_users"); + es.EstateGroups = LoadUUIDList(es.EstateID, "estate_groups"); + + return es; + } + + private void DoCreate(EstateSettings es) + { + List names = new List(FieldList); + + IDataReader r = null; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + names.Remove("EstateID"); + + string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")"; + + cmd.CommandText = sql; + cmd.Parameters.Clear(); + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.AddWithValue(":"+name, "1"); + else + cmd.Parameters.AddWithValue(":"+name, "0"); + } + else + { + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + + cmd.CommandText = "select LAST_INSERT_ROWID() as id"; + cmd.Parameters.Clear(); + + r = cmd.ExecuteReader(); + } + + r.Read(); + + es.EstateID = Convert.ToUInt32(r["id"]); + + r.Close(); + + es.Save(); + } + + public void StoreEstateSettings(EstateSettings es) + { + List fields = new List(FieldList); + fields.Remove("EstateID"); + + List terms = new List(); + + foreach (string f in fields) + terms.Add(f+" = :"+f); + + string sql = "update estate_settings set "+String.Join(", ", terms.ToArray())+" where EstateID = :EstateID"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + + foreach (string name in FieldList) + { + if (m_FieldMap[name].GetValue(es) is bool) + { + if ((bool)m_FieldMap[name].GetValue(es)) + cmd.Parameters.AddWithValue(":"+name, "1"); + else + cmd.Parameters.AddWithValue(":"+name, "0"); + } + else + { + cmd.Parameters.AddWithValue(":"+name, m_FieldMap[name].GetValue(es).ToString()); + } + } + + cmd.ExecuteNonQuery(); + } + + SaveBanList(es); + SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers); + SaveUUIDList(es.EstateID, "estate_users", es.EstateAccess); + SaveUUIDList(es.EstateID, "estate_groups", es.EstateGroups); + } + + private void LoadBanList(EstateSettings es) + { + es.ClearBans(); + + IDataReader r; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID"; + cmd.Parameters.AddWithValue(":EstateID", es.EstateID); + + r = cmd.ExecuteReader(); + } + + while (r.Read()) + { + EstateBan eb = new EstateBan(); + + UUID uuid = new UUID(); + UUID.TryParse(r["bannedUUID"].ToString(), out uuid); + + eb.BannedUserID = uuid; + eb.BannedHostAddress = "0.0.0.0"; + eb.BannedHostIPMask = "0.0.0.0"; + es.AddBan(eb); + } + r.Close(); + } + + private void SaveBanList(EstateSettings es) + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "delete from estateban where EstateID = :EstateID"; + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into estateban (EstateID, bannedUUID, bannedIp, bannedIpHostMask, bannedNameMask) values ( :EstateID, :bannedUUID, '', '', '' )"; + + foreach (EstateBan b in es.EstateBans) + { + cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString()); + cmd.Parameters.AddWithValue(":bannedUUID", b.BannedUserID.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + void SaveUUIDList(uint EstateID, string table, UUID[] data) + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "delete from "+table+" where EstateID = :EstateID"; + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); + + cmd.ExecuteNonQuery(); + + cmd.Parameters.Clear(); + + cmd.CommandText = "insert into "+table+" (EstateID, uuid) values ( :EstateID, :uuid )"; + + foreach (UUID uuid in data) + { + cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString()); + cmd.Parameters.AddWithValue(":uuid", uuid.ToString()); + + cmd.ExecuteNonQuery(); + cmd.Parameters.Clear(); + } + } + } + + UUID[] LoadUUIDList(uint EstateID, string table) + { + List uuids = new List(); + IDataReader r; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID"; + cmd.Parameters.AddWithValue(":EstateID", EstateID); + + r = cmd.ExecuteReader(); + } + + while (r.Read()) + { + // EstateBan eb = new EstateBan(); + + UUID uuid = new UUID(); + UUID.TryParse(r["uuid"].ToString(), out uuid); + + uuids.Add(uuid); + } + r.Close(); + + return uuids.ToArray(); + } + + public EstateSettings LoadEstateSettings(int estateID) + { + string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_settings where estate_settings.EstateID = :EstateID"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + + return DoLoad(cmd, UUID.Zero, false); + } + } + + public List LoadEstateSettingsAll() + { + List estateSettings = new List(); + + List estateIds = GetEstatesAll(); + foreach (int estateId in estateIds) + estateSettings.Add(LoadEstateSettings(estateId)); + + return estateSettings; + } + + public List GetEstates(string search) + { + List result = new List(); + + string sql = "select EstateID from estate_settings where estate_settings.EstateName = :EstateName"; + IDataReader r; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + cmd.Parameters.AddWithValue(":EstateName", search); + + r = cmd.ExecuteReader(); + } + + while (r.Read()) + { + result.Add(Convert.ToInt32(r["EstateID"])); + } + r.Close(); + + return result; + } + + public List GetEstatesAll() + { + List result = new List(); + + string sql = "select EstateID from estate_settings"; + IDataReader r; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + + r = cmd.ExecuteReader(); + } + + while (r.Read()) + { + result.Add(Convert.ToInt32(r["EstateID"])); + } + r.Close(); + + return result; + } + + public List GetEstatesByOwner(UUID ownerID) + { + List result = new List(); + + string sql = "select EstateID from estate_settings where estate_settings.EstateOwner = :EstateOwner"; + IDataReader r; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = sql; + cmd.Parameters.AddWithValue(":EstateOwner", ownerID); + + r = cmd.ExecuteReader(); + } + + while (r.Read()) + { + result.Add(Convert.ToInt32(r["EstateID"])); + } + r.Close(); + + return result; + } + + public bool LinkRegion(UUID regionID, int estateID) + { + SqliteTransaction transaction = m_connection.BeginTransaction(); + + // Delete any existing estate mapping for this region. + using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "delete from estate_map where RegionID = :RegionID"; + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + + cmd.ExecuteNonQuery(); + } + + using(SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = "insert into estate_map values (:RegionID, :EstateID)"; + cmd.Transaction = transaction; + cmd.Parameters.AddWithValue(":RegionID", regionID.ToString()); + cmd.Parameters.AddWithValue(":EstateID", estateID.ToString()); + + if (cmd.ExecuteNonQuery() == 0) + { + transaction.Rollback(); + return false; + } + else + { + transaction.Commit(); + return true; + } + } + } + + public List GetRegions(int estateID) + { + return new List(); + } + + public bool DeleteEstate(int estateID) + { + return false; + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteFramework.cs b/OpenSim/Data/SQLite/SQLiteFramework.cs new file mode 100644 index 0000000000..35b9a2fe31 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteFramework.cs @@ -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.Collections; +using System.Collections.Generic; +using System.Data; +using OpenMetaverse; +using OpenSim.Framework; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + /// + /// A database interface class to a user profile storage system + /// + public class SQLiteFramework + { + protected Object m_lockObject = new Object(); + + protected SQLiteFramework(string connectionString) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + } + + ////////////////////////////////////////////////////////////// + // + // All non queries are funneled through one connection + // to increase performance a little + // + protected int ExecuteNonQuery(SqliteCommand cmd, SqliteConnection connection) + { + lock (connection) + { +/* + SqliteConnection newConnection = + (SqliteConnection)((ICloneable)connection).Clone(); + newConnection.Open(); + + cmd.Connection = newConnection; +*/ + cmd.Connection = connection; + //Console.WriteLine("XXX " + cmd.CommandText); + + return cmd.ExecuteNonQuery(); + } + } + + protected IDataReader ExecuteReader(SqliteCommand cmd, SqliteConnection connection) + { + lock (connection) + { + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)connection).Clone(); + //newConnection.Open(); + + //cmd.Connection = newConnection; + cmd.Connection = connection; + //Console.WriteLine("XXX " + cmd.CommandText); + + return cmd.ExecuteReader(); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteFriendsData.cs b/OpenSim/Data/SQLite/SQLiteFriendsData.cs new file mode 100644 index 0000000000..331f426a46 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteFriendsData.cs @@ -0,0 +1,85 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + public class SQLiteFriendsData : SQLiteGenericTableHandler, IFriendsData + { + public SQLiteFriendsData(string connectionString, string realm) + : base(connectionString, realm, "FriendsStore") + { + } + + public FriendsData[] GetFriends(UUID principalID) + { + return GetFriends(principalID.ToString()); + } + + public FriendsData[] GetFriends(string userID) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("select a.*,case when b.Flags is null then -1 else b.Flags end as TheirFlags from {0} as a left join {0} as b on a.PrincipalID = b.Friend and a.Friend = b.PrincipalID where a.PrincipalID = :PrincipalID", m_Realm); + cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString()); + + return DoQuery(cmd); + } + } + + public bool Delete(UUID principalID, string friend) + { + return Delete(principalID.ToString(), friend); + } + + public override bool Delete(string principalID, string friend) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm); + cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString()); + cmd.Parameters.AddWithValue(":Friend", friend); + + ExecuteNonQuery(cmd, m_Connection); + } + + return true; + } + + } +} diff --git a/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs new file mode 100644 index 0000000000..9fbeb100ce --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteGenericTableHandler.cs @@ -0,0 +1,298 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteGenericTableHandler : SQLiteFramework where T: class, new() + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_Fields = + new Dictionary(); + + protected List m_ColumnNames = null; + protected string m_Realm; + protected FieldInfo m_DataField = null; + + protected static SqliteConnection m_Connection; + private static bool m_initialized; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteGenericTableHandler(string connectionString, + string realm, string storeName) : base(connectionString) + { + m_Realm = realm; + + if (!m_initialized) + { + m_Connection = new SqliteConnection(connectionString); + //Console.WriteLine(string.Format("OPENING CONNECTION FOR {0} USING {1}", storeName, connectionString)); + m_Connection.Open(); + + if (storeName != String.Empty) + { + //SqliteConnection newConnection = + // (SqliteConnection)((ICloneable)m_Connection).Clone(); + //newConnection.Open(); + + //Migration m = new Migration(newConnection, Assembly, storeName); + Migration m = new Migration(m_Connection, Assembly, storeName); + m.Update(); + //newConnection.Close(); + //newConnection.Dispose(); + } + + m_initialized = true; + } + + Type t = typeof(T); + FieldInfo[] fields = t.GetFields(BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.DeclaredOnly); + + if (fields.Length == 0) + return; + + foreach (FieldInfo f in fields) + { + if (f.Name != "Data") + m_Fields[f.Name] = f; + else + m_DataField = f; + } + } + + private void CheckColumnNames(IDataReader reader) + { + if (m_ColumnNames != null) + return; + + m_ColumnNames = new List(); + + DataTable schemaTable = reader.GetSchemaTable(); + foreach (DataRow row in schemaTable.Rows) + { + if (row["ColumnName"] != null && + (!m_Fields.ContainsKey(row["ColumnName"].ToString()))) + m_ColumnNames.Add(row["ColumnName"].ToString()); + } + } + + public virtual T[] Get(string field, string key) + { + return Get(new string[] { field }, new string[] { key }); + } + + public virtual T[] Get(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return new T[0]; + + List terms = new List(); + + using (SqliteCommand cmd = new SqliteCommand()) + { + for (int i = 0 ; i < fields.Length ; i++) + { + cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i])); + terms.Add("`" + fields[i] + "` = :" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + } + + protected T[] DoQuery(SqliteCommand cmd) + { + IDataReader reader = ExecuteReader(cmd, m_Connection); + if (reader == null) + return new T[0]; + + CheckColumnNames(reader); + + List result = new List(); + + while (reader.Read()) + { + T row = new T(); + + foreach (string name in m_Fields.Keys) + { + if (m_Fields[name].GetValue(row) is bool) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v != 0 ? true : false); + } + else if (m_Fields[name].GetValue(row) is UUID) + { + UUID uuid = UUID.Zero; + + UUID.TryParse(reader[name].ToString(), out uuid); + m_Fields[name].SetValue(row, uuid); + } + else if (m_Fields[name].GetValue(row) is int) + { + int v = Convert.ToInt32(reader[name]); + m_Fields[name].SetValue(row, v); + } + else + { + m_Fields[name].SetValue(row, reader[name]); + } + } + + if (m_DataField != null) + { + Dictionary data = + new Dictionary(); + + foreach (string col in m_ColumnNames) + { + data[col] = reader[col].ToString(); + if (data[col] == null) + data[col] = String.Empty; + } + + m_DataField.SetValue(row, data); + } + + result.Add(row); + } + + //CloseCommand(cmd); + + return result.ToArray(); + } + + public virtual T[] Get(string where) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + string query = String.Format("select * from {0} where {1}", + m_Realm, where); + + cmd.CommandText = query; + + return DoQuery(cmd); + } + } + + public virtual bool Store(T row) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + string query = ""; + List names = new List(); + List values = new List(); + + foreach (FieldInfo fi in m_Fields.Values) + { + names.Add(fi.Name); + values.Add(":" + fi.Name); + cmd.Parameters.Add(new SqliteParameter(":" + fi.Name, fi.GetValue(row).ToString())); + } + + if (m_DataField != null) + { + Dictionary data = + (Dictionary)m_DataField.GetValue(row); + + foreach (KeyValuePair kvp in data) + { + names.Add(kvp.Key); + values.Add(":" + kvp.Key); + cmd.Parameters.Add(new SqliteParameter(":" + kvp.Key, kvp.Value)); + } + } + + query = String.Format("replace into {0} (`", m_Realm) + String.Join("`,`", names.ToArray()) + "`) values (" + String.Join(",", values.ToArray()) + ")"; + + cmd.CommandText = query; + + if (ExecuteNonQuery(cmd, m_Connection) > 0) + return true; + } + + return false; + } + + public virtual bool Delete(string field, string key) + { + return Delete(new string[] { field }, new string[] { key }); + } + + public virtual bool Delete(string[] fields, string[] keys) + { + if (fields.Length != keys.Length) + return false; + + List terms = new List(); + + using (SqliteCommand cmd = new SqliteCommand()) + { + for (int i = 0 ; i < fields.Length ; i++) + { + cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i])); + terms.Add("`" + fields[i] + "` = :" + fields[i]); + } + + string where = String.Join(" and ", terms.ToArray()); + + string query = String.Format("delete from {0} where {1}", m_Realm, where); + + cmd.CommandText = query; + + return ExecuteNonQuery(cmd, m_Connection) > 0; + } + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteGridUserData.cs b/OpenSim/Data/SQLite/SQLiteGridUserData.cs new file mode 100644 index 0000000000..d8c52f8880 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteGridUserData.cs @@ -0,0 +1,65 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQL Interface for user grid data + /// + public class SQLiteGridUserData : SQLiteGenericTableHandler, IGridUserData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteGridUserData(string connectionString, string realm) + : base(connectionString, realm, "GridUserStore") {} + + public new GridUserData Get(string userID) + { + GridUserData[] ret = Get("UserID", userID); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public GridUserData[] GetAll(string userID) + { + return base.Get(String.Format("UserID LIKE '{0}%'", userID)); + } + + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteHGTravelData.cs b/OpenSim/Data/SQLite/SQLiteHGTravelData.cs new file mode 100644 index 0000000000..db288b2486 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteHGTravelData.cs @@ -0,0 +1,82 @@ +/* + * 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.Data; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using Mono.Data.Sqlite; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQL Interface for user grid data + /// + public class SQLiteHGTravelData : SQLiteGenericTableHandler, IHGTravelingData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public SQLiteHGTravelData(string connectionString, string realm) + : base(connectionString, realm, "HGTravelStore") {} + + public HGTravelingData Get(UUID sessionID) + { + HGTravelingData[] ret = Get("SessionID", sessionID.ToString()); + + if (ret.Length == 0) + return null; + + return ret[0]; + } + + public HGTravelingData[] GetSessions(UUID userID) + { + return base.Get("UserID", userID.ToString()); + } + + public bool Delete(UUID sessionID) + { + return Delete("SessionID", sessionID.ToString()); + } + + public void DeleteOld() + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("delete from {0} where TMStamp < datetime('now', '-2 day') ", m_Realm); + + DoQuery(cmd); + } + + } + + } +} \ No newline at end of file diff --git a/OpenSim/Data/SQLite/SQLiteInventoryStore.cs b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs new file mode 100644 index 0000000000..7d493ca014 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteInventoryStore.cs @@ -0,0 +1,916 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// An Inventory Interface to the SQLite database + /// + public class SQLiteInventoryStore : SQLiteUtil, IInventoryDataPlugin + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private const string invItemsSelect = "select * from inventoryitems"; + private const string invFoldersSelect = "select * from inventoryfolders"; + + private static SqliteConnection conn; + private static DataSet ds; + private static SqliteDataAdapter invItemsDa; + private static SqliteDataAdapter invFoldersDa; + + private static bool m_Initialized = false; + + public void Initialise() + { + m_log.Info("[SQLiteInventoryData]: " + Name + " cannot be default-initialized!"); + throw new PluginNotInitialisedException(Name); + } + + /// + /// + /// Initialises Inventory interface + /// Loads and initialises a new SQLite connection and maintains it. + /// use default URI if connect string string is empty. + /// + /// + /// connect string + public void Initialise(string dbconnect) + { + if (!m_Initialized) + { + m_Initialized = true; + + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + if (dbconnect == string.Empty) + { + dbconnect = "URI=file:inventoryStore.db,version=3"; + } + m_log.Info("[INVENTORY DB]: Sqlite - connecting: " + dbconnect); + conn = new SqliteConnection(dbconnect); + + conn.Open(); + + Assembly assem = GetType().Assembly; + Migration m = new Migration(conn, assem, "InventoryStore"); + m.Update(); + + SqliteCommand itemsSelectCmd = new SqliteCommand(invItemsSelect, conn); + invItemsDa = new SqliteDataAdapter(itemsSelectCmd); + // SqliteCommandBuilder primCb = new SqliteCommandBuilder(primDa); + + SqliteCommand foldersSelectCmd = new SqliteCommand(invFoldersSelect, conn); + invFoldersDa = new SqliteDataAdapter(foldersSelectCmd); + + ds = new DataSet(); + + ds.Tables.Add(createInventoryFoldersTable()); + invFoldersDa.Fill(ds.Tables["inventoryfolders"]); + setupFoldersCommands(invFoldersDa, conn); + CreateDataSetMapping(invFoldersDa, "inventoryfolders"); + m_log.Info("[INVENTORY DB]: Populated Inventory Folders Definitions"); + + ds.Tables.Add(createInventoryItemsTable()); + invItemsDa.Fill(ds.Tables["inventoryitems"]); + setupItemsCommands(invItemsDa, conn); + CreateDataSetMapping(invItemsDa, "inventoryitems"); + m_log.Info("[INVENTORY DB]: Populated Inventory Items Definitions"); + + ds.AcceptChanges(); + } + } + + /// + /// Closes the inventory interface + /// + public void Dispose() + { + if (conn != null) + { + conn.Close(); + conn = null; + } + if (invItemsDa != null) + { + invItemsDa.Dispose(); + invItemsDa = null; + } + if (invFoldersDa != null) + { + invFoldersDa.Dispose(); + invFoldersDa = null; + } + if (ds != null) + { + ds.Dispose(); + ds = null; + } + } + + /// + /// + /// + /// + /// + public InventoryItemBase buildItem(DataRow row) + { + InventoryItemBase item = new InventoryItemBase(); + item.ID = new UUID((string) row["UUID"]); + item.AssetID = new UUID((string) row["assetID"]); + item.AssetType = Convert.ToInt32(row["assetType"]); + item.InvType = Convert.ToInt32(row["invType"]); + item.Folder = new UUID((string) row["parentFolderID"]); + item.Owner = new UUID((string) row["avatarID"]); + item.CreatorIdentification = (string)row["creatorsID"]; + item.Name = (string) row["inventoryName"]; + item.Description = (string) row["inventoryDescription"]; + + item.NextPermissions = Convert.ToUInt32(row["inventoryNextPermissions"]); + item.CurrentPermissions = Convert.ToUInt32(row["inventoryCurrentPermissions"]); + item.BasePermissions = Convert.ToUInt32(row["inventoryBasePermissions"]); + item.EveryOnePermissions = Convert.ToUInt32(row["inventoryEveryOnePermissions"]); + item.GroupPermissions = Convert.ToUInt32(row["inventoryGroupPermissions"]); + + // new fields + if (!Convert.IsDBNull(row["salePrice"])) + item.SalePrice = Convert.ToInt32(row["salePrice"]); + + if (!Convert.IsDBNull(row["saleType"])) + item.SaleType = Convert.ToByte(row["saleType"]); + + if (!Convert.IsDBNull(row["creationDate"])) + item.CreationDate = Convert.ToInt32(row["creationDate"]); + + if (!Convert.IsDBNull(row["groupID"])) + item.GroupID = new UUID((string)row["groupID"]); + + if (!Convert.IsDBNull(row["groupOwned"])) + item.GroupOwned = Convert.ToBoolean(row["groupOwned"]); + + if (!Convert.IsDBNull(row["Flags"])) + item.Flags = Convert.ToUInt32(row["Flags"]); + + return item; + } + + /// + /// Fill a database row with item data + /// + /// + /// + private static void fillItemRow(DataRow row, InventoryItemBase item) + { + row["UUID"] = item.ID.ToString(); + row["assetID"] = item.AssetID.ToString(); + row["assetType"] = item.AssetType; + row["invType"] = item.InvType; + row["parentFolderID"] = item.Folder.ToString(); + row["avatarID"] = item.Owner.ToString(); + row["creatorsID"] = item.CreatorIdentification.ToString(); + row["inventoryName"] = item.Name; + row["inventoryDescription"] = item.Description; + + row["inventoryNextPermissions"] = item.NextPermissions; + row["inventoryCurrentPermissions"] = item.CurrentPermissions; + row["inventoryBasePermissions"] = item.BasePermissions; + row["inventoryEveryOnePermissions"] = item.EveryOnePermissions; + row["inventoryGroupPermissions"] = item.GroupPermissions; + + // new fields + row["salePrice"] = item.SalePrice; + row["saleType"] = item.SaleType; + row["creationDate"] = item.CreationDate; + row["groupID"] = item.GroupID.ToString(); + row["groupOwned"] = item.GroupOwned; + row["flags"] = item.Flags; + } + + /// + /// Add inventory folder + /// + /// Folder base + /// true=create folder. false=update existing folder + /// nasty + private void addFolder(InventoryFolderBase folder, bool add) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); + if (inventoryRow == null) + { + if (! add) + m_log.ErrorFormat("Interface Misuse: Attempting to Update non-existent inventory folder: {0}", folder.ID); + + inventoryRow = inventoryFolderTable.NewRow(); + fillFolderRow(inventoryRow, folder); + inventoryFolderTable.Rows.Add(inventoryRow); + } + else + { + if (add) + m_log.ErrorFormat("Interface Misuse: Attempting to Add inventory folder that already exists: {0}", folder.ID); + + fillFolderRow(inventoryRow, folder); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// Move an inventory folder + /// + /// folder base + private void moveFolder(InventoryFolderBase folder) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + DataRow inventoryRow = inventoryFolderTable.Rows.Find(folder.ID.ToString()); + if (inventoryRow == null) + { + inventoryRow = inventoryFolderTable.NewRow(); + fillFolderRow(inventoryRow, folder); + inventoryFolderTable.Rows.Add(inventoryRow); + } + else + { + moveFolderRow(inventoryRow, folder); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// add an item in inventory + /// + /// the item + /// true=add item ; false=update existing item + private void addItem(InventoryItemBase item, bool add) + { + lock (ds) + { + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + + DataRow inventoryRow = inventoryItemTable.Rows.Find(item.ID.ToString()); + if (inventoryRow == null) + { + if (!add) + m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Update non-existent inventory item: {0}", item.ID); + + inventoryRow = inventoryItemTable.NewRow(); + fillItemRow(inventoryRow, item); + inventoryItemTable.Rows.Add(inventoryRow); + } + else + { + if (add) + m_log.ErrorFormat("[INVENTORY DB]: Interface Misuse: Attempting to Add inventory item that already exists: {0}", item.ID); + + fillItemRow(inventoryRow, item); + } + + invItemsDa.Update(ds, "inventoryitems"); + + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + + inventoryRow = inventoryFolderTable.Rows.Find(item.Folder.ToString()); + if (inventoryRow != null) //MySQL doesn't throw an exception here, so sqlite shouldn't either. + inventoryRow["version"] = (int)inventoryRow["version"] + 1; + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /// + /// TODO : DataSet commit + /// + public void Shutdown() + { + // TODO: DataSet commit + } + + /// + /// The name of this DB provider + /// + /// Name of DB provider + public string Name + { + get { return "SQLite Inventory Data Interface"; } + } + + /// + /// Returns the version of this DB provider + /// + /// A string containing the DB provider version + public string Version + { + get + { + Module module = GetType().Module; + // string dllName = module.Assembly.ManifestModule.Name; + Version dllVersion = module.Assembly.GetName().Version; + + + return + string.Format("{0}.{1}.{2}.{3}", dllVersion.Major, dllVersion.Minor, dllVersion.Build, + dllVersion.Revision); + } + } + + /// + /// Returns a list of inventory items contained within the specified folder + /// + /// The UUID of the target folder + /// A List of InventoryItemBase items + public List getInventoryInFolder(UUID folderID) + { + lock (ds) + { + List retval = new List(); + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + string selectExp = "parentFolderID = '" + folderID + "'"; + DataRow[] rows = inventoryItemTable.Select(selectExp); + foreach (DataRow row in rows) + { + retval.Add(buildItem(row)); + } + + return retval; + } + } + + /// + /// Returns a list of the root folders within a users inventory + /// + /// The user whos inventory is to be searched + /// A list of folder objects + public List getUserRootFolders(UUID user) + { + return new List(); + } + + // see InventoryItemBase.getUserRootFolder + public InventoryFolderBase getUserRootFolder(UUID user) + { + lock (ds) + { + List folders = new List(); + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "agentID = '" + user + "' AND parentID = '" + UUID.Zero + "'"; + DataRow[] rows = inventoryFolderTable.Select(selectExp); + foreach (DataRow row in rows) + { + folders.Add(buildFolder(row)); + } + + // There should only ever be one root folder for a user. However, if there's more + // than one we'll simply use the first one rather than failing. It would be even + // nicer to print some message to this effect, but this feels like it's too low a + // to put such a message out, and it's too minor right now to spare the time to + // suitably refactor. + if (folders.Count > 0) + { + return folders[0]; + } + + return null; + } + } + + /// + /// Append a list of all the child folders of a parent folder + /// + /// list where folders will be appended + /// ID of parent + protected void getInventoryFolders(ref List folders, UUID parentID) + { + lock (ds) + { + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "parentID = '" + parentID + "'"; + DataRow[] rows = inventoryFolderTable.Select(selectExp); + foreach (DataRow row in rows) + { + folders.Add(buildFolder(row)); + } + + } + } + + /// + /// Returns a list of inventory folders contained in the folder 'parentID' + /// + /// The folder to get subfolders for + /// A list of inventory folders + public List getInventoryFolders(UUID parentID) + { + List folders = new List(); + getInventoryFolders(ref folders, parentID); + return folders; + } + + /// + /// See IInventoryDataPlugin + /// + /// + /// + public List getFolderHierarchy(UUID parentID) + { + /* Note: There are subtle changes between this implementation of getFolderHierarchy and the previous one + * - We will only need to hit the database twice instead of n times. + * - We assume the database is well-formed - no stranded/dangling folders, all folders in heirarchy owned + * by the same person, each user only has 1 inventory heirarchy + * - The returned list is not ordered, instead of breadth-first ordered + There are basically 2 usage cases for getFolderHeirarchy: + 1) Getting the user's entire inventory heirarchy when they log in + 2) Finding a subfolder heirarchy to delete when emptying the trash. + This implementation will pull all inventory folders from the database, and then prune away any folder that + is not part of the requested sub-heirarchy. The theory is that it is cheaper to make 1 request from the + database than to make n requests. This pays off only if requested heirarchy is large. + By making this choice, we are making the worst case better at the cost of making the best case worse + - Francis + */ + + List folders = new List(); + DataRow[] folderRows = null, parentRow; + InventoryFolderBase parentFolder = null; + lock (ds) + { + /* Fetch the parent folder from the database to determine the agent ID. + * Then fetch all inventory folders for that agent from the agent ID. + */ + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + string selectExp = "UUID = '" + parentID + "'"; + parentRow = inventoryFolderTable.Select(selectExp); // Assume at most 1 result + if (parentRow.GetLength(0) >= 1) // No result means parent folder does not exist + { + parentFolder = buildFolder(parentRow[0]); + UUID agentID = parentFolder.Owner; + selectExp = "agentID = '" + agentID + "'"; + folderRows = inventoryFolderTable.Select(selectExp); + } + + if (folderRows != null && folderRows.GetLength(0) >= 1) // No result means parent folder does not exist + { // or has no children + /* if we're querying the root folder, just return an unordered list of all folders in the user's + * inventory + */ + if (parentFolder.ParentID == UUID.Zero) + { + foreach (DataRow row in folderRows) + { + InventoryFolderBase curFolder = buildFolder(row); + if (curFolder.ID != parentID) // Return all folders except the parent folder of heirarchy + folders.Add(buildFolder(row)); + } + } // If requesting root folder + /* else we are querying a non-root folder. We currently have a list of all of the user's folders, + * we must construct a list of all folders in the heirarchy below parentID. + * Our first step will be to construct a hash table of all folders, indexed by parent ID. + * Once we have constructed the hash table, we will do a breadth-first traversal on the tree using the + * hash table to find child folders. + */ + else + { // Querying a non-root folder + + // Build a hash table of all user's inventory folders, indexed by each folder's parent ID + Dictionary> hashtable = + new Dictionary>(folderRows.GetLength(0)); + + foreach (DataRow row in folderRows) + { + InventoryFolderBase curFolder = buildFolder(row); + if (curFolder.ParentID != UUID.Zero) // Discard root of tree - not needed + { + if (hashtable.ContainsKey(curFolder.ParentID)) + { + // Current folder already has a sibling - append to sibling list + hashtable[curFolder.ParentID].Add(curFolder); + } + else + { + List siblingList = new List(); + siblingList.Add(curFolder); + // Current folder has no known (yet) siblings + hashtable.Add(curFolder.ParentID, siblingList); + } + } + } // For all inventory folders + + // Note: Could release the ds lock here - we don't access folderRows or the database anymore. + // This is somewhat of a moot point as the callers of this function usually lock db anyways. + + if (hashtable.ContainsKey(parentID)) // if requested folder does have children + folders.AddRange(hashtable[parentID]); + + // BreadthFirstSearch build inventory tree **Note: folders.Count is *not* static + for (int i = 0; i < folders.Count; i++) + if (hashtable.ContainsKey(folders[i].ID)) + folders.AddRange(hashtable[folders[i].ID]); + + } // if requesting a subfolder heirarchy + } // if folder parentID exists and has children + } // lock ds + return folders; + } + + /// + /// Returns an inventory item by its UUID + /// + /// The UUID of the item to be returned + /// A class containing item information + public InventoryItemBase getInventoryItem(UUID item) + { + lock (ds) + { + DataRow row = ds.Tables["inventoryitems"].Rows.Find(item.ToString()); + if (row != null) + { + return buildItem(row); + } + else + { + return null; + } + } + } + + /// + /// Returns a specified inventory folder by its UUID + /// + /// The UUID of the folder to be returned + /// A class containing folder information + public InventoryFolderBase getInventoryFolder(UUID folder) + { + // TODO: Deep voodoo here. If you enable this code then + // multi region breaks. No idea why, but I figured it was + // better to leave multi region at this point. It does mean + // that you don't get to see system textures why creating + // clothes and the like. :( + lock (ds) + { + DataRow row = ds.Tables["inventoryfolders"].Rows.Find(folder.ToString()); + if (row != null) + { + return buildFolder(row); + } + else + { + return null; + } + } + } + + /// + /// Creates a new inventory item based on item + /// + /// The item to be created + public void addInventoryItem(InventoryItemBase item) + { + addItem(item, true); + } + + /// + /// Updates an inventory item with item (updates based on ID) + /// + /// The updated item + public void updateInventoryItem(InventoryItemBase item) + { + addItem(item, false); + } + + /// + /// Delete an inventory item + /// + /// The item UUID + public void deleteInventoryItem(UUID itemID) + { + lock (ds) + { + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + + DataRow inventoryRow = inventoryItemTable.Rows.Find(itemID.ToString()); + if (inventoryRow != null) + { + inventoryRow.Delete(); + } + + invItemsDa.Update(ds, "inventoryitems"); + } + } + + public InventoryItemBase queryInventoryItem(UUID itemID) + { + return getInventoryItem(itemID); + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + /// + /// Delete all items in the specified folder + /// + /// id of the folder, whose item content should be deleted + /// this is horribly inefficient, but I don't want to ruin the overall structure of this implementation + private void deleteItemsInFolder(UUID folderId) + { + List items = getInventoryInFolder(folderId); + + foreach (InventoryItemBase i in items) + deleteInventoryItem(i.ID); + } + + /// + /// Adds a new folder specified by folder + /// + /// The inventory folder + public void addInventoryFolder(InventoryFolderBase folder) + { + addFolder(folder, true); + } + + /// + /// Updates a folder based on its ID with folder + /// + /// The inventory folder + public void updateInventoryFolder(InventoryFolderBase folder) + { + addFolder(folder, false); + } + + /// + /// Moves a folder based on its ID with folder + /// + /// The inventory folder + public void moveInventoryFolder(InventoryFolderBase folder) + { + moveFolder(folder); + } + + /// + /// Delete a folder + /// + /// + /// This will clean-up any child folders and child items as well + /// + /// the folder UUID + public void deleteInventoryFolder(UUID folderID) + { + lock (ds) + { + List subFolders = getFolderHierarchy(folderID); + + DataTable inventoryFolderTable = ds.Tables["inventoryfolders"]; + DataRow inventoryRow; + + //Delete all sub-folders + foreach (InventoryFolderBase f in subFolders) + { + inventoryRow = inventoryFolderTable.Rows.Find(f.ID.ToString()); + if (inventoryRow != null) + { + deleteItemsInFolder(f.ID); + inventoryRow.Delete(); + } + } + + //Delete the actual row + inventoryRow = inventoryFolderTable.Rows.Find(folderID.ToString()); + if (inventoryRow != null) + { + deleteItemsInFolder(folderID); + inventoryRow.Delete(); + } + + invFoldersDa.Update(ds, "inventoryfolders"); + } + } + + /*********************************************************************** + * + * Data Table definitions + * + **********************************************************************/ + + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + + /// + /// Create the "inventoryitems" table + /// + private static DataTable createInventoryItemsTable() + { + DataTable inv = new DataTable("inventoryitems"); + + createCol(inv, "UUID", typeof (String)); //inventoryID + createCol(inv, "assetID", typeof (String)); + createCol(inv, "assetType", typeof (Int32)); + createCol(inv, "invType", typeof (Int32)); + createCol(inv, "parentFolderID", typeof (String)); + createCol(inv, "avatarID", typeof (String)); + createCol(inv, "creatorsID", typeof (String)); + + createCol(inv, "inventoryName", typeof (String)); + createCol(inv, "inventoryDescription", typeof (String)); + // permissions + createCol(inv, "inventoryNextPermissions", typeof (Int32)); + createCol(inv, "inventoryCurrentPermissions", typeof (Int32)); + createCol(inv, "inventoryBasePermissions", typeof (Int32)); + createCol(inv, "inventoryEveryOnePermissions", typeof (Int32)); + createCol(inv, "inventoryGroupPermissions", typeof (Int32)); + + // sale info + createCol(inv, "salePrice", typeof(Int32)); + createCol(inv, "saleType", typeof(Byte)); + + // creation date + createCol(inv, "creationDate", typeof(Int32)); + + // group info + createCol(inv, "groupID", typeof(String)); + createCol(inv, "groupOwned", typeof(Boolean)); + + // Flags + createCol(inv, "flags", typeof(UInt32)); + + inv.PrimaryKey = new DataColumn[] { inv.Columns["UUID"] }; + return inv; + } + + /// + /// Creates the "inventoryfolders" table + /// + /// + private static DataTable createInventoryFoldersTable() + { + DataTable fol = new DataTable("inventoryfolders"); + + createCol(fol, "UUID", typeof (String)); //folderID + createCol(fol, "name", typeof (String)); + createCol(fol, "agentID", typeof (String)); + createCol(fol, "parentID", typeof (String)); + createCol(fol, "type", typeof (Int32)); + createCol(fol, "version", typeof (Int32)); + + fol.PrimaryKey = new DataColumn[] {fol.Columns["UUID"]}; + return fol; + } + + /// + /// + /// + /// + /// + private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + lock (ds) + { + da.InsertCommand = createInsertCommand("inventoryitems", ds.Tables["inventoryitems"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("inventoryitems", "UUID=:UUID", ds.Tables["inventoryitems"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from inventoryitems where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + } + + /// + /// + /// + /// + /// + private void setupFoldersCommands(SqliteDataAdapter da, SqliteConnection conn) + { + lock (ds) + { + da.InsertCommand = createInsertCommand("inventoryfolders", ds.Tables["inventoryfolders"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("inventoryfolders", "UUID=:UUID", ds.Tables["inventoryfolders"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from inventoryfolders where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + } + + /// + /// + /// + /// + /// + private static InventoryFolderBase buildFolder(DataRow row) + { + InventoryFolderBase folder = new InventoryFolderBase(); + folder.ID = new UUID((string) row["UUID"]); + folder.Name = (string) row["name"]; + folder.Owner = new UUID((string) row["agentID"]); + folder.ParentID = new UUID((string) row["parentID"]); + folder.Type = Convert.ToInt16(row["type"]); + folder.Version = Convert.ToUInt16(row["version"]); + return folder; + } + + /// + /// + /// + /// + /// + private static void fillFolderRow(DataRow row, InventoryFolderBase folder) + { + row["UUID"] = folder.ID.ToString(); + row["name"] = folder.Name; + row["agentID"] = folder.Owner.ToString(); + row["parentID"] = folder.ParentID.ToString(); + row["type"] = folder.Type; + row["version"] = folder.Version; + } + + /// + /// + /// + /// + /// + private static void moveFolderRow(DataRow row, InventoryFolderBase folder) + { + row["UUID"] = folder.ID.ToString(); + row["parentID"] = folder.ParentID.ToString(); + } + + public List fetchActiveGestures (UUID avatarID) + { + lock (ds) + { + List items = new List(); + + DataTable inventoryItemTable = ds.Tables["inventoryitems"]; + string selectExp + = "avatarID = '" + avatarID + "' AND assetType = " + (int)AssetType.Gesture + " AND flags = 1"; + //m_log.DebugFormat("[SQL]: sql = " + selectExp); + DataRow[] rows = inventoryItemTable.Select(selectExp); + foreach (DataRow row in rows) + { + items.Add(buildItem(row)); + } + return items; + } + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs new file mode 100644 index 0000000000..6ed3d40321 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -0,0 +1,2960 @@ +/* + * 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.Data; +using System.Drawing; +using System.IO; +using System.Reflection; +using log4net; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else +using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Data.SQLite +{ + /// + /// A RegionData Interface to the SQLite database + /// + public class SQLiteSimulationData : ISimulationDataStore + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[REGION DB SQLITE]"; + + private const string primSelect = "select * from prims"; + private const string shapeSelect = "select * from primshapes"; + private const string itemsSelect = "select * from primitems"; + private const string terrainSelect = "select * from terrain limit 1"; + private const string landSelect = "select * from land"; + private const string landAccessListSelect = "select distinct * from landaccesslist"; + private const string regionbanListSelect = "select * from regionban"; + private const string regionSettingsSelect = "select * from regionsettings"; + private const string regionWindlightSelect = "select * from regionwindlight"; + private const string regionEnvironmentSelect = "select * from regionenvironment"; + private const string regionSpawnPointsSelect = "select * from spawn_points"; + + private DataSet ds; + private SqliteDataAdapter primDa; + private SqliteDataAdapter shapeDa; + private SqliteDataAdapter itemsDa; + private SqliteDataAdapter terrainDa; + private SqliteDataAdapter landDa; + private SqliteDataAdapter landAccessListDa; + private SqliteDataAdapter regionSettingsDa; + private SqliteDataAdapter regionWindlightDa; + private SqliteDataAdapter regionEnvironmentDa; + private SqliteDataAdapter regionSpawnPointsDa; + + private SqliteConnection m_conn; + private String m_connectionString; + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteSimulationData() + { + } + + public SQLiteSimulationData(string connectionString) + { + Initialise(connectionString); + } + + // Temporary attribute while this is experimental + + /*********************************************************************** + * + * Public Interface Functions + * + **********************************************************************/ + + /// + /// + /// Initialises RegionData Interface + /// Loads and initialises a new SQLite connection and maintains it. + /// + /// + /// the connection string + public void Initialise(string connectionString) + { + try + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_connectionString = connectionString; + + ds = new DataSet("Region"); + + m_log.Info("[SQLITE REGION DB]: Sqlite - connecting: " + connectionString); + m_conn = new SqliteConnection(m_connectionString); + m_conn.Open(); + + SqliteCommand primSelectCmd = new SqliteCommand(primSelect, m_conn); + primDa = new SqliteDataAdapter(primSelectCmd); + + SqliteCommand shapeSelectCmd = new SqliteCommand(shapeSelect, m_conn); + shapeDa = new SqliteDataAdapter(shapeSelectCmd); + // SqliteCommandBuilder shapeCb = new SqliteCommandBuilder(shapeDa); + + SqliteCommand itemsSelectCmd = new SqliteCommand(itemsSelect, m_conn); + itemsDa = new SqliteDataAdapter(itemsSelectCmd); + + SqliteCommand terrainSelectCmd = new SqliteCommand(terrainSelect, m_conn); + terrainDa = new SqliteDataAdapter(terrainSelectCmd); + + SqliteCommand landSelectCmd = new SqliteCommand(landSelect, m_conn); + landDa = new SqliteDataAdapter(landSelectCmd); + + SqliteCommand landAccessListSelectCmd = new SqliteCommand(landAccessListSelect, m_conn); + landAccessListDa = new SqliteDataAdapter(landAccessListSelectCmd); + + SqliteCommand regionSettingsSelectCmd = new SqliteCommand(regionSettingsSelect, m_conn); + regionSettingsDa = new SqliteDataAdapter(regionSettingsSelectCmd); + + SqliteCommand regionWindlightSelectCmd = new SqliteCommand(regionWindlightSelect, m_conn); + regionWindlightDa = new SqliteDataAdapter(regionWindlightSelectCmd); + + SqliteCommand regionEnvironmentSelectCmd = new SqliteCommand(regionEnvironmentSelect, m_conn); + regionEnvironmentDa = new SqliteDataAdapter(regionEnvironmentSelectCmd); + + SqliteCommand regionSpawnPointsSelectCmd = new SqliteCommand(regionSpawnPointsSelect, m_conn); + regionSpawnPointsDa = new SqliteDataAdapter(regionSpawnPointsSelectCmd); + + // This actually does the roll forward assembly stuff + Migration m = new Migration(m_conn, Assembly, "RegionStore"); + m.Update(); + + lock (ds) + { + ds.Tables.Add(createPrimTable()); + setupPrimCommands(primDa, m_conn); + + ds.Tables.Add(createShapeTable()); + setupShapeCommands(shapeDa, m_conn); + + ds.Tables.Add(createItemsTable()); + setupItemsCommands(itemsDa, m_conn); + + ds.Tables.Add(createTerrainTable()); + setupTerrainCommands(terrainDa, m_conn); + + ds.Tables.Add(createLandTable()); + setupLandCommands(landDa, m_conn); + + ds.Tables.Add(createLandAccessListTable()); + setupLandAccessCommands(landAccessListDa, m_conn); + + ds.Tables.Add(createRegionSettingsTable()); + setupRegionSettingsCommands(regionSettingsDa, m_conn); + + ds.Tables.Add(createRegionWindlightTable()); + setupRegionWindlightCommands(regionWindlightDa, m_conn); + + ds.Tables.Add(createRegionEnvironmentTable()); + setupRegionEnvironmentCommands(regionEnvironmentDa, m_conn); + + ds.Tables.Add(createRegionSpawnPointsTable()); + setupRegionSpawnPointsCommands(regionSpawnPointsDa, m_conn); + + // WORKAROUND: This is a work around for sqlite on + // windows, which gets really unhappy with blob columns + // that have no sample data in them. At some point we + // need to actually find a proper way to handle this. + try + { + primDa.Fill(ds.Tables["prims"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on prims table :{0}", e.Message); + } + + try + { + shapeDa.Fill(ds.Tables["primshapes"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on primshapes table :{0}", e.Message); + } + + try + { + itemsDa.Fill(ds.Tables["primitems"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on primitems table :{0}", e.Message); + } + + try + { + terrainDa.Fill(ds.Tables["terrain"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on terrain table :{0}", e.Message); + } + + try + { + landDa.Fill(ds.Tables["land"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on land table :{0}", e.Message); + } + + try + { + landAccessListDa.Fill(ds.Tables["landaccesslist"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on landaccesslist table :{0}", e.Message); + } + + try + { + regionSettingsDa.Fill(ds.Tables["regionsettings"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on regionsettings table :{0}", e.Message); + } + + try + { + regionWindlightDa.Fill(ds.Tables["regionwindlight"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on regionwindlight table :{0}", e.Message); + } + + try + { + regionEnvironmentDa.Fill(ds.Tables["regionenvironment"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on regionenvironment table :{0}", e.Message); + } + + try + { + regionSpawnPointsDa.Fill(ds.Tables["spawn_points"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: Caught fill error on spawn_points table :{0}", e.Message); + } + + // We have to create a data set mapping for every table, otherwise the IDataAdaptor.Update() will not populate rows with values! + // Not sure exactly why this is - this kind of thing was not necessary before - justincc 20100409 + // Possibly because we manually set up our own DataTables before connecting to the database + CreateDataSetMapping(primDa, "prims"); + CreateDataSetMapping(shapeDa, "primshapes"); + CreateDataSetMapping(itemsDa, "primitems"); + CreateDataSetMapping(terrainDa, "terrain"); + CreateDataSetMapping(landDa, "land"); + CreateDataSetMapping(landAccessListDa, "landaccesslist"); + CreateDataSetMapping(regionSettingsDa, "regionsettings"); + CreateDataSetMapping(regionWindlightDa, "regionwindlight"); + CreateDataSetMapping(regionEnvironmentDa, "regionenvironment"); + CreateDataSetMapping(regionSpawnPointsDa, "spawn_points"); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[SQLITE REGION DB]: {0} - {1}", e.Message, e.StackTrace); + Environment.Exit(23); + } + return; + } + + public void Dispose() + { + if (m_conn != null) + { + m_conn.Close(); + m_conn = null; + } + if (ds != null) + { + ds.Dispose(); + ds = null; + } + if (primDa != null) + { + primDa.Dispose(); + primDa = null; + } + if (shapeDa != null) + { + shapeDa.Dispose(); + shapeDa = null; + } + if (itemsDa != null) + { + itemsDa.Dispose(); + itemsDa = null; + } + if (terrainDa != null) + { + terrainDa.Dispose(); + terrainDa = null; + } + if (landDa != null) + { + landDa.Dispose(); + landDa = null; + } + if (landAccessListDa != null) + { + landAccessListDa.Dispose(); + landAccessListDa = null; + } + if (regionSettingsDa != null) + { + regionSettingsDa.Dispose(); + regionSettingsDa = null; + } + if (regionWindlightDa != null) + { + regionWindlightDa.Dispose(); + regionWindlightDa = null; + } + if (regionEnvironmentDa != null) + { + regionEnvironmentDa.Dispose(); + regionEnvironmentDa = null; + } + if (regionSpawnPointsDa != null) + { + regionSpawnPointsDa.Dispose(); + regionWindlightDa = null; + } + } + + public void StoreRegionSettings(RegionSettings rs) + { + lock (ds) + { + DataTable regionsettings = ds.Tables["regionsettings"]; + + DataRow settingsRow = regionsettings.Rows.Find(rs.RegionUUID.ToString()); + if (settingsRow == null) + { + settingsRow = regionsettings.NewRow(); + fillRegionSettingsRow(settingsRow, rs); + regionsettings.Rows.Add(settingsRow); + } + else + { + fillRegionSettingsRow(settingsRow, rs); + } + + StoreSpawnPoints(rs); + + Commit(); + } + + } + + public void StoreSpawnPoints(RegionSettings rs) + { + lock (ds) + { + // DataTable spawnpoints = ds.Tables["spawn_points"]; + + // remove region's spawnpoints + using ( + SqliteCommand cmd = + new SqliteCommand("delete from spawn_points where RegionID=:RegionID", + m_conn)) + { + + cmd.Parameters.Add(new SqliteParameter(":RegionID", rs.RegionUUID.ToString())); + cmd.ExecuteNonQuery(); + } + } + + foreach (SpawnPoint sp in rs.SpawnPoints()) + { + using (SqliteCommand cmd = new SqliteCommand("insert into spawn_points(RegionID, Yaw, Pitch, Distance)" + + "values ( :RegionID, :Yaw, :Pitch, :Distance)", m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionID", rs.RegionUUID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Yaw", sp.Yaw)); + cmd.Parameters.Add(new SqliteParameter(":Pitch", sp.Pitch)); + cmd.Parameters.Add(new SqliteParameter(":Distance", sp.Distance)); + cmd.ExecuteNonQuery(); + } + } + } + + /// + /// Load windlight settings from region storage + /// + /// RegionID + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + RegionLightShareData wl = null; + + lock (ds) + { + DataTable windlightTable = ds.Tables["regionwindlight"]; + DataRow windlightRow = windlightTable.Rows.Find(regionUUID.ToString()); + if (windlightRow == null) + { + wl = new RegionLightShareData(); + wl.regionID = regionUUID; + StoreRegionWindlightSettings(wl); + return wl; + } + wl = buildRegionWindlight(windlightRow); + return wl; + } + } + + /// + /// Remove windlight settings from region storage + /// + /// RegionID + public void RemoveRegionWindlightSettings(UUID regionID) + { + lock (ds) + { + DataTable windlightTable = ds.Tables["regionwindlight"]; + DataRow windlightRow = windlightTable.Rows.Find(regionID.ToString()); + + if (windlightRow != null) + { + windlightRow.Delete(); + } + } + Commit(); + } + + /// + /// Adds an windlight into region storage + /// + /// RegionLightShareData + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + lock (ds) + { + DataTable windlightTable = ds.Tables["regionwindlight"]; + DataRow windlightRow = windlightTable.Rows.Find(wl.regionID.ToString()); + + if (windlightRow == null) + { + windlightRow = windlightTable.NewRow(); + fillRegionWindlightRow(windlightRow, wl); + windlightTable.Rows.Add(windlightRow); + } + else + { + fillRegionWindlightRow(windlightRow, wl); + } + + Commit(); + } + } + + #region Region Environment Settings + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + lock (ds) + { + DataTable environmentTable = ds.Tables["regionenvironment"]; + DataRow row = environmentTable.Rows.Find(regionUUID.ToString()); + if (row == null) + { + return String.Empty; + } + + return (String)row["llsd_settings"]; + } + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + lock (ds) + { + DataTable environmentTable = ds.Tables["regionenvironment"]; + DataRow row = environmentTable.Rows.Find(regionUUID.ToString()); + + if (row == null) + { + row = environmentTable.NewRow(); + row["region_id"] = regionUUID.ToString(); + row["llsd_settings"] = settings; + environmentTable.Rows.Add(row); + } + else + { + row["llsd_settings"] = settings; + } + + regionEnvironmentDa.Update(ds, "regionenvironment"); + } + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + lock (ds) + { + DataTable environmentTable = ds.Tables["regionenvironment"]; + DataRow row = environmentTable.Rows.Find(regionUUID.ToString()); + + if (row != null) + { + row.Delete(); + } + + regionEnvironmentDa.Update(ds, "regionenvironment"); + } + } + + #endregion + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + lock (ds) + { + DataTable regionsettings = ds.Tables["regionsettings"]; + + string searchExp = "regionUUID = '" + regionUUID.ToString() + "'"; + DataRow[] rawsettings = regionsettings.Select(searchExp); + if (rawsettings.Length == 0) + { + RegionSettings rs = new RegionSettings(); + rs.RegionUUID = regionUUID; + rs.OnSave += StoreRegionSettings; + + StoreRegionSettings(rs); + + return rs; + } + DataRow row = rawsettings[0]; + + RegionSettings newSettings = buildRegionSettings(row); + newSettings.OnSave += StoreRegionSettings; + + LoadSpawnPoints(newSettings); + + return newSettings; + } + } + + private void LoadSpawnPoints(RegionSettings rs) + { + rs.ClearSpawnPoints(); + + DataTable spawnpoints = ds.Tables["spawn_points"]; + string byRegion = "RegionID = '" + rs.RegionUUID + "'"; + DataRow[] spForRegion = spawnpoints.Select(byRegion); + + foreach (DataRow spRow in spForRegion) + { + SpawnPoint sp = new SpawnPoint(); + sp.Pitch = (float)spRow["Pitch"]; + sp.Yaw = (float)spRow["Yaw"]; + sp.Distance = (float)spRow["Distance"]; + + rs.AddSpawnPoint(sp); + } + } + + /// + /// Adds an object into region storage + /// + /// the object + /// the region UUID + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + uint flags = obj.RootPart.GetEffectiveObjectFlags(); + + // Eligibility check + // + if ((flags & (uint)PrimFlags.Temporary) != 0) + return; + if ((flags & (uint)PrimFlags.TemporaryOnRez) != 0) + return; + + lock (ds) + { + foreach (SceneObjectPart prim in obj.Parts) + { +// m_log.Info("[REGION DB]: Adding obj: " + obj.UUID + " to region: " + regionUUID); + addPrim(prim, obj.UUID, regionUUID); + } + } + + Commit(); +// m_log.Info("[Dump of prims]: " + ds.GetXml()); + } + + /// + /// Removes an object from region storage + /// + /// the object + /// the region UUID + public void RemoveObject(UUID obj, UUID regionUUID) + { +// m_log.InfoFormat("[REGION DB]: Removing obj: {0} from region: {1}", obj.Guid, regionUUID); + + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + string selectExp = "SceneGroupID = '" + obj + "' and RegionUUID = '" + regionUUID + "'"; + lock (ds) + { + DataRow[] primRows = prims.Select(selectExp); + foreach (DataRow row in primRows) + { + // Remove shape rows + UUID uuid = new UUID((string)row["UUID"]); + DataRow shapeRow = shapes.Rows.Find(uuid.ToString()); + if (shapeRow != null) + { + shapeRow.Delete(); + } + + RemoveItems(uuid); + + // Remove prim row + row.Delete(); + } + } + + Commit(); + } + + /// + /// Remove all persisted items of the given prim. + /// The caller must acquire the necessrary synchronization locks and commit or rollback changes. + /// + /// The item UUID + private void RemoveItems(UUID uuid) + { + DataTable items = ds.Tables["primitems"]; + + String sql = String.Format("primID = '{0}'", uuid); + DataRow[] itemRows = items.Select(sql); + + foreach (DataRow itemRow in itemRows) + { + itemRow.Delete(); + } + } + + /// + /// Load persisted objects from region storage. + /// + /// The region UUID + /// List of loaded groups + public List LoadObjects(UUID regionUUID) + { + Dictionary createdObjects = new Dictionary(); + + List retvals = new List(); + + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + string byRegion = "RegionUUID = '" + regionUUID + "'"; + + lock (ds) + { + DataRow[] primsForRegion = prims.Select(byRegion); +// m_log.Info("[SQLITE REGION DB]: Loaded " + primsForRegion.Length + " prims for region: " + regionUUID); + + // First, create all groups + foreach (DataRow primRow in primsForRegion) + { + try + { + SceneObjectPart prim = null; + + string uuid = (string)primRow["UUID"]; + string objID = (string)primRow["SceneGroupID"]; + + if (uuid == objID) //is new SceneObjectGroup ? + { + prim = buildPrim(primRow); + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow != null) + { + prim.Shape = buildShape(shapeRow); + } + else + { + m_log.Warn( + "[SQLITE REGION DB]: No shape found for prim in storage, so setting default box shape"); + prim.Shape = PrimitiveBaseShape.Default; + } + + SceneObjectGroup group = new SceneObjectGroup(prim); + + createdObjects.Add(group.UUID, group); + retvals.Add(group); + LoadItems(prim); + + + } + } + catch (Exception e) + { + m_log.Error("[SQLITE REGION DB]: Failed create prim object in new group, exception and data follows"); + m_log.Error("[SQLITE REGION DB]: ", e); + foreach (DataColumn col in prims.Columns) + { + m_log.Error("[SQLITE REGION DB]: Col: " + col.ColumnName + " => " + primRow[col]); + } + } + } + + // Now fill the groups with part data + foreach (DataRow primRow in primsForRegion) + { + try + { + SceneObjectPart prim = null; + + string uuid = (string)primRow["UUID"]; + string objID = (string)primRow["SceneGroupID"]; + if (uuid != objID) //is new SceneObjectGroup ? + { + prim = buildPrim(primRow); + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow != null) + { + prim.Shape = buildShape(shapeRow); + } + else + { + m_log.Warn( + "[SQLITE REGION DB]: No shape found for prim in storage, so setting default box shape"); + prim.Shape = PrimitiveBaseShape.Default; + } + + createdObjects[new UUID(objID)].AddPart(prim); + LoadItems(prim); + } + } + catch (Exception e) + { + m_log.Error("[SQLITE REGION DB]: Failed create prim object in group, exception and data follows"); + m_log.Error("[SQLITE REGION DB]: ", e); + foreach (DataColumn col in prims.Columns) + { + m_log.Error("[SQLITE REGION DB]: Col: " + col.ColumnName + " => " + primRow[col]); + } + } + } + } + return retvals; + } + + /// + /// Load in a prim's persisted inventory. + /// + /// the prim + private void LoadItems(SceneObjectPart prim) + { +// m_log.DebugFormat("[SQLITE REGION DB]: Loading inventory for {0} {1}", prim.Name, prim.UUID); + + DataTable dbItems = ds.Tables["primitems"]; + String sql = String.Format("primID = '{0}'", prim.UUID.ToString()); + DataRow[] dbItemRows = dbItems.Select(sql); + IList inventory = new List(); + +// m_log.DebugFormat("[SQLITE REGION DB]: Found {0} items for {1} {2}", dbItemRows.Length, prim.Name, prim.UUID); + + foreach (DataRow row in dbItemRows) + { + TaskInventoryItem item = buildItem(row); + inventory.Add(item); + +// m_log.DebugFormat("[SQLITE REGION DB]: Restored item {0} {1}", item.Name, item.ItemID); + } + + prim.Inventory.RestoreInventoryItems(inventory); + } + + // Legacy entry point for when terrain was always a 256x256 hieghtmap + public void StoreTerrain(double[,] ter, UUID regionID) + { + StoreTerrain(new HeightmapTerrainData(ter), regionID); + } + + /// + /// Store a terrain revision in region storage + /// + /// terrain heightfield + /// region UUID + public void StoreTerrain(TerrainData terrData, UUID regionID) + { + lock (ds) + { + using ( + SqliteCommand cmd = new SqliteCommand("delete from terrain where RegionUUID=:RegionUUID", m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + cmd.ExecuteNonQuery(); + } + + // the following is an work around for .NET. The perf + // issues associated with it aren't as bad as you think. + String sql = "insert into terrain(RegionUUID, Revision, Heightfield)" + + " values(:RegionUUID, :Revision, :Heightfield)"; + + int terrainDBRevision; + Array terrainDBblob; + terrData.GetDatabaseBlob(out terrainDBRevision, out terrainDBblob); + + m_log.DebugFormat("{0} Storing terrain revision r {1}", LogHeader, terrainDBRevision); + + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":Revision", terrainDBRevision)); + cmd.Parameters.Add(new SqliteParameter(":Heightfield", terrainDBblob)); + cmd.ExecuteNonQuery(); + } + } + } + + /// + /// Load the latest terrain revision from region storage + /// + /// the region UUID + /// Heightfield data + public double[,] LoadTerrain(UUID regionID) + { + double[,] ret = null; + TerrainData terrData = LoadTerrain(regionID, (int)Constants.RegionSize, (int)Constants.RegionSize, (int)Constants.RegionHeight); + if (terrData != null) + ret = terrData.GetDoubles(); + return ret; + } + + // Returns 'null' if region not found + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + TerrainData terrData = null; + + lock (ds) + { + String sql = "select RegionUUID, Revision, Heightfield from terrain" + + " where RegionUUID=:RegionUUID order by Revision desc"; + + using (SqliteCommand cmd = new SqliteCommand(sql, m_conn)) + { + cmd.Parameters.Add(new SqliteParameter(":RegionUUID", regionID.ToString())); + + using (IDataReader row = cmd.ExecuteReader()) + { + int rev = 0; + if (row.Read()) + { + rev = Convert.ToInt32(row["Revision"]); + byte[] blob = (byte[])row["Heightfield"]; + terrData = TerrainData.CreateFromDatabaseBlobFactory(pSizeX, pSizeY, pSizeZ, rev, blob); + } + else + { + m_log.Warn("[SQLITE REGION DB]: No terrain found for region"); + return null; + } + + m_log.Debug("[SQLITE REGION DB]: Loaded terrain revision r" + rev.ToString()); + } + } + } + return terrData; + } + + public void RemoveLandObject(UUID globalID) + { + lock (ds) + { + // Can't use blanket SQL statements when using SqlAdapters unless you re-read the data into the adapter + // after you're done. + // replaced below code with the SqliteAdapter version. + //using (SqliteCommand cmd = new SqliteCommand("delete from land where UUID=:UUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString())); + // cmd.ExecuteNonQuery(); + //} + + //using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:UUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":UUID", globalID.ToString())); + // cmd.ExecuteNonQuery(); + //} + + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + DataRow landRow = land.Rows.Find(globalID.ToString()); + if (landRow != null) + { + landRow.Delete(); + } + List rowsToDelete = new List(); + foreach (DataRow rowToCheck in landaccesslist.Rows) + { + if (rowToCheck["LandUUID"].ToString() == globalID.ToString()) + rowsToDelete.Add(rowToCheck); + } + for (int iter = 0; iter < rowsToDelete.Count; iter++) + { + rowsToDelete[iter].Delete(); + } + } + Commit(); + } + + /// + /// + /// + /// + public void StoreLandObject(ILandObject parcel) + { + lock (ds) + { + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + + DataRow landRow = land.Rows.Find(parcel.LandData.GlobalID.ToString()); + if (landRow == null) + { + landRow = land.NewRow(); + fillLandRow(landRow, parcel.LandData, parcel.RegionUUID); + land.Rows.Add(landRow); + } + else + { + fillLandRow(landRow, parcel.LandData, parcel.RegionUUID); + } + + // I know this caused someone issues before, but OpenSim is unusable if we leave this stuff around + //using (SqliteCommand cmd = new SqliteCommand("delete from landaccesslist where LandUUID=:LandUUID", m_conn)) + //{ + // cmd.Parameters.Add(new SqliteParameter(":LandUUID", parcel.LandData.GlobalID.ToString())); + // cmd.ExecuteNonQuery(); + + // } + + // This is the slower.. but more appropriate thing to do + + // We can't modify the table with direct queries before calling Commit() and re-filling them. + List rowsToDelete = new List(); + foreach (DataRow rowToCheck in landaccesslist.Rows) + { + if (rowToCheck["LandUUID"].ToString() == parcel.LandData.GlobalID.ToString()) + rowsToDelete.Add(rowToCheck); + } + for (int iter = 0; iter < rowsToDelete.Count; iter++) + { + rowsToDelete[iter].Delete(); + landaccesslist.Rows.Remove(rowsToDelete[iter]); + } + rowsToDelete.Clear(); + foreach (LandAccessEntry entry in parcel.LandData.ParcelAccessList) + { + DataRow newAccessRow = landaccesslist.NewRow(); + fillLandAccessRow(newAccessRow, entry, parcel.LandData.GlobalID); + landaccesslist.Rows.Add(newAccessRow); + } + } + + Commit(); + } + + /// + /// + /// + /// + /// + public List LoadLandObjects(UUID regionUUID) + { + List landDataForRegion = new List(); + lock (ds) + { + DataTable land = ds.Tables["land"]; + DataTable landaccesslist = ds.Tables["landaccesslist"]; + string searchExp = "RegionUUID = '" + regionUUID + "'"; + DataRow[] rawDataForRegion = land.Select(searchExp); + foreach (DataRow rawDataLand in rawDataForRegion) + { + LandData newLand = buildLandData(rawDataLand); + string accessListSearchExp = "LandUUID = '" + newLand.GlobalID + "'"; + DataRow[] rawDataForLandAccessList = landaccesslist.Select(accessListSearchExp); + foreach (DataRow rawDataLandAccess in rawDataForLandAccessList) + { + newLand.ParcelAccessList.Add(buildLandAccessData(rawDataLandAccess)); + } + + landDataForRegion.Add(newLand); + } + } + return landDataForRegion; + } + + /// + /// + /// + public void Commit() + { +// m_log.Debug("[SQLITE]: Starting commit"); + lock (ds) + { + primDa.Update(ds, "prims"); + shapeDa.Update(ds, "primshapes"); + + itemsDa.Update(ds, "primitems"); + + terrainDa.Update(ds, "terrain"); + landDa.Update(ds, "land"); + landAccessListDa.Update(ds, "landaccesslist"); + try + { + regionSettingsDa.Update(ds, "regionsettings"); + regionWindlightDa.Update(ds, "regionwindlight"); + } + catch (SqliteException SqlEx) + { + throw new Exception( + "There was a SQL error or connection string configuration error when saving the region settings. This could be a bug, it could also happen if ConnectionString is defined in the [DatabaseService] section of StandaloneCommon.ini in the config_include folder. This could also happen if the config_include folder doesn't exist or if the OpenSim.ini [Architecture] section isn't set. If this is your first time running OpenSimulator, please restart the simulator and bug a developer to fix this!", + SqlEx); + } + ds.AcceptChanges(); + } + } + + /// + /// See + /// + public void Shutdown() + { + Commit(); + } + + /*********************************************************************** + * + * Database Definition Functions + * + * This should be db agnostic as we define them in ADO.NET terms + * + **********************************************************************/ + + protected void CreateDataSetMapping(IDataAdapter da, string tableName) + { + ITableMapping dbMapping = da.TableMappings.Add(tableName, tableName); + foreach (DataColumn col in ds.Tables[tableName].Columns) + { + dbMapping.ColumnMappings.Add(col.ColumnName, col.ColumnName); + } + } + + /// + /// + /// + /// + /// + /// + private static void createCol(DataTable dt, string name, Type type) + { + DataColumn col = new DataColumn(name, type); + dt.Columns.Add(col); + } + + /// + /// Creates the "terrain" table + /// + /// terrain table DataTable + private static DataTable createTerrainTable() + { + DataTable terrain = new DataTable("terrain"); + + createCol(terrain, "RegionUUID", typeof(String)); + createCol(terrain, "Revision", typeof(Int32)); + createCol(terrain, "Heightfield", typeof(Byte[])); + + return terrain; + } + + /// + /// Creates the "prims" table + /// + /// prim table DataTable + private static DataTable createPrimTable() + { + DataTable prims = new DataTable("prims"); + + createCol(prims, "UUID", typeof(String)); + createCol(prims, "RegionUUID", typeof(String)); + createCol(prims, "CreationDate", typeof(Int32)); + createCol(prims, "Name", typeof(String)); + createCol(prims, "SceneGroupID", typeof(String)); + // various text fields + createCol(prims, "Text", typeof(String)); + createCol(prims, "ColorR", typeof(Int32)); + createCol(prims, "ColorG", typeof(Int32)); + createCol(prims, "ColorB", typeof(Int32)); + createCol(prims, "ColorA", typeof(Int32)); + createCol(prims, "Description", typeof(String)); + createCol(prims, "SitName", typeof(String)); + createCol(prims, "TouchName", typeof(String)); + // permissions + createCol(prims, "ObjectFlags", typeof(Int32)); + createCol(prims, "CreatorID", typeof(String)); + createCol(prims, "OwnerID", typeof(String)); + createCol(prims, "GroupID", typeof(String)); + createCol(prims, "LastOwnerID", typeof(String)); + createCol(prims, "OwnerMask", typeof(Int32)); + createCol(prims, "NextOwnerMask", typeof(Int32)); + createCol(prims, "GroupMask", typeof(Int32)); + createCol(prims, "EveryoneMask", typeof(Int32)); + createCol(prims, "BaseMask", typeof(Int32)); + // vectors + createCol(prims, "PositionX", typeof(Double)); + createCol(prims, "PositionY", typeof(Double)); + createCol(prims, "PositionZ", typeof(Double)); + createCol(prims, "GroupPositionX", typeof(Double)); + createCol(prims, "GroupPositionY", typeof(Double)); + createCol(prims, "GroupPositionZ", typeof(Double)); + createCol(prims, "VelocityX", typeof(Double)); + createCol(prims, "VelocityY", typeof(Double)); + createCol(prims, "VelocityZ", typeof(Double)); + createCol(prims, "AngularVelocityX", typeof(Double)); + createCol(prims, "AngularVelocityY", typeof(Double)); + createCol(prims, "AngularVelocityZ", typeof(Double)); + createCol(prims, "AccelerationX", typeof(Double)); + createCol(prims, "AccelerationY", typeof(Double)); + createCol(prims, "AccelerationZ", typeof(Double)); + // quaternions + createCol(prims, "RotationX", typeof(Double)); + createCol(prims, "RotationY", typeof(Double)); + createCol(prims, "RotationZ", typeof(Double)); + createCol(prims, "RotationW", typeof(Double)); + + // sit target + createCol(prims, "SitTargetOffsetX", typeof(Double)); + createCol(prims, "SitTargetOffsetY", typeof(Double)); + createCol(prims, "SitTargetOffsetZ", typeof(Double)); + + createCol(prims, "SitTargetOrientW", typeof(Double)); + createCol(prims, "SitTargetOrientX", typeof(Double)); + createCol(prims, "SitTargetOrientY", typeof(Double)); + createCol(prims, "SitTargetOrientZ", typeof(Double)); + + createCol(prims, "PayPrice", typeof(Int32)); + createCol(prims, "PayButton1", typeof(Int32)); + createCol(prims, "PayButton2", typeof(Int32)); + createCol(prims, "PayButton3", typeof(Int32)); + createCol(prims, "PayButton4", typeof(Int32)); + + createCol(prims, "LoopedSound", typeof(String)); + createCol(prims, "LoopedSoundGain", typeof(Double)); + createCol(prims, "TextureAnimation", typeof(String)); + createCol(prims, "ParticleSystem", typeof(String)); + + createCol(prims, "OmegaX", typeof(Double)); + createCol(prims, "OmegaY", typeof(Double)); + createCol(prims, "OmegaZ", typeof(Double)); + + createCol(prims, "CameraEyeOffsetX", typeof(Double)); + createCol(prims, "CameraEyeOffsetY", typeof(Double)); + createCol(prims, "CameraEyeOffsetZ", typeof(Double)); + + createCol(prims, "CameraAtOffsetX", typeof(Double)); + createCol(prims, "CameraAtOffsetY", typeof(Double)); + createCol(prims, "CameraAtOffsetZ", typeof(Double)); + + createCol(prims, "ForceMouselook", typeof(Int16)); + + createCol(prims, "ScriptAccessPin", typeof(Int32)); + + createCol(prims, "AllowedDrop", typeof(Int16)); + createCol(prims, "DieAtEdge", typeof(Int16)); + + createCol(prims, "SalePrice", typeof(Int32)); + createCol(prims, "SaleType", typeof(Int16)); + + // click action + createCol(prims, "ClickAction", typeof(Byte)); + + createCol(prims, "Material", typeof(Byte)); + + createCol(prims, "CollisionSound", typeof(String)); + createCol(prims, "CollisionSoundVolume", typeof(Double)); + + createCol(prims, "VolumeDetect", typeof(Int16)); + + createCol(prims, "MediaURL", typeof(String)); + + createCol(prims, "AttachedPosX", typeof(Double)); + createCol(prims, "AttachedPosY", typeof(Double)); + createCol(prims, "AttachedPosZ", typeof(Double)); + + createCol(prims, "DynAttrs", typeof(String)); + + createCol(prims, "PhysicsShapeType", typeof(Byte)); + createCol(prims, "Density", typeof(Double)); + createCol(prims, "GravityModifier", typeof(Double)); + createCol(prims, "Friction", typeof(Double)); + createCol(prims, "Restitution", typeof(Double)); + + createCol(prims, "KeyframeMotion", typeof(Byte[])); + // Add in contraints + prims.PrimaryKey = new DataColumn[] { prims.Columns["UUID"] }; + + return prims; + } + + /// + /// Creates "primshapes" table + /// + /// shape table DataTable + private static DataTable createShapeTable() + { + DataTable shapes = new DataTable("primshapes"); + createCol(shapes, "UUID", typeof(String)); + // shape is an enum + createCol(shapes, "Shape", typeof(Int32)); + // vectors + createCol(shapes, "ScaleX", typeof(Double)); + createCol(shapes, "ScaleY", typeof(Double)); + createCol(shapes, "ScaleZ", typeof(Double)); + // paths + createCol(shapes, "PCode", typeof(Int32)); + createCol(shapes, "PathBegin", typeof(Int32)); + createCol(shapes, "PathEnd", typeof(Int32)); + createCol(shapes, "PathScaleX", typeof(Int32)); + createCol(shapes, "PathScaleY", typeof(Int32)); + createCol(shapes, "PathShearX", typeof(Int32)); + createCol(shapes, "PathShearY", typeof(Int32)); + createCol(shapes, "PathSkew", typeof(Int32)); + createCol(shapes, "PathCurve", typeof(Int32)); + createCol(shapes, "PathRadiusOffset", typeof(Int32)); + createCol(shapes, "PathRevolutions", typeof(Int32)); + createCol(shapes, "PathTaperX", typeof(Int32)); + createCol(shapes, "PathTaperY", typeof(Int32)); + createCol(shapes, "PathTwist", typeof(Int32)); + createCol(shapes, "PathTwistBegin", typeof(Int32)); + // profile + createCol(shapes, "ProfileBegin", typeof(Int32)); + createCol(shapes, "ProfileEnd", typeof(Int32)); + createCol(shapes, "ProfileCurve", typeof(Int32)); + createCol(shapes, "ProfileHollow", typeof(Int32)); + createCol(shapes, "State", typeof(Int32)); + // text TODO: this isn't right, but I'm not sure the right + // way to specify this as a blob atm + createCol(shapes, "Texture", typeof(Byte[])); + createCol(shapes, "ExtraParams", typeof(Byte[])); + createCol(shapes, "Media", typeof(String)); + + shapes.PrimaryKey = new DataColumn[] { shapes.Columns["UUID"] }; + + return shapes; + } + + /// + /// creates "primitems" table + /// + /// item table DataTable + private static DataTable createItemsTable() + { + DataTable items = new DataTable("primitems"); + + createCol(items, "itemID", typeof(String)); + createCol(items, "primID", typeof(String)); + createCol(items, "assetID", typeof(String)); + createCol(items, "parentFolderID", typeof(String)); + + createCol(items, "invType", typeof(Int32)); + createCol(items, "assetType", typeof(Int32)); + + createCol(items, "name", typeof(String)); + createCol(items, "description", typeof(String)); + + createCol(items, "creationDate", typeof(Int64)); + createCol(items, "creatorID", typeof(String)); + createCol(items, "ownerID", typeof(String)); + createCol(items, "lastOwnerID", typeof(String)); + createCol(items, "groupID", typeof(String)); + + createCol(items, "nextPermissions", typeof(UInt32)); + createCol(items, "currentPermissions", typeof(UInt32)); + createCol(items, "basePermissions", typeof(UInt32)); + createCol(items, "everyonePermissions", typeof(UInt32)); + createCol(items, "groupPermissions", typeof(UInt32)); + createCol(items, "flags", typeof(UInt32)); + + items.PrimaryKey = new DataColumn[] { items.Columns["itemID"] }; + + return items; + } + + /// + /// Creates "land" table + /// + /// land table DataTable + private static DataTable createLandTable() + { + DataTable land = new DataTable("land"); + createCol(land, "UUID", typeof(String)); + createCol(land, "RegionUUID", typeof(String)); + createCol(land, "LocalLandID", typeof(UInt32)); + + // Bitmap is a byte[512] + createCol(land, "Bitmap", typeof(Byte[])); + + createCol(land, "Name", typeof(String)); + createCol(land, "Desc", typeof(String)); + createCol(land, "OwnerUUID", typeof(String)); + createCol(land, "IsGroupOwned", typeof(Boolean)); + createCol(land, "Area", typeof(Int32)); + createCol(land, "AuctionID", typeof(Int32)); //Unemplemented + createCol(land, "Category", typeof(Int32)); //Enum OpenMetaverse.Parcel.ParcelCategory + createCol(land, "ClaimDate", typeof(Int32)); + createCol(land, "ClaimPrice", typeof(Int32)); + createCol(land, "GroupUUID", typeof(string)); + createCol(land, "SalePrice", typeof(Int32)); + createCol(land, "LandStatus", typeof(Int32)); //Enum. OpenMetaverse.Parcel.ParcelStatus + createCol(land, "LandFlags", typeof(UInt32)); + createCol(land, "LandingType", typeof(Byte)); + createCol(land, "MediaAutoScale", typeof(Byte)); + createCol(land, "MediaTextureUUID", typeof(String)); + createCol(land, "MediaURL", typeof(String)); + createCol(land, "MusicURL", typeof(String)); + createCol(land, "PassHours", typeof(Double)); + createCol(land, "PassPrice", typeof(UInt32)); + createCol(land, "SnapshotUUID", typeof(String)); + createCol(land, "UserLocationX", typeof(Double)); + createCol(land, "UserLocationY", typeof(Double)); + createCol(land, "UserLocationZ", typeof(Double)); + createCol(land, "UserLookAtX", typeof(Double)); + createCol(land, "UserLookAtY", typeof(Double)); + createCol(land, "UserLookAtZ", typeof(Double)); + createCol(land, "AuthbuyerID", typeof(String)); + createCol(land, "OtherCleanTime", typeof(Int32)); + createCol(land, "Dwell", typeof(Int32)); + createCol(land, "MediaType", typeof(String)); + createCol(land, "MediaDescription", typeof(String)); + createCol(land, "MediaSize", typeof(String)); + createCol(land, "MediaLoop", typeof(Boolean)); + createCol(land, "ObscureMedia", typeof(Boolean)); + createCol(land, "ObscureMusic", typeof(Boolean)); + + land.PrimaryKey = new DataColumn[] { land.Columns["UUID"] }; + + return land; + } + + /// + /// create "landaccesslist" table + /// + /// Landacceslist DataTable + private static DataTable createLandAccessListTable() + { + DataTable landaccess = new DataTable("landaccesslist"); + createCol(landaccess, "LandUUID", typeof(String)); + createCol(landaccess, "AccessUUID", typeof(String)); + createCol(landaccess, "Flags", typeof(UInt32)); + + return landaccess; + } + + private static DataTable createRegionSettingsTable() + { + DataTable regionsettings = new DataTable("regionsettings"); + createCol(regionsettings, "regionUUID", typeof(String)); + createCol(regionsettings, "block_terraform", typeof(Int32)); + createCol(regionsettings, "block_fly", typeof(Int32)); + createCol(regionsettings, "allow_damage", typeof(Int32)); + createCol(regionsettings, "restrict_pushing", typeof(Int32)); + createCol(regionsettings, "allow_land_resell", typeof(Int32)); + createCol(regionsettings, "allow_land_join_divide", typeof(Int32)); + createCol(regionsettings, "block_show_in_search", typeof(Int32)); + createCol(regionsettings, "agent_limit", typeof(Int32)); + createCol(regionsettings, "object_bonus", typeof(Double)); + createCol(regionsettings, "maturity", typeof(Int32)); + createCol(regionsettings, "disable_scripts", typeof(Int32)); + createCol(regionsettings, "disable_collisions", typeof(Int32)); + createCol(regionsettings, "disable_physics", typeof(Int32)); + createCol(regionsettings, "terrain_texture_1", typeof(String)); + createCol(regionsettings, "terrain_texture_2", typeof(String)); + createCol(regionsettings, "terrain_texture_3", typeof(String)); + createCol(regionsettings, "terrain_texture_4", typeof(String)); + createCol(regionsettings, "elevation_1_nw", typeof(Double)); + createCol(regionsettings, "elevation_2_nw", typeof(Double)); + createCol(regionsettings, "elevation_1_ne", typeof(Double)); + createCol(regionsettings, "elevation_2_ne", typeof(Double)); + createCol(regionsettings, "elevation_1_se", typeof(Double)); + createCol(regionsettings, "elevation_2_se", typeof(Double)); + createCol(regionsettings, "elevation_1_sw", typeof(Double)); + createCol(regionsettings, "elevation_2_sw", typeof(Double)); + createCol(regionsettings, "water_height", typeof(Double)); + createCol(regionsettings, "terrain_raise_limit", typeof(Double)); + createCol(regionsettings, "terrain_lower_limit", typeof(Double)); + createCol(regionsettings, "use_estate_sun", typeof(Int32)); + createCol(regionsettings, "sandbox", typeof(Int32)); + createCol(regionsettings, "sunvectorx", typeof(Double)); + createCol(regionsettings, "sunvectory", typeof(Double)); + createCol(regionsettings, "sunvectorz", typeof(Double)); + createCol(regionsettings, "fixed_sun", typeof(Int32)); + createCol(regionsettings, "sun_position", typeof(Double)); + createCol(regionsettings, "covenant", typeof(String)); + createCol(regionsettings, "covenant_datetime", typeof(Int32)); + createCol(regionsettings, "map_tile_ID", typeof(String)); + createCol(regionsettings, "TelehubObject", typeof(String)); + createCol(regionsettings, "parcel_tile_ID", typeof(String)); + regionsettings.PrimaryKey = new DataColumn[] { regionsettings.Columns["regionUUID"] }; + return regionsettings; + } + + /// + /// create "regionwindlight" table + /// + /// RegionWindlight DataTable + private static DataTable createRegionWindlightTable() + { + DataTable regionwindlight = new DataTable("regionwindlight"); + createCol(regionwindlight, "region_id", typeof(String)); + createCol(regionwindlight, "water_color_r", typeof(Double)); + createCol(regionwindlight, "water_color_g", typeof(Double)); + createCol(regionwindlight, "water_color_b", typeof(Double)); + createCol(regionwindlight, "water_color_i", typeof(Double)); + createCol(regionwindlight, "water_fog_density_exponent", typeof(Double)); + createCol(regionwindlight, "underwater_fog_modifier", typeof(Double)); + createCol(regionwindlight, "reflection_wavelet_scale_1", typeof(Double)); + createCol(regionwindlight, "reflection_wavelet_scale_2", typeof(Double)); + createCol(regionwindlight, "reflection_wavelet_scale_3", typeof(Double)); + createCol(regionwindlight, "fresnel_scale", typeof(Double)); + createCol(regionwindlight, "fresnel_offset", typeof(Double)); + createCol(regionwindlight, "refract_scale_above", typeof(Double)); + createCol(regionwindlight, "refract_scale_below", typeof(Double)); + createCol(regionwindlight, "blur_multiplier", typeof(Double)); + createCol(regionwindlight, "big_wave_direction_x", typeof(Double)); + createCol(regionwindlight, "big_wave_direction_y", typeof(Double)); + createCol(regionwindlight, "little_wave_direction_x", typeof(Double)); + createCol(regionwindlight, "little_wave_direction_y", typeof(Double)); + createCol(regionwindlight, "normal_map_texture", typeof(String)); + createCol(regionwindlight, "horizon_r", typeof(Double)); + createCol(regionwindlight, "horizon_g", typeof(Double)); + createCol(regionwindlight, "horizon_b", typeof(Double)); + createCol(regionwindlight, "horizon_i", typeof(Double)); + createCol(regionwindlight, "haze_horizon", typeof(Double)); + createCol(regionwindlight, "blue_density_r", typeof(Double)); + createCol(regionwindlight, "blue_density_g", typeof(Double)); + createCol(regionwindlight, "blue_density_b", typeof(Double)); + createCol(regionwindlight, "blue_density_i", typeof(Double)); + createCol(regionwindlight, "haze_density", typeof(Double)); + createCol(regionwindlight, "density_multiplier", typeof(Double)); + createCol(regionwindlight, "distance_multiplier", typeof(Double)); + createCol(regionwindlight, "max_altitude", typeof(Int32)); + createCol(regionwindlight, "sun_moon_color_r", typeof(Double)); + createCol(regionwindlight, "sun_moon_color_g", typeof(Double)); + createCol(regionwindlight, "sun_moon_color_b", typeof(Double)); + createCol(regionwindlight, "sun_moon_color_i", typeof(Double)); + createCol(regionwindlight, "sun_moon_position", typeof(Double)); + createCol(regionwindlight, "ambient_r", typeof(Double)); + createCol(regionwindlight, "ambient_g", typeof(Double)); + createCol(regionwindlight, "ambient_b", typeof(Double)); + createCol(regionwindlight, "ambient_i", typeof(Double)); + createCol(regionwindlight, "east_angle", typeof(Double)); + createCol(regionwindlight, "sun_glow_focus", typeof(Double)); + createCol(regionwindlight, "sun_glow_size", typeof(Double)); + createCol(regionwindlight, "scene_gamma", typeof(Double)); + createCol(regionwindlight, "star_brightness", typeof(Double)); + createCol(regionwindlight, "cloud_color_r", typeof(Double)); + createCol(regionwindlight, "cloud_color_g", typeof(Double)); + createCol(regionwindlight, "cloud_color_b", typeof(Double)); + createCol(regionwindlight, "cloud_color_i", typeof(Double)); + createCol(regionwindlight, "cloud_x", typeof(Double)); + createCol(regionwindlight, "cloud_y", typeof(Double)); + createCol(regionwindlight, "cloud_density", typeof(Double)); + createCol(regionwindlight, "cloud_coverage", typeof(Double)); + createCol(regionwindlight, "cloud_scale", typeof(Double)); + createCol(regionwindlight, "cloud_detail_x", typeof(Double)); + createCol(regionwindlight, "cloud_detail_y", typeof(Double)); + createCol(regionwindlight, "cloud_detail_density", typeof(Double)); + createCol(regionwindlight, "cloud_scroll_x", typeof(Double)); + createCol(regionwindlight, "cloud_scroll_x_lock", typeof(Int32)); + createCol(regionwindlight, "cloud_scroll_y", typeof(Double)); + createCol(regionwindlight, "cloud_scroll_y_lock", typeof(Int32)); + createCol(regionwindlight, "draw_classic_clouds", typeof(Int32)); + + regionwindlight.PrimaryKey = new DataColumn[] { regionwindlight.Columns["region_id"] }; + return regionwindlight; + } + + private static DataTable createRegionEnvironmentTable() + { + DataTable regionEnvironment = new DataTable("regionenvironment"); + createCol(regionEnvironment, "region_id", typeof(String)); + createCol(regionEnvironment, "llsd_settings", typeof(String)); + + regionEnvironment.PrimaryKey = new DataColumn[] { regionEnvironment.Columns["region_id"] }; + + return regionEnvironment; + } + + private static DataTable createRegionSpawnPointsTable() + { + DataTable spawn_points = new DataTable("spawn_points"); + createCol(spawn_points, "regionID", typeof(String)); + createCol(spawn_points, "Yaw", typeof(float)); + createCol(spawn_points, "Pitch", typeof(float)); + createCol(spawn_points, "Distance", typeof(float)); + + return spawn_points; + } + + /*********************************************************************** + * + * Convert between ADO.NET <=> OpenSim Objects + * + * These should be database independant + * + **********************************************************************/ + + /// + /// + /// + /// + /// + private SceneObjectPart buildPrim(DataRow row) + { + // Code commented. Uncomment to test the unit test inline. + + // The unit test mentions this commented code for the purposes + // of debugging a unit test failure + + // SceneObjectGroup sog = new SceneObjectGroup(); + // SceneObjectPart sop = new SceneObjectPart(); + // sop.LocalId = 1; + // sop.Name = "object1"; + // sop.Description = "object1"; + // sop.Text = ""; + // sop.SitName = ""; + // sop.TouchName = ""; + // sop.UUID = UUID.Random(); + // sop.Shape = PrimitiveBaseShape.Default; + // sog.SetRootPart(sop); + // Add breakpoint in above line. Check sop fields. + + // TODO: this doesn't work yet because something more + // interesting has to be done to actually get these values + // back out. Not enough time to figure it out yet. + + SceneObjectPart prim = new SceneObjectPart(); + prim.UUID = new UUID((String)row["UUID"]); + // explicit conversion of integers is required, which sort + // of sucks. No idea if there is a shortcut here or not. + prim.CreationDate = Convert.ToInt32(row["CreationDate"]); + prim.Name = row["Name"] == DBNull.Value ? string.Empty : (string)row["Name"]; + // various text fields + prim.Text = (String)row["Text"]; + prim.Color = Color.FromArgb(Convert.ToInt32(row["ColorA"]), + Convert.ToInt32(row["ColorR"]), + Convert.ToInt32(row["ColorG"]), + Convert.ToInt32(row["ColorB"])); + prim.Description = (String)row["Description"]; + prim.SitName = (String)row["SitName"]; + prim.TouchName = (String)row["TouchName"]; + // permissions + prim.Flags = (PrimFlags)Convert.ToUInt32(row["ObjectFlags"]); + prim.CreatorIdentification = (String)row["CreatorID"]; + prim.OwnerID = new UUID((String)row["OwnerID"]); + prim.GroupID = new UUID((String)row["GroupID"]); + prim.LastOwnerID = new UUID((String)row["LastOwnerID"]); + prim.OwnerMask = Convert.ToUInt32(row["OwnerMask"]); + prim.NextOwnerMask = Convert.ToUInt32(row["NextOwnerMask"]); + prim.GroupMask = Convert.ToUInt32(row["GroupMask"]); + prim.EveryoneMask = Convert.ToUInt32(row["EveryoneMask"]); + prim.BaseMask = Convert.ToUInt32(row["BaseMask"]); + // vectors + prim.OffsetPosition = new Vector3( + Convert.ToSingle(row["PositionX"]), + Convert.ToSingle(row["PositionY"]), + Convert.ToSingle(row["PositionZ"]) + ); + prim.GroupPosition = new Vector3( + Convert.ToSingle(row["GroupPositionX"]), + Convert.ToSingle(row["GroupPositionY"]), + Convert.ToSingle(row["GroupPositionZ"]) + ); + prim.Velocity = new Vector3( + Convert.ToSingle(row["VelocityX"]), + Convert.ToSingle(row["VelocityY"]), + Convert.ToSingle(row["VelocityZ"]) + ); + prim.AngularVelocity = new Vector3( + Convert.ToSingle(row["AngularVelocityX"]), + Convert.ToSingle(row["AngularVelocityY"]), + Convert.ToSingle(row["AngularVelocityZ"]) + ); + prim.Acceleration = new Vector3( + Convert.ToSingle(row["AccelerationX"]), + Convert.ToSingle(row["AccelerationY"]), + Convert.ToSingle(row["AccelerationZ"]) + ); + // quaternions + prim.RotationOffset = new Quaternion( + Convert.ToSingle(row["RotationX"]), + Convert.ToSingle(row["RotationY"]), + Convert.ToSingle(row["RotationZ"]), + Convert.ToSingle(row["RotationW"]) + ); + + prim.SitTargetPositionLL = new Vector3( + Convert.ToSingle(row["SitTargetOffsetX"]), + Convert.ToSingle(row["SitTargetOffsetY"]), + Convert.ToSingle(row["SitTargetOffsetZ"])); + prim.SitTargetOrientationLL = new Quaternion( + Convert.ToSingle( + row["SitTargetOrientX"]), + Convert.ToSingle( + row["SitTargetOrientY"]), + Convert.ToSingle( + row["SitTargetOrientZ"]), + Convert.ToSingle( + row["SitTargetOrientW"])); + + prim.ClickAction = Convert.ToByte(row["ClickAction"]); + prim.PayPrice[0] = Convert.ToInt32(row["PayPrice"]); + prim.PayPrice[1] = Convert.ToInt32(row["PayButton1"]); + prim.PayPrice[2] = Convert.ToInt32(row["PayButton2"]); + prim.PayPrice[3] = Convert.ToInt32(row["PayButton3"]); + prim.PayPrice[4] = Convert.ToInt32(row["PayButton4"]); + + prim.Sound = new UUID(row["LoopedSound"].ToString()); + prim.SoundGain = Convert.ToSingle(row["LoopedSoundGain"]); + prim.SoundFlags = 1; // If it's persisted at all, it's looped + + if (!row.IsNull("TextureAnimation")) + prim.TextureAnimation = Convert.FromBase64String(row["TextureAnimation"].ToString()); + if (!row.IsNull("ParticleSystem")) + prim.ParticleSystem = Convert.FromBase64String(row["ParticleSystem"].ToString()); + + prim.AngularVelocity = new Vector3( + Convert.ToSingle(row["OmegaX"]), + Convert.ToSingle(row["OmegaY"]), + Convert.ToSingle(row["OmegaZ"]) + ); + + prim.SetCameraEyeOffset(new Vector3( + Convert.ToSingle(row["CameraEyeOffsetX"]), + Convert.ToSingle(row["CameraEyeOffsetY"]), + Convert.ToSingle(row["CameraEyeOffsetZ"]) + )); + + prim.SetCameraAtOffset(new Vector3( + Convert.ToSingle(row["CameraAtOffsetX"]), + Convert.ToSingle(row["CameraAtOffsetY"]), + Convert.ToSingle(row["CameraAtOffsetZ"]) + )); + + if (Convert.ToInt16(row["ForceMouselook"]) != 0) + prim.SetForceMouselook(true); + + prim.ScriptAccessPin = Convert.ToInt32(row["ScriptAccessPin"]); + + if (Convert.ToInt16(row["AllowedDrop"]) != 0) + prim.AllowedDrop = true; + + if (Convert.ToInt16(row["DieAtEdge"]) != 0) + prim.DIE_AT_EDGE = true; + + prim.SalePrice = Convert.ToInt32(row["SalePrice"]); + prim.ObjectSaleType = Convert.ToByte(row["SaleType"]); + + prim.Material = Convert.ToByte(row["Material"]); + + prim.CollisionSound = new UUID(row["CollisionSound"].ToString()); + prim.CollisionSoundVolume = Convert.ToSingle(row["CollisionSoundVolume"]); + + if (Convert.ToInt16(row["VolumeDetect"]) != 0) + prim.VolumeDetectActive = true; + + if (!(row["MediaURL"] is System.DBNull)) + { +// m_log.DebugFormat("[SQLITE]: MediaUrl type [{0}]", row["MediaURL"].GetType()); + prim.MediaUrl = (string)row["MediaURL"]; + } + + prim.AttachedPos = new Vector3( + Convert.ToSingle(row["AttachedPosX"]), + Convert.ToSingle(row["AttachedPosY"]), + Convert.ToSingle(row["AttachedPosZ"]) + ); + + if (!(row["DynAttrs"] is System.DBNull)) + { + //m_log.DebugFormat("[SQLITE]: DynAttrs type [{0}]", row["DynAttrs"].GetType()); + prim.DynAttrs = DAMap.FromXml((string)row["DynAttrs"]); + } + else + { + prim.DynAttrs = new DAMap(); + } + + prim.PhysicsShapeType = Convert.ToByte(row["PhysicsShapeType"]); + prim.Density = Convert.ToSingle(row["Density"]); + prim.GravityModifier = Convert.ToSingle(row["GravityModifier"]); + prim.Friction = Convert.ToSingle(row["Friction"]); + prim.Restitution = Convert.ToSingle(row["Restitution"]); + + + if (!(row["KeyframeMotion"] is DBNull)) + { + Byte[] data = (byte[])row["KeyframeMotion"]; + if (data.Length > 0) + prim.KeyframeMotion = KeyframeMotion.FromData(null, data); + else + prim.KeyframeMotion = null; + } + else + { + prim.KeyframeMotion = null; + } + + return prim; + } + + /// + /// Build a prim inventory item from the persisted data. + /// + /// + /// + private static TaskInventoryItem buildItem(DataRow row) + { + TaskInventoryItem taskItem = new TaskInventoryItem(); + + taskItem.ItemID = new UUID((String)row["itemID"]); + taskItem.ParentPartID = new UUID((String)row["primID"]); + taskItem.AssetID = new UUID((String)row["assetID"]); + taskItem.ParentID = new UUID((String)row["parentFolderID"]); + + taskItem.InvType = Convert.ToInt32(row["invType"]); + taskItem.Type = Convert.ToInt32(row["assetType"]); + + taskItem.Name = (String)row["name"]; + taskItem.Description = (String)row["description"]; + taskItem.CreationDate = Convert.ToUInt32(row["creationDate"]); + taskItem.CreatorIdentification = (String)row["creatorID"]; + taskItem.OwnerID = new UUID((String)row["ownerID"]); + taskItem.LastOwnerID = new UUID((String)row["lastOwnerID"]); + taskItem.GroupID = new UUID((String)row["groupID"]); + + taskItem.NextPermissions = Convert.ToUInt32(row["nextPermissions"]); + taskItem.CurrentPermissions = Convert.ToUInt32(row["currentPermissions"]); + taskItem.BasePermissions = Convert.ToUInt32(row["basePermissions"]); + taskItem.EveryonePermissions = Convert.ToUInt32(row["everyonePermissions"]); + taskItem.GroupPermissions = Convert.ToUInt32(row["groupPermissions"]); + taskItem.Flags = Convert.ToUInt32(row["flags"]); + + return taskItem; + } + + /// + /// Build a Land Data from the persisted data. + /// + /// + /// + private LandData buildLandData(DataRow row) + { + LandData newData = new LandData(); + + newData.GlobalID = new UUID((String)row["UUID"]); + newData.LocalID = Convert.ToInt32(row["LocalLandID"]); + + // Bitmap is a byte[512] + newData.Bitmap = (Byte[])row["Bitmap"]; + + newData.Name = (String)row["Name"]; + newData.Description = (String)row["Desc"]; + newData.OwnerID = (UUID)(String)row["OwnerUUID"]; + newData.IsGroupOwned = (Boolean)row["IsGroupOwned"]; + newData.Area = Convert.ToInt32(row["Area"]); + newData.AuctionID = Convert.ToUInt32(row["AuctionID"]); //Unemplemented + newData.Category = (ParcelCategory)Convert.ToInt32(row["Category"]); + //Enum OpenMetaverse.Parcel.ParcelCategory + newData.ClaimDate = Convert.ToInt32(row["ClaimDate"]); + newData.ClaimPrice = Convert.ToInt32(row["ClaimPrice"]); + newData.GroupID = new UUID((String)row["GroupUUID"]); + newData.SalePrice = Convert.ToInt32(row["SalePrice"]); + newData.Status = (ParcelStatus)Convert.ToInt32(row["LandStatus"]); + //Enum. OpenMetaverse.Parcel.ParcelStatus + newData.Flags = Convert.ToUInt32(row["LandFlags"]); + newData.LandingType = (Byte)row["LandingType"]; + newData.MediaAutoScale = (Byte)row["MediaAutoScale"]; + newData.MediaID = new UUID((String)row["MediaTextureUUID"]); + newData.MediaURL = (String)row["MediaURL"]; + newData.MusicURL = (String)row["MusicURL"]; + newData.PassHours = Convert.ToSingle(row["PassHours"]); + newData.PassPrice = Convert.ToInt32(row["PassPrice"]); + newData.SnapshotID = (UUID)(String)row["SnapshotUUID"]; + newData.Dwell = Convert.ToInt32(row["Dwell"]); + newData.MediaType = (String)row["MediaType"]; + newData.MediaDescription = (String)row["MediaDescription"]; + newData.MediaWidth = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[0]); + newData.MediaHeight = Convert.ToInt32((((string)row["MediaSize"]).Split(','))[1]); + newData.MediaLoop = Convert.ToBoolean(row["MediaLoop"]); + newData.ObscureMedia = Convert.ToBoolean(row["ObscureMedia"]); + newData.ObscureMusic = Convert.ToBoolean(row["ObscureMusic"]); + try + { + newData.UserLocation = + new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]), + Convert.ToSingle(row["UserLocationZ"])); + newData.UserLookAt = + new Vector3(Convert.ToSingle(row["UserLookAtX"]), Convert.ToSingle(row["UserLookAtY"]), + Convert.ToSingle(row["UserLookAtZ"])); + + } + catch (InvalidCastException) + { + m_log.ErrorFormat("[SQLITE REGION DB]: unable to get parcel telehub settings for {1}", newData.Name); + newData.UserLocation = Vector3.Zero; + newData.UserLookAt = Vector3.Zero; + } + newData.ParcelAccessList = new List(); + UUID authBuyerID = UUID.Zero; + + UUID.TryParse((string)row["AuthbuyerID"], out authBuyerID); + + newData.OtherCleanTime = Convert.ToInt32(row["OtherCleanTime"]); + + return newData; + } + + private RegionSettings buildRegionSettings(DataRow row) + { + RegionSettings newSettings = new RegionSettings(); + + newSettings.RegionUUID = new UUID((string)row["regionUUID"]); + newSettings.BlockTerraform = Convert.ToBoolean(row["block_terraform"]); + newSettings.AllowDamage = Convert.ToBoolean(row["allow_damage"]); + newSettings.BlockFly = Convert.ToBoolean(row["block_fly"]); + newSettings.RestrictPushing = Convert.ToBoolean(row["restrict_pushing"]); + newSettings.AllowLandResell = Convert.ToBoolean(row["allow_land_resell"]); + newSettings.AllowLandJoinDivide = Convert.ToBoolean(row["allow_land_join_divide"]); + newSettings.BlockShowInSearch = Convert.ToBoolean(row["block_show_in_search"]); + newSettings.AgentLimit = Convert.ToInt32(row["agent_limit"]); + newSettings.ObjectBonus = Convert.ToDouble(row["object_bonus"]); + newSettings.Maturity = Convert.ToInt32(row["maturity"]); + newSettings.DisableScripts = Convert.ToBoolean(row["disable_scripts"]); + newSettings.DisableCollisions = Convert.ToBoolean(row["disable_collisions"]); + newSettings.DisablePhysics = Convert.ToBoolean(row["disable_physics"]); + newSettings.TerrainTexture1 = new UUID((String)row["terrain_texture_1"]); + newSettings.TerrainTexture2 = new UUID((String)row["terrain_texture_2"]); + newSettings.TerrainTexture3 = new UUID((String)row["terrain_texture_3"]); + newSettings.TerrainTexture4 = new UUID((String)row["terrain_texture_4"]); + newSettings.Elevation1NW = Convert.ToDouble(row["elevation_1_nw"]); + newSettings.Elevation2NW = Convert.ToDouble(row["elevation_2_nw"]); + newSettings.Elevation1NE = Convert.ToDouble(row["elevation_1_ne"]); + newSettings.Elevation2NE = Convert.ToDouble(row["elevation_2_ne"]); + newSettings.Elevation1SE = Convert.ToDouble(row["elevation_1_se"]); + newSettings.Elevation2SE = Convert.ToDouble(row["elevation_2_se"]); + newSettings.Elevation1SW = Convert.ToDouble(row["elevation_1_sw"]); + newSettings.Elevation2SW = Convert.ToDouble(row["elevation_2_sw"]); + newSettings.WaterHeight = Convert.ToDouble(row["water_height"]); + newSettings.TerrainRaiseLimit = Convert.ToDouble(row["terrain_raise_limit"]); + newSettings.TerrainLowerLimit = Convert.ToDouble(row["terrain_lower_limit"]); + newSettings.UseEstateSun = Convert.ToBoolean(row["use_estate_sun"]); + newSettings.Sandbox = Convert.ToBoolean(row["sandbox"]); + newSettings.SunVector = new Vector3( + Convert.ToSingle(row["sunvectorx"]), + Convert.ToSingle(row["sunvectory"]), + Convert.ToSingle(row["sunvectorz"]) + ); + newSettings.FixedSun = Convert.ToBoolean(row["fixed_sun"]); + newSettings.SunPosition = Convert.ToDouble(row["sun_position"]); + newSettings.Covenant = new UUID((String)row["covenant"]); + newSettings.CovenantChangedDateTime = Convert.ToInt32(row["covenant_datetime"]); + newSettings.TerrainImageID = new UUID((String)row["map_tile_ID"]); + newSettings.TelehubObject = new UUID((String)row["TelehubObject"]); + newSettings.ParcelImageID = new UUID((String)row["parcel_tile_ID"]); + + return newSettings; + } + + /// + /// Build a windlight entry from the persisted data. + /// + /// + /// RegionLightShareData + private RegionLightShareData buildRegionWindlight(DataRow row) + { + RegionLightShareData windlight = new RegionLightShareData(); + + windlight.regionID = new UUID((string)row["region_id"]); + windlight.waterColor.X = Convert.ToSingle(row["water_color_r"]); + windlight.waterColor.Y = Convert.ToSingle(row["water_color_g"]); + windlight.waterColor.Z = Convert.ToSingle(row["water_color_b"]); + //windlight.waterColor.W = Convert.ToSingle(row["water_color_i"]); //not implemented + windlight.waterFogDensityExponent = Convert.ToSingle(row["water_fog_density_exponent"]); + windlight.underwaterFogModifier = Convert.ToSingle(row["underwater_fog_modifier"]); + windlight.reflectionWaveletScale.X = Convert.ToSingle(row["reflection_wavelet_scale_1"]); + windlight.reflectionWaveletScale.Y = Convert.ToSingle(row["reflection_wavelet_scale_2"]); + windlight.reflectionWaveletScale.Z = Convert.ToSingle(row["reflection_wavelet_scale_3"]); + windlight.fresnelScale = Convert.ToSingle(row["fresnel_scale"]); + windlight.fresnelOffset = Convert.ToSingle(row["fresnel_offset"]); + windlight.refractScaleAbove = Convert.ToSingle(row["refract_scale_above"]); + windlight.refractScaleBelow = Convert.ToSingle(row["refract_scale_below"]); + windlight.blurMultiplier = Convert.ToSingle(row["blur_multiplier"]); + windlight.bigWaveDirection.X = Convert.ToSingle(row["big_wave_direction_x"]); + windlight.bigWaveDirection.Y = Convert.ToSingle(row["big_wave_direction_y"]); + windlight.littleWaveDirection.X = Convert.ToSingle(row["little_wave_direction_x"]); + windlight.littleWaveDirection.Y = Convert.ToSingle(row["little_wave_direction_y"]); + windlight.normalMapTexture = new UUID((string)row["normal_map_texture"]); + windlight.horizon.X = Convert.ToSingle(row["horizon_r"]); + windlight.horizon.Y = Convert.ToSingle(row["horizon_g"]); + windlight.horizon.Z = Convert.ToSingle(row["horizon_b"]); + windlight.horizon.W = Convert.ToSingle(row["horizon_i"]); + windlight.hazeHorizon = Convert.ToSingle(row["haze_horizon"]); + windlight.blueDensity.X = Convert.ToSingle(row["blue_density_r"]); + windlight.blueDensity.Y = Convert.ToSingle(row["blue_density_g"]); + windlight.blueDensity.Z = Convert.ToSingle(row["blue_density_b"]); + windlight.blueDensity.W = Convert.ToSingle(row["blue_density_i"]); + windlight.hazeDensity = Convert.ToSingle(row["haze_density"]); + windlight.densityMultiplier = Convert.ToSingle(row["density_multiplier"]); + windlight.distanceMultiplier = Convert.ToSingle(row["distance_multiplier"]); + windlight.maxAltitude = Convert.ToUInt16(row["max_altitude"]); + windlight.sunMoonColor.X = Convert.ToSingle(row["sun_moon_color_r"]); + windlight.sunMoonColor.Y = Convert.ToSingle(row["sun_moon_color_g"]); + windlight.sunMoonColor.Z = Convert.ToSingle(row["sun_moon_color_b"]); + windlight.sunMoonColor.W = Convert.ToSingle(row["sun_moon_color_i"]); + windlight.sunMoonPosition = Convert.ToSingle(row["sun_moon_position"]); + windlight.ambient.X = Convert.ToSingle(row["ambient_r"]); + windlight.ambient.Y = Convert.ToSingle(row["ambient_g"]); + windlight.ambient.Z = Convert.ToSingle(row["ambient_b"]); + windlight.ambient.W = Convert.ToSingle(row["ambient_i"]); + windlight.eastAngle = Convert.ToSingle(row["east_angle"]); + windlight.sunGlowFocus = Convert.ToSingle(row["sun_glow_focus"]); + windlight.sunGlowSize = Convert.ToSingle(row["sun_glow_size"]); + windlight.sceneGamma = Convert.ToSingle(row["scene_gamma"]); + windlight.starBrightness = Convert.ToSingle(row["star_brightness"]); + windlight.cloudColor.X = Convert.ToSingle(row["cloud_color_r"]); + windlight.cloudColor.Y = Convert.ToSingle(row["cloud_color_g"]); + windlight.cloudColor.Z = Convert.ToSingle(row["cloud_color_b"]); + windlight.cloudColor.W = Convert.ToSingle(row["cloud_color_i"]); + windlight.cloudXYDensity.X = Convert.ToSingle(row["cloud_x"]); + windlight.cloudXYDensity.Y = Convert.ToSingle(row["cloud_y"]); + windlight.cloudXYDensity.Z = Convert.ToSingle(row["cloud_density"]); + windlight.cloudCoverage = Convert.ToSingle(row["cloud_coverage"]); + windlight.cloudScale = Convert.ToSingle(row["cloud_scale"]); + windlight.cloudDetailXYDensity.X = Convert.ToSingle(row["cloud_detail_x"]); + windlight.cloudDetailXYDensity.Y = Convert.ToSingle(row["cloud_detail_y"]); + windlight.cloudDetailXYDensity.Z = Convert.ToSingle(row["cloud_detail_density"]); + windlight.cloudScrollX = Convert.ToSingle(row["cloud_scroll_x"]); + windlight.cloudScrollXLock = Convert.ToBoolean(row["cloud_scroll_x_lock"]); + windlight.cloudScrollY = Convert.ToSingle(row["cloud_scroll_y"]); + windlight.cloudScrollYLock = Convert.ToBoolean(row["cloud_scroll_y_lock"]); + windlight.drawClassicClouds = Convert.ToBoolean(row["draw_classic_clouds"]); + + return windlight; + } + + /// + /// Build a land access entry from the persisted data. + /// + /// + /// + private static LandAccessEntry buildLandAccessData(DataRow row) + { + LandAccessEntry entry = new LandAccessEntry(); + entry.AgentID = new UUID((string)row["AccessUUID"]); + entry.Flags = (AccessList)row["Flags"]; + entry.Expires = 0; + return entry; + } + + /// + /// + /// + /// + /// + /// + /// + private static void fillPrimRow(DataRow row, SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + row["UUID"] = prim.UUID.ToString(); + row["RegionUUID"] = regionUUID.ToString(); + row["CreationDate"] = prim.CreationDate; + row["Name"] = prim.Name; + row["SceneGroupID"] = sceneGroupID.ToString(); + // the UUID of the root part for this SceneObjectGroup + // various text fields + row["Text"] = prim.Text; + row["Description"] = prim.Description; + row["SitName"] = prim.SitName; + row["TouchName"] = prim.TouchName; + // permissions + row["ObjectFlags"] = (uint)prim.Flags; + row["CreatorID"] = prim.CreatorIdentification.ToString(); + row["OwnerID"] = prim.OwnerID.ToString(); + row["GroupID"] = prim.GroupID.ToString(); + row["LastOwnerID"] = prim.LastOwnerID.ToString(); + row["OwnerMask"] = prim.OwnerMask; + row["NextOwnerMask"] = prim.NextOwnerMask; + row["GroupMask"] = prim.GroupMask; + row["EveryoneMask"] = prim.EveryoneMask; + row["BaseMask"] = prim.BaseMask; + // vectors + row["PositionX"] = prim.OffsetPosition.X; + row["PositionY"] = prim.OffsetPosition.Y; + row["PositionZ"] = prim.OffsetPosition.Z; + row["GroupPositionX"] = prim.GroupPosition.X; + row["GroupPositionY"] = prim.GroupPosition.Y; + row["GroupPositionZ"] = prim.GroupPosition.Z; + row["VelocityX"] = prim.Velocity.X; + row["VelocityY"] = prim.Velocity.Y; + row["VelocityZ"] = prim.Velocity.Z; + row["AngularVelocityX"] = prim.AngularVelocity.X; + row["AngularVelocityY"] = prim.AngularVelocity.Y; + row["AngularVelocityZ"] = prim.AngularVelocity.Z; + row["AccelerationX"] = prim.Acceleration.X; + row["AccelerationY"] = prim.Acceleration.Y; + row["AccelerationZ"] = prim.Acceleration.Z; + // quaternions + row["RotationX"] = prim.RotationOffset.X; + row["RotationY"] = prim.RotationOffset.Y; + row["RotationZ"] = prim.RotationOffset.Z; + row["RotationW"] = prim.RotationOffset.W; + + // Sit target + Vector3 sitTargetPos = prim.SitTargetPositionLL; + row["SitTargetOffsetX"] = sitTargetPos.X; + row["SitTargetOffsetY"] = sitTargetPos.Y; + row["SitTargetOffsetZ"] = sitTargetPos.Z; + + Quaternion sitTargetOrient = prim.SitTargetOrientationLL; + row["SitTargetOrientW"] = sitTargetOrient.W; + row["SitTargetOrientX"] = sitTargetOrient.X; + row["SitTargetOrientY"] = sitTargetOrient.Y; + row["SitTargetOrientZ"] = sitTargetOrient.Z; + row["ColorR"] = Convert.ToInt32(prim.Color.R); + row["ColorG"] = Convert.ToInt32(prim.Color.G); + row["ColorB"] = Convert.ToInt32(prim.Color.B); + row["ColorA"] = Convert.ToInt32(prim.Color.A); + row["PayPrice"] = prim.PayPrice[0]; + row["PayButton1"] = prim.PayPrice[1]; + row["PayButton2"] = prim.PayPrice[2]; + row["PayButton3"] = prim.PayPrice[3]; + row["PayButton4"] = prim.PayPrice[4]; + + row["TextureAnimation"] = Convert.ToBase64String(prim.TextureAnimation); + row["ParticleSystem"] = Convert.ToBase64String(prim.ParticleSystem); + + row["OmegaX"] = prim.AngularVelocity.X; + row["OmegaY"] = prim.AngularVelocity.Y; + row["OmegaZ"] = prim.AngularVelocity.Z; + + row["CameraEyeOffsetX"] = prim.GetCameraEyeOffset().X; + row["CameraEyeOffsetY"] = prim.GetCameraEyeOffset().Y; + row["CameraEyeOffsetZ"] = prim.GetCameraEyeOffset().Z; + + row["CameraAtOffsetX"] = prim.GetCameraAtOffset().X; + row["CameraAtOffsetY"] = prim.GetCameraAtOffset().Y; + row["CameraAtOffsetZ"] = prim.GetCameraAtOffset().Z; + + + if ((prim.SoundFlags & 1) != 0) // Looped + { + row["LoopedSound"] = prim.Sound.ToString(); + row["LoopedSoundGain"] = prim.SoundGain; + } + else + { + row["LoopedSound"] = UUID.Zero.ToString(); + row["LoopedSoundGain"] = 0.0f; + } + + if (prim.GetForceMouselook()) + row["ForceMouselook"] = 1; + else + row["ForceMouselook"] = 0; + + row["ScriptAccessPin"] = prim.ScriptAccessPin; + + if (prim.AllowedDrop) + row["AllowedDrop"] = 1; + else + row["AllowedDrop"] = 0; + + if (prim.DIE_AT_EDGE) + row["DieAtEdge"] = 1; + else + row["DieAtEdge"] = 0; + + row["SalePrice"] = prim.SalePrice; + row["SaleType"] = Convert.ToInt16(prim.ObjectSaleType); + + // click action + row["ClickAction"] = prim.ClickAction; + + row["SalePrice"] = prim.SalePrice; + row["Material"] = prim.Material; + + row["CollisionSound"] = prim.CollisionSound.ToString(); + row["CollisionSoundVolume"] = prim.CollisionSoundVolume; + if (prim.VolumeDetectActive) + row["VolumeDetect"] = 1; + else + row["VolumeDetect"] = 0; + + row["MediaURL"] = prim.MediaUrl; + + row["AttachedPosX"] = prim.AttachedPos.X; + row["AttachedPosY"] = prim.AttachedPos.Y; + row["AttachedPosZ"] = prim.AttachedPos.Z; + + if (prim.DynAttrs.CountNamespaces > 0) + row["DynAttrs"] = prim.DynAttrs.ToXml(); + else + row["DynAttrs"] = null; + + row["PhysicsShapeType"] = prim.PhysicsShapeType; + row["Density"] = (double)prim.Density; + row["GravityModifier"] = (double)prim.GravityModifier; + row["Friction"] = (double)prim.Friction; + row["Restitution"] = (double)prim.Restitution; + + if (prim.KeyframeMotion != null) + row["KeyframeMotion"] = prim.KeyframeMotion.Serialize(); + else + row["KeyframeMotion"] = new Byte[0]; + + + } + + /// + /// + /// + /// + /// + private static void fillItemRow(DataRow row, TaskInventoryItem taskItem) + { + row["itemID"] = taskItem.ItemID.ToString(); + row["primID"] = taskItem.ParentPartID.ToString(); + row["assetID"] = taskItem.AssetID.ToString(); + row["parentFolderID"] = taskItem.ParentID.ToString(); + + row["invType"] = taskItem.InvType; + row["assetType"] = taskItem.Type; + + row["name"] = taskItem.Name; + row["description"] = taskItem.Description; + row["creationDate"] = taskItem.CreationDate; + row["creatorID"] = taskItem.CreatorIdentification.ToString(); + row["ownerID"] = taskItem.OwnerID.ToString(); + row["lastOwnerID"] = taskItem.LastOwnerID.ToString(); + row["groupID"] = taskItem.GroupID.ToString(); + row["nextPermissions"] = taskItem.NextPermissions; + row["currentPermissions"] = taskItem.CurrentPermissions; + row["basePermissions"] = taskItem.BasePermissions; + row["everyonePermissions"] = taskItem.EveryonePermissions; + row["groupPermissions"] = taskItem.GroupPermissions; + row["flags"] = taskItem.Flags; + } + + /// + /// + /// + /// + /// + /// + private static void fillLandRow(DataRow row, LandData land, UUID regionUUID) + { + row["UUID"] = land.GlobalID.ToString(); + row["RegionUUID"] = regionUUID.ToString(); + row["LocalLandID"] = land.LocalID; + + // Bitmap is a byte[512] + row["Bitmap"] = land.Bitmap; + + row["Name"] = land.Name; + row["Desc"] = land.Description; + row["OwnerUUID"] = land.OwnerID.ToString(); + row["IsGroupOwned"] = land.IsGroupOwned; + row["Area"] = land.Area; + row["AuctionID"] = land.AuctionID; //Unemplemented + row["Category"] = land.Category; //Enum OpenMetaverse.Parcel.ParcelCategory + row["ClaimDate"] = land.ClaimDate; + row["ClaimPrice"] = land.ClaimPrice; + row["GroupUUID"] = land.GroupID.ToString(); + row["SalePrice"] = land.SalePrice; + row["LandStatus"] = land.Status; //Enum. OpenMetaverse.Parcel.ParcelStatus + row["LandFlags"] = land.Flags; + row["LandingType"] = land.LandingType; + row["MediaAutoScale"] = land.MediaAutoScale; + row["MediaTextureUUID"] = land.MediaID.ToString(); + row["MediaURL"] = land.MediaURL; + row["MusicURL"] = land.MusicURL; + row["PassHours"] = land.PassHours; + row["PassPrice"] = land.PassPrice; + row["SnapshotUUID"] = land.SnapshotID.ToString(); + row["UserLocationX"] = land.UserLocation.X; + row["UserLocationY"] = land.UserLocation.Y; + row["UserLocationZ"] = land.UserLocation.Z; + row["UserLookAtX"] = land.UserLookAt.X; + row["UserLookAtY"] = land.UserLookAt.Y; + row["UserLookAtZ"] = land.UserLookAt.Z; + row["AuthbuyerID"] = land.AuthBuyerID.ToString(); + row["OtherCleanTime"] = land.OtherCleanTime; + row["Dwell"] = land.Dwell; + row["MediaType"] = land.MediaType; + row["MediaDescription"] = land.MediaDescription; + row["MediaSize"] = String.Format("{0},{1}", land.MediaWidth, land.MediaHeight); + row["MediaLoop"] = land.MediaLoop; + row["ObscureMusic"] = land.ObscureMusic; + row["ObscureMedia"] = land.ObscureMedia; + } + + /// + /// + /// + /// + /// + /// + private static void fillLandAccessRow(DataRow row, LandAccessEntry entry, UUID parcelID) + { + row["LandUUID"] = parcelID.ToString(); + row["AccessUUID"] = entry.AgentID.ToString(); + row["Flags"] = entry.Flags; + } + + private static void fillRegionSettingsRow(DataRow row, RegionSettings settings) + { + row["regionUUID"] = settings.RegionUUID.ToString(); + row["block_terraform"] = settings.BlockTerraform; + row["block_fly"] = settings.BlockFly; + row["allow_damage"] = settings.AllowDamage; + row["restrict_pushing"] = settings.RestrictPushing; + row["allow_land_resell"] = settings.AllowLandResell; + row["allow_land_join_divide"] = settings.AllowLandJoinDivide; + row["block_show_in_search"] = settings.BlockShowInSearch; + row["agent_limit"] = settings.AgentLimit; + row["object_bonus"] = settings.ObjectBonus; + row["maturity"] = settings.Maturity; + row["disable_scripts"] = settings.DisableScripts; + row["disable_collisions"] = settings.DisableCollisions; + row["disable_physics"] = settings.DisablePhysics; + row["terrain_texture_1"] = settings.TerrainTexture1.ToString(); + row["terrain_texture_2"] = settings.TerrainTexture2.ToString(); + row["terrain_texture_3"] = settings.TerrainTexture3.ToString(); + row["terrain_texture_4"] = settings.TerrainTexture4.ToString(); + row["elevation_1_nw"] = settings.Elevation1NW; + row["elevation_2_nw"] = settings.Elevation2NW; + row["elevation_1_ne"] = settings.Elevation1NE; + row["elevation_2_ne"] = settings.Elevation2NE; + row["elevation_1_se"] = settings.Elevation1SE; + row["elevation_2_se"] = settings.Elevation2SE; + row["elevation_1_sw"] = settings.Elevation1SW; + row["elevation_2_sw"] = settings.Elevation2SW; + row["water_height"] = settings.WaterHeight; + row["terrain_raise_limit"] = settings.TerrainRaiseLimit; + row["terrain_lower_limit"] = settings.TerrainLowerLimit; + row["use_estate_sun"] = settings.UseEstateSun; + row["sandbox"] = settings.Sandbox; // unlike other database modules, sqlite uses a lower case s for sandbox! + row["sunvectorx"] = settings.SunVector.X; + row["sunvectory"] = settings.SunVector.Y; + row["sunvectorz"] = settings.SunVector.Z; + row["fixed_sun"] = settings.FixedSun; + row["sun_position"] = settings.SunPosition; + row["covenant"] = settings.Covenant.ToString(); + row["covenant_datetime"] = settings.CovenantChangedDateTime; + row["map_tile_ID"] = settings.TerrainImageID.ToString(); + row["TelehubObject"] = settings.TelehubObject.ToString(); + row["parcel_tile_ID"] = settings.ParcelImageID.ToString(); + } + + /// + /// + /// + /// + /// + private static void fillRegionWindlightRow(DataRow row, RegionLightShareData windlight) + { + row["region_id"] = windlight.regionID.ToString(); + row["water_color_r"] = windlight.waterColor.X; + row["water_color_g"] = windlight.waterColor.Y; + row["water_color_b"] = windlight.waterColor.Z; + row["water_color_i"] = 1; //windlight.waterColor.W; //not implemented + row["water_fog_density_exponent"] = windlight.waterFogDensityExponent; + row["underwater_fog_modifier"] = windlight.underwaterFogModifier; + row["reflection_wavelet_scale_1"] = windlight.reflectionWaveletScale.X; + row["reflection_wavelet_scale_2"] = windlight.reflectionWaveletScale.Y; + row["reflection_wavelet_scale_3"] = windlight.reflectionWaveletScale.Z; + row["fresnel_scale"] = windlight.fresnelScale; + row["fresnel_offset"] = windlight.fresnelOffset; + row["refract_scale_above"] = windlight.refractScaleAbove; + row["refract_scale_below"] = windlight.refractScaleBelow; + row["blur_multiplier"] = windlight.blurMultiplier; + row["big_wave_direction_x"] = windlight.bigWaveDirection.X; + row["big_wave_direction_y"] = windlight.bigWaveDirection.Y; + row["little_wave_direction_x"] = windlight.littleWaveDirection.X; + row["little_wave_direction_y"] = windlight.littleWaveDirection.Y; + row["normal_map_texture"] = windlight.normalMapTexture.ToString(); + row["horizon_r"] = windlight.horizon.X; + row["horizon_g"] = windlight.horizon.Y; + row["horizon_b"] = windlight.horizon.Z; + row["horizon_i"] = windlight.horizon.W; + row["haze_horizon"] = windlight.hazeHorizon; + row["blue_density_r"] = windlight.blueDensity.X; + row["blue_density_g"] = windlight.blueDensity.Y; + row["blue_density_b"] = windlight.blueDensity.Z; + row["blue_density_i"] = windlight.blueDensity.W; + row["haze_density"] = windlight.hazeDensity; + row["density_multiplier"] = windlight.densityMultiplier; + row["distance_multiplier"] = windlight.distanceMultiplier; + row["max_altitude"] = windlight.maxAltitude; + row["sun_moon_color_r"] = windlight.sunMoonColor.X; + row["sun_moon_color_g"] = windlight.sunMoonColor.Y; + row["sun_moon_color_b"] = windlight.sunMoonColor.Z; + row["sun_moon_color_i"] = windlight.sunMoonColor.W; + row["sun_moon_position"] = windlight.sunMoonPosition; + row["ambient_r"] = windlight.ambient.X; + row["ambient_g"] = windlight.ambient.Y; + row["ambient_b"] = windlight.ambient.Z; + row["ambient_i"] = windlight.ambient.W; + row["east_angle"] = windlight.eastAngle; + row["sun_glow_focus"] = windlight.sunGlowFocus; + row["sun_glow_size"] = windlight.sunGlowSize; + row["scene_gamma"] = windlight.sceneGamma; + row["star_brightness"] = windlight.starBrightness; + row["cloud_color_r"] = windlight.cloudColor.X; + row["cloud_color_g"] = windlight.cloudColor.Y; + row["cloud_color_b"] = windlight.cloudColor.Z; + row["cloud_color_i"] = windlight.cloudColor.W; + row["cloud_x"] = windlight.cloudXYDensity.X; + row["cloud_y"] = windlight.cloudXYDensity.Y; + row["cloud_density"] = windlight.cloudXYDensity.Z; + row["cloud_coverage"] = windlight.cloudCoverage; + row["cloud_scale"] = windlight.cloudScale; + row["cloud_detail_x"] = windlight.cloudDetailXYDensity.X; + row["cloud_detail_y"] = windlight.cloudDetailXYDensity.Y; + row["cloud_detail_density"] = windlight.cloudDetailXYDensity.Z; + row["cloud_scroll_x"] = windlight.cloudScrollX; + row["cloud_scroll_x_lock"] = windlight.cloudScrollXLock; + row["cloud_scroll_y"] = windlight.cloudScrollY; + row["cloud_scroll_y_lock"] = windlight.cloudScrollYLock; + row["draw_classic_clouds"] = windlight.drawClassicClouds; + } + + /// + /// + /// + /// + /// + private PrimitiveBaseShape buildShape(DataRow row) + { + PrimitiveBaseShape s = new PrimitiveBaseShape(); + s.Scale = new Vector3( + Convert.ToSingle(row["ScaleX"]), + Convert.ToSingle(row["ScaleY"]), + Convert.ToSingle(row["ScaleZ"]) + ); + // paths + s.PCode = Convert.ToByte(row["PCode"]); + s.PathBegin = Convert.ToUInt16(row["PathBegin"]); + s.PathEnd = Convert.ToUInt16(row["PathEnd"]); + s.PathScaleX = Convert.ToByte(row["PathScaleX"]); + s.PathScaleY = Convert.ToByte(row["PathScaleY"]); + s.PathShearX = Convert.ToByte(row["PathShearX"]); + s.PathShearY = Convert.ToByte(row["PathShearY"]); + s.PathSkew = Convert.ToSByte(row["PathSkew"]); + s.PathCurve = Convert.ToByte(row["PathCurve"]); + s.PathRadiusOffset = Convert.ToSByte(row["PathRadiusOffset"]); + s.PathRevolutions = Convert.ToByte(row["PathRevolutions"]); + s.PathTaperX = Convert.ToSByte(row["PathTaperX"]); + s.PathTaperY = Convert.ToSByte(row["PathTaperY"]); + s.PathTwist = Convert.ToSByte(row["PathTwist"]); + s.PathTwistBegin = Convert.ToSByte(row["PathTwistBegin"]); + // profile + s.ProfileBegin = Convert.ToUInt16(row["ProfileBegin"]); + s.ProfileEnd = Convert.ToUInt16(row["ProfileEnd"]); + s.ProfileCurve = Convert.ToByte(row["ProfileCurve"]); + s.ProfileHollow = Convert.ToUInt16(row["ProfileHollow"]); + s.State = Convert.ToByte(row["State"]); + s.LastAttachPoint = Convert.ToByte(row["LastAttachPoint"]); + + byte[] textureEntry = (byte[])row["Texture"]; + s.TextureEntry = textureEntry; + + s.ExtraParams = (byte[])row["ExtraParams"]; + + if (!(row["Media"] is System.DBNull)) + s.Media = PrimitiveBaseShape.MediaList.FromXml((string)row["Media"]); + + return s; + } + + /// + /// + /// + /// + /// + private static void fillShapeRow(DataRow row, SceneObjectPart prim) + { + PrimitiveBaseShape s = prim.Shape; + row["UUID"] = prim.UUID.ToString(); + // shape is an enum + row["Shape"] = 0; + // vectors + row["ScaleX"] = s.Scale.X; + row["ScaleY"] = s.Scale.Y; + row["ScaleZ"] = s.Scale.Z; + // paths + row["PCode"] = s.PCode; + row["PathBegin"] = s.PathBegin; + row["PathEnd"] = s.PathEnd; + row["PathScaleX"] = s.PathScaleX; + row["PathScaleY"] = s.PathScaleY; + row["PathShearX"] = s.PathShearX; + row["PathShearY"] = s.PathShearY; + row["PathSkew"] = s.PathSkew; + row["PathCurve"] = s.PathCurve; + row["PathRadiusOffset"] = s.PathRadiusOffset; + row["PathRevolutions"] = s.PathRevolutions; + row["PathTaperX"] = s.PathTaperX; + row["PathTaperY"] = s.PathTaperY; + row["PathTwist"] = s.PathTwist; + row["PathTwistBegin"] = s.PathTwistBegin; + // profile + row["ProfileBegin"] = s.ProfileBegin; + row["ProfileEnd"] = s.ProfileEnd; + row["ProfileCurve"] = s.ProfileCurve; + row["ProfileHollow"] = s.ProfileHollow; + row["State"] = s.State; + row["LastAttachPoint"] = s.LastAttachPoint; + + row["Texture"] = s.TextureEntry; + row["ExtraParams"] = s.ExtraParams; + + if (s.Media != null) + row["Media"] = s.Media.ToXml(); + } + + /// + /// Persistently store a prim. + /// + /// + /// + /// + private void addPrim(SceneObjectPart prim, UUID sceneGroupID, UUID regionUUID) + { + DataTable prims = ds.Tables["prims"]; + DataTable shapes = ds.Tables["primshapes"]; + + DataRow primRow = prims.Rows.Find(prim.UUID.ToString()); + if (primRow == null) + { + primRow = prims.NewRow(); + fillPrimRow(primRow, prim, sceneGroupID, regionUUID); + prims.Rows.Add(primRow); + } + else + { + fillPrimRow(primRow, prim, sceneGroupID, regionUUID); + } + + DataRow shapeRow = shapes.Rows.Find(prim.UUID.ToString()); + if (shapeRow == null) + { + shapeRow = shapes.NewRow(); + fillShapeRow(shapeRow, prim); + shapes.Rows.Add(shapeRow); + } + else + { + fillShapeRow(shapeRow, prim); + } + } + + /// + /// + /// + /// + public void StorePrimInventory(UUID primID, ICollection items) + { +// m_log.DebugFormat("[SQLITE REGION DB]: Entered StorePrimInventory with prim ID {0}", primID); + + DataTable dbItems = ds.Tables["primitems"]; + + // For now, we're just going to crudely remove all the previous inventory items + // no matter whether they have changed or not, and replace them with the current set. + lock (ds) + { + RemoveItems(primID); + + // repalce with current inventory details + foreach (TaskInventoryItem newItem in items) + { + // m_log.InfoFormat( + // "[DATASTORE]: ", + // "Adding item {0}, {1} to prim ID {2}", + // newItem.Name, newItem.ItemID, newItem.ParentPartID); + + DataRow newItemRow = dbItems.NewRow(); + fillItemRow(newItemRow, newItem); + dbItems.Rows.Add(newItemRow); + } + } + + Commit(); + } + + /*********************************************************************** + * + * SQL Statement Creation Functions + * + * These functions create SQL statements for update, insert, and create. + * They can probably be factored later to have a db independant + * portion and a db specific portion + * + **********************************************************************/ + + /// + /// Create an insert command + /// + /// table name + /// data table + /// the created command + /// + /// This is subtle enough to deserve some commentary. + /// Instead of doing *lots* and *lots of hardcoded strings + /// for database definitions we'll use the fact that + /// realistically all insert statements look like "insert + /// into A(b, c) values(:b, :c) on the parameterized query + /// front. If we just have a list of b, c, etc... we can + /// generate these strings instead of typing them out. + /// + private static SqliteCommand createInsertCommand(string table, DataTable dt) + { + string[] cols = new string[dt.Columns.Count]; + for (int i = 0; i < dt.Columns.Count; i++) + { + DataColumn col = dt.Columns[i]; + cols[i] = col.ColumnName; + } + + string sql = "insert into " + table + "("; + sql += String.Join(", ", cols); + // important, the first ':' needs to be here, the rest get added in the join + sql += ") values (:"; + sql += String.Join(", :", cols); + sql += ")"; +// m_log.DebugFormat("[SQLITE]: Created insert command {0}", sql); + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + private static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + private static SqliteCommand createUpdateCommand(string table, string pk1, string pk2, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk1 + " and " + pk2; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// + /// + /// Data Table + /// + // private static string defineTable(DataTable dt) + // { + // string sql = "create table " + dt.TableName + "("; + // string subsql = String.Empty; + // foreach (DataColumn col in dt.Columns) + // { + // if (subsql.Length > 0) + // { + // // a map function would rock so much here + // subsql += ",\n"; + // } + // subsql += col.ColumnName + " " + sqliteType(col.DataType); + // if (dt.PrimaryKey.Length > 0 && col == dt.PrimaryKey[0]) + // { + // subsql += " primary key"; + // } + // } + // sql += subsql; + // sql += ")"; + // return sql; + // } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + /// + /// This is a convenience function that collapses 5 repetitive + /// lines for defining SqliteParameters to 2 parameters: + /// column name and database type. + /// + /// It assumes certain conventions like :param as the param + /// name to replace in parametrized queries, and that source + /// version is always current version, both of which are fine + /// for us. + /// + ///a built sqlite parameter + private static SqliteParameter createSqliteParameter(string name, Type type) + { + SqliteParameter param = new SqliteParameter(); + param.ParameterName = ":" + name; + param.DbType = dbtypeFromType(type); + param.SourceColumn = name; + param.SourceVersion = DataRowVersion.Current; + return param; + } + + /// + /// + /// + /// + /// + private void setupPrimCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("prims", ds.Tables["prims"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("prims", "UUID=:UUID", ds.Tables["prims"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from prims where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /// + /// + /// + /// + /// + private void setupItemsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("primitems", ds.Tables["primitems"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("primitems", "itemID = :itemID", ds.Tables["primitems"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from primitems where itemID = :itemID"); + delete.Parameters.Add(createSqliteParameter("itemID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /// + /// + /// + /// + /// + private void setupTerrainCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("terrain", ds.Tables["terrain"]); + da.InsertCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupLandCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("land", ds.Tables["land"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("land", "UUID=:UUID", ds.Tables["land"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from land where UUID=:UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + da.DeleteCommand = delete; + da.DeleteCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupLandAccessCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("landaccesslist", ds.Tables["landaccesslist"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("landaccesslist", "LandUUID=:landUUID", "AccessUUID=:AccessUUID", ds.Tables["landaccesslist"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from landaccesslist where LandUUID= :LandUUID and AccessUUID= :AccessUUID"); + delete.Parameters.Add(createSqliteParameter("LandUUID", typeof(String))); + delete.Parameters.Add(createSqliteParameter("AccessUUID", typeof(String))); + da.DeleteCommand = delete; + da.DeleteCommand.Connection = conn; + } + + private void setupRegionSettingsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("regionsettings", ds.Tables["regionsettings"]); + da.InsertCommand.Connection = conn; + da.UpdateCommand = createUpdateCommand("regionsettings", "regionUUID=:regionUUID", ds.Tables["regionsettings"]); + da.UpdateCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupRegionWindlightCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("regionwindlight", ds.Tables["regionwindlight"]); + da.InsertCommand.Connection = conn; + da.UpdateCommand = createUpdateCommand("regionwindlight", "region_id=:region_id", ds.Tables["regionwindlight"]); + da.UpdateCommand.Connection = conn; + } + + private void setupRegionEnvironmentCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("regionenvironment", ds.Tables["regionenvironment"]); + da.InsertCommand.Connection = conn; + da.UpdateCommand = createUpdateCommand("regionenvironment", "region_id=:region_id", ds.Tables["regionenvironment"]); + da.UpdateCommand.Connection = conn; + } + + private void setupRegionSpawnPointsCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("spawn_points", ds.Tables["spawn_points"]); + da.InsertCommand.Connection = conn; + da.UpdateCommand = createUpdateCommand("spawn_points", "RegionID=:RegionID", ds.Tables["spawn_points"]); + da.UpdateCommand.Connection = conn; + } + + /// + /// + /// + /// + /// + private void setupShapeCommands(SqliteDataAdapter da, SqliteConnection conn) + { + da.InsertCommand = createInsertCommand("primshapes", ds.Tables["primshapes"]); + da.InsertCommand.Connection = conn; + + da.UpdateCommand = createUpdateCommand("primshapes", "UUID=:UUID", ds.Tables["primshapes"]); + da.UpdateCommand.Connection = conn; + + SqliteCommand delete = new SqliteCommand("delete from primshapes where UUID = :UUID"); + delete.Parameters.Add(createSqliteParameter("UUID", typeof(String))); + delete.Connection = conn; + da.DeleteCommand = delete; + } + + /*********************************************************************** + * + * Type conversion functions + * + **********************************************************************/ + + /// + /// Type conversion function + /// + /// + /// + private static DbType dbtypeFromType(Type type) + { + if (type == typeof(String)) + { + return DbType.String; + } + else if (type == typeof(Int32)) + { + return DbType.Int32; + } + else if (type == typeof(Double)) + { + return DbType.Double; + } + else if (type == typeof(Byte)) + { + return DbType.Byte; + } + else if (type == typeof(Double)) + { + return DbType.Double; + } + else if (type == typeof(Byte[])) + { + return DbType.Binary; + } + else + { + return DbType.String; + } + } + + static void PrintDataSet(DataSet ds) + { + // Print out any name and extended properties. + Console.WriteLine("DataSet is named: {0}", ds.DataSetName); + foreach (System.Collections.DictionaryEntry de in ds.ExtendedProperties) + { + Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value); + } + Console.WriteLine(); + foreach (DataTable dt in ds.Tables) + { + Console.WriteLine("=> {0} Table:", dt.TableName); + // Print out the column names. + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Columns[curCol].ColumnName + "\t"); + } + Console.WriteLine("\n----------------------------------"); + // Print the DataTable. + for (int curRow = 0; curRow < dt.Rows.Count; curRow++) + { + for (int curCol = 0; curCol < dt.Columns.Count; curCol++) + { + Console.Write(dt.Rows[curRow][curCol].ToString() + "\t"); + } + Console.WriteLine(); + } + } + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteUserAccountData.cs b/OpenSim/Data/SQLite/SQLiteUserAccountData.cs new file mode 100644 index 0000000000..f98d37660f --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteUserAccountData.cs @@ -0,0 +1,86 @@ +/* + * 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.Data; +using OpenMetaverse; +using OpenSim.Framework; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + public class SQLiteUserAccountData : SQLiteGenericTableHandler, IUserAccountData + { + public SQLiteUserAccountData(string connectionString, string realm) + : base(connectionString, realm, "UserAccount") + { + } + + public UserAccountData[] GetUsers(UUID scopeID, string query) + { + string[] words = query.Split(new char[] {' '}); + + for (int i = 0 ; i < words.Length ; i++) + { + if (words[i].Length < 3) + { + if (i != words.Length - 1) + Array.Copy(words, i + 1, words, i, words.Length - i - 1); + Array.Resize(ref words, words.Length - 1); + } + } + + if (words.Length == 0) + return new UserAccountData[0]; + + if (words.Length > 2) + return new UserAccountData[0]; + + using (SqliteCommand cmd = new SqliteCommand()) + { + if (words.Length == 1) + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{2}%')", + m_Realm, scopeID.ToString(), words[0]); + } + else + { + cmd.CommandText = String.Format("select * from {0} where (ScopeID='{1}' or ScopeID='00000000-0000-0000-0000-000000000000') and (FirstName like '{2}%' or LastName like '{3}%')", + m_Realm, scopeID.ToString(), words[0], words[1]); + } + + return DoQuery(cmd); + } + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs b/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs new file mode 100644 index 0000000000..cd3e8b65c3 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteUserProfilesData.cs @@ -0,0 +1,981 @@ +/* + * 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.Data; +using System.Reflection; +using log4net; +#if CSharpSqlite +using Community.CsharpSqlite.Sqlite; +#else +using Mono.Data.Sqlite; +#endif +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; + +namespace OpenSim.Data.SQLite +{ + public class SQLiteUserProfilesData: IProfilesData + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SqliteConnection m_connection; + private string m_connectionString; + + private Dictionary m_FieldMap = + new Dictionary(); + + protected virtual Assembly Assembly + { + get { return GetType().Assembly; } + } + + public SQLiteUserProfilesData() + { + } + + public SQLiteUserProfilesData(string connectionString) + { + Initialise(connectionString); + } + + public void Initialise(string connectionString) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_connectionString = connectionString; + + m_log.Info("[PROFILES_DATA]: Sqlite - connecting: "+m_connectionString); + + m_connection = new SqliteConnection(m_connectionString); + m_connection.Open(); + + Migration m = new Migration(m_connection, Assembly, "UserProfiles"); + m.Update(); + } + + private string[] FieldList + { + get { return new List(m_FieldMap.Keys).ToArray(); } + } + + #region IProfilesData implementation + public OSDArray GetClassifiedRecords(UUID creatorId) + { + OSDArray data = new OSDArray(); + string query = "SELECT classifieduuid, name FROM classifieds WHERE creatoruuid = :Id"; + IDataReader reader = null; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", creatorId); + reader = cmd.ExecuteReader(); + } + + while (reader.Read()) + { + OSDMap n = new OSDMap(); + UUID Id = UUID.Zero; + string Name = null; + try + { + UUID.TryParse(Convert.ToString( reader["classifieduuid"]), out Id); + Name = Convert.ToString(reader["name"]); + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UserAccount exception {0}", e.Message); + } + n.Add("classifieduuid", OSD.FromUUID(Id)); + n.Add("name", OSD.FromString(Name)); + data.Add(n); + } + + reader.Close(); + + return data; + } + public bool UpdateClassifiedRecord(UserClassifiedAdd ad, ref string result) + { + string query = string.Empty; + + query += "INSERT OR REPLACE INTO classifieds ("; + query += "`classifieduuid`,"; + query += "`creatoruuid`,"; + query += "`creationdate`,"; + query += "`expirationdate`,"; + query += "`category`,"; + query += "`name`,"; + query += "`description`,"; + query += "`parceluuid`,"; + query += "`parentestate`,"; + query += "`snapshotuuid`,"; + query += "`simname`,"; + query += "`posglobal`,"; + query += "`parcelname`,"; + query += "`classifiedflags`,"; + query += "`priceforlisting`) "; + query += "VALUES ("; + query += ":ClassifiedId,"; + query += ":CreatorId,"; + query += ":CreatedDate,"; + query += ":ExpirationDate,"; + query += ":Category,"; + query += ":Name,"; + query += ":Description,"; + query += ":ParcelId,"; + query += ":ParentEstate,"; + query += ":SnapshotId,"; + query += ":SimName,"; + query += ":GlobalPos,"; + query += ":ParcelName,"; + query += ":Flags,"; + query += ":ListingPrice ) "; + + if(string.IsNullOrEmpty(ad.ParcelName)) + ad.ParcelName = "Unknown"; + if(ad.ParcelId == null) + ad.ParcelId = UUID.Zero; + if(string.IsNullOrEmpty(ad.Description)) + ad.Description = "No Description"; + + DateTime epoch = new DateTime(1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan epochnow = now - epoch; + TimeSpan duration; + DateTime expiration; + TimeSpan epochexp; + + if(ad.Flags == 2) + { + duration = new TimeSpan(7,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + else + { + duration = new TimeSpan(365,0,0,0); + expiration = now.Add(duration); + epochexp = expiration - epoch; + } + ad.CreationDate = (int)epochnow.TotalSeconds; + ad.ExpirationDate = (int)epochexp.TotalSeconds; + + try { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ClassifiedId", ad.ClassifiedId.ToString()); + cmd.Parameters.AddWithValue(":CreatorId", ad.CreatorId.ToString()); + cmd.Parameters.AddWithValue(":CreatedDate", ad.CreationDate.ToString()); + cmd.Parameters.AddWithValue(":ExpirationDate", ad.ExpirationDate.ToString()); + cmd.Parameters.AddWithValue(":Category", ad.Category.ToString()); + cmd.Parameters.AddWithValue(":Name", ad.Name.ToString()); + cmd.Parameters.AddWithValue(":Description", ad.Description.ToString()); + cmd.Parameters.AddWithValue(":ParcelId", ad.ParcelId.ToString()); + cmd.Parameters.AddWithValue(":ParentEstate", ad.ParentEstate.ToString()); + cmd.Parameters.AddWithValue(":SnapshotId", ad.SnapshotId.ToString ()); + cmd.Parameters.AddWithValue(":SimName", ad.SimName.ToString()); + cmd.Parameters.AddWithValue(":GlobalPos", ad.GlobalPos.ToString()); + cmd.Parameters.AddWithValue(":ParcelName", ad.ParcelName.ToString()); + cmd.Parameters.AddWithValue(":Flags", ad.Flags.ToString()); + cmd.Parameters.AddWithValue(":ListingPrice", ad.Price.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": ClassifiedesUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + public bool DeleteClassifiedRecord(UUID recordId) + { + string query = string.Empty; + + query += "DELETE FROM classifieds WHERE "; + query += "classifieduuid = :ClasifiedId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ClassifiedId", recordId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": DeleteClassifiedRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetClassifiedInfo(ref UserClassifiedAdd ad, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM classifieds WHERE "; + query += "classifieduuid = :AdId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":AdId", ad.ClassifiedId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + if(reader.Read ()) + { + ad.CreatorId = new UUID(reader["creatoruuid"].ToString()); + ad.ParcelId = new UUID(reader["parceluuid"].ToString ()); + ad.SnapshotId = new UUID(reader["snapshotuuid"].ToString ()); + ad.CreationDate = Convert.ToInt32(reader["creationdate"]); + ad.ExpirationDate = Convert.ToInt32(reader["expirationdate"]); + ad.ParentEstate = Convert.ToInt32(reader["parentestate"]); + ad.Flags = (byte) Convert.ToUInt32(reader["classifiedflags"]); + ad.Category = Convert.ToInt32(reader["category"]); + ad.Price = Convert.ToInt16(reader["priceforlisting"]); + ad.Name = reader["name"].ToString(); + ad.Description = reader["description"].ToString(); + ad.SimName = reader["simname"].ToString(); + ad.GlobalPos = reader["posglobal"].ToString(); + ad.ParcelName = reader["parcelname"].ToString(); + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return true; + } + + public OSDArray GetAvatarPicks(UUID avatarId) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT `pickuuid`,`name` FROM userpicks WHERE "; + query += "creatoruuid = :Id"; + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + OSDMap record = new OSDMap(); + + record.Add("pickuuid",OSD.FromString((string)reader["pickuuid"])); + record.Add("name",OSD.FromString((string)reader["name"])); + data.Add(record); + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarPicks exception {0}", e.Message); + } + return data; + } + public UserProfilePick GetPickInfo(UUID avatarId, UUID pickId) + { + IDataReader reader = null; + string query = string.Empty; + UserProfilePick pick = new UserProfilePick(); + + query += "SELECT * FROM userpicks WHERE "; + query += "creatoruuid = :CreatorId AND "; + query += "pickuuid = :PickId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":CreatorId", avatarId.ToString()); + cmd.Parameters.AddWithValue(":PickId", pickId.ToString()); + + using (reader = cmd.ExecuteReader()) + { + + while (reader.Read()) + { + string description = (string)reader["description"]; + + if (string.IsNullOrEmpty(description)) + description = "No description given."; + + UUID.TryParse((string)reader["pickuuid"], out pick.PickId); + UUID.TryParse((string)reader["creatoruuid"], out pick.CreatorId); + UUID.TryParse((string)reader["parceluuid"], out pick.ParcelId); + UUID.TryParse((string)reader["snapshotuuid"], out pick.SnapshotId); + pick.GlobalPos = (string)reader["posglobal"]; + bool.TryParse((string)reader["toppick"].ToString(), out pick.TopPick); + bool.TryParse((string)reader["enabled"].ToString(), out pick.Enabled); + pick.Name = (string)reader["name"]; + pick.Desc = description; + pick.ParcelName = (string)reader["user"]; + pick.OriginalName = (string)reader["originalname"]; + pick.SimName = (string)reader["simname"]; + pick.SortOrder = (int)reader["sortorder"]; + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetPickInfo exception {0}", e.Message); + } + return pick; + } + + public bool UpdatePicksRecord(UserProfilePick pick) + { + string query = string.Empty; + + query += "INSERT OR REPLACE INTO userpicks ("; + query += "pickuuid, "; + query += "creatoruuid, "; + query += "toppick, "; + query += "parceluuid, "; + query += "name, "; + query += "description, "; + query += "snapshotuuid, "; + query += "user, "; + query += "originalname, "; + query += "simname, "; + query += "posglobal, "; + query += "sortorder, "; + query += "enabled ) "; + query += "VALUES ("; + query += ":PickId,"; + query += ":CreatorId,"; + query += ":TopPick,"; + query += ":ParcelId,"; + query += ":Name,"; + query += ":Desc,"; + query += ":SnapshotId,"; + query += ":User,"; + query += ":Original,"; + query += ":SimName,"; + query += ":GlobalPos,"; + query += ":SortOrder,"; + query += ":Enabled) "; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + int top_pick; + int.TryParse(pick.TopPick.ToString(), out top_pick); + int enabled; + int.TryParse(pick.Enabled.ToString(), out enabled); + + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":PickId", pick.PickId.ToString()); + cmd.Parameters.AddWithValue(":CreatorId", pick.CreatorId.ToString()); + cmd.Parameters.AddWithValue(":TopPick", top_pick); + cmd.Parameters.AddWithValue(":ParcelId", pick.ParcelId.ToString()); + cmd.Parameters.AddWithValue(":Name", pick.Name.ToString()); + cmd.Parameters.AddWithValue(":Desc", pick.Desc.ToString()); + cmd.Parameters.AddWithValue(":SnapshotId", pick.SnapshotId.ToString()); + cmd.Parameters.AddWithValue(":User", pick.ParcelName.ToString()); + cmd.Parameters.AddWithValue(":Original", pick.OriginalName.ToString()); + cmd.Parameters.AddWithValue(":SimName",pick.SimName.ToString()); + cmd.Parameters.AddWithValue(":GlobalPos", pick.GlobalPos); + cmd.Parameters.AddWithValue(":SortOrder", pick.SortOrder.ToString ()); + cmd.Parameters.AddWithValue(":Enabled", enabled); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool DeletePicksRecord(UUID pickId) + { + string query = string.Empty; + + query += "DELETE FROM userpicks WHERE "; + query += "pickuuid = :PickId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":PickId", pickId.ToString()); + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": DeleteUserPickRecord exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetAvatarNotes(ref UserProfileNotes notes) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT `notes` FROM usernotes WHERE "; + query += "useruuid = :Id AND "; + query += "targetuuid = :TargetId"; + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", notes.UserId.ToString()); + cmd.Parameters.AddWithValue(":TargetId", notes.TargetId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + while (reader.Read()) + { + notes.Notes = OSD.FromString((string)reader["notes"]); + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return true; + } + + public bool UpdateAvatarNotes(ref UserProfileNotes note, ref string result) + { + string query = string.Empty; + bool remove; + + if(string.IsNullOrEmpty(note.Notes)) + { + remove = true; + query += "DELETE FROM usernotes WHERE "; + query += "useruuid=:UserId AND "; + query += "targetuuid=:TargetId"; + } + else + { + remove = false; + query += "INSERT OR REPLACE INTO usernotes VALUES ( "; + query += ":UserId,"; + query += ":TargetId,"; + query += ":Notes )"; + } + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + + if(!remove) + cmd.Parameters.AddWithValue(":Notes", note.Notes); + cmd.Parameters.AddWithValue(":TargetId", note.TargetId.ToString ()); + cmd.Parameters.AddWithValue(":UserId", note.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": UpdateAvatarNotes exception {0}", e.Message); + return false; + } + return true; + } + + public bool GetAvatarProperties(ref UserProfileProperties props, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM userprofile WHERE "; + query += "useruuid = :Id"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", props.UserId.ToString()); + + + try + { + reader = cmd.ExecuteReader(); + } + catch(Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarProperties exception {0}", e.Message); + result = e.Message; + return false; + } + if(reader != null && reader.Read()) + { + props.WebUrl = (string)reader["profileURL"]; + UUID.TryParse((string)reader["profileImage"], out props.ImageId); + props.AboutText = (string)reader["profileAboutText"]; + UUID.TryParse((string)reader["profileFirstImage"], out props.FirstLifeImageId); + props.FirstLifeText = (string)reader["profileFirstText"]; + UUID.TryParse((string)reader["profilePartner"], out props.PartnerId); + props.WantToMask = (int)reader["profileWantToMask"]; + props.WantToText = (string)reader["profileWantToText"]; + props.SkillsMask = (int)reader["profileSkillsMask"]; + props.SkillsText = (string)reader["profileSkillsText"]; + props.Language = (string)reader["profileLanguages"]; + } + else + { + props.WebUrl = string.Empty; + props.ImageId = UUID.Zero; + props.AboutText = string.Empty; + props.FirstLifeImageId = UUID.Zero; + props.FirstLifeText = string.Empty; + props.PartnerId = UUID.Zero; + props.WantToMask = 0; + props.WantToText = string.Empty; + props.SkillsMask = 0; + props.SkillsText = string.Empty; + props.Language = string.Empty; + props.PublishProfile = false; + props.PublishMature = false; + + query = "INSERT INTO userprofile ("; + query += "useruuid, "; + query += "profilePartner, "; + query += "profileAllowPublish, "; + query += "profileMaturePublish, "; + query += "profileURL, "; + query += "profileWantToMask, "; + query += "profileWantToText, "; + query += "profileSkillsMask, "; + query += "profileSkillsText, "; + query += "profileLanguages, "; + query += "profileImage, "; + query += "profileAboutText, "; + query += "profileFirstImage, "; + query += "profileFirstText) VALUES ("; + query += ":userId, "; + query += ":profilePartner, "; + query += ":profileAllowPublish, "; + query += ":profileMaturePublish, "; + query += ":profileURL, "; + query += ":profileWantToMask, "; + query += ":profileWantToText, "; + query += ":profileSkillsMask, "; + query += ":profileSkillsText, "; + query += ":profileLanguages, "; + query += ":profileImage, "; + query += ":profileAboutText, "; + query += ":profileFirstImage, "; + query += ":profileFirstText)"; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.CommandText = query; + put.Parameters.AddWithValue(":userId", props.UserId.ToString()); + put.Parameters.AddWithValue(":profilePartner", props.PartnerId.ToString()); + put.Parameters.AddWithValue(":profileAllowPublish", props.PublishProfile); + put.Parameters.AddWithValue(":profileMaturePublish", props.PublishMature); + put.Parameters.AddWithValue(":profileURL", props.WebUrl); + put.Parameters.AddWithValue(":profileWantToMask", props.WantToMask); + put.Parameters.AddWithValue(":profileWantToText", props.WantToText); + put.Parameters.AddWithValue(":profileSkillsMask", props.SkillsMask); + put.Parameters.AddWithValue(":profileSkillsText", props.SkillsText); + put.Parameters.AddWithValue(":profileLanguages", props.Language); + put.Parameters.AddWithValue(":profileImage", props.ImageId.ToString()); + put.Parameters.AddWithValue(":profileAboutText", props.AboutText); + put.Parameters.AddWithValue(":profileFirstImage", props.FirstLifeImageId.ToString()); + put.Parameters.AddWithValue(":profileFirstText", props.FirstLifeText); + + put.ExecuteNonQuery(); + } + } + } + return true; + } + + public bool UpdateAvatarProperties(ref UserProfileProperties props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileURL=:profileURL, "; + query += "profileImage=:image, "; + query += "profileAboutText=:abouttext,"; + query += "profileFirstImage=:firstlifeimage,"; + query += "profileFirstText=:firstlifetext "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":profileURL", props.WebUrl); + cmd.Parameters.AddWithValue(":image", props.ImageId.ToString()); + cmd.Parameters.AddWithValue(":abouttext", props.AboutText); + cmd.Parameters.AddWithValue(":firstlifeimage", props.FirstLifeImageId.ToString()); + cmd.Parameters.AddWithValue(":firstlifetext", props.FirstLifeText); + cmd.Parameters.AddWithValue(":uuid", props.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": AgentPropertiesUpdate exception {0}", e.Message); + + return false; + } + return true; + } + + public bool UpdateAvatarInterests(UserProfileProperties up, ref string result) + { + string query = string.Empty; + + query += "UPDATE userprofile SET "; + query += "profileWantToMask=:WantMask, "; + query += "profileWantToText=:WantText,"; + query += "profileSkillsMask=:SkillsMask,"; + query += "profileSkillsText=:SkillsText, "; + query += "profileLanguages=:Languages "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":WantMask", up.WantToMask); + cmd.Parameters.AddWithValue(":WantText", up.WantToText); + cmd.Parameters.AddWithValue(":SkillsMask", up.SkillsMask); + cmd.Parameters.AddWithValue(":SkillsText", up.SkillsText); + cmd.Parameters.AddWithValue(":Languages", up.Language); + cmd.Parameters.AddWithValue(":uuid", up.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool UpdateUserPreferences(ref UserPreferences pref, ref string result) + { + string query = string.Empty; + + query += "UPDATE usersettings SET "; + query += "imviaemail=:ImViaEmail, "; + query += "visible=:Visible, "; + query += "email=:EMail "; + query += "WHERE useruuid=:uuid"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":ImViaEmail", pref.IMViaEmail); + cmd.Parameters.AddWithValue(":Visible", pref.Visible); + cmd.Parameters.AddWithValue(":EMail", pref.EMail); + cmd.Parameters.AddWithValue(":uuid", pref.UserId.ToString()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": AgentInterestsUpdate exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool GetUserPreferences(ref UserPreferences pref, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT imviaemail,visible,email FROM "; + query += "usersettings WHERE "; + query += "useruuid = :Id"; + + OSDArray data = new OSDArray(); + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue("?Id", pref.UserId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + bool.TryParse((string)reader["imviaemail"], out pref.IMViaEmail); + bool.TryParse((string)reader["visible"], out pref.Visible); + pref.EMail = (string)reader["email"]; + } + else + { + query = "INSERT INTO usersettings VALUES "; + query += "(:Id,'false','false', :Email)"; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.Parameters.AddWithValue(":Id", pref.UserId.ToString()); + put.Parameters.AddWithValue(":Email", pref.EMail); + put.ExecuteNonQuery(); + + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": Get preferences exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + + public bool GetUserAppData(ref UserAppData props, ref string result) + { + IDataReader reader = null; + string query = string.Empty; + + query += "SELECT * FROM `userdata` WHERE "; + query += "UserId = :Id AND "; + query += "TagId = :TagId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", props.UserId.ToString()); + cmd.Parameters.AddWithValue (":TagId", props.TagId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + props.DataKey = (string)reader["DataKey"]; + props.DataVal = (string)reader["DataVal"]; + } + else + { + query += "INSERT INTO userdata VALUES ( "; + query += ":UserId,"; + query += ":TagId,"; + query += ":DataKey,"; + query += ":DataVal) "; + + using (SqliteCommand put = (SqliteCommand)m_connection.CreateCommand()) + { + put.Parameters.AddWithValue(":Id", props.UserId.ToString()); + put.Parameters.AddWithValue(":TagId", props.TagId.ToString()); + put.Parameters.AddWithValue(":DataKey", props.DataKey.ToString()); + put.Parameters.AddWithValue(":DataVal", props.DataVal.ToString()); + + put.ExecuteNonQuery(); + } + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": Requst application data exception {0}", e.Message); + result = e.Message; + return false; + } + return true; + } + public bool SetUserAppData(UserAppData props, ref string result) + { + string query = string.Empty; + + query += "UPDATE userdata SET "; + query += "TagId = :TagId, "; + query += "DataKey = :DataKey, "; + query += "DataVal = :DataVal WHERE "; + query += "UserId = :UserId AND "; + query += "TagId = :TagId"; + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":UserId", props.UserId.ToString()); + cmd.Parameters.AddWithValue(":TagId", props.TagId.ToString ()); + cmd.Parameters.AddWithValue(":DataKey", props.DataKey.ToString ()); + cmd.Parameters.AddWithValue(":DataVal", props.DataKey.ToString ()); + + cmd.ExecuteNonQuery(); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": SetUserData exception {0}", e.Message); + return false; + } + return true; + } + public OSDArray GetUserImageAssets(UUID avatarId) + { + IDataReader reader = null; + OSDArray data = new OSDArray(); + string query = "SELECT `snapshotuuid` FROM {0} WHERE `creatoruuid` = :Id"; + + // Get classified image assets + + + try + { + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + while(reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString())); + } + } + } + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + data.Add(new OSDString((string)reader["snapshotuuid"].ToString ())); + } + } + } + + query = "SELECT `profileImage`, `profileFirstImage` FROM `userprofile` WHERE `useruuid` = :Id"; + + using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand()) + { + cmd.CommandText = query; + cmd.Parameters.AddWithValue(":Id", avatarId.ToString()); + + using (reader = cmd.ExecuteReader(CommandBehavior.SingleRow)) + { + if(reader.Read()) + { + data.Add(new OSDString((string)reader["profileImage"].ToString ())); + data.Add(new OSDString((string)reader["profileFirstImage"].ToString ())); + } + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[PROFILES_DATA]" + + ": GetAvatarNotes exception {0}", e.Message); + } + return data; + } + #endregion + } +} + diff --git a/OpenSim/Data/SQLite/SQLiteUtils.cs b/OpenSim/Data/SQLite/SQLiteUtils.cs new file mode 100644 index 0000000000..ca5861fefd --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteUtils.cs @@ -0,0 +1,311 @@ +/* + * 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.Data; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif + +namespace OpenSim.Data.SQLite +{ + /// + /// A base class for methods needed by all SQLite database classes + /// + public class SQLiteUtil + { + /*********************************************************************** + * + * Database Definition Helper Functions + * + * This should be db agnostic as we define them in ADO.NET terms + * + **********************************************************************/ + + /// + /// + /// + /// + /// + /// + public static void createCol(DataTable dt, string name, Type type) + { + DataColumn col = new DataColumn(name, type); + dt.Columns.Add(col); + } + + /*********************************************************************** + * + * SQL Statement Creation Functions + * + * These functions create SQL statements for update, insert, and create. + * They can probably be factored later to have a db independant + * portion and a db specific portion + * + **********************************************************************/ + + /// + /// Create an insert command + /// + /// table name + /// data table + /// the created command + /// + /// This is subtle enough to deserve some commentary. + /// Instead of doing *lots* and *lots of hardcoded strings + /// for database definitions we'll use the fact that + /// realistically all insert statements look like "insert + /// into A(b, c) values(:b, :c) on the parameterized query + /// front. If we just have a list of b, c, etc... we can + /// generate these strings instead of typing them out. + /// + public static SqliteCommand createInsertCommand(string table, DataTable dt) + { + + string[] cols = new string[dt.Columns.Count]; + for (int i = 0; i < dt.Columns.Count; i++) + { + DataColumn col = dt.Columns[i]; + cols[i] = col.ColumnName; + } + + string sql = "insert into " + table + "("; + sql += String.Join(", ", cols); + // important, the first ':' needs to be here, the rest get added in the join + sql += ") values (:"; + sql += String.Join(", :", cols); + sql += ")"; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// create an update command + /// + /// table name + /// + /// + /// the created command + public static SqliteCommand createUpdateCommand(string table, string pk, DataTable dt) + { + string sql = "update " + table + " set "; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ", "; + } + subsql += col.ColumnName + "= :" + col.ColumnName; + } + sql += subsql; + sql += " where " + pk; + SqliteCommand cmd = new SqliteCommand(sql); + + // this provides the binding for all our parameters, so + // much less code than it used to be + + foreach (DataColumn col in dt.Columns) + { + cmd.Parameters.Add(createSqliteParameter(col.ColumnName, col.DataType)); + } + return cmd; + } + + /// + /// + /// + /// Data Table + /// + public static string defineTable(DataTable dt) + { + string sql = "create table " + dt.TableName + "("; + string subsql = String.Empty; + foreach (DataColumn col in dt.Columns) + { + if (subsql.Length > 0) + { + // a map function would rock so much here + subsql += ",\n"; + } + subsql += col.ColumnName + " " + sqliteType(col.DataType); + if (dt.PrimaryKey.Length > 0) + { + if (col == dt.PrimaryKey[0]) + { + subsql += " primary key"; + } + } + } + sql += subsql; + sql += ")"; + return sql; + } + + /*********************************************************************** + * + * Database Binding functions + * + * These will be db specific due to typing, and minor differences + * in databases. + * + **********************************************************************/ + + /// + /// + /// This is a convenience function that collapses 5 repetitive + /// lines for defining SqliteParameters to 2 parameters: + /// column name and database type. + /// + /// + /// + /// It assumes certain conventions like :param as the param + /// name to replace in parametrized queries, and that source + /// version is always current version, both of which are fine + /// for us. + /// + /// + /// + /// + ///a built sqlite parameter + public static SqliteParameter createSqliteParameter(string name, Type type) + { + SqliteParameter param = new SqliteParameter(); + param.ParameterName = ":" + name; + param.DbType = dbtypeFromType(type); + param.SourceColumn = name; + param.SourceVersion = DataRowVersion.Current; + return param; + } + + /*********************************************************************** + * + * Type conversion functions + * + **********************************************************************/ + + /// + /// Type conversion function + /// + /// a type + /// a DbType + public static DbType dbtypeFromType(Type type) + { + if (type == typeof (String)) + { + return DbType.String; + } + else if (type == typeof (Int32)) + { + return DbType.Int32; + } + else if (type == typeof (UInt32)) + { + return DbType.UInt32; + } + else if (type == typeof (Int64)) + { + return DbType.Int64; + } + else if (type == typeof (UInt64)) + { + return DbType.UInt64; + } + else if (type == typeof (Double)) + { + return DbType.Double; + } + else if (type == typeof (Boolean)) + { + return DbType.Boolean; + } + else if (type == typeof (Byte[])) + { + return DbType.Binary; + } + else + { + return DbType.String; + } + } + + /// + /// + /// a Type + /// a string + /// this is something we'll need to implement for each db slightly differently. + public static string sqliteType(Type type) + { + if (type == typeof (String)) + { + return "varchar(255)"; + } + else if (type == typeof (Int32)) + { + return "integer"; + } + else if (type == typeof (UInt32)) + { + return "integer"; + } + else if (type == typeof (Int64)) + { + return "varchar(255)"; + } + else if (type == typeof (UInt64)) + { + return "varchar(255)"; + } + else if (type == typeof (Double)) + { + return "float"; + } + else if (type == typeof (Boolean)) + { + return "integer"; + } + else if (type == typeof (Byte[])) + { + return "blob"; + } + else + { + return "string"; + } + } + } +} diff --git a/OpenSim/Data/SQLite/SQLiteXInventoryData.cs b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs new file mode 100644 index 0000000000..2a0a8f6cc9 --- /dev/null +++ b/OpenSim/Data/SQLite/SQLiteXInventoryData.cs @@ -0,0 +1,324 @@ +/* + * 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.Data; +using System.Reflection; +using System.Collections.Generic; +#if CSharpSqlite + using Community.CsharpSqlite.Sqlite; +#else + using Mono.Data.Sqlite; +#endif +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Data.SQLite +{ + /// + /// A SQLite Interface for the Asset Server + /// + public class SQLiteXInventoryData : IXInventoryData + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SqliteFolderHandler m_Folders; + private SqliteItemHandler m_Items; + + public SQLiteXInventoryData(string conn, string realm) + { + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + m_Folders = new SqliteFolderHandler( + conn, "inventoryfolders", "XInventoryStore"); + m_Items = new SqliteItemHandler( + conn, "inventoryitems", String.Empty); + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { + return m_Folders.Get(fields, vals); + } + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { + return m_Items.Get(fields, vals); + } + + public bool StoreFolder(XInventoryFolder folder) + { + if (folder.folderName.Length > 64) + folder.folderName = folder.folderName.Substring(0, 64); + + return m_Folders.Store(folder); + } + + public bool StoreItem(XInventoryItem item) + { + if (item.inventoryName.Length > 64) + item.inventoryName = item.inventoryName.Substring(0, 64); + if (item.inventoryDescription.Length > 128) + item.inventoryDescription = item.inventoryDescription.Substring(0, 128); + + return m_Items.Store(item); + } + + public bool DeleteFolders(string field, string val) + { + return m_Folders.Delete(field, val); + } + + public bool DeleteFolders(string[] fields, string[] vals) + { + return m_Folders.Delete(fields, vals); + } + + public bool DeleteItems(string field, string val) + { + return m_Items.Delete(field, val); + } + + public bool DeleteItems(string[] fields, string[] vals) + { + return m_Items.Delete(fields, vals); + } + + public bool MoveItem(string id, string newParent) + { + return m_Items.MoveItem(id, newParent); + } + + public bool MoveFolder(string id, string newParent) + { + return m_Folders.MoveFolder(id, newParent); + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + return m_Items.GetActiveGestures(principalID); + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Items.GetAssetPermissions(principalID, assetID); + } + } + + public class SqliteItemHandler : SqliteInventoryHandler + { + public SqliteItemHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public override bool Store(XInventoryItem item) + { + if (!base.Store(item)) + return false; + + IncrementFolderVersion(item.parentFolderID); + + return true; + } + + public override bool Delete(string field, string val) + { + XInventoryItem[] retrievedItems = Get(new string[] { field }, new string[] { val }); + if (retrievedItems.Length == 0) + return false; + + if (!base.Delete(field, val)) + return false; + + // Don't increment folder version here since Delete(string, string) calls Delete(string[], string[]) +// IncrementFolderVersion(retrievedItems[0].parentFolderID); + + return true; + } + + public override bool Delete(string[] fields, string[] vals) + { + XInventoryItem[] retrievedItems = Get(fields, vals); + if (retrievedItems.Length == 0) + return false; + + if (!base.Delete(fields, vals)) + return false; + + HashSet deletedItemFolderUUIDs = new HashSet(); + + Array.ForEach(retrievedItems, i => deletedItemFolderUUIDs.Add(i.parentFolderID)); + + foreach (UUID deletedItemFolderUUID in deletedItemFolderUUIDs) + IncrementFolderVersion(deletedItemFolderUUID); + + return true; + } + + public bool MoveItem(string id, string newParent) + { + XInventoryItem[] retrievedItems = Get(new string[] { "inventoryID" }, new string[] { id }); + if (retrievedItems.Length == 0) + return false; + + UUID oldParent = retrievedItems[0].parentFolderID; + + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where inventoryID = :InventoryID", m_Realm); + cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParent)); + cmd.Parameters.Add(new SqliteParameter(":InventoryID", id)); + + if (ExecuteNonQuery(cmd, m_Connection) == 0) + return false; + } + + IncrementFolderVersion(oldParent); + IncrementFolderVersion(newParent); + + return true; + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) + { + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("select * from inventoryitems where avatarId = :uuid and assetType = :type and flags = 1", m_Realm); + + cmd.Parameters.Add(new SqliteParameter(":uuid", principalID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":type", (int)AssetType.Gesture)); + + return DoQuery(cmd); + } + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + IDataReader reader; + + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("select inventoryCurrentPermissions from inventoryitems where avatarID = :PrincipalID and assetID = :AssetID", m_Realm); + cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString())); + cmd.Parameters.Add(new SqliteParameter(":AssetID", assetID.ToString())); + + reader = ExecuteReader(cmd, m_Connection); + } + + int perms = 0; + + while (reader.Read()) + { + perms |= Convert.ToInt32(reader["inventoryCurrentPermissions"]); + } + + reader.Close(); + //CloseCommand(cmd); + + return perms; + } + } + + public class SqliteFolderHandler : SqliteInventoryHandler + { + public SqliteFolderHandler(string c, string t, string m) : + base(c, t, m) + { + } + + public override bool Store(XInventoryFolder folder) + { + if (!base.Store(folder)) + return false; + + IncrementFolderVersion(folder.parentFolderID); + + return true; + } + + public bool MoveFolder(string id, string newParentFolderID) + { + XInventoryFolder[] folders = Get(new string[] { "folderID" }, new string[] { id }); + + if (folders.Length == 0) + return false; + + UUID oldParentFolderUUID = folders[0].parentFolderID; + + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = String.Format("update {0} set parentFolderID = :ParentFolderID where folderID = :FolderID", m_Realm); + cmd.Parameters.Add(new SqliteParameter(":ParentFolderID", newParentFolderID)); + cmd.Parameters.Add(new SqliteParameter(":FolderID", id)); + + if (ExecuteNonQuery(cmd, m_Connection) == 0) + return false; + } + + IncrementFolderVersion(oldParentFolderUUID); + IncrementFolderVersion(newParentFolderID); + + return true; + } + + } + + public class SqliteInventoryHandler : SQLiteGenericTableHandler where T: class, new() + { + public SqliteInventoryHandler(string c, string t, string m) : base(c, t, m) {} + + protected bool IncrementFolderVersion(UUID folderID) + { + return IncrementFolderVersion(folderID.ToString()); + } + + protected bool IncrementFolderVersion(string folderID) + { +// m_log.DebugFormat("[MYSQL ITEM HANDLER]: Incrementing version on folder {0}", folderID); +// Util.PrintCallStack(); + + using (SqliteCommand cmd = new SqliteCommand()) + { + cmd.CommandText = "update inventoryfolders set version=version+1 where folderID = ?folderID"; + cmd.Parameters.Add(new SqliteParameter(":folderID", folderID)); + + try + { + cmd.ExecuteNonQuery(); + } + catch (Exception) + { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Tests/AssetTests.cs b/OpenSim/Data/Tests/AssetTests.cs new file mode 100644 index 0000000000..5d7b16962f --- /dev/null +++ b/OpenSim/Data/Tests/AssetTests.cs @@ -0,0 +1,206 @@ +/* + * 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 log4net.Config; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Tests.Common; +using System.Data.Common; +using log4net; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + +namespace OpenSim.Data.Tests +{ + [TestFixture(Description = "Asset store tests (SQLite)")] + public class SQLiteAssetTests : AssetTests + { + } + + [TestFixture(Description = "Asset store tests (MySQL)")] + public class MySqlAssetTests : AssetTests + { + } + + public class AssetTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TAssetData : AssetDataBase, new() + { + TAssetData m_db; + + public UUID uuid1 = UUID.Random(); + public UUID uuid2 = UUID.Random(); + public UUID uuid3 = UUID.Random(); + + public string critter1 = UUID.Random().ToString(); + public string critter2 = UUID.Random().ToString(); + public string critter3 = UUID.Random().ToString(); + + public byte[] data1 = new byte[100]; + + PropertyScrambler scrambler = new PropertyScrambler() + .DontScramble(x => x.ID) + .DontScramble(x => x.Type) + .DontScramble(x => x.FullID) + .DontScramble(x => x.Metadata.ID) + .DontScramble(x => x.Metadata.CreatorID) + .DontScramble(x => x.Metadata.ContentType) + .DontScramble(x => x.Metadata.FullID) + .DontScramble(x => x.Data); + + protected override void InitService(object service) + { + ClearDB(); + m_db = (TAssetData)service; + m_db.Initialise(m_connStr); + } + + private void ClearDB() + { + DropTables("assets"); + ResetMigrations("AssetStore"); + } + + + [Test] + public void T001_LoadEmpty() + { + TestHelpers.InMethod(); + + bool[] exist = m_db.AssetsExist(new[] { uuid1, uuid2, uuid3 }); + Assert.IsFalse(exist[0]); + Assert.IsFalse(exist[1]); + Assert.IsFalse(exist[2]); + } + + [Test] + public void T010_StoreReadVerifyAssets() + { + TestHelpers.InMethod(); + + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1.ToString()); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, critter2.ToString()); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, critter3.ToString()); + a1.Data = data1; + a2.Data = data1; + a3.Data = data1; + + scrambler.Scramble(a1); + scrambler.Scramble(a2); + scrambler.Scramble(a3); + + m_db.StoreAsset(a1); + m_db.StoreAsset(a2); + m_db.StoreAsset(a3); + + AssetBase a1a = m_db.GetAsset(uuid1); + Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); + + AssetBase a2a = m_db.GetAsset(uuid2); + Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); + + AssetBase a3a = m_db.GetAsset(uuid3); + Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); + + scrambler.Scramble(a1a); + scrambler.Scramble(a2a); + scrambler.Scramble(a3a); + + m_db.StoreAsset(a1a); + m_db.StoreAsset(a2a); + m_db.StoreAsset(a3a); + + AssetBase a1b = m_db.GetAsset(uuid1); + Assert.That(a1b, Constraints.PropertyCompareConstraint(a1a)); + + AssetBase a2b = m_db.GetAsset(uuid2); + Assert.That(a2b, Constraints.PropertyCompareConstraint(a2a)); + + AssetBase a3b = m_db.GetAsset(uuid3); + Assert.That(a3b, Constraints.PropertyCompareConstraint(a3a)); + + bool[] exist = m_db.AssetsExist(new[] { uuid1, uuid2, uuid3 }); + Assert.IsTrue(exist[0]); + Assert.IsTrue(exist[1]); + Assert.IsTrue(exist[2]); + + List metadatas = m_db.FetchAssetMetadataSet(0, 1000); + + Assert.That(metadatas.Count >= 3, "FetchAssetMetadataSet() should have returned at least 3 assets!"); + + // It is possible that the Asset table is filled with data, in which case we don't try to find "our" + // assets there: + if (metadatas.Count < 1000) + { + AssetMetadata metadata = metadatas.Find(x => x.FullID == uuid1); + Assert.That(metadata.Name, Is.EqualTo(a1b.Name)); + Assert.That(metadata.Description, Is.EqualTo(a1b.Description)); + Assert.That(metadata.Type, Is.EqualTo(a1b.Type)); + Assert.That(metadata.Temporary, Is.EqualTo(a1b.Temporary)); + Assert.That(metadata.FullID, Is.EqualTo(a1b.FullID)); + } + } + + [Test] + public void T020_CheckForWeirdCreatorID() + { + TestHelpers.InMethod(); + + // It is expected that eventually the CreatorID might be an arbitrary string (an URI) + // rather than a valid UUID (?). This test is to make sure that the database layer does not + // attempt to convert CreatorID to GUID, but just passes it both ways as a string. + AssetBase a1 = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, critter1); + AssetBase a2 = new AssetBase(uuid2, "asset two", (sbyte)AssetType.Texture, "This is not a GUID!"); + AssetBase a3 = new AssetBase(uuid3, "asset three", (sbyte)AssetType.Texture, ""); + a1.Data = data1; + a2.Data = data1; + a3.Data = data1; + + m_db.StoreAsset(a1); + m_db.StoreAsset(a2); + m_db.StoreAsset(a3); + + AssetBase a1a = m_db.GetAsset(uuid1); + Assert.That(a1a, Constraints.PropertyCompareConstraint(a1)); + + AssetBase a2a = m_db.GetAsset(uuid2); + Assert.That(a2a, Constraints.PropertyCompareConstraint(a2)); + + AssetBase a3a = m_db.GetAsset(uuid3); + Assert.That(a3a, Constraints.PropertyCompareConstraint(a3)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Tests/BasicDataServiceTest.cs b/OpenSim/Data/Tests/BasicDataServiceTest.cs new file mode 100644 index 0000000000..acfebd0206 --- /dev/null +++ b/OpenSim/Data/Tests/BasicDataServiceTest.cs @@ -0,0 +1,271 @@ +/* + * 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.Collections.Generic; +using log4net.Config; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Tests.Common; +using log4net; +using System.Data; +using System.Data.Common; +using System.Reflection; + +namespace OpenSim.Data.Tests +{ + /// This is a base class for testing any Data service for any DBMS. + /// Requires NUnit 2.5 or better (to support the generics). + /// + /// + /// FIXME: Should extend OpenSimTestCase but compile on mono 2.4.3 currently fails with + /// AssetTests`2 : System.MemberAccessException : Cannot create an instance of OpenSim.Data.Tests.AssetTests`2[TConn,TAssetData] because Type.ContainsGenericParameters is true. + /// and similar on EstateTests, InventoryTests and RegionTests. + /// Runs fine with mono 2.10.8.1, so easiest thing is to wait until min Mono version uplifts. + /// + /// + /// + public class BasicDataServiceTest + where TConn : DbConnection, new() + where TService : class, new() + { + protected string m_connStr; + private TService m_service; + private string m_file; + + // TODO: Is this in the right place here? + // Later: apparently it's not, but does it matter here? +// protected static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected ILog m_log; // doesn't matter here that it's not static, init to correct type in instance .ctor + + public BasicDataServiceTest() + : this("") + { + } + + public BasicDataServiceTest(string conn) + { + m_connStr = !String.IsNullOrEmpty(conn) ? conn : DefaultTestConns.Get(typeof(TConn)); + + m_log = LogManager.GetLogger(this.GetType()); + OpenSim.Tests.Common.TestLogging.LogToConsole(); // TODO: Is that right? + } + + /// + /// To be overridden in derived classes. Do whatever init with the m_service, like setting the conn string to it. + /// You'd probably want to to cast the 'service' to a more specific type and store it in a member var. + /// This framework takes care of disposing it, if it's disposable. + /// + /// The service being tested + protected virtual void InitService(object service) + { + } + + [TestFixtureSetUp] + public void Init() + { + // Sorry, some SQLite-specific stuff goes here (not a big deal, as its just some file ops) + if (typeof(TConn).Name.StartsWith("Sqlite")) + { + // SQLite doesn't work on power or z linux + if (Directory.Exists("/proc/ppc64") || Directory.Exists("/proc/dasd")) + Assert.Ignore(); + + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + // for SQLite, if no explicit conn string is specified, use a temp file + if (String.IsNullOrEmpty(m_connStr)) + { + m_file = Path.GetTempFileName() + ".db"; + m_connStr = "URI=file:" + m_file + ",version=3"; + } + } + + if (String.IsNullOrEmpty(m_connStr)) + { + string msg = String.Format("Connection string for {0} is not defined, ignoring tests", typeof(TConn).Name); + m_log.Warn(msg); + Assert.Ignore(msg); + } + + // Try the connection, ignore tests if Open() fails + using (TConn conn = new TConn()) + { + conn.ConnectionString = m_connStr; + try + { + conn.Open(); + conn.Close(); + } + catch + { + string msg = String.Format("{0} is unable to connect to the database, ignoring tests", typeof(TConn).Name); + m_log.Warn(msg); + Assert.Ignore(msg); + } + } + + // If we manage to connect to the database with the user + // and password above it is our test database, and run + // these tests. If anything goes wrong, ignore these + // tests. + try + { + m_service = new TService(); + InitService(m_service); + } + catch (Exception e) + { + m_log.Error(e.ToString()); + Assert.Ignore(); + } + } + + [TestFixtureTearDown] + public void Cleanup() + { + if (m_service != null) + { + if (m_service is IDisposable) + ((IDisposable)m_service).Dispose(); + m_service = null; + } + + if (!String.IsNullOrEmpty(m_file) && File.Exists(m_file)) + File.Delete(m_file); + } + + protected virtual DbConnection Connect() + { + DbConnection cnn = new TConn(); + cnn.ConnectionString = m_connStr; + cnn.Open(); + return cnn; + } + + protected virtual void ExecuteSql(string sql) + { + using (DbConnection dbcon = Connect()) + { + using (DbCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + } + } + + protected delegate bool ProcessRow(IDataReader reader); + + protected virtual int ExecQuery(string sql, bool bSingleRow, ProcessRow action) + { + int nRecs = 0; + using (DbConnection dbcon = Connect()) + { + using (DbCommand cmd = dbcon.CreateCommand()) + { + cmd.CommandText = sql; + CommandBehavior cb = bSingleRow ? CommandBehavior.SingleRow : CommandBehavior.Default; + using (DbDataReader rdr = cmd.ExecuteReader(cb)) + { + while (rdr.Read()) + { + nRecs++; + if (!action(rdr)) + break; + } + } + } + } + return nRecs; + } + + /// Drop tables (listed as parameters). There is no "DROP IF EXISTS" syntax common for all + /// databases, so we just DROP and ignore an exception. + /// + /// + protected virtual void DropTables(params string[] tables) + { + foreach (string tbl in tables) + { + try + { + ExecuteSql("DROP TABLE " + tbl + ";"); + }catch + { + } + } + } + + /// Clear tables listed as parameters (without dropping them). + /// + /// + protected virtual void ResetMigrations(params string[] stores) + { + string lst = ""; + foreach (string store in stores) + { + string s = "'" + store + "'"; + if (lst == "") + lst = s; + else + lst += ", " + s; + } + + string sCond = stores.Length > 1 ? ("in (" + lst + ")") : ("=" + lst); + try + { + ExecuteSql("DELETE FROM migrations where name " + sCond); + } + catch + { + } + } + + /// Clear tables listed as parameters (without dropping them). + /// + /// + protected virtual void ClearTables(params string[] tables) + { + foreach (string tbl in tables) + { + try + { + ExecuteSql("DELETE FROM " + tbl + ";"); + } + catch + { + } + } + } + } +} diff --git a/OpenSim/Data/Tests/DataTestUtil.cs b/OpenSim/Data/Tests/DataTestUtil.cs new file mode 100644 index 0000000000..5393529592 --- /dev/null +++ b/OpenSim/Data/Tests/DataTestUtil.cs @@ -0,0 +1,88 @@ +/* + * 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.Text; +using OpenMetaverse; +using NUnit.Framework; + +namespace OpenSim.Data.Tests +{ + /// + /// Shared constants and methods for database unit tests. + /// + public class DataTestUtil + { + public const uint UNSIGNED_INTEGER_MIN = uint.MinValue; + //public const uint UNSIGNED_INTEGER_MAX = uint.MaxValue; + public const uint UNSIGNED_INTEGER_MAX = INTEGER_MAX; + + public const int INTEGER_MIN = int.MinValue + 1; // Postgresql requires +1 to .NET int.MinValue + public const int INTEGER_MAX = int.MaxValue; + + public const float FLOAT_MIN = float.MinValue * (1 - FLOAT_PRECISSION); + public const float FLOAT_MAX = float.MaxValue * (1 - FLOAT_PRECISSION); + public const float FLOAT_ACCURATE = 1.234567890123456789012f; + public const float FLOAT_PRECISSION = 1E-5f; // Native MySQL is severly limited with floating accuracy + + public const double DOUBLE_MIN = -1E52 * (1 - DOUBLE_PRECISSION); + public const double DOUBLE_MAX = 1E52 * (1 - DOUBLE_PRECISSION); + public const double DOUBLE_ACCURATE = 1.2345678901234567890123456789012345678901234567890123f; + public const double DOUBLE_PRECISSION = 1E-14; // Native MySQL is severly limited with double accuracy + + public const string STRING_MIN = ""; + public static string STRING_MAX(int length) + { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < length; i++) + { + stringBuilder.Append(i % 10); + } + return stringBuilder.ToString(); + } + + public static UUID UUID_MIN = new UUID("00000000-0000-0000-0000-000000000000"); + public static UUID UUID_MAX = new UUID("ffffffff-ffff-ffff-ffff-ffffffffffff"); + + public const bool BOOLEAN_MIN = false; + public const bool BOOLEAN_MAX = true; + + public static void AssertFloatEqualsWithTolerance(float expectedValue, float actualValue) + { + Assert.GreaterOrEqual(actualValue, expectedValue - Math.Abs(expectedValue) * FLOAT_PRECISSION); + Assert.LessOrEqual(actualValue, expectedValue + Math.Abs(expectedValue) * FLOAT_PRECISSION); + } + + public static void AssertDoubleEqualsWithTolerance(double expectedValue, double actualValue) + { + Assert.GreaterOrEqual(actualValue, expectedValue - Math.Abs(expectedValue) * DOUBLE_PRECISSION); + Assert.LessOrEqual(actualValue, expectedValue + Math.Abs(expectedValue) * DOUBLE_PRECISSION); + } + } +} + diff --git a/OpenSim/Data/Tests/DefaultTestConns.cs b/OpenSim/Data/Tests/DefaultTestConns.cs new file mode 100644 index 0000000000..7c47bddd21 --- /dev/null +++ b/OpenSim/Data/Tests/DefaultTestConns.cs @@ -0,0 +1,90 @@ +/* + * 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.Text; +using System.Reflection; +using System.IO; +using Nini.Config; + +namespace OpenSim.Data.Tests +{ + /// This static class looks for TestDataConnections.ini file in the /bin directory to obtain + /// a connection string for testing one of the supported databases. + /// The connections must be in the section [TestConnections] with names matching the connection class + /// name for the specific database, e.g.: + /// + /// [TestConnections] + /// MySqlConnection="..." + /// SqlConnection="..." + /// SqliteConnection="..." + /// + /// Note that the conn string may also be set explicitly in the [TestCase()] attribute of test classes + /// based on BasicDataServiceTest.cs. + /// + + static class DefaultTestConns + { + private static Dictionary conns = new Dictionary(); + + public static string Get(Type connType) + { + string sConn; + + if (conns.TryGetValue(connType, out sConn)) + return sConn; + + Assembly asm = Assembly.GetExecutingAssembly(); + string sType = connType.Name; + + // Note: when running from NUnit, the DLL is located in some temp dir, so how do we get + // to the INI file? Ok, so put it into the resources! + // string iniName = Path.Combine(Path.GetDirectoryName(asm.Location), "TestDataConnections.ini"); + + string[] allres = asm.GetManifestResourceNames(); + string sResFile = Array.Find(allres, s => s.Contains("TestDataConnections.ini")); + + if (String.IsNullOrEmpty(sResFile)) + throw new Exception(String.Format("Please add resource TestDataConnections.ini, with section [TestConnections] and settings like {0}=\"...\"", + sType)); + + using (Stream resource = asm.GetManifestResourceStream(sResFile)) + { + IConfigSource source = new IniConfigSource(resource); + var cfg = source.Configs["TestConnections"]; + sConn = cfg.Get(sType, ""); + } + + if (!String.IsNullOrEmpty(sConn)) + conns[connType] = sConn; + + return sConn; + } + } +} diff --git a/OpenSim/Data/Tests/EstateTests.cs b/OpenSim/Data/Tests/EstateTests.cs new file mode 100644 index 0000000000..e2b2d12aaa --- /dev/null +++ b/OpenSim/Data/Tests/EstateTests.cs @@ -0,0 +1,521 @@ +/* + * 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 log4net.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Tests.Common; +using System.Text; +using log4net; +using System.Reflection; +using System.Data.Common; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + +namespace OpenSim.Data.Tests +{ + [TestFixture(Description = "Estate store tests (SQLite)")] + public class SQLiteEstateTests : EstateTests + { + } + + [TestFixture(Description = "Estate store tests (MySQL)")] + public class MySqlEstateTests : EstateTests + { + } + + public class EstateTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TEstateStore : class, IEstateDataStore, new() + { + public IEstateDataStore db; + + public static UUID REGION_ID = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed7"); + + public static UUID USER_ID_1 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed1"); + public static UUID USER_ID_2 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed2"); + + public static UUID MANAGER_ID_1 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed3"); + public static UUID MANAGER_ID_2 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed4"); + + public static UUID GROUP_ID_1 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed5"); + public static UUID GROUP_ID_2 = new UUID("250d214e-1c7e-4f9b-a488-87c5e53feed6"); + + protected override void InitService(object service) + { + ClearDB(); + db = (IEstateDataStore)service; + db.Initialise(m_connStr); + } + + private void ClearDB() + { + // if a new table is added, it has to be dropped here + DropTables( + "estate_managers", + "estate_groups", + "estate_users", + "estateban", + "estate_settings", + "estate_map" + ); + ResetMigrations("EstateStore"); + } + + #region 0Tests + + [Test] + public void T010_EstateSettingsSimpleStorage_MinimumParameterSet() + { + TestHelpers.InMethod(); + + EstateSettingsSimpleStorage( + REGION_ID, + DataTestUtil.STRING_MIN, + DataTestUtil.UNSIGNED_INTEGER_MIN, + DataTestUtil.FLOAT_MIN, + DataTestUtil.INTEGER_MIN, + DataTestUtil.INTEGER_MIN, + DataTestUtil.INTEGER_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.DOUBLE_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.STRING_MIN, + DataTestUtil.UUID_MIN + ); + } + + [Test] + public void T011_EstateSettingsSimpleStorage_MaximumParameterSet() + { + TestHelpers.InMethod(); + + EstateSettingsSimpleStorage( + REGION_ID, + DataTestUtil.STRING_MAX(64), + DataTestUtil.UNSIGNED_INTEGER_MAX, + DataTestUtil.FLOAT_MAX, + DataTestUtil.INTEGER_MAX, + DataTestUtil.INTEGER_MAX, + DataTestUtil.INTEGER_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.DOUBLE_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.BOOLEAN_MAX, + DataTestUtil.STRING_MAX(255), + DataTestUtil.UUID_MAX + ); + } + + [Test] + public void T012_EstateSettingsSimpleStorage_AccurateParameterSet() + { + TestHelpers.InMethod(); + + EstateSettingsSimpleStorage( + REGION_ID, + DataTestUtil.STRING_MAX(1), + DataTestUtil.UNSIGNED_INTEGER_MIN, + DataTestUtil.FLOAT_ACCURATE, + DataTestUtil.INTEGER_MIN, + DataTestUtil.INTEGER_MIN, + DataTestUtil.INTEGER_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.DOUBLE_ACCURATE, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.BOOLEAN_MIN, + DataTestUtil.STRING_MAX(1), + DataTestUtil.UUID_MIN + ); + } + + [Test] + public void T012_EstateSettingsRandomStorage() + { + TestHelpers.InMethod(); + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(REGION_ID, true); + new PropertyScrambler() + .DontScramble(x=>x.EstateID) + .Scramble(originalSettings); + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(REGION_ID, true); + + // Checking that loaded values are correct. + Assert.That(loadedSettings, Constraints.PropertyCompareConstraint(originalSettings)); + } + + [Test] + public void T020_EstateSettingsManagerList() + { + TestHelpers.InMethod(); + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(REGION_ID, true); + + originalSettings.EstateManagers = new UUID[] { MANAGER_ID_1, MANAGER_ID_2 }; + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(REGION_ID, true); + + Assert.AreEqual(2, loadedSettings.EstateManagers.Length); + Assert.AreEqual(MANAGER_ID_1, loadedSettings.EstateManagers[0]); + Assert.AreEqual(MANAGER_ID_2, loadedSettings.EstateManagers[1]); + } + + [Test] + public void T021_EstateSettingsUserList() + { + TestHelpers.InMethod(); + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(REGION_ID, true); + + originalSettings.EstateAccess = new UUID[] { USER_ID_1, USER_ID_2 }; + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(REGION_ID, true); + + Assert.AreEqual(2, loadedSettings.EstateAccess.Length); + Assert.AreEqual(USER_ID_1, loadedSettings.EstateAccess[0]); + Assert.AreEqual(USER_ID_2, loadedSettings.EstateAccess[1]); + } + + [Test] + public void T022_EstateSettingsGroupList() + { + TestHelpers.InMethod(); + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(REGION_ID, true); + + originalSettings.EstateGroups = new UUID[] { GROUP_ID_1, GROUP_ID_2 }; + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(REGION_ID, true); + + Assert.AreEqual(2, loadedSettings.EstateAccess.Length); + Assert.AreEqual(GROUP_ID_1, loadedSettings.EstateGroups[0]); + Assert.AreEqual(GROUP_ID_2, loadedSettings.EstateGroups[1]); + } + + [Test] + public void T022_EstateSettingsBanList() + { + TestHelpers.InMethod(); + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(REGION_ID, true); + + EstateBan estateBan1 = new EstateBan(); + estateBan1.BannedUserID = DataTestUtil.UUID_MIN; + + EstateBan estateBan2 = new EstateBan(); + estateBan2.BannedUserID = DataTestUtil.UUID_MAX; + + originalSettings.EstateBans = new EstateBan[] { estateBan1, estateBan2 }; + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(REGION_ID, true); + + Assert.AreEqual(2, loadedSettings.EstateBans.Length); + Assert.AreEqual(DataTestUtil.UUID_MIN, loadedSettings.EstateBans[0].BannedUserID); + + Assert.AreEqual(DataTestUtil.UUID_MAX, loadedSettings.EstateBans[1].BannedUserID); + + } + + #endregion + + #region Parametrizable Test Implementations + + private void EstateSettingsSimpleStorage( + UUID regionId, + string estateName, + uint parentEstateID, + float billableFactor, + int pricePerMeter, + int redirectGridX, + int redirectGridY, + bool useGlobalTime, + bool fixedSun, + double sunPosition, + bool allowVoice, + bool allowDirectTeleport, + bool resetHomeOnTeleport, + bool denyAnonymous, + bool denyIdentified, + bool denyTransacted, + bool denyMinors, + bool abuseEmailToEstateOwner, + bool blockDwell, + bool estateSkipScripts, + bool taxFree, + bool publicAccess, + string abuseEmail, + UUID estateOwner + ) + { + + // Letting estate store generate rows to database for us + EstateSettings originalSettings = db.LoadEstateSettings(regionId, true); + + SetEstateSettings(originalSettings, + estateName, + parentEstateID, + billableFactor, + pricePerMeter, + redirectGridX, + redirectGridY, + useGlobalTime, + fixedSun, + sunPosition, + allowVoice, + allowDirectTeleport, + resetHomeOnTeleport, + denyAnonymous, + denyIdentified, + denyTransacted, + denyMinors, + abuseEmailToEstateOwner, + blockDwell, + estateSkipScripts, + taxFree, + publicAccess, + abuseEmail, + estateOwner + ); + + // Saving settings. + db.StoreEstateSettings(originalSettings); + + // Loading settings to another instance variable. + EstateSettings loadedSettings = db.LoadEstateSettings(regionId, true); + + // Checking that loaded values are correct. + ValidateEstateSettings(loadedSettings, + estateName, + parentEstateID, + billableFactor, + pricePerMeter, + redirectGridX, + redirectGridY, + useGlobalTime, + fixedSun, + sunPosition, + allowVoice, + allowDirectTeleport, + resetHomeOnTeleport, + denyAnonymous, + denyIdentified, + denyTransacted, + denyMinors, + abuseEmailToEstateOwner, + blockDwell, + estateSkipScripts, + taxFree, + publicAccess, + abuseEmail, + estateOwner + ); + + } + + #endregion + + #region EstateSetting Initialization and Validation Methods + + private void SetEstateSettings( + EstateSettings estateSettings, + string estateName, + uint parentEstateID, + float billableFactor, + int pricePerMeter, + int redirectGridX, + int redirectGridY, + bool useGlobalTime, + bool fixedSun, + double sunPosition, + bool allowVoice, + bool allowDirectTeleport, + bool resetHomeOnTeleport, + bool denyAnonymous, + bool denyIdentified, + bool denyTransacted, + bool denyMinors, + bool abuseEmailToEstateOwner, + bool blockDwell, + bool estateSkipScripts, + bool taxFree, + bool publicAccess, + string abuseEmail, + UUID estateOwner + ) + { + estateSettings.EstateName = estateName; + estateSettings.ParentEstateID = parentEstateID; + estateSettings.BillableFactor = billableFactor; + estateSettings.PricePerMeter = pricePerMeter; + estateSettings.RedirectGridX = redirectGridX; + estateSettings.RedirectGridY = redirectGridY; + estateSettings.UseGlobalTime = useGlobalTime; + estateSettings.FixedSun = fixedSun; + estateSettings.SunPosition = sunPosition; + estateSettings.AllowVoice = allowVoice; + estateSettings.AllowDirectTeleport = allowDirectTeleport; + estateSettings.ResetHomeOnTeleport = resetHomeOnTeleport; + estateSettings.DenyAnonymous = denyAnonymous; + estateSettings.DenyIdentified = denyIdentified; + estateSettings.DenyTransacted = denyTransacted; + estateSettings.DenyMinors = denyMinors; + estateSettings.AbuseEmailToEstateOwner = abuseEmailToEstateOwner; + estateSettings.BlockDwell = blockDwell; + estateSettings.EstateSkipScripts = estateSkipScripts; + estateSettings.TaxFree = taxFree; + estateSettings.PublicAccess = publicAccess; + estateSettings.AbuseEmail = abuseEmail; + estateSettings.EstateOwner = estateOwner; + } + + private void ValidateEstateSettings( + EstateSettings estateSettings, + string estateName, + uint parentEstateID, + float billableFactor, + int pricePerMeter, + int redirectGridX, + int redirectGridY, + bool useGlobalTime, + bool fixedSun, + double sunPosition, + bool allowVoice, + bool allowDirectTeleport, + bool resetHomeOnTeleport, + bool denyAnonymous, + bool denyIdentified, + bool denyTransacted, + bool denyMinors, + bool abuseEmailToEstateOwner, + bool blockDwell, + bool estateSkipScripts, + bool taxFree, + bool publicAccess, + string abuseEmail, + UUID estateOwner + ) + { + Assert.AreEqual(estateName, estateSettings.EstateName); + Assert.AreEqual(parentEstateID, estateSettings.ParentEstateID); + + DataTestUtil.AssertFloatEqualsWithTolerance(billableFactor, estateSettings.BillableFactor); + + Assert.AreEqual(pricePerMeter, estateSettings.PricePerMeter); + Assert.AreEqual(redirectGridX, estateSettings.RedirectGridX); + Assert.AreEqual(redirectGridY, estateSettings.RedirectGridY); + Assert.AreEqual(useGlobalTime, estateSettings.UseGlobalTime); + Assert.AreEqual(fixedSun, estateSettings.FixedSun); + + DataTestUtil.AssertDoubleEqualsWithTolerance(sunPosition, estateSettings.SunPosition); + + Assert.AreEqual(allowVoice, estateSettings.AllowVoice); + Assert.AreEqual(allowDirectTeleport, estateSettings.AllowDirectTeleport); + Assert.AreEqual(resetHomeOnTeleport, estateSettings.ResetHomeOnTeleport); + Assert.AreEqual(denyAnonymous, estateSettings.DenyAnonymous); + Assert.AreEqual(denyIdentified, estateSettings.DenyIdentified); + Assert.AreEqual(denyTransacted, estateSettings.DenyTransacted); + Assert.AreEqual(denyMinors, estateSettings.DenyMinors); + Assert.AreEqual(abuseEmailToEstateOwner, estateSettings.AbuseEmailToEstateOwner); + Assert.AreEqual(blockDwell, estateSettings.BlockDwell); + Assert.AreEqual(estateSkipScripts, estateSettings.EstateSkipScripts); + Assert.AreEqual(taxFree, estateSettings.TaxFree); + Assert.AreEqual(publicAccess, estateSettings.PublicAccess); + Assert.AreEqual(abuseEmail, estateSettings.AbuseEmail); + Assert.AreEqual(estateOwner, estateSettings.EstateOwner); + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Data/Tests/InventoryTests.cs b/OpenSim/Data/Tests/InventoryTests.cs new file mode 100644 index 0000000000..3edf89d722 --- /dev/null +++ b/OpenSim/Data/Tests/InventoryTests.cs @@ -0,0 +1,381 @@ +/* + * 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 log4net.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Tests.Common; +using log4net; +using System.Reflection; +using System.Data.Common; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + +namespace OpenSim.Data.Tests +{ + [TestFixture(Description = "Inventory store tests (SQLite)")] + public class SQLiteInventoryTests : InventoryTests + { + } + + [TestFixture(Description = "Inventory store tests (MySQL)")] + public class MySqlInventoryTests : InventoryTests + { + } + + public class InventoryTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TInvStore : class, IInventoryDataPlugin, new() + { + public IInventoryDataPlugin db; + + public UUID zero = UUID.Zero; + + public UUID folder1 = UUID.Random(); + public UUID folder2 = UUID.Random(); + public UUID folder3 = UUID.Random(); + public UUID owner1 = UUID.Random(); + public UUID owner2 = UUID.Random(); + public UUID owner3 = UUID.Random(); + + public UUID item1 = UUID.Random(); + public UUID item2 = UUID.Random(); + public UUID item3 = UUID.Random(); + public UUID asset1 = UUID.Random(); + public UUID asset2 = UUID.Random(); + public UUID asset3 = UUID.Random(); + + public string name1; + public string name2 = "First Level folder"; + public string name3 = "First Level folder 2"; + public string niname1 = "My Shirt"; + public string iname1 = "Shirt"; + public string iname2 = "Text Board"; + public string iname3 = "No Pants Barrel"; + + public InventoryTests(string conn) : base(conn) + { + name1 = "Root Folder for " + owner1.ToString(); + } + public InventoryTests() : this("") { } + + protected override void InitService(object service) + { + ClearDB(); + db = (IInventoryDataPlugin)service; + db.Initialise(m_connStr); + } + + private void ClearDB() + { + DropTables("inventoryitems", "inventoryfolders"); + ResetMigrations("InventoryStore"); + } + + [Test] + public void T001_LoadEmpty() + { + TestHelpers.InMethod(); + + Assert.That(db.getInventoryFolder(zero), Is.Null); + Assert.That(db.getInventoryFolder(folder1), Is.Null); + Assert.That(db.getInventoryFolder(folder2), Is.Null); + Assert.That(db.getInventoryFolder(folder3), Is.Null); + + Assert.That(db.getInventoryItem(zero), Is.Null); + Assert.That(db.getInventoryItem(item1), Is.Null); + Assert.That(db.getInventoryItem(item2), Is.Null); + Assert.That(db.getInventoryItem(item3), Is.Null); + + Assert.That(db.getUserRootFolder(zero), Is.Null); + Assert.That(db.getUserRootFolder(owner1), Is.Null); + } + + // 01x - folder tests + [Test] + public void T010_FolderNonParent() + { + TestHelpers.InMethod(); + + InventoryFolderBase f1 = NewFolder(folder2, folder1, owner1, name2); + // the folder will go in + db.addInventoryFolder(f1); + InventoryFolderBase f1a = db.getUserRootFolder(owner1); + Assert.That(f1a, Is.Null); + } + + [Test] + public void T011_FolderCreate() + { + TestHelpers.InMethod(); + + InventoryFolderBase f1 = NewFolder(folder1, zero, owner1, name1); + // TODO: this is probably wrong behavior, but is what we have + // db.updateInventoryFolder(f1); + // InventoryFolderBase f1a = db.getUserRootFolder(owner1); + // Assert.That(uuid1, Is.EqualTo(f1a.ID)) + // Assert.That(name1, Text.Matches(f1a.Name), "Assert.That(name1, Text.Matches(f1a.Name))"); + // Assert.That(db.getUserRootFolder(owner1), Is.Null); + + // succeed with true + db.addInventoryFolder(f1); + InventoryFolderBase f1a = db.getUserRootFolder(owner1); + Assert.That(folder1, Is.EqualTo(f1a.ID), "Assert.That(folder1, Is.EqualTo(f1a.ID))"); + Assert.That(name1, Is.StringMatching(f1a.Name), "Assert.That(name1, Text.Matches(f1a.Name))"); + } + + // we now have the following tree + // folder1 + // +--- folder2 + // +--- folder3 + + [Test] + public void T012_FolderList() + { + TestHelpers.InMethod(); + + InventoryFolderBase f2 = NewFolder(folder3, folder1, owner1, name3); + db.addInventoryFolder(f2); + + Assert.That(db.getInventoryFolders(zero).Count, Is.EqualTo(1), "Assert.That(db.getInventoryFolders(zero).Count, Is.EqualTo(1))"); + Assert.That(db.getInventoryFolders(folder1).Count, Is.EqualTo(2), "Assert.That(db.getInventoryFolders(folder1).Count, Is.EqualTo(2))"); + Assert.That(db.getInventoryFolders(folder2).Count, Is.EqualTo(0), "Assert.That(db.getInventoryFolders(folder2).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryFolders(folder3).Count, Is.EqualTo(0), "Assert.That(db.getInventoryFolders(folder3).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryFolders(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.getInventoryFolders(UUID.Random()).Count, Is.EqualTo(0))"); + + } + + [Test] + public void T013_FolderHierarchy() + { + TestHelpers.InMethod(); + + int n = db.getFolderHierarchy(zero).Count; // (for dbg - easier to see what's returned) + Assert.That(n, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0))"); + n = db.getFolderHierarchy(folder1).Count; + Assert.That(n, Is.EqualTo(2), "Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2))"); + Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0))"); + Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(0))"); + Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0))"); + } + + + [Test] + public void T014_MoveFolder() + { + TestHelpers.InMethod(); + + InventoryFolderBase f2 = db.getInventoryFolder(folder2); + f2.ParentID = folder3; + db.moveInventoryFolder(f2); + + Assert.That(db.getInventoryFolders(zero).Count, Is.EqualTo(1), "Assert.That(db.getInventoryFolders(zero).Count, Is.EqualTo(1))"); + Assert.That(db.getInventoryFolders(folder1).Count, Is.EqualTo(1), "Assert.That(db.getInventoryFolders(folder1).Count, Is.EqualTo(1))"); + Assert.That(db.getInventoryFolders(folder2).Count, Is.EqualTo(0), "Assert.That(db.getInventoryFolders(folder2).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryFolders(folder3).Count, Is.EqualTo(1), "Assert.That(db.getInventoryFolders(folder3).Count, Is.EqualTo(1))"); + Assert.That(db.getInventoryFolders(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.getInventoryFolders(UUID.Random()).Count, Is.EqualTo(0))"); + } + + [Test] + public void T015_FolderHierarchy() + { + TestHelpers.InMethod(); + + Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(zero).Count, Is.EqualTo(0))"); + Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2), "Assert.That(db.getFolderHierarchy(folder1).Count, Is.EqualTo(2))"); + Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(folder2).Count, Is.EqualTo(0))"); + Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(1), "Assert.That(db.getFolderHierarchy(folder3).Count, Is.EqualTo(1))"); + Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.getFolderHierarchy(UUID.Random()).Count, Is.EqualTo(0))"); + } + + // Item tests + [Test] + public void T100_NoItems() + { + TestHelpers.InMethod(); + + Assert.That(db.getInventoryInFolder(zero).Count, Is.EqualTo(0), "Assert.That(db.getInventoryInFolder(zero).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryInFolder(folder1).Count, Is.EqualTo(0), "Assert.That(db.getInventoryInFolder(folder1).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryInFolder(folder2).Count, Is.EqualTo(0), "Assert.That(db.getInventoryInFolder(folder2).Count, Is.EqualTo(0))"); + Assert.That(db.getInventoryInFolder(folder3).Count, Is.EqualTo(0), "Assert.That(db.getInventoryInFolder(folder3).Count, Is.EqualTo(0))"); + } + + // TODO: Feeding a bad inventory item down the data path will + // crash the system. This is largely due to the builder + // routines. That should be fixed and tested for. + [Test] + public void T101_CreatItems() + { + TestHelpers.InMethod(); + + db.addInventoryItem(NewItem(item1, folder3, owner1, iname1, asset1)); + db.addInventoryItem(NewItem(item2, folder3, owner1, iname2, asset2)); + db.addInventoryItem(NewItem(item3, folder3, owner1, iname3, asset3)); + Assert.That(db.getInventoryInFolder(folder3).Count, Is.EqualTo(3), "Assert.That(db.getInventoryInFolder(folder3).Count, Is.EqualTo(3))"); + } + + [Test] + public void T102_CompareItems() + { + TestHelpers.InMethod(); + + InventoryItemBase i1 = db.getInventoryItem(item1); + InventoryItemBase i2 = db.getInventoryItem(item2); + InventoryItemBase i3 = db.getInventoryItem(item3); + Assert.That(i1.Name, Is.EqualTo(iname1), "Assert.That(i1.Name, Is.EqualTo(iname1))"); + Assert.That(i2.Name, Is.EqualTo(iname2), "Assert.That(i2.Name, Is.EqualTo(iname2))"); + Assert.That(i3.Name, Is.EqualTo(iname3), "Assert.That(i3.Name, Is.EqualTo(iname3))"); + Assert.That(i1.Owner, Is.EqualTo(owner1), "Assert.That(i1.Owner, Is.EqualTo(owner1))"); + Assert.That(i2.Owner, Is.EqualTo(owner1), "Assert.That(i2.Owner, Is.EqualTo(owner1))"); + Assert.That(i3.Owner, Is.EqualTo(owner1), "Assert.That(i3.Owner, Is.EqualTo(owner1))"); + Assert.That(i1.AssetID, Is.EqualTo(asset1), "Assert.That(i1.AssetID, Is.EqualTo(asset1))"); + Assert.That(i2.AssetID, Is.EqualTo(asset2), "Assert.That(i2.AssetID, Is.EqualTo(asset2))"); + Assert.That(i3.AssetID, Is.EqualTo(asset3), "Assert.That(i3.AssetID, Is.EqualTo(asset3))"); + } + + [Test] + public void T103_UpdateItem() + { + TestHelpers.InMethod(); + + // TODO: probably shouldn't have the ability to have an + // owner of an item in a folder not owned by the user + + InventoryItemBase i1 = db.getInventoryItem(item1); + i1.Name = niname1; + i1.Description = niname1; + i1.Owner = owner2; + db.updateInventoryItem(i1); + + i1 = db.getInventoryItem(item1); + Assert.That(i1.Name, Is.EqualTo(niname1), "Assert.That(i1.Name, Is.EqualTo(niname1))"); + Assert.That(i1.Description, Is.EqualTo(niname1), "Assert.That(i1.Description, Is.EqualTo(niname1))"); + Assert.That(i1.Owner, Is.EqualTo(owner2), "Assert.That(i1.Owner, Is.EqualTo(owner2))"); + } + + [Test] + public void T104_RandomUpdateItem() + { + TestHelpers.InMethod(); + + PropertyScrambler folderScrambler = + new PropertyScrambler() + .DontScramble(x => x.Owner) + .DontScramble(x => x.ParentID) + .DontScramble(x => x.ID); + UUID owner = UUID.Random(); + UUID folder = UUID.Random(); + UUID rootId = UUID.Random(); + UUID rootAsset = UUID.Random(); + InventoryFolderBase f1 = NewFolder(folder, zero, owner, name1); + folderScrambler.Scramble(f1); + + db.addInventoryFolder(f1); + InventoryFolderBase f1a = db.getUserRootFolder(owner); + Assert.That(f1a, Constraints.PropertyCompareConstraint(f1)); + + folderScrambler.Scramble(f1a); + + db.updateInventoryFolder(f1a); + + InventoryFolderBase f1b = db.getUserRootFolder(owner); + Assert.That(f1b, Constraints.PropertyCompareConstraint(f1a)); + + //Now we have a valid folder to insert into, we can insert the item. + PropertyScrambler inventoryScrambler = + new PropertyScrambler() + .DontScramble(x => x.ID) + .DontScramble(x => x.AssetID) + .DontScramble(x => x.Owner) + .DontScramble(x => x.Folder); + InventoryItemBase root = NewItem(rootId, folder, owner, iname1, rootAsset); + inventoryScrambler.Scramble(root); + db.addInventoryItem(root); + + InventoryItemBase expected = db.getInventoryItem(rootId); + Assert.That(expected, Constraints.PropertyCompareConstraint(root) + .IgnoreProperty(x => x.InvType) + .IgnoreProperty(x => x.CreatorIdAsUuid) + .IgnoreProperty(x => x.Description) + .IgnoreProperty(x => x.CreatorIdentification) + .IgnoreProperty(x => x.CreatorData)); + + inventoryScrambler.Scramble(expected); + db.updateInventoryItem(expected); + + InventoryItemBase actual = db.getInventoryItem(rootId); + Assert.That(actual, Constraints.PropertyCompareConstraint(expected) + .IgnoreProperty(x => x.InvType) + .IgnoreProperty(x => x.CreatorIdAsUuid) + .IgnoreProperty(x => x.Description) + .IgnoreProperty(x => x.CreatorIdentification) + .IgnoreProperty(x => x.CreatorData)); + } + + [Test] + public void T999_StillNull() + { + TestHelpers.InMethod(); + + // After all tests are run, these should still return no results + Assert.That(db.getInventoryFolder(zero), Is.Null); + Assert.That(db.getInventoryItem(zero), Is.Null); + Assert.That(db.getUserRootFolder(zero), Is.Null); + Assert.That(db.getInventoryInFolder(zero).Count, Is.EqualTo(0), "Assert.That(db.getInventoryInFolder(zero).Count, Is.EqualTo(0))"); + } + + private InventoryItemBase NewItem(UUID id, UUID parent, UUID owner, string name, UUID asset) + { + InventoryItemBase i = new InventoryItemBase(); + i.ID = id; + i.Folder = parent; + i.Owner = owner; + i.CreatorId = owner.ToString(); + i.Name = name; + i.Description = name; + i.AssetID = asset; + return i; + } + + private InventoryFolderBase NewFolder(UUID id, UUID parent, UUID owner, string name) + { + InventoryFolderBase f = new InventoryFolderBase(); + f.ID = id; + f.ParentID = parent; + f.Owner = owner; + f.Name = name; + return f; + } + } +} diff --git a/OpenSim/Data/Tests/PropertyCompareConstraint.cs b/OpenSim/Data/Tests/PropertyCompareConstraint.cs new file mode 100644 index 0000000000..b99525a74a --- /dev/null +++ b/OpenSim/Data/Tests/PropertyCompareConstraint.cs @@ -0,0 +1,413 @@ +/* + * 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.Drawing; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using NUnit.Framework; +using NUnit.Framework.Constraints; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.Tests +{ + public static class Constraints + { + //This is here because C# has a gap in the language, you can't infer type from a constructor + public static PropertyCompareConstraint PropertyCompareConstraint(T expected) + { + return new PropertyCompareConstraint(expected); + } + } + + public class PropertyCompareConstraint : NUnit.Framework.Constraints.Constraint + { + private readonly object _expected; + //the reason everywhere uses propertyNames.Reverse().ToArray() is because the stack is backwards of the order we want to display the properties in. + private string failingPropertyName = string.Empty; + private object failingExpected; + private object failingActual; + + public PropertyCompareConstraint(T expected) + { + _expected = expected; + } + + public override bool Matches(object actual) + { + return ObjectCompare(_expected, actual, new Stack()); + } + + private bool ObjectCompare(object expected, object actual, Stack propertyNames) + { + //If they are both null, they are equal + if (actual == null && expected == null) + return true; + + //If only one is null, then they aren't + if (actual == null || expected == null) + { + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + failingActual = actual; + failingExpected = expected; + return false; + } + + //prevent loops... + if (propertyNames.Count > 50) + { + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + failingActual = actual; + failingExpected = expected; + return false; + } + + if (actual.GetType() != expected.GetType()) + { + propertyNames.Push("GetType()"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + propertyNames.Pop(); + failingActual = actual.GetType(); + failingExpected = expected.GetType(); + return false; + } + + if (actual.GetType() == typeof(Color)) + { + Color actualColor = (Color) actual; + Color expectedColor = (Color) expected; + if (actualColor.R != expectedColor.R) + { + propertyNames.Push("R"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + propertyNames.Pop(); + failingActual = actualColor.R; + failingExpected = expectedColor.R; + return false; + } + if (actualColor.G != expectedColor.G) + { + propertyNames.Push("G"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + propertyNames.Pop(); + failingActual = actualColor.G; + failingExpected = expectedColor.G; + return false; + } + if (actualColor.B != expectedColor.B) + { + propertyNames.Push("B"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + propertyNames.Pop(); + failingActual = actualColor.B; + failingExpected = expectedColor.B; + return false; + } + if (actualColor.A != expectedColor.A) + { + propertyNames.Push("A"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + propertyNames.Pop(); + failingActual = actualColor.A; + failingExpected = expectedColor.A; + return false; + } + return true; + } + + IComparable comp = actual as IComparable; + if (comp != null) + { + if (comp.CompareTo(expected) != 0) + { + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + failingActual = actual; + failingExpected = expected; + return false; + } + return true; + } + + //Now try the much more annoying IComparable + Type icomparableInterface = actual.GetType().GetInterface("IComparable`1"); + if (icomparableInterface != null) + { + int result = (int)icomparableInterface.GetMethod("CompareTo").Invoke(actual, new[] { expected }); + if (result != 0) + { + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + failingActual = actual; + failingExpected = expected; + return false; + } + return true; + } + + IEnumerable arr = actual as IEnumerable; + if (arr != null) + { + List actualList = arr.Cast().ToList(); + List expectedList = ((IEnumerable)expected).Cast().ToList(); + if (actualList.Count != expectedList.Count) + { + propertyNames.Push("Count"); + failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray()); + failingActual = actualList.Count; + failingExpected = expectedList.Count; + propertyNames.Pop(); + return false; + } + //actualList and expectedList should be the same size. + for (int i = 0; i < actualList.Count; i++) + { + propertyNames.Push("[" + i + "]"); + if (!ObjectCompare(expectedList[i], actualList[i], propertyNames)) + return false; + propertyNames.Pop(); + } + //Everything seems okay... + return true; + } + + //Skip static properties. I had a nasty problem comparing colors because of all of the public static colors. + PropertyInfo[] properties = expected.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (var property in properties) + { + if (ignores.Contains(property.Name)) + continue; + + object actualValue = property.GetValue(actual, null); + object expectedValue = property.GetValue(expected, null); + + propertyNames.Push(property.Name); + if (!ObjectCompare(expectedValue, actualValue, propertyNames)) + return false; + propertyNames.Pop(); + } + + return true; + } + + public override void WriteDescriptionTo(MessageWriter writer) + { + writer.WriteExpectedValue(failingExpected); + } + + public override void WriteActualValueTo(MessageWriter writer) + { + writer.WriteActualValue(failingActual); + writer.WriteLine(); + writer.Write(" On Property: " + failingPropertyName); + } + + //These notes assume the lambda: (x=>x.Parent.Value) + //ignores should really contain like a fully dotted version of the property name, but I'm starting with small steps + readonly List ignores = new List(); + public PropertyCompareConstraint IgnoreProperty(Expression> func) + { + Expression express = func.Body; + PullApartExpression(express); + + return this; + } + + private void PullApartExpression(Expression express) + { + //This deals with any casts... like implicit casts to object. Not all UnaryExpression are casts, but this is a first attempt. + if (express is UnaryExpression) + PullApartExpression(((UnaryExpression)express).Operand); + if (express is MemberExpression) + { + //If the inside of the lambda is the access to x, we've hit the end of the chain. + // We should track by the fully scoped parameter name, but this is the first rev of doing this. + ignores.Add(((MemberExpression)express).Member.Name); + } + } + } + + [TestFixture] + public class PropertyCompareConstraintTest : OpenSimTestCase + { + public class HasInt + { + public int TheValue { get; set; } + } + + [Test] + public void IntShouldMatch() + { + HasInt actual = new HasInt { TheValue = 5 }; + HasInt expected = new HasInt { TheValue = 5 }; + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.True); + } + + [Test] + public void IntShouldNotMatch() + { + HasInt actual = new HasInt { TheValue = 5 }; + HasInt expected = new HasInt { TheValue = 4 }; + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.False); + } + + + [Test] + public void IntShouldIgnore() + { + HasInt actual = new HasInt { TheValue = 5 }; + HasInt expected = new HasInt { TheValue = 4 }; + var constraint = Constraints.PropertyCompareConstraint(expected).IgnoreProperty(x => x.TheValue); + + Assert.That(constraint.Matches(actual), Is.True); + } + + [Test] + public void AssetShouldMatch() + { + UUID uuid1 = UUID.Random(); + AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + AssetBase expected = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.True); + } + + [Test] + public void AssetShouldNotMatch() + { + UUID uuid1 = UUID.Random(); + AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + AssetBase expected = new AssetBase(UUID.Random(), "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.False); + } + + [Test] + public void AssetShouldNotMatch2() + { + UUID uuid1 = UUID.Random(); + AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + AssetBase expected = new AssetBase(uuid1, "asset two", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.False); + } + + [Test] + public void UUIDShouldMatch() + { + UUID uuid1 = UUID.Random(); + UUID uuid2 = UUID.Parse(uuid1.ToString()); + + var constraint = Constraints.PropertyCompareConstraint(uuid1); + + Assert.That(constraint.Matches(uuid2), Is.True); + } + + [Test] + public void UUIDShouldNotMatch() + { + UUID uuid1 = UUID.Random(); + UUID uuid2 = UUID.Random(); + + var constraint = Constraints.PropertyCompareConstraint(uuid1); + + Assert.That(constraint.Matches(uuid2), Is.False); + } + + [Test] + public void TestColors() + { + Color actual = Color.Red; + Color expected = Color.FromArgb(actual.A, actual.R, actual.G, actual.B); + + var constraint = Constraints.PropertyCompareConstraint(expected); + + Assert.That(constraint.Matches(actual), Is.True); + } + + [Test] + public void ShouldCompareLists() + { + List expected = new List { 1, 2, 3 }; + List actual = new List { 1, 2, 3 }; + + var constraint = Constraints.PropertyCompareConstraint(expected); + Assert.That(constraint.Matches(actual), Is.True); + } + + + [Test] + public void ShouldFailToCompareListsThatAreDifferent() + { + List expected = new List { 1, 2, 3 }; + List actual = new List { 1, 2, 4 }; + + var constraint = Constraints.PropertyCompareConstraint(expected); + Assert.That(constraint.Matches(actual), Is.False); + } + + [Test] + public void ShouldFailToCompareListsThatAreDifferentLengths() + { + List expected = new List { 1, 2, 3 }; + List actual = new List { 1, 2 }; + + var constraint = Constraints.PropertyCompareConstraint(expected); + Assert.That(constraint.Matches(actual), Is.False); + } + + public class Recursive + { + public Recursive Other { get; set; } + } + + [Test] + public void ErrorsOutOnRecursive() + { + Recursive parent = new Recursive(); + Recursive child = new Recursive(); + parent.Other = child; + child.Other = parent; + + var constraint = Constraints.PropertyCompareConstraint(child); + Assert.That(constraint.Matches(child), Is.False); + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Tests/PropertyScrambler.cs b/OpenSim/Data/Tests/PropertyScrambler.cs new file mode 100644 index 0000000000..e0f5862b93 --- /dev/null +++ b/OpenSim/Data/Tests/PropertyScrambler.cs @@ -0,0 +1,184 @@ +/* + * 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.Linq.Expressions; +using System.Reflection; +using System.Text; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Tests.Common; + +namespace OpenSim.Data.Tests +{ + //This is generic so that the lambda expressions will work right in IDEs. + public class PropertyScrambler + { + readonly System.Collections.Generic.List membersToNotScramble = new List(); + + private void AddExpressionToNotScrableList(Expression expression) + { + UnaryExpression unaryExpression = expression as UnaryExpression; + if (unaryExpression != null) + { + AddExpressionToNotScrableList(unaryExpression.Operand); + return; + } + + MemberExpression memberExpression = expression as MemberExpression; + if (memberExpression != null) + { + if (!(memberExpression.Member is PropertyInfo)) + { + throw new NotImplementedException("I don't know how deal with a MemberExpression that is a " + expression.Type); + } + membersToNotScramble.Add(memberExpression.Member.Name); + return; + } + + throw new NotImplementedException("I don't know how to parse a " + expression.Type); + } + + public PropertyScrambler DontScramble(Expression> expression) + { + AddExpressionToNotScrableList(expression.Body); + return this; + } + + public void Scramble(T obj) + { + internalScramble(obj); + } + + private void internalScramble(object obj) + { + PropertyInfo[] properties = obj.GetType().GetProperties(); + foreach (var property in properties) + { + //Skip indexers of classes. We will assume that everything that has an indexer + // is also IEnumberable. May not always be true, but should be true normally. + if (property.GetIndexParameters().Length > 0) + continue; + + RandomizeProperty(obj, property, null); + } + //Now if it implments IEnumberable, it's probably some kind of list, so we should randomize + // everything inside of it. + IEnumerable enumerable = obj as IEnumerable; + if (enumerable != null) + { + foreach (object value in enumerable) + { + internalScramble(value); + } + } + } + + private readonly Random random = new Random(); + private void RandomizeProperty(object obj, PropertyInfo property, object[] index) + {//I'd like a better way to compare, but I had lots of problems with InventoryFolderBase because the ID is inherited. + if (membersToNotScramble.Contains(property.Name)) + return; + Type t = property.PropertyType; + if (!property.CanWrite) + return; + object value = property.GetValue(obj, index); + if (value == null) + return; + + if (t == typeof(string)) + property.SetValue(obj, RandomName(), index); + else if (t == typeof(UUID)) + property.SetValue(obj, UUID.Random(), index); + else if (t == typeof(sbyte)) + property.SetValue(obj, (sbyte)random.Next(sbyte.MinValue, sbyte.MaxValue), index); + else if (t == typeof(short)) + property.SetValue(obj, (short)random.Next(short.MinValue, short.MaxValue), index); + else if (t == typeof(int)) + property.SetValue(obj, random.Next(), index); + else if (t == typeof(long)) + property.SetValue(obj, random.Next() * int.MaxValue, index); + else if (t == typeof(byte)) + property.SetValue(obj, (byte)random.Next(byte.MinValue, byte.MaxValue), index); + else if (t == typeof(ushort)) + property.SetValue(obj, (ushort)random.Next(ushort.MinValue, ushort.MaxValue), index); + else if (t == typeof(uint)) + property.SetValue(obj, Convert.ToUInt32(random.Next()), index); + else if (t == typeof(ulong)) + property.SetValue(obj, Convert.ToUInt64(random.Next()) * Convert.ToUInt64(UInt32.MaxValue), index); + else if (t == typeof(bool)) + property.SetValue(obj, true, index); + else if (t == typeof(byte[])) + { + byte[] bytes = new byte[30]; + random.NextBytes(bytes); + property.SetValue(obj, bytes, index); + } + else + internalScramble(value); + } + + private string RandomName() + { + StringBuilder name = new StringBuilder(); + int size = random.Next(5, 12); + for (int i = 0; i < size; i++) + { + char ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); + name.Append(ch); + } + return name.ToString(); + } + } + + [TestFixture] + public class PropertyScramblerTests : OpenSimTestCase + { + [Test] + public void TestScramble() + { + AssetBase actual = new AssetBase(UUID.Random(), "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + new PropertyScrambler().Scramble(actual); + } + + [Test] + public void DontScramble() + { + UUID uuid = UUID.Random(); + AssetBase asset = new AssetBase(uuid, "asset", (sbyte)AssetType.Texture, UUID.Zero.ToString()); + new PropertyScrambler() + .DontScramble(x => x.Metadata) + .DontScramble(x => x.FullID) + .DontScramble(x => x.ID) + .Scramble(asset); + Assert.That(asset.FullID, Is.EqualTo(uuid)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Data/Tests/RegionTests.cs b/OpenSim/Data/Tests/RegionTests.cs new file mode 100644 index 0000000000..8d4249a8cb --- /dev/null +++ b/OpenSim/Data/Tests/RegionTests.cs @@ -0,0 +1,1129 @@ +/* + * 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.Drawing; +using System.Text; +using log4net.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Tests.Common; +using log4net; +using System.Reflection; +using System.Data.Common; + +// DBMS-specific: +using MySql.Data.MySqlClient; +using OpenSim.Data.MySQL; + +using Mono.Data.Sqlite; +using OpenSim.Data.SQLite; + +namespace OpenSim.Data.Tests +{ + [TestFixture(Description = "Region store tests (SQLite)")] + public class SQLiteRegionTests : RegionTests + { + } + + [TestFixture(Description = "Region store tests (MySQL)")] + public class MySqlRegionTests : RegionTests + { + } + + public class RegionTests : BasicDataServiceTest + where TConn : DbConnection, new() + where TRegStore : class, ISimulationDataStore, new() + { + bool m_rebuildDB; + + public ISimulationDataStore db; + public UUID zero = UUID.Zero; + public UUID region1 = UUID.Random(); + public UUID region2 = UUID.Random(); + public UUID region3 = UUID.Random(); + public UUID region4 = UUID.Random(); + public UUID prim1 = UUID.Random(); + public UUID prim2 = UUID.Random(); + public UUID prim3 = UUID.Random(); + public UUID prim4 = UUID.Random(); + public UUID prim5 = UUID.Random(); + public UUID prim6 = UUID.Random(); + public UUID item1 = UUID.Random(); + public UUID item2 = UUID.Random(); + public UUID item3 = UUID.Random(); + + public static Random random = new Random(); + + public string itemname1 = "item1"; + + public uint localID = 1; + + public double height1 = 20; + public double height2 = 100; + + public RegionTests(string conn, bool rebuild) + : base(conn) + { + m_rebuildDB = rebuild; + } + + public RegionTests() : this("", true) { } + public RegionTests(string conn) : this(conn, true) {} + public RegionTests(bool rebuild): this("", rebuild) {} + + + protected override void InitService(object service) + { + ClearDB(); + db = (ISimulationDataStore)service; + db.Initialise(m_connStr); + } + + private void ClearDB() + { + string[] reg_tables = new string[] { + "prims", "primshapes", "primitems", "terrain", "land", "landaccesslist", "regionban", "regionsettings" + }; + + if (m_rebuildDB) + { + DropTables(reg_tables); + ResetMigrations("RegionStore"); + } + else + { + ClearTables(reg_tables); + } + } + + // Test Plan + // Prims + // - empty test - 001 + // - store / retrieve basic prims (most minimal we can make) - 010, 011 + // - store / retrieve parts in a scenegroup 012 + // - store a prim with complete information for consistency check 013 + // - update existing prims, make sure it sticks - 014 + // - tests empty inventory - 020 + // - add inventory items to prims make - 021 + // - retrieves the added item - 022 + // - update inventory items to prims - 023 + // - remove inventory items make sure it sticks - 024 + // - checks if all parameters are persistent - 025 + // - adds many items and see if it is handled correctly - 026 + + [Test] + public void T001_LoadEmpty() + { + TestHelpers.InMethod(); + + List objs = db.LoadObjects(region1); + List objs3 = db.LoadObjects(region3); + List land = db.LoadLandObjects(region1); + + Assert.That(objs.Count, Is.EqualTo(0), "Assert.That(objs.Count, Is.EqualTo(0))"); + Assert.That(objs3.Count, Is.EqualTo(0), "Assert.That(objs3.Count, Is.EqualTo(0))"); + Assert.That(land.Count, Is.EqualTo(0), "Assert.That(land.Count, Is.EqualTo(0))"); + } + + // SOG round trips + // * store objects, make sure they save + // * update + + [Test] + public void T010_StoreSimpleObject() + { + TestHelpers.InMethod(); + + SceneObjectGroup sog = NewSOG("object1", prim1, region1); + SceneObjectGroup sog2 = NewSOG("object2", prim2, region1); + + // in case the objects don't store + try + { + db.StoreObject(sog, region1); + } + catch (Exception e) + { + m_log.Error(e.ToString()); + Assert.Fail(); + } + + try + { + db.StoreObject(sog2, region1); + } + catch (Exception e) + { + m_log.Error(e.ToString()); + Assert.Fail(); + } + + // This tests the ADO.NET driver + List objs = db.LoadObjects(region1); + + Assert.That(objs.Count, Is.EqualTo(2), "Assert.That(objs.Count, Is.EqualTo(2))"); + } + + [Test] + public void T011_ObjectNames() + { + TestHelpers.InMethod(); + + List objs = db.LoadObjects(region1); + foreach (SceneObjectGroup sog in objs) + { + SceneObjectPart p = sog.RootPart; + Assert.That("", Is.Not.EqualTo(p.Name), "Assert.That(\"\", Is.Not.EqualTo(p.Name))"); + Assert.That(p.Name, Is.EqualTo(p.Description), "Assert.That(p.Name, Is.EqualTo(p.Description))"); + } + } + + [Test] + public void T012_SceneParts() + { + TestHelpers.InMethod(); + + UUID tmp0 = UUID.Random(); + UUID tmp1 = UUID.Random(); + UUID tmp2 = UUID.Random(); + UUID tmp3 = UUID.Random(); + UUID newregion = UUID.Random(); + SceneObjectPart p1 = NewSOP("SoP 1",tmp1); + SceneObjectPart p2 = NewSOP("SoP 2",tmp2); + SceneObjectPart p3 = NewSOP("SoP 3",tmp3); + SceneObjectGroup sog = NewSOG("Sop 0", tmp0, newregion); + sog.AddPart(p1); + sog.AddPart(p2); + sog.AddPart(p3); + + SceneObjectPart[] parts = sog.Parts; + Assert.That(parts.Length,Is.EqualTo(4), "Assert.That(parts.Length,Is.EqualTo(4))"); + + db.StoreObject(sog, newregion); + List sogs = db.LoadObjects(newregion); + Assert.That(sogs.Count,Is.EqualTo(1), "Assert.That(sogs.Count,Is.EqualTo(1))"); + SceneObjectGroup newsog = sogs[0]; + + SceneObjectPart[] newparts = newsog.Parts; + Assert.That(newparts.Length,Is.EqualTo(4), "Assert.That(newparts.Length,Is.EqualTo(4))"); + + Assert.That(newsog.ContainsPart(tmp0), "Assert.That(newsog.ContainsPart(tmp0))"); + Assert.That(newsog.ContainsPart(tmp1), "Assert.That(newsog.ContainsPart(tmp1))"); + Assert.That(newsog.ContainsPart(tmp2), "Assert.That(newsog.ContainsPart(tmp2))"); + Assert.That(newsog.ContainsPart(tmp3), "Assert.That(newsog.ContainsPart(tmp3))"); + } + + [Test] + public void T013_DatabasePersistency() + { + TestHelpers.InMethod(); + + // Sets all ScenePart parameters, stores and retrieves them, then check for consistency with initial data + // The commented Asserts are the ones that are unchangeable (when storing on the database, their "Set" values are ignored + // The ObjectFlags is an exception, if it is entered incorrectly, the object IS REJECTED on the database silently. + UUID creator,uuid = new UUID(); + creator = UUID.Random(); + uint iserial = (uint)random.Next(); + TaskInventoryDictionary dic = new TaskInventoryDictionary(); + uint objf = (uint) random.Next(); + uuid = prim4; + uint localid = localID+1; + localID = localID + 1; + string name = "Adam West"; + byte material = (byte) random.Next(127); + ulong regionh = (ulong)random.NextDouble() * (ulong)random.Next(); + int pin = random.Next(); + Byte[] partsys = new byte[8]; + Byte[] textani = new byte[8]; + random.NextBytes(textani); + random.NextBytes(partsys); + DateTime expires = new DateTime(2008, 12, 20); + DateTime rezzed = new DateTime(2009, 07, 15); + Vector3 groupos = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 offset = new Vector3(random.Next(),random.Next(),random.Next()); + Quaternion rotoff = new Quaternion(random.Next(),random.Next(),random.Next(),random.Next()); + Vector3 velocity = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 angvelo = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 accel = new Vector3(random.Next(),random.Next(),random.Next()); + string description = name; + Color color = Color.FromArgb(255, 165, 50, 100); + string text = "All Your Base Are Belong to Us"; + string sitname = "SitName"; + string touchname = "TouchName"; + int linknum = random.Next(); + byte clickaction = (byte) random.Next(127); + PrimitiveBaseShape pbshap = new PrimitiveBaseShape(); + pbshap = PrimitiveBaseShape.Default; + pbshap.PathBegin = ushort.MaxValue; + pbshap.PathEnd = ushort.MaxValue; + pbshap.ProfileBegin = ushort.MaxValue; + pbshap.ProfileEnd = ushort.MaxValue; + pbshap.ProfileHollow = ushort.MaxValue; + Vector3 scale = new Vector3(random.Next(),random.Next(),random.Next()); + + RegionInfo regionInfo = new RegionInfo(); + regionInfo.RegionID = region3; + regionInfo.RegionLocX = 0; + regionInfo.RegionLocY = 0; + + SceneObjectPart sop = new SceneObjectPart(); + SceneObjectGroup sog = new SceneObjectGroup(sop); + + sop.RegionHandle = regionh; + sop.UUID = uuid; + sop.LocalId = localid; + sop.Shape = pbshap; + sop.GroupPosition = groupos; + sop.RotationOffset = rotoff; + sop.CreatorID = creator; + sop.InventorySerial = iserial; + sop.TaskInventory = dic; + sop.Flags = (PrimFlags)objf; + sop.Name = name; + sop.Material = material; + sop.ScriptAccessPin = pin; + sop.TextureAnimation = textani; + sop.ParticleSystem = partsys; + sop.Expires = expires; + sop.Rezzed = rezzed; + sop.OffsetPosition = offset; + sop.Velocity = velocity; + sop.AngularVelocity = angvelo; + sop.Acceleration = accel; + sop.Description = description; + sop.Color = color; + sop.Text = text; + sop.SitName = sitname; + sop.TouchName = touchname; + sop.LinkNum = linknum; + sop.ClickAction = clickaction; + sop.Scale = scale; + + //Tests if local part accepted the parameters: + Assert.That(regionh,Is.EqualTo(sop.RegionHandle), "Assert.That(regionh,Is.EqualTo(sop.RegionHandle))"); + Assert.That(localid,Is.EqualTo(sop.LocalId), "Assert.That(localid,Is.EqualTo(sop.LocalId))"); + Assert.That(groupos,Is.EqualTo(sop.GroupPosition), "Assert.That(groupos,Is.EqualTo(sop.GroupPosition))"); + Assert.That(name,Is.EqualTo(sop.Name), "Assert.That(name,Is.EqualTo(sop.Name))"); + Assert.That(rotoff,Is.EqualTo(sop.RotationOffset), "Assert.That(rotoff,Is.EqualTo(sop.RotationOffset))"); + Assert.That(uuid,Is.EqualTo(sop.UUID), "Assert.That(uuid,Is.EqualTo(sop.UUID))"); + Assert.That(creator,Is.EqualTo(sop.CreatorID), "Assert.That(creator,Is.EqualTo(sop.CreatorID))"); + // Modified in-class + // Assert.That(iserial,Is.EqualTo(sop.InventorySerial), "Assert.That(iserial,Is.EqualTo(sop.InventorySerial))"); + Assert.That(dic,Is.EqualTo(sop.TaskInventory), "Assert.That(dic,Is.EqualTo(sop.TaskInventory))"); + Assert.That(objf, Is.EqualTo((uint)sop.Flags), "Assert.That(objf,Is.EqualTo(sop.Flags))"); + Assert.That(name,Is.EqualTo(sop.Name), "Assert.That(name,Is.EqualTo(sop.Name))"); + Assert.That(material,Is.EqualTo(sop.Material), "Assert.That(material,Is.EqualTo(sop.Material))"); + Assert.That(pin,Is.EqualTo(sop.ScriptAccessPin), "Assert.That(pin,Is.EqualTo(sop.ScriptAccessPin))"); + Assert.That(textani,Is.EqualTo(sop.TextureAnimation), "Assert.That(textani,Is.EqualTo(sop.TextureAnimation))"); + Assert.That(partsys,Is.EqualTo(sop.ParticleSystem), "Assert.That(partsys,Is.EqualTo(sop.ParticleSystem))"); + Assert.That(expires,Is.EqualTo(sop.Expires), "Assert.That(expires,Is.EqualTo(sop.Expires))"); + Assert.That(rezzed,Is.EqualTo(sop.Rezzed), "Assert.That(rezzed,Is.EqualTo(sop.Rezzed))"); + Assert.That(offset,Is.EqualTo(sop.OffsetPosition), "Assert.That(offset,Is.EqualTo(sop.OffsetPosition))"); + Assert.That(velocity,Is.EqualTo(sop.Velocity), "Assert.That(velocity,Is.EqualTo(sop.Velocity))"); + Assert.That(angvelo,Is.EqualTo(sop.AngularVelocity), "Assert.That(angvelo,Is.EqualTo(sop.AngularVelocity))"); + Assert.That(accel,Is.EqualTo(sop.Acceleration), "Assert.That(accel,Is.EqualTo(sop.Acceleration))"); + Assert.That(description,Is.EqualTo(sop.Description), "Assert.That(description,Is.EqualTo(sop.Description))"); + Assert.That(color,Is.EqualTo(sop.Color), "Assert.That(color,Is.EqualTo(sop.Color))"); + Assert.That(text,Is.EqualTo(sop.Text), "Assert.That(text,Is.EqualTo(sop.Text))"); + Assert.That(sitname,Is.EqualTo(sop.SitName), "Assert.That(sitname,Is.EqualTo(sop.SitName))"); + Assert.That(touchname,Is.EqualTo(sop.TouchName), "Assert.That(touchname,Is.EqualTo(sop.TouchName))"); + Assert.That(linknum,Is.EqualTo(sop.LinkNum), "Assert.That(linknum,Is.EqualTo(sop.LinkNum))"); + Assert.That(clickaction,Is.EqualTo(sop.ClickAction), "Assert.That(clickaction,Is.EqualTo(sop.ClickAction))"); + Assert.That(scale,Is.EqualTo(sop.Scale), "Assert.That(scale,Is.EqualTo(sop.Scale))"); + + // This is necessary or object will not be inserted in DB + sop.Flags = PrimFlags.None; + + // Inserts group in DB + db.StoreObject(sog,region3); + List sogs = db.LoadObjects(region3); + Assert.That(sogs.Count, Is.EqualTo(1), "Assert.That(sogs.Count, Is.EqualTo(1))"); + // Makes sure there are no double insertions: + db.StoreObject(sog,region3); + sogs = db.LoadObjects(region3); + Assert.That(sogs.Count, Is.EqualTo(1), "Assert.That(sogs.Count, Is.EqualTo(1))"); + + + // Tests if the parameters were inserted correctly + SceneObjectPart p = sogs[0].RootPart; + Assert.That(regionh,Is.EqualTo(p.RegionHandle), "Assert.That(regionh,Is.EqualTo(p.RegionHandle))"); + //Assert.That(localid,Is.EqualTo(p.LocalId), "Assert.That(localid,Is.EqualTo(p.LocalId))"); + Assert.That(groupos,Is.EqualTo(p.GroupPosition), "Assert.That(groupos,Is.EqualTo(p.GroupPosition))"); + Assert.That(name,Is.EqualTo(p.Name), "Assert.That(name,Is.EqualTo(p.Name))"); + Assert.That(rotoff,Is.EqualTo(p.RotationOffset), "Assert.That(rotoff,Is.EqualTo(p.RotationOffset))"); + Assert.That(uuid,Is.EqualTo(p.UUID), "Assert.That(uuid,Is.EqualTo(p.UUID))"); + Assert.That(creator,Is.EqualTo(p.CreatorID), "Assert.That(creator,Is.EqualTo(p.CreatorID))"); + //Assert.That(iserial,Is.EqualTo(p.InventorySerial), "Assert.That(iserial,Is.EqualTo(p.InventorySerial))"); + Assert.That(dic,Is.EqualTo(p.TaskInventory), "Assert.That(dic,Is.EqualTo(p.TaskInventory))"); + //Assert.That(objf, Is.EqualTo((uint)p.Flags), "Assert.That(objf,Is.EqualTo(p.Flags))"); + Assert.That(name,Is.EqualTo(p.Name), "Assert.That(name,Is.EqualTo(p.Name))"); + Assert.That(material,Is.EqualTo(p.Material), "Assert.That(material,Is.EqualTo(p.Material))"); + Assert.That(pin,Is.EqualTo(p.ScriptAccessPin), "Assert.That(pin,Is.EqualTo(p.ScriptAccessPin))"); + Assert.That(textani,Is.EqualTo(p.TextureAnimation), "Assert.That(textani,Is.EqualTo(p.TextureAnimation))"); + Assert.That(partsys,Is.EqualTo(p.ParticleSystem), "Assert.That(partsys,Is.EqualTo(p.ParticleSystem))"); + //Assert.That(expires,Is.EqualTo(p.Expires), "Assert.That(expires,Is.EqualTo(p.Expires))"); + //Assert.That(rezzed,Is.EqualTo(p.Rezzed), "Assert.That(rezzed,Is.EqualTo(p.Rezzed))"); + Assert.That(offset,Is.EqualTo(p.OffsetPosition), "Assert.That(offset,Is.EqualTo(p.OffsetPosition))"); + Assert.That(velocity,Is.EqualTo(p.Velocity), "Assert.That(velocity,Is.EqualTo(p.Velocity))"); + Assert.That(angvelo,Is.EqualTo(p.AngularVelocity), "Assert.That(angvelo,Is.EqualTo(p.AngularVelocity))"); + Assert.That(accel,Is.EqualTo(p.Acceleration), "Assert.That(accel,Is.EqualTo(p.Acceleration))"); + Assert.That(description,Is.EqualTo(p.Description), "Assert.That(description,Is.EqualTo(p.Description))"); + Assert.That(color,Is.EqualTo(p.Color), "Assert.That(color,Is.EqualTo(p.Color))"); + Assert.That(text,Is.EqualTo(p.Text), "Assert.That(text,Is.EqualTo(p.Text))"); + Assert.That(sitname,Is.EqualTo(p.SitName), "Assert.That(sitname,Is.EqualTo(p.SitName))"); + Assert.That(touchname,Is.EqualTo(p.TouchName), "Assert.That(touchname,Is.EqualTo(p.TouchName))"); + //Assert.That(linknum,Is.EqualTo(p.LinkNum), "Assert.That(linknum,Is.EqualTo(p.LinkNum))"); + Assert.That(clickaction,Is.EqualTo(p.ClickAction), "Assert.That(clickaction,Is.EqualTo(p.ClickAction))"); + Assert.That(scale,Is.EqualTo(p.Scale), "Assert.That(scale,Is.EqualTo(p.Scale))"); + + //Assert.That(updatef,Is.EqualTo(p.UpdateFlag), "Assert.That(updatef,Is.EqualTo(p.UpdateFlag))"); + + Assert.That(pbshap.PathBegin, Is.EqualTo(p.Shape.PathBegin), "Assert.That(pbshap.PathBegin, Is.EqualTo(p.Shape.PathBegin))"); + Assert.That(pbshap.PathEnd, Is.EqualTo(p.Shape.PathEnd), "Assert.That(pbshap.PathEnd, Is.EqualTo(p.Shape.PathEnd))"); + Assert.That(pbshap.ProfileBegin, Is.EqualTo(p.Shape.ProfileBegin), "Assert.That(pbshap.ProfileBegin, Is.EqualTo(p.Shape.ProfileBegin))"); + Assert.That(pbshap.ProfileEnd, Is.EqualTo(p.Shape.ProfileEnd), "Assert.That(pbshap.ProfileEnd, Is.EqualTo(p.Shape.ProfileEnd))"); + Assert.That(pbshap.ProfileHollow, Is.EqualTo(p.Shape.ProfileHollow), "Assert.That(pbshap.ProfileHollow, Is.EqualTo(p.Shape.ProfileHollow))"); + } + + [Test] + public void T014_UpdateObject() + { + TestHelpers.InMethod(); + + string text1 = "object1 text"; + SceneObjectGroup sog = FindSOG("object1", region1); + sog.RootPart.Text = text1; + db.StoreObject(sog, region1); + + sog = FindSOG("object1", region1); + Assert.That(text1, Is.EqualTo(sog.RootPart.Text), "Assert.That(text1, Is.EqualTo(sog.RootPart.Text))"); + + // Creates random values + UUID creator = new UUID(); + creator = UUID.Random(); + TaskInventoryDictionary dic = new TaskInventoryDictionary(); + localID = localID + 1; + string name = "West Adam"; + byte material = (byte) random.Next(127); + ulong regionh = (ulong)random.NextDouble() * (ulong)random.Next(); + int pin = random.Next(); + Byte[] partsys = new byte[8]; + Byte[] textani = new byte[8]; + random.NextBytes(textani); + random.NextBytes(partsys); + DateTime expires = new DateTime(2010, 12, 20); + DateTime rezzed = new DateTime(2005, 07, 15); + Vector3 groupos = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 offset = new Vector3(random.Next(),random.Next(),random.Next()); + Quaternion rotoff = new Quaternion(random.Next(),random.Next(),random.Next(),random.Next()); + Vector3 velocity = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 angvelo = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 accel = new Vector3(random.Next(),random.Next(),random.Next()); + string description = name; + Color color = Color.FromArgb(255, 255, 255, 0); + string text = "What You Say?{]\vz~"; + string sitname = RandomName(); + string touchname = RandomName(); + int linknum = random.Next(); + byte clickaction = (byte) random.Next(127); + PrimitiveBaseShape pbshap = new PrimitiveBaseShape(); + pbshap = PrimitiveBaseShape.Default; + Vector3 scale = new Vector3(random.Next(),random.Next(),random.Next()); + + // Updates the region with new values + SceneObjectGroup sog2 = FindSOG("Adam West", region3); + Assert.That(sog2,Is.Not.Null); + sog2.RootPart.RegionHandle = regionh; + sog2.RootPart.Shape = pbshap; + sog2.RootPart.GroupPosition = groupos; + sog2.RootPart.RotationOffset = rotoff; + sog2.RootPart.CreatorID = creator; + sog2.RootPart.TaskInventory = dic; + sog2.RootPart.Name = name; + sog2.RootPart.Material = material; + sog2.RootPart.ScriptAccessPin = pin; + sog2.RootPart.TextureAnimation = textani; + sog2.RootPart.ParticleSystem = partsys; + sog2.RootPart.Expires = expires; + sog2.RootPart.Rezzed = rezzed; + sog2.RootPart.OffsetPosition = offset; + sog2.RootPart.Velocity = velocity; + sog2.RootPart.AngularVelocity = angvelo; + sog2.RootPart.Acceleration = accel; + sog2.RootPart.Description = description; + sog2.RootPart.Color = color; + sog2.RootPart.Text = text; + sog2.RootPart.SitName = sitname; + sog2.RootPart.TouchName = touchname; + sog2.RootPart.LinkNum = linknum; + sog2.RootPart.ClickAction = clickaction; + sog2.RootPart.Scale = scale; + + db.StoreObject(sog2, region3); + List sogs = db.LoadObjects(region3); + Assert.That(sogs.Count, Is.EqualTo(1), "Assert.That(sogs.Count, Is.EqualTo(1))"); + + SceneObjectGroup retsog = FindSOG("West Adam", region3); + Assert.That(retsog,Is.Not.Null); + SceneObjectPart p = retsog.RootPart; + Assert.That(regionh,Is.EqualTo(p.RegionHandle), "Assert.That(regionh,Is.EqualTo(p.RegionHandle))"); + Assert.That(groupos,Is.EqualTo(p.GroupPosition), "Assert.That(groupos,Is.EqualTo(p.GroupPosition))"); + Assert.That(name,Is.EqualTo(p.Name), "Assert.That(name,Is.EqualTo(p.Name))"); + Assert.That(rotoff,Is.EqualTo(p.RotationOffset), "Assert.That(rotoff,Is.EqualTo(p.RotationOffset))"); + Assert.That(creator,Is.EqualTo(p.CreatorID), "Assert.That(creator,Is.EqualTo(p.CreatorID))"); + Assert.That(dic,Is.EqualTo(p.TaskInventory), "Assert.That(dic,Is.EqualTo(p.TaskInventory))"); + Assert.That(name,Is.EqualTo(p.Name), "Assert.That(name,Is.EqualTo(p.Name))"); + Assert.That(material,Is.EqualTo(p.Material), "Assert.That(material,Is.EqualTo(p.Material))"); + Assert.That(pin,Is.EqualTo(p.ScriptAccessPin), "Assert.That(pin,Is.EqualTo(p.ScriptAccessPin))"); + Assert.That(textani,Is.EqualTo(p.TextureAnimation), "Assert.That(textani,Is.EqualTo(p.TextureAnimation))"); + Assert.That(partsys,Is.EqualTo(p.ParticleSystem), "Assert.That(partsys,Is.EqualTo(p.ParticleSystem))"); + Assert.That(offset,Is.EqualTo(p.OffsetPosition), "Assert.That(offset,Is.EqualTo(p.OffsetPosition))"); + Assert.That(velocity,Is.EqualTo(p.Velocity), "Assert.That(velocity,Is.EqualTo(p.Velocity))"); + Assert.That(angvelo,Is.EqualTo(p.AngularVelocity), "Assert.That(angvelo,Is.EqualTo(p.AngularVelocity))"); + Assert.That(accel,Is.EqualTo(p.Acceleration), "Assert.That(accel,Is.EqualTo(p.Acceleration))"); + Assert.That(description,Is.EqualTo(p.Description), "Assert.That(description,Is.EqualTo(p.Description))"); + Assert.That(color,Is.EqualTo(p.Color), "Assert.That(color,Is.EqualTo(p.Color))"); + Assert.That(text,Is.EqualTo(p.Text), "Assert.That(text,Is.EqualTo(p.Text))"); + Assert.That(sitname,Is.EqualTo(p.SitName), "Assert.That(sitname,Is.EqualTo(p.SitName))"); + Assert.That(touchname,Is.EqualTo(p.TouchName), "Assert.That(touchname,Is.EqualTo(p.TouchName))"); + Assert.That(clickaction,Is.EqualTo(p.ClickAction), "Assert.That(clickaction,Is.EqualTo(p.ClickAction))"); + Assert.That(scale,Is.EqualTo(p.Scale), "Assert.That(scale,Is.EqualTo(p.Scale))"); + } + + /// + /// Test storage and retrieval of a scene object with a large number of parts. + /// + [Test] + public void T015_LargeSceneObjects() + { + TestHelpers.InMethod(); + + UUID id = UUID.Random(); + Dictionary mydic = new Dictionary(); + SceneObjectGroup sog = NewSOG("Test SOG", id, region4); + mydic.Add(sog.RootPart.UUID,sog.RootPart); + for (int i = 0; i < 30; i++) + { + UUID tmp = UUID.Random(); + SceneObjectPart sop = NewSOP(("Test SOP " + i.ToString()),tmp); + Vector3 groupos = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 offset = new Vector3(random.Next(),random.Next(),random.Next()); + Quaternion rotoff = new Quaternion(random.Next(),random.Next(),random.Next(),random.Next()); + Vector3 velocity = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 angvelo = new Vector3(random.Next(),random.Next(),random.Next()); + Vector3 accel = new Vector3(random.Next(),random.Next(),random.Next()); + + sop.GroupPosition = groupos; + sop.RotationOffset = rotoff; + sop.OffsetPosition = offset; + sop.Velocity = velocity; + sop.AngularVelocity = angvelo; + sop.Acceleration = accel; + + mydic.Add(tmp,sop); + sog.AddPart(sop); + } + + db.StoreObject(sog, region4); + + SceneObjectGroup retsog = FindSOG("Test SOG", region4); + SceneObjectPart[] parts = retsog.Parts; + for (int i = 0; i < 30; i++) + { + SceneObjectPart cursop = mydic[parts[i].UUID]; + Assert.That(cursop.GroupPosition,Is.EqualTo(parts[i].GroupPosition), "Assert.That(cursop.GroupPosition,Is.EqualTo(parts[i].GroupPosition))"); + Assert.That(cursop.RotationOffset,Is.EqualTo(parts[i].RotationOffset), "Assert.That(cursop.RotationOffset,Is.EqualTo(parts[i].RotationOffset))"); + Assert.That(cursop.OffsetPosition,Is.EqualTo(parts[i].OffsetPosition), "Assert.That(cursop.OffsetPosition,Is.EqualTo(parts[i].OffsetPosition))"); + Assert.That(cursop.Velocity,Is.EqualTo(parts[i].Velocity), "Assert.That(cursop.Velocity,Is.EqualTo(parts[i].Velocity))"); + Assert.That(cursop.AngularVelocity,Is.EqualTo(parts[i].AngularVelocity), "Assert.That(cursop.AngularVelocity,Is.EqualTo(parts[i].AngularVelocity))"); + Assert.That(cursop.Acceleration,Is.EqualTo(parts[i].Acceleration), "Assert.That(cursop.Acceleration,Is.EqualTo(parts[i].Acceleration))"); + } + } + + //[Test] + public void T016_RandomSogWithSceneParts() + { + TestHelpers.InMethod(); + + PropertyScrambler scrambler = + new PropertyScrambler() + .DontScramble(x => x.UUID); + UUID tmpSog = UUID.Random(); + UUID tmp1 = UUID.Random(); + UUID tmp2 = UUID.Random(); + UUID tmp3 = UUID.Random(); + UUID newregion = UUID.Random(); + SceneObjectPart p1 = new SceneObjectPart(); + SceneObjectPart p2 = new SceneObjectPart(); + SceneObjectPart p3 = new SceneObjectPart(); + p1.Shape = PrimitiveBaseShape.Default; + p2.Shape = PrimitiveBaseShape.Default; + p3.Shape = PrimitiveBaseShape.Default; + p1.UUID = tmp1; + p2.UUID = tmp2; + p3.UUID = tmp3; + scrambler.Scramble(p1); + scrambler.Scramble(p2); + scrambler.Scramble(p3); + + SceneObjectGroup sog = NewSOG("Sop 0", tmpSog, newregion); + PropertyScrambler sogScrambler = + new PropertyScrambler() + .DontScramble(x => x.UUID); + sogScrambler.Scramble(sog); + sog.UUID = tmpSog; + sog.AddPart(p1); + sog.AddPart(p2); + sog.AddPart(p3); + + SceneObjectPart[] parts = sog.Parts; + Assert.That(parts.Length, Is.EqualTo(4), "Assert.That(parts.Length,Is.EqualTo(4))"); + + db.StoreObject(sog, newregion); + List sogs = db.LoadObjects(newregion); + Assert.That(sogs.Count, Is.EqualTo(1), "Assert.That(sogs.Count,Is.EqualTo(1))"); + SceneObjectGroup newsog = sogs[0]; + + SceneObjectPart[] newparts = newsog.Parts; + Assert.That(newparts.Length, Is.EqualTo(4), "Assert.That(newparts.Length,Is.EqualTo(4))"); + + Assert.That(newsog, Constraints.PropertyCompareConstraint(sog) + .IgnoreProperty(x=>x.LocalId) + .IgnoreProperty(x=>x.HasGroupChanged) + .IgnoreProperty(x=>x.IsSelected) + .IgnoreProperty(x=>x.RegionHandle) + .IgnoreProperty(x=>x.RegionUUID) + .IgnoreProperty(x=>x.Scene) + .IgnoreProperty(x=>x.Parts) + .IgnoreProperty(x=>x.RootPart)); + } + + + private SceneObjectGroup GetMySOG(string name) + { + SceneObjectGroup sog = FindSOG(name, region1); + if (sog == null) + { + sog = NewSOG(name, prim1, region1); + db.StoreObject(sog, region1); + } + return sog; + } + + // NOTE: it is a bad practice to rely on some of the previous tests having been run before. + // If the tests are run manually, one at a time, each starts with full class init (DB cleared). + // Even when all tests are run, NUnit 2.5+ no longer guarantee a specific test order. + // We shouldn't expect to find anything in the DB if we haven't put it there *in the same test*! + + [Test] + public void T020_PrimInventoryEmpty() + { + TestHelpers.InMethod(); + + SceneObjectGroup sog = GetMySOG("object1"); + TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); + Assert.That(t, Is.Null); + } + + // TODO: Is there any point to call StorePrimInventory on a list, rather than on the prim itself? + + private void StoreInventory(SceneObjectGroup sog) + { + List list = new List(); + // TODO: seriously??? this is the way we need to loop to get this? + foreach (UUID uuid in sog.RootPart.Inventory.GetInventoryList()) + { + list.Add(sog.GetInventoryItem(sog.RootPart.LocalId, uuid)); + } + + db.StorePrimInventory(sog.RootPart.UUID, list); + } + + [Test] + public void T021_PrimInventoryBasic() + { + TestHelpers.InMethod(); + + SceneObjectGroup sog = GetMySOG("object1"); + InventoryItemBase i = NewItem(item1, zero, zero, itemname1, zero); + + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, i, zero), Is.True); + TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); + Assert.That(t.Name, Is.EqualTo(itemname1), "Assert.That(t.Name, Is.EqualTo(itemname1))"); + + StoreInventory(sog); + + SceneObjectGroup sog1 = FindSOG("object1", region1); + Assert.That(sog1, Is.Not.Null); + + TaskInventoryItem t1 = sog1.GetInventoryItem(sog1.RootPart.LocalId, item1); + Assert.That(t1, Is.Not.Null); + Assert.That(t1.Name, Is.EqualTo(itemname1), "Assert.That(t.Name, Is.EqualTo(itemname1))"); + + // Updating inventory + t1.Name = "My New Name"; + sog1.UpdateInventoryItem(t1); + + StoreInventory(sog1); + + SceneObjectGroup sog2 = FindSOG("object1", region1); + TaskInventoryItem t2 = sog2.GetInventoryItem(sog2.RootPart.LocalId, item1); + Assert.That(t2.Name, Is.EqualTo("My New Name"), "Assert.That(t.Name, Is.EqualTo(\"My New Name\"))"); + + // Removing inventory + List list = new List(); + db.StorePrimInventory(prim1, list); + + sog = FindSOG("object1", region1); + t = sog.GetInventoryItem(sog.RootPart.LocalId, item1); + Assert.That(t, Is.Null); + } + + [Test] + public void T025_PrimInventoryPersistency() + { + TestHelpers.InMethod(); + + InventoryItemBase i = new InventoryItemBase(); + UUID id = UUID.Random(); + i.ID = id; + UUID folder = UUID.Random(); + i.Folder = folder; + UUID owner = UUID.Random(); + i.Owner = owner; + UUID creator = UUID.Random(); + i.CreatorId = creator.ToString(); + string name = RandomName(); + i.Name = name; + i.Description = name; + UUID assetid = UUID.Random(); + i.AssetID = assetid; + int invtype = random.Next(); + i.InvType = invtype; + uint nextperm = (uint) random.Next(); + i.NextPermissions = nextperm; + uint curperm = (uint) random.Next(); + i.CurrentPermissions = curperm; + uint baseperm = (uint) random.Next(); + i.BasePermissions = baseperm; + uint eoperm = (uint) random.Next(); + i.EveryOnePermissions = eoperm; + int assettype = random.Next(); + i.AssetType = assettype; + UUID groupid = UUID.Random(); + i.GroupID = groupid; + bool groupown = true; + i.GroupOwned = groupown; + int saleprice = random.Next(); + i.SalePrice = saleprice; + byte saletype = (byte) random.Next(127); + i.SaleType = saletype; + uint flags = (uint) random.Next(); + i.Flags = flags; + int creationd = random.Next(); + i.CreationDate = creationd; + + SceneObjectGroup sog = GetMySOG("object1"); + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, i, zero), Is.True); + TaskInventoryItem t = sog.GetInventoryItem(sog.RootPart.LocalId, id); + + Assert.That(t.Name, Is.EqualTo(name), "Assert.That(t.Name, Is.EqualTo(name))"); + Assert.That(t.AssetID,Is.EqualTo(assetid), "Assert.That(t.AssetID,Is.EqualTo(assetid))"); + Assert.That(t.BasePermissions,Is.EqualTo(baseperm), "Assert.That(t.BasePermissions,Is.EqualTo(baseperm))"); + Assert.That(t.CreationDate,Is.EqualTo(creationd), "Assert.That(t.CreationDate,Is.EqualTo(creationd))"); + Assert.That(t.CreatorID,Is.EqualTo(creator), "Assert.That(t.CreatorID,Is.EqualTo(creator))"); + Assert.That(t.Description,Is.EqualTo(name), "Assert.That(t.Description,Is.EqualTo(name))"); + Assert.That(t.EveryonePermissions,Is.EqualTo(eoperm), "Assert.That(t.EveryonePermissions,Is.EqualTo(eoperm))"); + Assert.That(t.Flags,Is.EqualTo(flags), "Assert.That(t.Flags,Is.EqualTo(flags))"); + Assert.That(t.GroupID,Is.EqualTo(sog.RootPart.GroupID), "Assert.That(t.GroupID,Is.EqualTo(sog.RootPart.GroupID))"); + // Where is this group permissions?? + // Assert.That(t.GroupPermissions,Is.EqualTo(), "Assert.That(t.GroupPermissions,Is.EqualTo())"); + Assert.That(t.Type,Is.EqualTo(assettype), "Assert.That(t.Type,Is.EqualTo(assettype))"); + Assert.That(t.InvType, Is.EqualTo(invtype), "Assert.That(t.InvType, Is.EqualTo(invtype))"); + Assert.That(t.ItemID, Is.EqualTo(id), "Assert.That(t.ItemID, Is.EqualTo(id))"); + Assert.That(t.LastOwnerID, Is.EqualTo(sog.RootPart.LastOwnerID), "Assert.That(t.LastOwnerID, Is.EqualTo(sog.RootPart.LastOwnerID))"); + Assert.That(t.NextPermissions, Is.EqualTo(nextperm), "Assert.That(t.NextPermissions, Is.EqualTo(nextperm))"); + // Ownership changes when you drop an object into an object + // owned by someone else + Assert.That(t.OwnerID,Is.EqualTo(sog.RootPart.OwnerID), "Assert.That(t.OwnerID,Is.EqualTo(sog.RootPart.OwnerID))"); +// Assert.That(t.CurrentPermissions, Is.EqualTo(curperm | 16), "Assert.That(t.CurrentPermissions, Is.EqualTo(curperm | 8))"); + Assert.That(t.ParentID,Is.EqualTo(sog.RootPart.FolderID), "Assert.That(t.ParentID,Is.EqualTo(sog.RootPart.FolderID))"); + Assert.That(t.ParentPartID,Is.EqualTo(sog.RootPart.UUID), "Assert.That(t.ParentPartID,Is.EqualTo(sog.RootPart.UUID))"); + } + + [Test] + [ExpectedException(typeof(ArgumentException))] + public void T026_PrimInventoryMany() + { + TestHelpers.InMethod(); + + UUID i1,i2,i3,i4; + i1 = UUID.Random(); + i2 = UUID.Random(); + i3 = UUID.Random(); + i4 = i3; + InventoryItemBase ib1 = NewItem(i1, zero, zero, RandomName(), zero); + InventoryItemBase ib2 = NewItem(i2, zero, zero, RandomName(), zero); + InventoryItemBase ib3 = NewItem(i3, zero, zero, RandomName(), zero); + InventoryItemBase ib4 = NewItem(i4, zero, zero, RandomName(), zero); + + SceneObjectGroup sog = FindSOG("object1", region1); + + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, ib1, zero), Is.True); + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, ib2, zero), Is.True); + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, ib3, zero), Is.True); + Assert.That(sog.AddInventoryItem(zero, sog.RootPart.LocalId, ib4, zero), Is.True); + + TaskInventoryItem t1 = sog.GetInventoryItem(sog.RootPart.LocalId, i1); + Assert.That(t1.Name, Is.EqualTo(ib1.Name), "Assert.That(t1.Name, Is.EqualTo(ib1.Name))"); + TaskInventoryItem t2 = sog.GetInventoryItem(sog.RootPart.LocalId, i2); + Assert.That(t2.Name, Is.EqualTo(ib2.Name), "Assert.That(t2.Name, Is.EqualTo(ib2.Name))"); + TaskInventoryItem t3 = sog.GetInventoryItem(sog.RootPart.LocalId, i3); + Assert.That(t3.Name, Is.EqualTo(ib3.Name), "Assert.That(t3.Name, Is.EqualTo(ib3.Name))"); + TaskInventoryItem t4 = sog.GetInventoryItem(sog.RootPart.LocalId, i4); + Assert.That(t4, Is.Null); + } + + [Test] + public void T052_RemoveObject() + { + TestHelpers.InMethod(); + + db.RemoveObject(prim1, region1); + SceneObjectGroup sog = FindSOG("object1", region1); + Assert.That(sog, Is.Null); + } + + [Test] + public void T100_DefaultRegionInfo() + { + TestHelpers.InMethod(); + + RegionSettings r1 = db.LoadRegionSettings(region1); + Assert.That(r1.RegionUUID, Is.EqualTo(region1), "Assert.That(r1.RegionUUID, Is.EqualTo(region1))"); + + RegionSettings r2 = db.LoadRegionSettings(region2); + Assert.That(r2.RegionUUID, Is.EqualTo(region2), "Assert.That(r2.RegionUUID, Is.EqualTo(region2))"); + } + + [Test] + public void T101_UpdateRegionInfo() + { + TestHelpers.InMethod(); + + int agentlimit = random.Next(); + double objectbonus = random.Next(); + int maturity = random.Next(); + UUID tertex1 = UUID.Random(); + UUID tertex2 = UUID.Random(); + UUID tertex3 = UUID.Random(); + UUID tertex4 = UUID.Random(); + double elev1nw = random.Next(); + double elev2nw = random.Next(); + double elev1ne = random.Next(); + double elev2ne = random.Next(); + double elev1se = random.Next(); + double elev2se = random.Next(); + double elev1sw = random.Next(); + double elev2sw = random.Next(); + double waterh = random.Next(); + double terrainraise = random.Next(); + double terrainlower = random.Next(); + Vector3 sunvector = new Vector3((float)Math.Round(random.NextDouble(),5),(float)Math.Round(random.NextDouble(),5),(float)Math.Round(random.NextDouble(),5)); + UUID terimgid = UUID.Random(); + double sunpos = random.Next(); + UUID cov = UUID.Random(); + + RegionSettings r1 = db.LoadRegionSettings(region1); + r1.BlockTerraform = true; + r1.BlockFly = true; + r1.AllowDamage = true; + r1.RestrictPushing = true; + r1.AllowLandResell = false; + r1.AllowLandJoinDivide = false; + r1.BlockShowInSearch = true; + r1.AgentLimit = agentlimit; + r1.ObjectBonus = objectbonus; + r1.Maturity = maturity; + r1.DisableScripts = true; + r1.DisableCollisions = true; + r1.DisablePhysics = true; + r1.TerrainTexture1 = tertex1; + r1.TerrainTexture2 = tertex2; + r1.TerrainTexture3 = tertex3; + r1.TerrainTexture4 = tertex4; + r1.Elevation1NW = elev1nw; + r1.Elevation2NW = elev2nw; + r1.Elevation1NE = elev1ne; + r1.Elevation2NE = elev2ne; + r1.Elevation1SE = elev1se; + r1.Elevation2SE = elev2se; + r1.Elevation1SW = elev1sw; + r1.Elevation2SW = elev2sw; + r1.WaterHeight = waterh; + r1.TerrainRaiseLimit = terrainraise; + r1.TerrainLowerLimit = terrainlower; + r1.UseEstateSun = false; + r1.Sandbox = true; + r1.SunVector = sunvector; + r1.TerrainImageID = terimgid; + r1.FixedSun = true; + r1.SunPosition = sunpos; + r1.Covenant = cov; + + db.StoreRegionSettings(r1); + + RegionSettings r1a = db.LoadRegionSettings(region1); + Assert.That(r1a.RegionUUID, Is.EqualTo(region1), "Assert.That(r1a.RegionUUID, Is.EqualTo(region1))"); + Assert.That(r1a.BlockTerraform,Is.True); + Assert.That(r1a.BlockFly,Is.True); + Assert.That(r1a.AllowDamage,Is.True); + Assert.That(r1a.RestrictPushing,Is.True); + Assert.That(r1a.AllowLandResell,Is.False); + Assert.That(r1a.AllowLandJoinDivide,Is.False); + Assert.That(r1a.BlockShowInSearch,Is.True); + Assert.That(r1a.AgentLimit,Is.EqualTo(agentlimit), "Assert.That(r1a.AgentLimit,Is.EqualTo(agentlimit))"); + Assert.That(r1a.ObjectBonus,Is.EqualTo(objectbonus), "Assert.That(r1a.ObjectBonus,Is.EqualTo(objectbonus))"); + Assert.That(r1a.Maturity,Is.EqualTo(maturity), "Assert.That(r1a.Maturity,Is.EqualTo(maturity))"); + Assert.That(r1a.DisableScripts,Is.True); + Assert.That(r1a.DisableCollisions,Is.True); + Assert.That(r1a.DisablePhysics,Is.True); + Assert.That(r1a.TerrainTexture1,Is.EqualTo(tertex1), "Assert.That(r1a.TerrainTexture1,Is.EqualTo(tertex1))"); + Assert.That(r1a.TerrainTexture2,Is.EqualTo(tertex2), "Assert.That(r1a.TerrainTexture2,Is.EqualTo(tertex2))"); + Assert.That(r1a.TerrainTexture3,Is.EqualTo(tertex3), "Assert.That(r1a.TerrainTexture3,Is.EqualTo(tertex3))"); + Assert.That(r1a.TerrainTexture4,Is.EqualTo(tertex4), "Assert.That(r1a.TerrainTexture4,Is.EqualTo(tertex4))"); + Assert.That(r1a.Elevation1NW,Is.EqualTo(elev1nw), "Assert.That(r1a.Elevation1NW,Is.EqualTo(elev1nw))"); + Assert.That(r1a.Elevation2NW,Is.EqualTo(elev2nw), "Assert.That(r1a.Elevation2NW,Is.EqualTo(elev2nw))"); + Assert.That(r1a.Elevation1NE,Is.EqualTo(elev1ne), "Assert.That(r1a.Elevation1NE,Is.EqualTo(elev1ne))"); + Assert.That(r1a.Elevation2NE,Is.EqualTo(elev2ne), "Assert.That(r1a.Elevation2NE,Is.EqualTo(elev2ne))"); + Assert.That(r1a.Elevation1SE,Is.EqualTo(elev1se), "Assert.That(r1a.Elevation1SE,Is.EqualTo(elev1se))"); + Assert.That(r1a.Elevation2SE,Is.EqualTo(elev2se), "Assert.That(r1a.Elevation2SE,Is.EqualTo(elev2se))"); + Assert.That(r1a.Elevation1SW,Is.EqualTo(elev1sw), "Assert.That(r1a.Elevation1SW,Is.EqualTo(elev1sw))"); + Assert.That(r1a.Elevation2SW,Is.EqualTo(elev2sw), "Assert.That(r1a.Elevation2SW,Is.EqualTo(elev2sw))"); + Assert.That(r1a.WaterHeight,Is.EqualTo(waterh), "Assert.That(r1a.WaterHeight,Is.EqualTo(waterh))"); + Assert.That(r1a.TerrainRaiseLimit,Is.EqualTo(terrainraise), "Assert.That(r1a.TerrainRaiseLimit,Is.EqualTo(terrainraise))"); + Assert.That(r1a.TerrainLowerLimit,Is.EqualTo(terrainlower), "Assert.That(r1a.TerrainLowerLimit,Is.EqualTo(terrainlower))"); + Assert.That(r1a.UseEstateSun,Is.False); + Assert.That(r1a.Sandbox,Is.True); + Assert.That(r1a.SunVector,Is.EqualTo(sunvector), "Assert.That(r1a.SunVector,Is.EqualTo(sunvector))"); + //Assert.That(r1a.TerrainImageID,Is.EqualTo(terimgid), "Assert.That(r1a.TerrainImageID,Is.EqualTo(terimgid))"); + Assert.That(r1a.FixedSun,Is.True); + Assert.That(r1a.SunPosition, Is.EqualTo(sunpos), "Assert.That(r1a.SunPosition, Is.EqualTo(sunpos))"); + Assert.That(r1a.Covenant, Is.EqualTo(cov), "Assert.That(r1a.Covenant, Is.EqualTo(cov))"); + } + + [Test] + public void T300_NoTerrain() + { + TestHelpers.InMethod(); + + Assert.That(db.LoadTerrain(zero), Is.Null); + Assert.That(db.LoadTerrain(region1), Is.Null); + Assert.That(db.LoadTerrain(region2), Is.Null); + Assert.That(db.LoadTerrain(UUID.Random()), Is.Null); + } + + [Test] + public void T301_CreateTerrain() + { + TestHelpers.InMethod(); + + double[,] t1 = GenTerrain(height1); + db.StoreTerrain(t1, region1); + + Assert.That(db.LoadTerrain(zero), Is.Null); + Assert.That(db.LoadTerrain(region1), Is.Not.Null); + Assert.That(db.LoadTerrain(region2), Is.Null); + Assert.That(db.LoadTerrain(UUID.Random()), Is.Null); + } + + [Test] + public void T302_FetchTerrain() + { + TestHelpers.InMethod(); + + double[,] baseterrain1 = GenTerrain(height1); + double[,] baseterrain2 = GenTerrain(height2); + double[,] t1 = db.LoadTerrain(region1); + Assert.That(CompareTerrain(t1, baseterrain1), Is.True); + Assert.That(CompareTerrain(t1, baseterrain2), Is.False); + } + + [Test] + public void T303_UpdateTerrain() + { + TestHelpers.InMethod(); + + double[,] baseterrain1 = GenTerrain(height1); + double[,] baseterrain2 = GenTerrain(height2); + db.StoreTerrain(baseterrain2, region1); + + double[,] t1 = db.LoadTerrain(region1); + Assert.That(CompareTerrain(t1, baseterrain1), Is.False); + Assert.That(CompareTerrain(t1, baseterrain2), Is.True); + } + + [Test] + public void T400_EmptyLand() + { + TestHelpers.InMethod(); + + Assert.That(db.LoadLandObjects(zero).Count, Is.EqualTo(0), "Assert.That(db.LoadLandObjects(zero).Count, Is.EqualTo(0))"); + Assert.That(db.LoadLandObjects(region1).Count, Is.EqualTo(0), "Assert.That(db.LoadLandObjects(region1).Count, Is.EqualTo(0))"); + Assert.That(db.LoadLandObjects(region2).Count, Is.EqualTo(0), "Assert.That(db.LoadLandObjects(region2).Count, Is.EqualTo(0))"); + Assert.That(db.LoadLandObjects(UUID.Random()).Count, Is.EqualTo(0), "Assert.That(db.LoadLandObjects(UUID.Random()).Count, Is.EqualTo(0))"); + } + + // TODO: we should have real land tests, but Land is so + // intermingled with scene that you can't test it without a + // valid scene. That requires some disagregation. + + + //************************************************************************************// + // Extra private methods + + private double[,] GenTerrain(double value) + { + double[,] terret = new double[Constants.RegionSize, Constants.RegionSize]; + terret.Initialize(); + for (int x = 0; x < Constants.RegionSize; x++) + for (int y = 0; y < Constants.RegionSize; y++) + terret[x,y] = value; + + return terret; + } + + private bool CompareTerrain(double[,] one, double[,] two) + { + for (int x = 0; x < Constants.RegionSize; x++) + for (int y = 0; y < Constants.RegionSize; y++) + if (one[x,y] != two[x,y]) + return false; + + return true; + } + + private SceneObjectGroup FindSOG(string name, UUID r) + { + List objs = db.LoadObjects(r); + foreach (SceneObjectGroup sog in objs) + if (sog.Name == name) + return sog; + + return null; + } + + // This builds a minimalistic Prim, 1 SOG with 1 root SOP. A + // common failure case is people adding new fields that aren't + // initialized, but have non-null db constraints. We should + // honestly be passing more and more null things in here. + // + // Please note that in Sqlite.BuildPrim there is a commented out inline version + // of this so you can debug and step through the build process and check the fields + // + // Real World Value: Tests for situation where extending a SceneObjectGroup/SceneObjectPart + // causes the application to crash at the database layer because of null values + // in NOT NULL fields + // + private SceneObjectGroup NewSOG(string name, UUID uuid, UUID regionId) + { + RegionInfo regionInfo = new RegionInfo(); + regionInfo.RegionID = regionId; + regionInfo.RegionLocX = 0; + regionInfo.RegionLocY = 0; + + SceneObjectPart sop = new SceneObjectPart(); + sop.Name = name; + sop.Description = name; + sop.Text = RandomName(); + sop.SitName = RandomName(); + sop.TouchName = RandomName(); + sop.UUID = uuid; + sop.Shape = PrimitiveBaseShape.Default; + + SceneObjectGroup sog = new SceneObjectGroup(sop); +// sog.SetScene(scene); + + return sog; + } + + private SceneObjectPart NewSOP(string name, UUID uuid) + { + SceneObjectPart sop = new SceneObjectPart(); + sop.Name = name; + sop.Description = name; + sop.Text = RandomName(); + sop.SitName = RandomName(); + sop.TouchName = RandomName(); + sop.UUID = uuid; + sop.Shape = PrimitiveBaseShape.Default; + return sop; + } + + // These are copied from the Inventory Item tests + + private InventoryItemBase NewItem(UUID id, UUID parent, UUID owner, string name, UUID asset) + { + InventoryItemBase i = new InventoryItemBase(); + i.ID = id; + i.Folder = parent; + i.Owner = owner; + i.CreatorId = owner.ToString(); + i.Name = name; + i.Description = name; + i.AssetID = asset; + return i; + } + + private static string RandomName() + { + StringBuilder name = new StringBuilder(); + int size = random.Next(5,12); + char ch ; + for (int i=0; i + /// Circuit data for an agent. Connection information shared between + /// regions that accept UDP connections from a client + /// + public class AgentCircuitData + { +// DEBUG ON + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); +// DEBUG OFF + + /// + /// Avatar Unique Agent Identifier + /// + public UUID AgentID; + + /// + /// Avatar's Appearance + /// + public AvatarAppearance Appearance; + + /// + /// Agent's root inventory folder + /// + public UUID BaseFolder; + + /// + /// Base Caps path for user + /// + public string CapsPath = String.Empty; + + /// + /// Seed caps for neighbor regions that the user can see into + /// + public Dictionary ChildrenCapSeeds; + + /// + /// Root agent, or Child agent + /// + public bool child; + + /// + /// Number given to the client when they log-in that they provide + /// as credentials to the UDP server + /// + public uint circuitcode; + + /// + /// How this agent got here + /// + public uint teleportFlags; + + /// + /// Agent's account first name + /// + public string firstname; + public UUID InventoryFolder; + + /// + /// Agent's account last name + /// + public string lastname; + + /// + /// Agent's full name. + /// + public string Name { get { return string.Format("{0} {1}", firstname, lastname); } } + + /// + /// Random Unique GUID for this session. Client gets this at login and it's + /// only supposed to be disclosed over secure channels + /// + public UUID SecureSessionID; + + /// + /// Non secure Session ID + /// + public UUID SessionID; + + /// + /// Hypergrid service token; generated by the user domain, consumed by the receiving grid. + /// There is one such unique token for each grid visited. + /// + public string ServiceSessionID = string.Empty; + + /// + /// The client's IP address, as captured by the login service + /// + public string IPAddress; + + /// + /// Viewer's version string as reported by the viewer at login + /// + private string m_viewerInternal; + + /// + /// Viewer's version string + /// + public string Viewer + { + set { m_viewerInternal = value; } + + // Try to return consistent viewer string taking into account + // that viewers have chaagned how version is reported + // See http://opensimulator.org/mantis/view.php?id=6851 + get + { + // Old style version string contains viewer name followed by a space followed by a version number + if (m_viewerInternal == null || m_viewerInternal.Contains(" ")) + { + return m_viewerInternal; + } + else // New style version contains no spaces, just version number + { + return Channel + " " + m_viewerInternal; + } + } + } + + /// + /// The channel strinf sent by the viewer at login + /// + public string Channel; + + /// + /// The Mac address as reported by the viewer at login + /// + public string Mac; + + /// + /// The id0 as reported by the viewer at login + /// + public string Id0; + + /// + /// Position the Agent's Avatar starts in the region + /// + public Vector3 startpos; + + public Dictionary ServiceURLs; + + public AgentCircuitData() + { + } + + /// + /// Pack AgentCircuitData into an OSDMap for transmission over LLSD XML or LLSD json + /// + /// map of the agent circuit data + public OSDMap PackAgentCircuitData() + { + OSDMap args = new OSDMap(); + args["agent_id"] = OSD.FromUUID(AgentID); + args["base_folder"] = OSD.FromUUID(BaseFolder); + args["caps_path"] = OSD.FromString(CapsPath); + + if (ChildrenCapSeeds != null) + { + OSDArray childrenSeeds = new OSDArray(ChildrenCapSeeds.Count); + foreach (KeyValuePair kvp in ChildrenCapSeeds) + { + OSDMap pair = new OSDMap(); + pair["handle"] = OSD.FromString(kvp.Key.ToString()); + pair["seed"] = OSD.FromString(kvp.Value); + childrenSeeds.Add(pair); + } + if (ChildrenCapSeeds.Count > 0) + args["children_seeds"] = childrenSeeds; + } + args["child"] = OSD.FromBoolean(child); + args["circuit_code"] = OSD.FromString(circuitcode.ToString()); + args["first_name"] = OSD.FromString(firstname); + args["last_name"] = OSD.FromString(lastname); + args["inventory_folder"] = OSD.FromUUID(InventoryFolder); + args["secure_session_id"] = OSD.FromUUID(SecureSessionID); + args["session_id"] = OSD.FromUUID(SessionID); + + args["service_session_id"] = OSD.FromString(ServiceSessionID); + args["start_pos"] = OSD.FromString(startpos.ToString()); + args["client_ip"] = OSD.FromString(IPAddress); + args["viewer"] = OSD.FromString(Viewer); + args["channel"] = OSD.FromString(Channel); + args["mac"] = OSD.FromString(Mac); + args["id0"] = OSD.FromString(Id0); + + if (Appearance != null) + { + args["appearance_serial"] = OSD.FromInteger(Appearance.Serial); + + OSDMap appmap = Appearance.Pack(); + args["packed_appearance"] = appmap; + } + + // Old, bad way. Keeping it fow now for backwards compatibility + // OBSOLETE -- soon to be deleted + if (ServiceURLs != null && ServiceURLs.Count > 0) + { + OSDArray urls = new OSDArray(ServiceURLs.Count * 2); + foreach (KeyValuePair kvp in ServiceURLs) + { + //System.Console.WriteLine("XXX " + kvp.Key + "=" + kvp.Value); + urls.Add(OSD.FromString(kvp.Key)); + urls.Add(OSD.FromString((kvp.Value == null) ? string.Empty : kvp.Value.ToString())); + } + args["service_urls"] = urls; + } + + // again, this time the right way + if (ServiceURLs != null && ServiceURLs.Count > 0) + { + OSDMap urls = new OSDMap(); + foreach (KeyValuePair kvp in ServiceURLs) + { + //System.Console.WriteLine("XXX " + kvp.Key + "=" + kvp.Value); + urls[kvp.Key] = OSD.FromString((kvp.Value == null) ? string.Empty : kvp.Value.ToString()); + } + args["serviceurls"] = urls; + } + + + return args; + } + + /// + /// Unpack agent circuit data map into an AgentCiruitData object + /// + /// + public void UnpackAgentCircuitData(OSDMap args) + { + if (args["agent_id"] != null) + AgentID = args["agent_id"].AsUUID(); + if (args["base_folder"] != null) + BaseFolder = args["base_folder"].AsUUID(); + if (args["caps_path"] != null) + CapsPath = args["caps_path"].AsString(); + + if ((args["children_seeds"] != null) && (args["children_seeds"].Type == OSDType.Array)) + { + OSDArray childrenSeeds = (OSDArray)(args["children_seeds"]); + ChildrenCapSeeds = new Dictionary(); + foreach (OSD o in childrenSeeds) + { + if (o.Type == OSDType.Map) + { + ulong handle = 0; + string seed = ""; + OSDMap pair = (OSDMap)o; + if (pair["handle"] != null) + if (!UInt64.TryParse(pair["handle"].AsString(), out handle)) + continue; + if (pair["seed"] != null) + seed = pair["seed"].AsString(); + if (!ChildrenCapSeeds.ContainsKey(handle)) + ChildrenCapSeeds.Add(handle, seed); + } + } + } + else + ChildrenCapSeeds = new Dictionary(); + + if (args["child"] != null) + child = args["child"].AsBoolean(); + if (args["circuit_code"] != null) + UInt32.TryParse(args["circuit_code"].AsString(), out circuitcode); + if (args["first_name"] != null) + firstname = args["first_name"].AsString(); + if (args["last_name"] != null) + lastname = args["last_name"].AsString(); + if (args["inventory_folder"] != null) + InventoryFolder = args["inventory_folder"].AsUUID(); + if (args["secure_session_id"] != null) + SecureSessionID = args["secure_session_id"].AsUUID(); + if (args["session_id"] != null) + SessionID = args["session_id"].AsUUID(); + if (args["service_session_id"] != null) + ServiceSessionID = args["service_session_id"].AsString(); + if (args["client_ip"] != null) + IPAddress = args["client_ip"].AsString(); + if (args["viewer"] != null) + Viewer = args["viewer"].AsString(); + if (args["channel"] != null) + Channel = args["channel"].AsString(); + if (args["mac"] != null) + Mac = args["mac"].AsString(); + if (args["id0"] != null) + Id0 = args["id0"].AsString(); + if (args["teleport_flags"] != null) + teleportFlags = args["teleport_flags"].AsUInteger(); + + if (args["start_pos"] != null) + Vector3.TryParse(args["start_pos"].AsString(), out startpos); + + //m_log.InfoFormat("[AGENTCIRCUITDATA]: agentid={0}, child={1}, startpos={2}", AgentID, child, startpos); + + try + { + // Unpack various appearance elements + Appearance = new AvatarAppearance(); + + // Eventually this code should be deprecated, use full appearance + // packing in packed_appearance + if (args["appearance_serial"] != null) + Appearance.Serial = args["appearance_serial"].AsInteger(); + + if (args.ContainsKey("packed_appearance") && (args["packed_appearance"].Type == OSDType.Map)) + { + Appearance.Unpack((OSDMap)args["packed_appearance"]); +// m_log.InfoFormat("[AGENTCIRCUITDATA] unpacked appearance"); + } + else + { + m_log.Warn("[AGENTCIRCUITDATA]: failed to find a valid packed_appearance"); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[AGENTCIRCUITDATA] failed to unpack appearance; {0}",e.Message); + } + + ServiceURLs = new Dictionary(); + // Try parse the new way, OSDMap + if (args.ContainsKey("serviceurls") && args["serviceurls"] != null && (args["serviceurls"]).Type == OSDType.Map) + { + OSDMap urls = (OSDMap)(args["serviceurls"]); + foreach (KeyValuePair kvp in urls) + { + ServiceURLs[kvp.Key] = kvp.Value.AsString(); + //System.Console.WriteLine("XXX " + kvp.Key + "=" + ServiceURLs[kvp.Key]); + + } + } + // else try the old way, OSDArray + // OBSOLETE -- soon to be deleted + else if (args.ContainsKey("service_urls") && args["service_urls"] != null && (args["service_urls"]).Type == OSDType.Array) + { + OSDArray urls = (OSDArray)(args["service_urls"]); + for (int i = 0; i < urls.Count / 2; i++) + { + ServiceURLs[urls[i * 2].AsString()] = urls[(i * 2) + 1].AsString(); + //System.Console.WriteLine("XXX " + urls[i * 2].AsString() + "=" + urls[(i * 2) + 1].AsString()); + + } + } + } + + } + + +} diff --git a/OpenSim/Framework/AgentCircuitManager.cs b/OpenSim/Framework/AgentCircuitManager.cs new file mode 100644 index 0000000000..b6e48b4372 --- /dev/null +++ b/OpenSim/Framework/AgentCircuitManager.cs @@ -0,0 +1,229 @@ +/* + * 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.Generic; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + /// + /// Manage client circuits + /// + public class AgentCircuitManager + { + /// + /// Agent circuits indexed by circuit code. + /// + /// + /// We lock this for operations both on this dictionary and on m_agentCircuitsByUUID + /// + private Dictionary m_agentCircuits = new Dictionary(); + + /// + /// Agent circuits indexed by agent UUID. + /// + private Dictionary m_agentCircuitsByUUID = new Dictionary(); + + public virtual AuthenticateResponse AuthenticateSession(UUID sessionID, UUID agentID, uint circuitcode) + { + AgentCircuitData validcircuit = null; + + lock (m_agentCircuits) + { + if (m_agentCircuits.ContainsKey(circuitcode)) + validcircuit = m_agentCircuits[circuitcode]; + } + + AuthenticateResponse user = new AuthenticateResponse(); + + if (validcircuit == null) + { + //don't have this circuit code in our list + user.Authorised = false; + return user; + } + + if ((sessionID == validcircuit.SessionID) && (agentID == validcircuit.AgentID)) + { + user.Authorised = true; + user.LoginInfo = new Login(); + user.LoginInfo.Agent = agentID; + user.LoginInfo.Session = sessionID; + user.LoginInfo.SecureSession = validcircuit.SecureSessionID; + user.LoginInfo.First = validcircuit.firstname; + user.LoginInfo.Last = validcircuit.lastname; + user.LoginInfo.InventoryFolder = validcircuit.InventoryFolder; + user.LoginInfo.BaseFolder = validcircuit.BaseFolder; + user.LoginInfo.StartPos = validcircuit.startpos; + } + else + { + // Invalid + user.Authorised = false; + } + + return user; + } + + /// + /// Add information about a new circuit so that later on we can authenticate a new client session. + /// + /// + /// + public virtual void AddNewCircuit(uint circuitCode, AgentCircuitData agentData) + { + lock (m_agentCircuits) + { + if (m_agentCircuits.ContainsKey(circuitCode)) + { + m_agentCircuits[circuitCode] = agentData; + m_agentCircuitsByUUID[agentData.AgentID] = agentData; + } + else + { + m_agentCircuits.Add(circuitCode, agentData); + m_agentCircuitsByUUID[agentData.AgentID] = agentData; + } + } + } + + public virtual void RemoveCircuit(uint circuitCode) + { + lock (m_agentCircuits) + { + if (m_agentCircuits.ContainsKey(circuitCode)) + { + UUID agentID = m_agentCircuits[circuitCode].AgentID; + m_agentCircuits.Remove(circuitCode); + m_agentCircuitsByUUID.Remove(agentID); + } + } + } + + public virtual void RemoveCircuit(UUID agentID) + { + lock (m_agentCircuits) + { + if (m_agentCircuitsByUUID.ContainsKey(agentID)) + { + uint circuitCode = m_agentCircuitsByUUID[agentID].circuitcode; + m_agentCircuits.Remove(circuitCode); + m_agentCircuitsByUUID.Remove(agentID); + } + } + } + + public AgentCircuitData GetAgentCircuitData(uint circuitCode) + { + AgentCircuitData agentCircuit = null; + + lock (m_agentCircuits) + m_agentCircuits.TryGetValue(circuitCode, out agentCircuit); + + return agentCircuit; + } + + public AgentCircuitData GetAgentCircuitData(UUID agentID) + { + AgentCircuitData agentCircuit = null; + + lock (m_agentCircuits) + m_agentCircuitsByUUID.TryGetValue(agentID, out agentCircuit); + + return agentCircuit; + } + + /// + /// Get all current agent circuits indexed by agent UUID. + /// + /// + public Dictionary GetAgentCircuits() + { + lock (m_agentCircuits) + return new Dictionary(m_agentCircuitsByUUID); + } + + public void UpdateAgentData(AgentCircuitData agentData) + { + lock (m_agentCircuits) + { + if (m_agentCircuits.ContainsKey((uint) agentData.circuitcode)) + { + m_agentCircuits[(uint) agentData.circuitcode].firstname = agentData.firstname; + m_agentCircuits[(uint) agentData.circuitcode].lastname = agentData.lastname; + m_agentCircuits[(uint) agentData.circuitcode].startpos = agentData.startpos; + + // Updated for when we don't know them before calling Scene.NewUserConnection + m_agentCircuits[(uint) agentData.circuitcode].SecureSessionID = agentData.SecureSessionID; + m_agentCircuits[(uint) agentData.circuitcode].SessionID = agentData.SessionID; + + // m_log.Debug("update user start pos is " + agentData.startpos.X + " , " + agentData.startpos.Y + " , " + agentData.startpos.Z); + } + } + } + + /// + /// Sometimes the circuitcode may not be known before setting up the connection + /// + /// + /// + public bool TryChangeCiruitCode(uint circuitcode, uint newcircuitcode) + { + lock (m_agentCircuits) + { + if (m_agentCircuits.ContainsKey((uint)circuitcode) && !m_agentCircuits.ContainsKey((uint)newcircuitcode)) + { + AgentCircuitData agentData = m_agentCircuits[(uint)circuitcode]; + + agentData.circuitcode = newcircuitcode; + + m_agentCircuits.Remove((uint)circuitcode); + m_agentCircuits.Add(newcircuitcode, agentData); + return true; + } + } + + return false; + } + + public void UpdateAgentChildStatus(uint circuitcode, bool childstatus) + { + lock (m_agentCircuits) + if (m_agentCircuits.ContainsKey(circuitcode)) + m_agentCircuits[circuitcode].child = childstatus; + } + + public bool GetAgentChildStatus(uint circuitcode) + { + lock (m_agentCircuits) + if (m_agentCircuits.ContainsKey(circuitcode)) + return m_agentCircuits[circuitcode].child; + + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/AgentUpdateArgs.cs b/OpenSim/Framework/AgentUpdateArgs.cs new file mode 100644 index 0000000000..660bc327de --- /dev/null +++ b/OpenSim/Framework/AgentUpdateArgs.cs @@ -0,0 +1,90 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Client provided parameters for avatar movement + /// + public class AgentUpdateArgs : EventArgs + { + /// + /// Agent's unique ID + /// + public UUID AgentID; + + /// + /// Rotation of the avatar's body + /// + public Quaternion BodyRotation; + + /// + /// AT portion of the camera matrix + /// + public Vector3 CameraAtAxis; + + /// + /// Position of the camera in the Scene + /// + public Vector3 CameraCenter; + public Vector3 CameraLeftAxis; + public Vector3 CameraUpAxis; + + /// + /// Bitflag field for agent movement. Fly, forward, backward, turn left, turn right, go up, go down, Straffe, etc. + /// + public uint ControlFlags; + + /// + /// Agent's client Draw distance setting + /// + public float Far; + public byte Flags; + + /// + /// Rotation of the avatar's head + /// + public Quaternion HeadRotation; + + /// + /// Session Id + /// + public UUID SessionID; + public byte State; + + public Vector3 ClientAgentPosition; + public bool UseClientAgentPosition; + + public AgentUpdateArgs() + { + UseClientAgentPosition = false; + } + } +} diff --git a/OpenSim/Framework/Animation.cs b/OpenSim/Framework/Animation.cs new file mode 100644 index 0000000000..e958b75e8e --- /dev/null +++ b/OpenSim/Framework/Animation.cs @@ -0,0 +1,148 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// Information about an Animation + /// + [Serializable] + public class Animation + { + private UUID animID; + + /// + /// ID of Animation + /// + public UUID AnimID + { + get { return animID; } + set { animID = value; } + } + + private int sequenceNum; + public int SequenceNum + { + get { return sequenceNum; } + set { sequenceNum = value; } + } + + private UUID objectID; + + /// + /// Unique ID of object that is being animated + /// + public UUID ObjectID + { + get { return objectID; } + set { objectID = value; } + } + + public Animation() + { + } + + /// + /// Creates an Animation based on the data + /// + /// UUID ID of animation + /// + /// ID of object to be animated + public Animation(UUID animID, int sequenceNum, UUID objectID) + { + this.animID = animID; + this.sequenceNum = sequenceNum; + this.objectID = objectID; + } + + /// + /// Animation from OSDMap from LLSD XML or LLSD json + /// + /// + public Animation(OSDMap args) + { + UnpackUpdateMessage(args); + } + + + /// + /// Pack this object up as an OSDMap for transferring via LLSD XML or LLSD json + /// + /// + public OSDMap PackUpdateMessage() + { + OSDMap anim = new OSDMap(); + anim["animation"] = OSD.FromUUID(animID); + anim["object_id"] = OSD.FromUUID(objectID); + anim["seq_num"] = OSD.FromInteger(sequenceNum); + return anim; + } + + /// + /// Fill object with data from OSDMap + /// + /// + public void UnpackUpdateMessage(OSDMap args) + { + if (args["animation"] != null) + animID = args["animation"].AsUUID(); + if (args["object_id"] != null) + objectID = args["object_id"].AsUUID(); + if (args["seq_num"] != null) + sequenceNum = args["seq_num"].AsInteger(); + } + + public override bool Equals(object obj) + { + Animation other = obj as Animation; + if (other != null) + { + return (other.AnimID.Equals(this.AnimID) + && other.SequenceNum == this.SequenceNum + && other.ObjectID.Equals(this.ObjectID) ); + } + return base.Equals(obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return "AnimID=" + AnimID.ToString() + + "/seq=" + SequenceNum.ToString() + + "/objID=" + ObjectID.ToString(); + } + + } +} diff --git a/OpenSim/Framework/AssemblyInfo.cs b/OpenSim/Framework/AssemblyInfo.cs new file mode 100644 index 0000000000..a797424775 --- /dev/null +++ b/OpenSim/Framework/AssemblyInfo.cs @@ -0,0 +1,62 @@ +/* + * 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.Reflection; +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.Framework")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim.Framework")] +[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers 2007-2009")] +[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("a08e20c7-f191-4137-b1f0-9291408fa521")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// + +[assembly : AssemblyVersion("0.8.2.*")] diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs new file mode 100644 index 0000000000..2f04d2e565 --- /dev/null +++ b/OpenSim/Framework/AssetBase.cs @@ -0,0 +1,363 @@ +/* + * 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 System.Reflection; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + [Flags] + public enum AssetFlags : int + { + Normal = 0, // Immutable asset + Maptile = 1, // What it says + Rewritable = 2, // Content can be rewritten + Collectable = 4 // Can be GC'ed after some time + } + + /// + /// Asset class. All Assets are reference by this class or a class derived from this class + /// + [Serializable] + public class AssetBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly int MAX_ASSET_NAME = 64; + public static readonly int MAX_ASSET_DESC = 64; + + /// + /// Data of the Asset + /// + private byte[] m_data; + + /// + /// Meta Data of the Asset + /// + private AssetMetadata m_metadata; + + // This is needed for .NET serialization!!! + // Do NOT "Optimize" away! + public AssetBase() + { + m_metadata = new AssetMetadata(); + m_metadata.FullID = UUID.Zero; + m_metadata.ID = UUID.Zero.ToString(); + m_metadata.Type = (sbyte)AssetType.Unknown; + m_metadata.CreatorID = String.Empty; + } + + public AssetBase(UUID assetID, string name, sbyte assetType, string creatorID) + { + if (assetType == (sbyte)AssetType.Unknown) + { + System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(true); + m_log.ErrorFormat("[ASSETBASE]: Creating asset '{0}' ({1}) with an unknown asset type\n{2}", + name, assetID, trace.ToString()); + } + + m_metadata = new AssetMetadata(); + m_metadata.FullID = assetID; + m_metadata.Name = name; + m_metadata.Type = assetType; + m_metadata.CreatorID = creatorID; + } + + public AssetBase(string assetID, string name, sbyte assetType, string creatorID) + { + if (assetType == (sbyte)AssetType.Unknown) + { + System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(true); + m_log.ErrorFormat("[ASSETBASE]: Creating asset '{0}' ({1}) with an unknown asset type\n{2}", + name, assetID, trace.ToString()); + } + + m_metadata = new AssetMetadata(); + m_metadata.ID = assetID; + m_metadata.Name = name; + m_metadata.Type = assetType; + m_metadata.CreatorID = creatorID; + } + + public bool ContainsReferences + { + get + { + return + IsTextualAsset && ( + Type != (sbyte)AssetType.Notecard + && Type != (sbyte)AssetType.CallingCard + && Type != (sbyte)AssetType.LSLText + && Type != (sbyte)AssetType.Landmark); + } + } + + public bool IsTextualAsset + { + get + { + return !IsBinaryAsset; + } + + } + + /// + /// Checks if this asset is a binary or text asset + /// + public bool IsBinaryAsset + { + get + { + return + (Type == (sbyte)AssetType.Animation || + Type == (sbyte)AssetType.Gesture || + Type == (sbyte)AssetType.Simstate || + Type == (sbyte)AssetType.Unknown || + Type == (sbyte)AssetType.Object || + Type == (sbyte)AssetType.Sound || + Type == (sbyte)AssetType.SoundWAV || + Type == (sbyte)AssetType.Texture || + Type == (sbyte)AssetType.TextureTGA || + Type == (sbyte)AssetType.Folder || + Type == (sbyte)AssetType.ImageJPEG || + Type == (sbyte)AssetType.ImageTGA || + Type == (sbyte)AssetType.LSLBytecode); + } + } + + public virtual byte[] Data + { + get { return m_data; } + set { m_data = value; } + } + + /// + /// Asset UUID + /// + public UUID FullID + { + get { return m_metadata.FullID; } + set { m_metadata.FullID = value; } + } + + /// + /// Asset MetaData ID (transferring from UUID to string ID) + /// + public string ID + { + get { return m_metadata.ID; } + set { m_metadata.ID = value; } + } + + public string Name + { + get { return m_metadata.Name; } + set { m_metadata.Name = value; } + } + + public string Description + { + get { return m_metadata.Description; } + set { m_metadata.Description = value; } + } + + /// + /// (sbyte) AssetType enum + /// + public sbyte Type + { + get { return m_metadata.Type; } + set { m_metadata.Type = value; } + } + + /// + /// Is this a region only asset, or does this exist on the asset server also + /// + public bool Local + { + get { return m_metadata.Local; } + set { m_metadata.Local = value; } + } + + /// + /// Is this asset going to be saved to the asset database? + /// + public bool Temporary + { + get { return m_metadata.Temporary; } + set { m_metadata.Temporary = value; } + } + + public string CreatorID + { + get { return m_metadata.CreatorID; } + set { m_metadata.CreatorID = value; } + } + + public AssetFlags Flags + { + get { return m_metadata.Flags; } + set { m_metadata.Flags = value; } + } + + [XmlIgnore] + public AssetMetadata Metadata + { + get { return m_metadata; } + set { m_metadata = value; } + } + + public override string ToString() + { + return FullID.ToString(); + } + } + + [Serializable] + public class AssetMetadata + { + private UUID m_fullid; + private string m_id; + private string m_name = String.Empty; + private string m_description = String.Empty; + private DateTime m_creation_date; + private sbyte m_type = (sbyte)AssetType.Unknown; + private string m_content_type; + private byte[] m_sha1; + private bool m_local; + private bool m_temporary; + private string m_creatorid; + private AssetFlags m_flags; + + public UUID FullID + { + get { return m_fullid; } + set { m_fullid = value; m_id = m_fullid.ToString(); } + } + + public string ID + { + //get { return m_fullid.ToString(); } + //set { m_fullid = new UUID(value); } + get + { + if (String.IsNullOrEmpty(m_id)) + m_id = m_fullid.ToString(); + + return m_id; + } + + set + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(value, out uuid)) + { + m_fullid = uuid; + m_id = m_fullid.ToString(); + } + else + m_id = value; + } + } + + public string Name + { + get { return m_name; } + set { m_name = value; } + } + + public string Description + { + get { return m_description; } + set { m_description = value; } + } + + public DateTime CreationDate + { + get { return m_creation_date; } + set { m_creation_date = value; } + } + + public sbyte Type + { + get { return m_type; } + set { m_type = value; } + } + + public string ContentType + { + get + { + if (!String.IsNullOrEmpty(m_content_type)) + return m_content_type; + else + return SLUtil.SLAssetTypeToContentType(m_type); + } + set + { + m_content_type = value; + + sbyte type = (sbyte)SLUtil.ContentTypeToSLAssetType(value); + if (type != -1) + m_type = type; + } + } + + public byte[] SHA1 + { + get { return m_sha1; } + set { m_sha1 = value; } + } + + public bool Local + { + get { return m_local; } + set { m_local = value; } + } + + public bool Temporary + { + get { return m_temporary; } + set { m_temporary = value; } + } + + public string CreatorID + { + get { return m_creatorid; } + set { m_creatorid = value; } + } + + public AssetFlags Flags + { + get { return m_flags; } + set { m_flags = value; } + } + } +} diff --git a/OpenSim/Framework/AssetLandmark.cs b/OpenSim/Framework/AssetLandmark.cs new file mode 100644 index 0000000000..103f75676c --- /dev/null +++ b/OpenSim/Framework/AssetLandmark.cs @@ -0,0 +1,70 @@ +/* + * 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.Text; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class AssetLandmark : AssetBase + { + public Vector3 Position; + public ulong RegionHandle; + public UUID RegionID; + public string Gatekeeper = string.Empty; + public int Version; + + public AssetLandmark(AssetBase a) + : base(a.FullID, a.Name, a.Type, a.Metadata.CreatorID) + { + Data = a.Data; + Description = a.Description; + InternData(); + } + + private void InternData() + { + string temp = Util.UTF8.GetString(Data).Trim(); + string[] parts = temp.Split('\n'); + int.TryParse(parts[0].Substring(17, 1), out Version); + UUID.TryParse(parts[1].Substring(10, 36), out RegionID); + if (parts.Length >= 5) + Gatekeeper = parts[4].Replace("gatekeeper ", ""); + // The position is a vector with spaces as separators ("10.3 32.5 43"). + // Parse each scalar separately to take into account the system's culture setting. + string[] scalars = parts[2].Substring(10, parts[2].Length - 10).Split(' '); + if (scalars.Length > 0) + System.Single.TryParse(scalars[0], out Position.X); + if (scalars.Length > 1) + System.Single.TryParse(scalars[1], out Position.Y); + if (scalars.Length > 2) + System.Single.TryParse(scalars[2], out Position.Z); + + ulong.TryParse(parts[3].Substring(14, parts[3].Length - 14), out RegionHandle); + } + } +} diff --git a/OpenSim/Framework/AssetLoader/Filesystem/AssetLoaderFileSystem.cs b/OpenSim/Framework/AssetLoader/Filesystem/AssetLoaderFileSystem.cs new file mode 100644 index 0000000000..097ad7d6a3 --- /dev/null +++ b/OpenSim/Framework/AssetLoader/Filesystem/AssetLoaderFileSystem.cs @@ -0,0 +1,165 @@ +/* + * 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; + +/// +/// Loads assets from the filesystem location. Not yet a plugin, though it should be. +/// +namespace OpenSim.Framework.AssetLoader.Filesystem +{ + public class AssetLoaderFileSystem : IAssetLoader + { + private static readonly UUID LIBRARY_OWNER_ID = new UUID("11111111-1111-0000-0000-000100bba000"); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected static AssetBase CreateAsset(string assetIdStr, string name, string path, sbyte type) + { + AssetBase asset = new AssetBase(new UUID(assetIdStr), name, type, LIBRARY_OWNER_ID.ToString()); + + if (!String.IsNullOrEmpty(path)) + { + //m_log.InfoFormat("[ASSETS]: Loading: [{0}][{1}]", name, path); + + LoadAsset(asset, path); + } + else + { + m_log.InfoFormat("[ASSETS]: Instantiated: [{0}]", name); + } + + return asset; + } + + protected static void LoadAsset(AssetBase info, string path) + { +// bool image = +// (info.Type == (sbyte)AssetType.Texture || +// info.Type == (sbyte)AssetType.TextureTGA || +// info.Type == (sbyte)AssetType.ImageJPEG || +// info.Type == (sbyte)AssetType.ImageTGA); + + FileInfo fInfo = new FileInfo(path); + long numBytes = fInfo.Length; + if (fInfo.Exists) + { + FileStream fStream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] idata = new byte[numBytes]; + BinaryReader br = new BinaryReader(fStream); + idata = br.ReadBytes((int)numBytes); + br.Close(); + fStream.Close(); + info.Data = idata; + //info.loaded=true; + } + else + { + m_log.ErrorFormat("[ASSETS]: file: [{0}] not found !", path); + } + } + + public void ForEachDefaultXmlAsset(string assetSetFilename, Action action) + { + List assets = new List(); + if (File.Exists(assetSetFilename)) + { + string assetSetPath = "ERROR"; + string assetRootPath = ""; + try + { + XmlConfigSource source = new XmlConfigSource(assetSetFilename); + assetRootPath = Path.GetFullPath(source.SavePath); + assetRootPath = Path.GetDirectoryName(assetRootPath); + + for (int i = 0; i < source.Configs.Count; i++) + { + assetSetPath = source.Configs[i].GetString("file", String.Empty); + + LoadXmlAssetSet(Path.Combine(assetRootPath, assetSetPath), assets); + } + } + catch (XmlException e) + { + m_log.ErrorFormat("[ASSETS]: Error loading {0} : {1}", assetSetPath, e); + } + } + else + { + m_log.ErrorFormat("[ASSETS]: Asset set control file {0} does not exist! No assets loaded.", assetSetFilename); + } + + assets.ForEach(action); + } + + /// + /// Use the asset set information at path to load assets + /// + /// + /// + protected static void LoadXmlAssetSet(string assetSetPath, List assets) + { + //m_log.InfoFormat("[ASSETS]: Loading asset set {0}", assetSetPath); + + if (File.Exists(assetSetPath)) + { + try + { + XmlConfigSource source = new XmlConfigSource(assetSetPath); + String dir = Path.GetDirectoryName(assetSetPath); + + for (int i = 0; i < source.Configs.Count; i++) + { + string assetIdStr = source.Configs[i].GetString("assetID", UUID.Random().ToString()); + string name = source.Configs[i].GetString("name", String.Empty); + sbyte type = (sbyte)source.Configs[i].GetInt("assetType", 0); + string assetPath = Path.Combine(dir, source.Configs[i].GetString("fileName", String.Empty)); + + AssetBase newAsset = CreateAsset(assetIdStr, name, assetPath, type); + + newAsset.Type = type; + assets.Add(newAsset); + } + } + catch (XmlException e) + { + m_log.ErrorFormat("[ASSETS]: Error loading {0} : {1}", assetSetPath, e); + } + } + else + { + m_log.ErrorFormat("[ASSETS]: Asset set file {0} does not exist!", assetSetPath); + } + } + } +} diff --git a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cd182d6511 --- /dev/null +++ b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.AssetLoader.Filesystem")] +[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("8cb9cf69-4771-4d3a-a2ba-bac7230de326")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/AssetPermissions.cs b/OpenSim/Framework/AssetPermissions.cs new file mode 100644 index 0000000000..4a905c2f5d --- /dev/null +++ b/OpenSim/Framework/AssetPermissions.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +using Nini.Config; +using log4net; + +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class AssetPermissions + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private bool[] m_DisallowExport, m_DisallowImport; + private string[] m_AssetTypeNames; + + public AssetPermissions(IConfig config) + { + Type enumType = typeof(AssetType); + m_AssetTypeNames = Enum.GetNames(enumType); + for (int i = 0; i < m_AssetTypeNames.Length; i++) + m_AssetTypeNames[i] = m_AssetTypeNames[i].ToLower(); + int n = Enum.GetValues(enumType).Length; + m_DisallowExport = new bool[n]; + m_DisallowImport = new bool[n]; + + LoadPermsFromConfig(config, "DisallowExport", m_DisallowExport); + LoadPermsFromConfig(config, "DisallowImport", m_DisallowImport); + + } + + private void LoadPermsFromConfig(IConfig assetConfig, string variable, bool[] bitArray) + { + if (assetConfig == null) + return; + + string perms = assetConfig.GetString(variable, String.Empty); + string[] parts = perms.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string s in parts) + { + int index = Array.IndexOf(m_AssetTypeNames, s.Trim().ToLower()); + if (index >= 0) + bitArray[index] = true; + else + m_log.WarnFormat("[Asset Permissions]: Invalid AssetType {0}", s); + } + + } + + public bool AllowedExport(sbyte type) + { + string assetTypeName = ((AssetType)type).ToString(); + + int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower()); + if (index >= 0 && m_DisallowExport[index]) + { + m_log.DebugFormat("[Asset Permissions]: Export denied: configuration does not allow export of AssetType {0}", assetTypeName); + return false; + } + + return true; + } + + public bool AllowedImport(sbyte type) + { + string assetTypeName = ((AssetType)type).ToString(); + + int index = Array.IndexOf(m_AssetTypeNames, assetTypeName.ToLower()); + if (index >= 0 && m_DisallowImport[index]) + { + m_log.DebugFormat("[Asset Permissions]: Import denied: configuration does not allow import of AssetType {0}", assetTypeName); + return false; + } + + return true; + } + + + } +} diff --git a/OpenSim/Framework/AssetRequestToClient.cs b/OpenSim/Framework/AssetRequestToClient.cs new file mode 100644 index 0000000000..d602c1f9b6 --- /dev/null +++ b/OpenSim/Framework/AssetRequestToClient.cs @@ -0,0 +1,60 @@ +/* + * 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 +{ + /// + /// This class was created to refactor OutPacket out of AssetCache + /// There is a conflict between + /// OpenSim.Framework.Communications.Cache.AssetRequest and OpenSim.Framework.AssetRequest + /// and unifying them results in a prebuild chicken and egg problem with OpenSim.Framework requiring + /// OpenSim.Framework.Communications.Cache while OpenSim.Framework.Communications.Cache + /// requiring OpenSim.Framework + /// + public class AssetRequestToClient + { + public UUID RequestAssetID; + public AssetBase AssetInf; + public AssetBase ImageInfo; + public UUID TransferRequestID; + public long DataPointer = 0; + public int NumPackets = 0; + public int PacketCounter = 0; + public bool IsTextureRequest; + public byte AssetRequestSource = 2; + public byte[] Params = null; + //public bool AssetInCache; + //public int TimeRequested; + public int DiscardLevel = -1; + + public AssetRequestToClient() + { + } + } +} diff --git a/OpenSim/Framework/AuthenticateResponse.cs b/OpenSim/Framework/AuthenticateResponse.cs new file mode 100644 index 0000000000..bb346e4945 --- /dev/null +++ b/OpenSim/Framework/AuthenticateResponse.cs @@ -0,0 +1,39 @@ +/* + * 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 +{ + public class AuthenticateResponse + { + public bool Authorised; + public Login LoginInfo; + + public AuthenticateResponse() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs new file mode 100644 index 0000000000..69113b1d9c --- /dev/null +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -0,0 +1,1639 @@ +/* + * 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.Collections; +using System.Collections.Generic; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; + +namespace OpenSim.Framework +{ + /// + /// Contains the Avatar's Appearance and methods to manipulate the appearance. + /// + public class AvatarAppearance + { + // SL box diferent to size + const float AVBOXAJUST = 0.2f; + // constrains for ubitode physics + const float AVBOXMINX = 0.2f; + const float AVBOXMINY = 0.3f; + const float AVBOXMINZ = 1.2f; + + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // this is viewer capabilities and weared things dependent + // should be only used as initial default value ( V1 viewers ) + public readonly static int VISUALPARAM_COUNT = 218; + + public readonly static int TEXTURE_COUNT = 21; + public readonly static byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; + + protected int m_serial = 0; + protected byte[] m_visualparams; + protected Primitive.TextureEntry m_texture; + protected AvatarWearable[] m_wearables; + protected Dictionary> m_attachments; + protected float m_avatarHeight = 0; + protected Vector3 m_avatarSize = new Vector3(0.45f, 0.6f, 1.9f); // sl Z cloud value + protected Vector3 m_avatarBoxSize = new Vector3(0.45f, 0.6f, 1.9f); + protected float m_avatarFeetOffset = 0; + protected float m_avatarAnimOffset = 0; + protected WearableCacheItem[] m_cacheitems; + protected bool m_cacheItemsDirty = true; + + public virtual int Serial + { + get { return m_serial; } + set { m_serial = value; } + } + + public virtual byte[] VisualParams + { + get { return m_visualparams; } + set { m_visualparams = value; } + } + + public virtual Vector3 AvatarSize + { + get { return m_avatarSize; } + } + + public virtual Vector3 AvatarBoxSize + { + get { return m_avatarBoxSize; } + } + + public virtual float AvatarFeetOffset + { + get { return m_avatarFeetOffset + m_avatarAnimOffset; } + } + + public virtual Primitive.TextureEntry Texture + { + get { return m_texture; } + set + { +// m_log.DebugFormat("[AVATAR APPEARANCE]: Set TextureEntry to {0}", value); + m_texture = value; + } + } + + public virtual AvatarWearable[] Wearables + { + get { return m_wearables; } + set { m_wearables = value; } + } + + public virtual float AvatarHeight + { + get { return m_avatarHeight; } + set { m_avatarHeight = value; } + } + + public virtual WearableCacheItem[] WearableCacheItems + { + get { return m_cacheitems; } + set { m_cacheitems = value; } + } + + public virtual bool WearableCacheItemsDirty + { + get { return m_cacheItemsDirty; } + set { m_cacheItemsDirty = value; } + } + + public AvatarAppearance() + { +// m_log.WarnFormat("[AVATAR APPEARANCE]: create empty appearance"); + + m_serial = 0; + SetDefaultWearables(); + SetDefaultTexture(); + SetDefaultParams(); +// SetHeight(); + SetSize(new Vector3(0.45f,0.6f,1.9f)); + m_attachments = new Dictionary>(); + } + + public AvatarAppearance(OSDMap map) + { +// m_log.WarnFormat("[AVATAR APPEARANCE]: create appearance from OSDMap"); + + Unpack(map); +// SetHeight(); done in Unpack + } + + public AvatarAppearance(AvatarWearable[] wearables, Primitive.TextureEntry textureEntry, byte[] visualParams) + { +// m_log.WarnFormat("[AVATAR APPEARANCE] create initialized appearance"); + + m_serial = 0; + + if (wearables != null) + m_wearables = wearables; + else + SetDefaultWearables(); + + if (textureEntry != null) + m_texture = textureEntry; + else + SetDefaultTexture(); + + if (visualParams != null) + m_visualparams = visualParams; + else + SetDefaultParams(); + +// SetHeight(); + if(m_avatarHeight == 0) + SetSize(new Vector3(0.45f,0.6f,1.9f)); + + m_attachments = new Dictionary>(); + } + + public AvatarAppearance(AvatarAppearance appearance) : this(appearance, true) + { + } + + public AvatarAppearance(AvatarAppearance appearance, bool copyWearables) + { +// m_log.WarnFormat("[AVATAR APPEARANCE] create from an existing appearance"); + + if (appearance == null) + { + m_serial = 0; + SetDefaultWearables(); + SetDefaultTexture(); + SetDefaultParams(); +// SetHeight(); + SetSize(new Vector3(0.45f, 0.6f, 1.9f)); + m_attachments = new Dictionary>(); + + return; + } + + m_serial = appearance.Serial; + + m_wearables = new AvatarWearable[AvatarWearable.MAX_WEARABLES]; + for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + m_wearables[i] = new AvatarWearable(); + + if (copyWearables && (appearance.Wearables != null)) + { + for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + SetWearable(i,appearance.Wearables[i]); + } + + m_texture = null; + if (appearance.Texture != null) + { + byte[] tbytes = appearance.Texture.GetBytes(); + m_texture = new Primitive.TextureEntry(tbytes,0,tbytes.Length); + } + + m_visualparams = null; + if (appearance.VisualParams != null) + m_visualparams = (byte[])appearance.VisualParams.Clone(); + +// m_avatarHeight = appearance.m_avatarHeight; + SetSize(appearance.AvatarSize); + + // Copy the attachment, force append mode since that ensures consistency + m_attachments = new Dictionary>(); + foreach (AvatarAttachment attachment in appearance.GetAttachments()) + AppendAttachment(new AvatarAttachment(attachment)); + } + + public void GetAssetsFrom(AvatarAppearance app) + { + for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + { + for (int j = 0; j < m_wearables[i].Count; j++) + { + UUID itemID = m_wearables[i][j].ItemID; + UUID assetID = app.Wearables[i].GetAsset(itemID); + + if (assetID != UUID.Zero) + m_wearables[i].Add(itemID, assetID); + } + } + } + + public void ClearWearables() + { + m_wearables = new AvatarWearable[AvatarWearable.MAX_WEARABLES]; + for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + m_wearables[i] = new AvatarWearable(); + } + + protected virtual void SetDefaultWearables() + { + m_wearables = AvatarWearable.DefaultWearables; + } + + /// + /// Invalidate all of the baked textures in the appearance, useful + /// if you know that none are valid + /// + public virtual void ResetAppearance() + { +// m_log.WarnFormat("[AVATAR APPEARANCE]: Reset appearance"); + + m_serial = 0; + + SetDefaultTexture(); + + //for (int i = 0; i < BAKE_INDICES.Length; i++) + // { + // int idx = BAKE_INDICES[i]; + // m_texture.FaceTextures[idx].TextureID = UUID.Zero; + // } + } + + protected virtual void SetDefaultParams() + { + m_visualparams = new byte[] { 33,61,85,23,58,127,63,85,63,42,0,85,63,36,85,95,153,63,34,0,63,109,88,132,63,136,81,85,103,136,127,0,150,150,150,127,0,0,0,0,0,127,0,0,255,127,114,127,99,63,127,140,127,127,0,0,0,191,0,104,0,0,0,0,0,0,0,0,0,145,216,133,0,127,0,127,170,0,0,127,127,109,85,127,127,63,85,42,150,150,150,150,150,150,150,25,150,150,150,0,127,0,0,144,85,127,132,127,85,0,127,127,127,127,127,127,59,127,85,127,127,106,47,79,127,127,204,2,141,66,0,0,127,127,0,0,0,0,127,0,159,0,0,178,127,36,85,131,127,127,127,153,95,0,140,75,27,127,127,0,150,150,198,0,0,63,30,127,165,209,198,127,127,153,204,51,51,255,255,255,204,0,255,150,150,150,150,150,150,150,150,150,150,0,150,150,150,150,150,0,127,127,150,150,150,150,150,150,150,150,0,0,150,51,132,150,150,150 }; +// for (int i = 0; i < VISUALPARAM_COUNT; i++) +// { +// m_visualparams[i] = 150; +// } + } + + /// + /// Invalidate all of the baked textures in the appearance, useful + /// if you know that none are valid + /// + public virtual void ResetBakedTextures() + { + SetDefaultTexture(); + + //for (int i = 0; i < BAKE_INDICES.Length; i++) + // { + // int idx = BAKE_INDICES[i]; + // m_texture.FaceTextures[idx].TextureID = UUID.Zero; + // } + } + + protected virtual void SetDefaultTexture() + { + m_texture = new Primitive.TextureEntry(new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE)); + + // for (uint i = 0; i < TEXTURE_COUNT; i++) + // m_texture.CreateFace(i).TextureID = new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE); + } + + /// + /// Set up appearance texture ids. + /// + /// + /// True if any existing texture id was changed by the new data. + /// False if there were no changes or no existing texture ids. + /// + public virtual bool SetTextureEntries(Primitive.TextureEntry textureEntry) + { + if (textureEntry == null) + return false; + + // There are much simpler versions of this copy that could be + // made. We determine if any of the textures actually + // changed to know if the appearance should be saved later + bool changed = false; + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + { + Primitive.TextureEntryFace newface = textureEntry.FaceTextures[i]; + Primitive.TextureEntryFace oldface = m_texture.FaceTextures[i]; + + if (newface == null) + { + if (oldface == null) + continue; + } + else + { + if (oldface != null && oldface.TextureID == newface.TextureID) + continue; + } + + changed = true; + } + + m_texture = textureEntry; + + return changed; + } + + /// + /// Set up visual parameters for the avatar and refresh the avatar height + /// + /// + /// True if any existing visual parameter was changed by the new data. + /// False if there were no changes or no existing visual parameters. + /// + public virtual bool SetVisualParams(byte[] visualParams) + { + if (visualParams == null) + return false; + + // There are much simpler versions of this copy that could be + // made. We determine if any of the visual parameters actually + // changed to know if the appearance should be saved later + bool changed = false; + + int newsize = visualParams.Length; + + if (newsize != m_visualparams.Length) + { + changed = true; + m_visualparams = (byte[])visualParams.Clone(); + } + else + { + + for (int i = 0; i < newsize; i++) + { + if (visualParams[i] != m_visualparams[i]) + { + // DEBUG ON + // m_log.WarnFormat("[AVATARAPPEARANCE] vparams changed [{0}] {1} ==> {2}", + // i,m_visualparams[i],visualParams[i]); + // DEBUG OFF + m_visualparams[i] = visualParams[i]; + changed = true; + } + } + } + // Reset the height if the visual parameters actually changed +// if (changed) +// SetHeight(); + + return changed; + } + + public virtual void SetAppearance(Primitive.TextureEntry textureEntry, byte[] visualParams) + { + SetTextureEntries(textureEntry); + SetVisualParams(visualParams); + } + + /// + /// Set avatar height by a calculation based on their visual parameters. + /// + public virtual void SetHeight() + { +/* + // Start with shortest possible female avatar height + m_avatarHeight = 1.14597f; + // Add offset for male avatars + if (m_visualparams[(int)VPElement.SHAPE_MALE] != 0) + m_avatarHeight += 0.0848f; + // Add offsets for visual params + m_avatarHeight += 0.516945f * (float)m_visualparams[(int)VPElement.SHAPE_HEIGHT] / 255.0f + + 0.08117f * (float)m_visualparams[(int)VPElement.SHAPE_HEAD_SIZE] / 255.0f + + 0.3836f * (float)m_visualparams[(int)VPElement.SHAPE_LEG_LENGTH] / 255.0f + + 0.07f * (float)m_visualparams[(int)VPElement.SHOES_PLATFORM_HEIGHT] / 255.0f + + 0.08f * (float)m_visualparams[(int)VPElement.SHOES_HEEL_HEIGHT] / 255.0f + + 0.076f * (float)m_visualparams[(int)VPElement.SHAPE_NECK_LENGTH] / 255.0f; +*/ + } + + public void SetSize(Vector3 avSize) + { + if (avSize.X > 32f) + avSize.X = 32f; + else if (avSize.X < 0.1f) + avSize.X = 0.1f; + + if (avSize.Y > 32f) + avSize.Y = 32f; + else if (avSize.Y < 0.1f) + avSize.Y = 0.1f; + if (avSize.Z > 32f) + avSize.Z = 32f; + else if (avSize.Z < 0.1f) + avSize.Z = 0.1f; + + m_avatarSize = avSize; + m_avatarBoxSize = avSize; + m_avatarBoxSize.Z += AVBOXAJUST; + if (m_avatarBoxSize.X < AVBOXMINX) + m_avatarBoxSize.X = AVBOXMINX; + if (m_avatarBoxSize.Y < AVBOXMINY) + m_avatarBoxSize.Y = AVBOXMINY; + if (m_avatarBoxSize.Z < AVBOXMINZ) + m_avatarBoxSize.Z = AVBOXMINZ; + m_avatarHeight = m_avatarSize.Z; + } + + public virtual void SetWearable(int wearableId, AvatarWearable wearable) + { +// DEBUG ON +// m_log.WarnFormat("[AVATARAPPEARANCE] set wearable {0} --> {1}:{2}",wearableId,wearable.ItemID,wearable.AssetID); +// DEBUG OFF + m_wearables[wearableId].Clear(); + for (int i = 0; i < wearable.Count; i++) + m_wearables[wearableId].Add(wearable[i].ItemID, wearable[i].AssetID); + } + +// DEBUG ON + public override String ToString() + { + String s = ""; + + s += String.Format("Serial: {0}\n",m_serial); + + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + if (m_texture.FaceTextures[i] != null) + s += String.Format("Texture: {0} --> {1}\n",i,m_texture.FaceTextures[i].TextureID); + + foreach (AvatarWearable awear in m_wearables) + { + for (int i = 0; i < awear.Count; i++) + s += String.Format("Wearable: item={0}, asset={1}\n",awear[i].ItemID,awear[i].AssetID); + } + + s += "Visual Params: "; + // for (uint j = 0; j < AvatarAppearance.VISUALPARAM_COUNT; j++) + for (uint j = 0; j < m_visualparams.Length; j++) + s += String.Format("{0},",m_visualparams[j]); + s += "\n"; + + return s; + } +// DEBUG OFF + + /// + /// Get a list of the attachments. + /// + /// + /// There may be duplicate attachpoints + /// + public List GetAttachments() + { + lock (m_attachments) + { + List alist = new List(); + foreach (KeyValuePair> kvp in m_attachments) + { + foreach (AvatarAttachment attach in kvp.Value) + alist.Add(new AvatarAttachment(attach)); + } + return alist; + } + } + + internal void AppendAttachment(AvatarAttachment attach) + { +// m_log.DebugFormat( +// "[AVATAR APPEARNCE]: Appending itemID={0}, assetID={1} at {2}", +// attach.ItemID, attach.AssetID, attach.AttachPoint); + + lock (m_attachments) + { + if (!m_attachments.ContainsKey(attach.AttachPoint)) + m_attachments[attach.AttachPoint] = new List(); + + foreach (AvatarAttachment prev in m_attachments[attach.AttachPoint]) + { + if (prev.ItemID == attach.ItemID) + return; + } + + m_attachments[attach.AttachPoint].Add(attach); + } + } + + internal void ReplaceAttachment(AvatarAttachment attach) + { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Replacing itemID={0}, assetID={1} at {2}", +// attach.ItemID, attach.AssetID, attach.AttachPoint); + + lock (m_attachments) + { + m_attachments[attach.AttachPoint] = new List(); + m_attachments[attach.AttachPoint].Add(attach); + } + } + + /// + /// Set an attachment + /// + /// + /// If the attachpoint has the + /// 0x80 bit set then we assume this is an append + /// operation otherwise we replace whatever is + /// currently attached at the attachpoint + /// + /// + /// If UUID.Zero, then an any attachment at the attachpoint is removed. + /// + /// + /// return true if something actually changed + /// + public bool SetAttachment(int attachpoint, UUID item, UUID asset) + { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Setting attachment at {0} with item ID {1}, asset ID {2}", +// attachpoint, item, asset); + + if (attachpoint == 0) + return false; + + lock (m_attachments) + { + if (item == UUID.Zero) + { + if (m_attachments.ContainsKey(attachpoint)) + { + m_attachments.Remove(attachpoint); + return true; + } + + return false; + } + + // When a user logs in, the attachment item ids are pulled from persistence in the Avatars table. However, + // the asset ids are not saved. When the avatar enters a simulator the attachments are set again. If + // we simply perform an item check here then the asset ids (which are now present) are never set, and NPC attachments + // later fail unless the attachment is detached and reattached. + // + // Therefore, we will carry on with the set if the existing attachment has no asset id. + AvatarAttachment existingAttachment = GetAttachmentForItem(item); + if (existingAttachment != null) + { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Found existing attachment for {0}, asset {1} at point {2}", +// existingAttachment.ItemID, existingAttachment.AssetID, existingAttachment.AttachPoint); + + if (existingAttachment.AssetID != UUID.Zero && existingAttachment.AttachPoint == (attachpoint & 0x7F)) + { + m_log.DebugFormat( + "[AVATAR APPEARANCE]: Ignoring attempt to attach an already attached item {0} at point {1}", + item, attachpoint); + + return false; + } + else + { + // Remove it here so that the later append does not add a second attachment but we still update + // the assetID + DetachAttachment(existingAttachment.ItemID); + } + } + + // check if this is an append or a replace, 0x80 marks it as an append + if ((attachpoint & 0x80) > 0) + { + // strip the append bit + int point = attachpoint & 0x7F; + AppendAttachment(new AvatarAttachment(point, item, asset)); + } + else + { + ReplaceAttachment(new AvatarAttachment(attachpoint,item, asset)); + } + } + + return true; + } + + /// + /// If the item is already attached, return it. + /// + /// + /// Returns null if this item is not attached. + public AvatarAttachment GetAttachmentForItem(UUID itemID) + { + lock (m_attachments) + { + foreach (KeyValuePair> kvp in m_attachments) + { + int index = kvp.Value.FindIndex(delegate(AvatarAttachment a) { return a.ItemID == itemID; }); + if (index >= 0) + return kvp.Value[index]; + } + } + + return null; + } + + public int GetAttachpoint(UUID itemID) + { + lock (m_attachments) + { + foreach (KeyValuePair> kvp in m_attachments) + { + int index = kvp.Value.FindIndex(delegate(AvatarAttachment a) { return a.ItemID == itemID; }); + if (index >= 0) + return kvp.Key; + } + } + return 0; + } + + public bool DetachAttachment(UUID itemID) + { + lock (m_attachments) + { + foreach (KeyValuePair> kvp in m_attachments) + { + int index = kvp.Value.FindIndex(delegate(AvatarAttachment a) { return a.ItemID == itemID; }); + if (index >= 0) + { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Detaching attachment {0}, index {1}, point {2}", +// m_attachments[kvp.Key][index].ItemID, index, m_attachments[kvp.Key][index].AttachPoint); + + // Remove it from the list of attachments at that attach point + m_attachments[kvp.Key].RemoveAt(index); + + // And remove the list if there are no more attachments here + if (m_attachments[kvp.Key].Count == 0) + m_attachments.Remove(kvp.Key); + + return true; + } + } + } + + return false; + } + + public void ClearAttachments() + { + lock (m_attachments) + m_attachments.Clear(); + } + + #region Packing Functions + + /// + /// Create an OSDMap from the appearance data + /// + public OSDMap Pack() + { + OSDMap data = new OSDMap(); + + data["serial"] = OSD.FromInteger(m_serial); + data["height"] = OSD.FromReal(m_avatarHeight); + + // Wearables + OSDArray wears = new OSDArray(AvatarWearable.MAX_WEARABLES); + for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + wears.Add(m_wearables[i].Pack()); + data["wearables"] = wears; + + // Avatar Textures + OSDArray textures = new OSDArray(AvatarAppearance.TEXTURE_COUNT); + for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++) + { + if (m_texture.FaceTextures[i] != null) + textures.Add(OSD.FromUUID(m_texture.FaceTextures[i].TextureID)); + else + textures.Add(OSD.FromUUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE)); + } + data["textures"] = textures; + + // Visual Parameters + OSDBinary visualparams = new OSDBinary(m_visualparams); + data["visualparams"] = visualparams; + + lock (m_attachments) + { + // Attachments + OSDArray attachs = new OSDArray(m_attachments.Count); + foreach (AvatarAttachment attach in GetAttachments()) + attachs.Add(attach.Pack()); + data["attachments"] = attachs; + } + + return data; + } + + /// + /// Unpack and OSDMap and initialize the appearance + /// from it + /// + public void Unpack(OSDMap data) + { + if ((data != null) && (data["serial"] != null)) + m_serial = data["serial"].AsInteger(); + if ((data != null) && (data["height"] != null)) +// m_avatarHeight = (float)data["height"].AsReal(); + SetSize(new Vector3(0.45f,0.6f, (float)data["height"].AsReal())); + + try + { + // Wearables + SetDefaultWearables(); + if ((data != null) && (data["wearables"] != null) && (data["wearables"]).Type == OSDType.Array) + { + OSDArray wears = (OSDArray)(data["wearables"]); + for (int i = 0; i < wears.Count; i++) + m_wearables[i] = new AvatarWearable((OSDArray)wears[i]); + } + else + { + m_log.Warn("[AVATAR APPEARANCE]: failed to unpack wearables"); + } + + // Avatar Textures + SetDefaultTexture(); + if ((data != null) && (data["textures"] != null) && (data["textures"]).Type == OSDType.Array) + { + OSDArray textures = (OSDArray)(data["textures"]); + for (int i = 0; i < AvatarAppearance.TEXTURE_COUNT && i < textures.Count; i++) + { + UUID textureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE; + if (textures[i] != null) + textureID = textures[i].AsUUID(); + m_texture.CreateFace((uint)i).TextureID = new UUID(textureID); + } + } + else + { + m_log.Warn("[AVATAR APPEARANCE]: failed to unpack textures"); + } + + // Visual Parameters + SetDefaultParams(); + if ((data != null) && (data["visualparams"] != null)) + { + if ((data["visualparams"].Type == OSDType.Binary) || (data["visualparams"].Type == OSDType.Array)) + m_visualparams = data["visualparams"].AsBinary(); + } + else + { + m_log.Warn("[AVATAR APPEARANCE]: failed to unpack visual parameters"); + } + + // Attachments + m_attachments = new Dictionary>(); + if ((data != null) && (data["attachments"] != null) && (data["attachments"]).Type == OSDType.Array) + { + OSDArray attachs = (OSDArray)(data["attachments"]); + for (int i = 0; i < attachs.Count; i++) + { + AvatarAttachment att = new AvatarAttachment((OSDMap)attachs[i]); + AppendAttachment(att); + +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Unpacked attachment itemID {0}, assetID {1}, point {2}", +// att.ItemID, att.AssetID, att.AttachPoint); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("[AVATAR APPEARANCE]: unpack failed badly: {0}{1}", e.Message, e.StackTrace); + } + } + + #endregion + + #region VPElement + + /// + /// Viewer Params Array Element for AgentSetAppearance + /// Generated from LibOMV's Visual Params list + /// + public enum VPElement : int + { + /// + /// Brow Size - Small 0--+255 Large + /// + SHAPE_BIG_BROW = 0, + /// + /// Nose Size - Small 0--+255 Large + /// + SHAPE_NOSE_BIG_OUT = 1, + /// + /// Nostril Width - Narrow 0--+255 Broad + /// + SHAPE_BROAD_NOSTRILS = 2, + /// + /// Chin Cleft - Round 0--+255 Cleft + /// + SHAPE_CLEFT_CHIN = 3, + /// + /// Nose Tip Shape - Pointy 0--+255 Bulbous + /// + SHAPE_BULBOUS_NOSE_TIP = 4, + /// + /// Chin Angle - Chin Out 0--+255 Chin In + /// + SHAPE_WEAK_CHIN = 5, + /// + /// Chin-Neck - Tight Chin 0--+255 Double Chin + /// + SHAPE_DOUBLE_CHIN = 6, + /// + /// Lower Cheeks - Well-Fed 0--+255 Sunken + /// + SHAPE_SUNKEN_CHEEKS = 7, + /// + /// Upper Bridge - Low 0--+255 High + /// + SHAPE_NOBLE_NOSE_BRIDGE = 8, + /// + /// - Less 0--+255 More + /// + SHAPE_JOWLS = 9, + /// + /// Upper Chin Cleft - Round 0--+255 Cleft + /// + SHAPE_CLEFT_CHIN_UPPER = 10, + /// + /// Cheek Bones - Low 0--+255 High + /// + SHAPE_HIGH_CHEEK_BONES = 11, + /// + /// Ear Angle - In 0--+255 Out + /// + SHAPE_EARS_OUT = 12, + /// + /// Eyebrow Points - Smooth 0--+255 Pointy + /// + HAIR_POINTY_EYEBROWS = 13, + /// + /// Jaw Shape - Pointy 0--+255 Square + /// + SHAPE_SQUARE_JAW = 14, + /// + /// Upper Cheeks - Thin 0--+255 Puffy + /// + SHAPE_PUFFY_UPPER_CHEEKS = 15, + /// + /// Nose Tip Angle - Downturned 0--+255 Upturned + /// + SHAPE_UPTURNED_NOSE_TIP = 16, + /// + /// Nose Thickness - Thin Nose 0--+255 Bulbous Nose + /// + SHAPE_BULBOUS_NOSE = 17, + /// + /// Upper Eyelid Fold - Uncreased 0--+255 Creased + /// + SHAPE_UPPER_EYELID_FOLD = 18, + /// + /// Attached Earlobes - Unattached 0--+255 Attached + /// + SHAPE_ATTACHED_EARLOBES = 19, + /// + /// Eye Bags - Smooth 0--+255 Baggy + /// + SHAPE_BAGGY_EYES = 20, + /// + /// Eye Opening - Narrow 0--+255 Wide + /// + SHAPE_WIDE_EYES = 21, + /// + /// Lip Cleft - Narrow 0--+255 Wide + /// + SHAPE_WIDE_LIP_CLEFT = 22, + /// + /// Bridge Width - Narrow 0--+255 Wide + /// + SHAPE_WIDE_NOSE_BRIDGE = 23, + /// + /// Eyebrow Arc - Flat 0--+255 Arced + /// + HAIR_ARCED_EYEBROWS = 24, + /// + /// Height - Short 0--+255 Tall + /// + SHAPE_HEIGHT = 25, + /// + /// Body Thickness - Body Thin 0--+255 Body Thick + /// + SHAPE_THICKNESS = 26, + /// + /// Ear Size - Small 0--+255 Large + /// + SHAPE_BIG_EARS = 27, + /// + /// Shoulders - Narrow 0--+255 Broad + /// + SHAPE_SHOULDERS = 28, + /// + /// Hip Width - Narrow 0--+255 Wide + /// + SHAPE_HIP_WIDTH = 29, + /// + /// - Short Torso 0--+255 Long Torso + /// + SHAPE_TORSO_LENGTH = 30, + SHAPE_MALE = 31, + /// + /// - Short 0--+255 Long + /// + GLOVES_GLOVE_LENGTH = 32, + /// + /// - Darker 0--+255 Lighter + /// + EYES_EYE_LIGHTNESS = 33, + /// + /// - Natural 0--+255 Unnatural + /// + EYES_EYE_COLOR = 34, + /// + /// - Small 0--+255 Large + /// + SHAPE_BREAST_SIZE = 35, + /// + /// - None 0--+255 Wild + /// + SKIN_RAINBOW_COLOR = 36, + /// + /// Ruddiness - Pale 0--+255 Ruddy + /// + SKIN_RED_SKIN = 37, + /// + /// - Light 0--+255 Dark + /// + SKIN_PIGMENT = 38, + HAIR_RAINBOW_COLOR_39 = 39, + /// + /// - No Red 0--+255 Very Red + /// + HAIR_RED_HAIR = 40, + /// + /// - Black 0--+255 Blonde + /// + HAIR_BLONDE_HAIR = 41, + /// + /// - No White 0--+255 All White + /// + HAIR_WHITE_HAIR = 42, + /// + /// - Less Rosy 0--+255 More Rosy + /// + SKIN_ROSY_COMPLEXION = 43, + /// + /// - Darker 0--+255 Pinker + /// + SKIN_LIP_PINKNESS = 44, + /// + /// - Thin Eyebrows 0--+255 Bushy Eyebrows + /// + HAIR_EYEBROW_SIZE = 45, + /// + /// - Short 0--+255 Long + /// + HAIR_FRONT_FRINGE = 46, + /// + /// - Short 0--+255 Long + /// + HAIR_SIDE_FRINGE = 47, + /// + /// - Short 0--+255 Long + /// + HAIR_BACK_FRINGE = 48, + /// + /// - Short 0--+255 Long + /// + HAIR_HAIR_FRONT = 49, + /// + /// - Short 0--+255 Long + /// + HAIR_HAIR_SIDES = 50, + /// + /// - Short 0--+255 Long + /// + HAIR_HAIR_BACK = 51, + /// + /// - Sweep Forward 0--+255 Sweep Back + /// + HAIR_HAIR_SWEEP = 52, + /// + /// - Left 0--+255 Right + /// + HAIR_HAIR_TILT = 53, + /// + /// Middle Part - No Part 0--+255 Part + /// + HAIR_HAIR_PART_MIDDLE = 54, + /// + /// Right Part - No Part 0--+255 Part + /// + HAIR_HAIR_PART_RIGHT = 55, + /// + /// Left Part - No Part 0--+255 Part + /// + HAIR_HAIR_PART_LEFT = 56, + /// + /// Full Hair Sides - Mowhawk 0--+255 Full Sides + /// + HAIR_HAIR_SIDES_FULL = 57, + /// + /// - Less 0--+255 More + /// + SKIN_BODY_DEFINITION = 58, + /// + /// Lip Width - Narrow Lips 0--+255 Wide Lips + /// + SHAPE_LIP_WIDTH = 59, + /// + /// - Small 0--+255 Big + /// + SHAPE_BELLY_SIZE = 60, + /// + /// - Less 0--+255 More + /// + SKIN_FACIAL_DEFINITION = 61, + /// + /// - Less 0--+255 More + /// + SKIN_WRINKLES = 62, + /// + /// - Less 0--+255 More + /// + SKIN_FRECKLES = 63, + /// + /// - Short Sideburns 0--+255 Mutton Chops + /// + HAIR_SIDEBURNS = 64, + /// + /// - Chaplin 0--+255 Handlebars + /// + HAIR_MOUSTACHE = 65, + /// + /// - Less soul 0--+255 More soul + /// + HAIR_SOULPATCH = 66, + /// + /// - Less Curtains 0--+255 More Curtains + /// + HAIR_CHIN_CURTAINS = 67, + /// + /// Rumpled Hair - Smooth Hair 0--+255 Rumpled Hair + /// + HAIR_HAIR_RUMPLED = 68, + /// + /// Big Hair Front - Less 0--+255 More + /// + HAIR_HAIR_BIG_FRONT = 69, + /// + /// Big Hair Top - Less 0--+255 More + /// + HAIR_HAIR_BIG_TOP = 70, + /// + /// Big Hair Back - Less 0--+255 More + /// + HAIR_HAIR_BIG_BACK = 71, + /// + /// Spiked Hair - No Spikes 0--+255 Big Spikes + /// + HAIR_HAIR_SPIKED = 72, + /// + /// Chin Depth - Shallow 0--+255 Deep + /// + SHAPE_DEEP_CHIN = 73, + /// + /// Part Bangs - No Part 0--+255 Part Bangs + /// + HAIR_BANGS_PART_MIDDLE = 74, + /// + /// Head Shape - More Square 0--+255 More Round + /// + SHAPE_HEAD_SHAPE = 75, + /// + /// Eye Spacing - Close Set Eyes 0--+255 Far Set Eyes + /// + SHAPE_EYE_SPACING = 76, + /// + /// - Low Heels 0--+255 High Heels + /// + SHOES_HEEL_HEIGHT = 77, + /// + /// - Low Platforms 0--+255 High Platforms + /// + SHOES_PLATFORM_HEIGHT = 78, + /// + /// - Thin Lips 0--+255 Fat Lips + /// + SHAPE_LIP_THICKNESS = 79, + /// + /// Mouth Position - High 0--+255 Low + /// + SHAPE_MOUTH_HEIGHT = 80, + /// + /// Breast Buoyancy - Less Gravity 0--+255 More Gravity + /// + SHAPE_BREAST_GRAVITY = 81, + /// + /// Platform Width - Narrow 0--+255 Wide + /// + SHOES_SHOE_PLATFORM_WIDTH = 82, + /// + /// - Pointy Heels 0--+255 Thick Heels + /// + SHOES_HEEL_SHAPE = 83, + /// + /// - Pointy 0--+255 Square + /// + SHOES_TOE_SHAPE = 84, + /// + /// Foot Size - Small 0--+255 Big + /// + SHAPE_FOOT_SIZE = 85, + /// + /// Nose Width - Narrow 0--+255 Wide + /// + SHAPE_WIDE_NOSE = 86, + /// + /// Eyelash Length - Short 0--+255 Long + /// + SHAPE_EYELASHES_LONG = 87, + /// + /// - Short 0--+255 Long + /// + UNDERSHIRT_SLEEVE_LENGTH = 88, + /// + /// - Short 0--+255 Long + /// + UNDERSHIRT_BOTTOM = 89, + /// + /// - Low 0--+255 High + /// + UNDERSHIRT_COLLAR_FRONT = 90, + JACKET_SLEEVE_LENGTH_91 = 91, + JACKET_COLLAR_FRONT_92 = 92, + /// + /// Jacket Length - Short 0--+255 Long + /// + JACKET_BOTTOM_LENGTH_LOWER = 93, + /// + /// Open Front - Open 0--+255 Closed + /// + JACKET_OPEN_JACKET = 94, + /// + /// - Short 0--+255 Tall + /// + SHOES_SHOE_HEIGHT = 95, + /// + /// - Short 0--+255 Long + /// + SOCKS_SOCKS_LENGTH = 96, + /// + /// - Short 0--+255 Long + /// + UNDERPANTS_PANTS_LENGTH = 97, + /// + /// - Low 0--+255 High + /// + UNDERPANTS_PANTS_WAIST = 98, + /// + /// Cuff Flare - Tight Cuffs 0--+255 Flared Cuffs + /// + PANTS_LEG_PANTFLAIR = 99, + /// + /// - More Vertical 0--+255 More Sloped + /// + SHAPE_FOREHEAD_ANGLE = 100, + /// + /// - Less Body Fat 0--+255 More Body Fat + /// + SHAPE_BODY_FAT = 101, + /// + /// Pants Crotch - High and Tight 0--+255 Low and Loose + /// + PANTS_LOW_CROTCH = 102, + /// + /// Egg Head - Chin Heavy 0--+255 Forehead Heavy + /// + SHAPE_EGG_HEAD = 103, + /// + /// Head Stretch - Squash Head 0--+255 Stretch Head + /// + SHAPE_SQUASH_STRETCH_HEAD = 104, + /// + /// Torso Muscles - Less Muscular 0--+255 More Muscular + /// + SHAPE_TORSO_MUSCLES = 105, + /// + /// Outer Eye Corner - Corner Down 0--+255 Corner Up + /// + SHAPE_EYELID_CORNER_UP = 106, + /// + /// - Less Muscular 0--+255 More Muscular + /// + SHAPE_LEG_MUSCLES = 107, + /// + /// Lip Fullness - Less Full 0--+255 More Full + /// + SHAPE_TALL_LIPS = 108, + /// + /// Toe Thickness - Flat Toe 0--+255 Thick Toe + /// + SHOES_SHOE_TOE_THICK = 109, + /// + /// Crooked Nose - Nose Left 0--+255 Nose Right + /// + SHAPE_CROOKED_NOSE = 110, + /// + /// - Corner Down 0--+255 Corner Up + /// + SHAPE_MOUTH_CORNER = 111, + /// + /// - Shear Right Up 0--+255 Shear Left Up + /// + SHAPE_FACE_SHEAR = 112, + /// + /// Shift Mouth - Shift Left 0--+255 Shift Right + /// + SHAPE_SHIFT_MOUTH = 113, + /// + /// Eye Pop - Pop Right Eye 0--+255 Pop Left Eye + /// + SHAPE_POP_EYE = 114, + /// + /// Jaw Jut - Overbite 0--+255 Underbite + /// + SHAPE_JAW_JUT = 115, + /// + /// Shear Back - Full Back 0--+255 Sheared Back + /// + HAIR_HAIR_SHEAR_BACK = 116, + /// + /// - Small Hands 0--+255 Large Hands + /// + SHAPE_HAND_SIZE = 117, + /// + /// Love Handles - Less Love 0--+255 More Love + /// + SHAPE_LOVE_HANDLES = 118, + SHAPE_TORSO_MUSCLES_119 = 119, + /// + /// Head Size - Small Head 0--+255 Big Head + /// + SHAPE_HEAD_SIZE = 120, + /// + /// - Skinny Neck 0--+255 Thick Neck + /// + SHAPE_NECK_THICKNESS = 121, + /// + /// Breast Cleavage - Separate 0--+255 Join + /// + SHAPE_BREAST_FEMALE_CLEAVAGE = 122, + /// + /// Pectorals - Big Pectorals 0--+255 Sunken Chest + /// + SHAPE_CHEST_MALE_NO_PECS = 123, + /// + /// Eye Size - Beady Eyes 0--+255 Anime Eyes + /// + SHAPE_EYE_SIZE = 124, + /// + /// - Short Legs 0--+255 Long Legs + /// + SHAPE_LEG_LENGTH = 125, + /// + /// - Short Arms 0--+255 Long arms + /// + SHAPE_ARM_LENGTH = 126, + /// + /// - Pink 0--+255 Black + /// + SKIN_LIPSTICK_COLOR = 127, + /// + /// - No Lipstick 0--+255 More Lipstick + /// + SKIN_LIPSTICK = 128, + /// + /// - No Lipgloss 0--+255 Glossy + /// + SKIN_LIPGLOSS = 129, + /// + /// - No Eyeliner 0--+255 Full Eyeliner + /// + SKIN_EYELINER = 130, + /// + /// - No Blush 0--+255 More Blush + /// + SKIN_BLUSH = 131, + /// + /// - Pink 0--+255 Orange + /// + SKIN_BLUSH_COLOR = 132, + /// + /// - Clear 0--+255 Opaque + /// + SKIN_OUT_SHDW_OPACITY = 133, + /// + /// - No Eyeshadow 0--+255 More Eyeshadow + /// + SKIN_OUTER_SHADOW = 134, + /// + /// - Light 0--+255 Dark + /// + SKIN_OUT_SHDW_COLOR = 135, + /// + /// - No Eyeshadow 0--+255 More Eyeshadow + /// + SKIN_INNER_SHADOW = 136, + /// + /// - No Polish 0--+255 Painted Nails + /// + SKIN_NAIL_POLISH = 137, + /// + /// - Clear 0--+255 Opaque + /// + SKIN_BLUSH_OPACITY = 138, + /// + /// - Light 0--+255 Dark + /// + SKIN_IN_SHDW_COLOR = 139, + /// + /// - Clear 0--+255 Opaque + /// + SKIN_IN_SHDW_OPACITY = 140, + /// + /// - Dark Green 0--+255 Black + /// + SKIN_EYELINER_COLOR = 141, + /// + /// - Pink 0--+255 Black + /// + SKIN_NAIL_POLISH_COLOR = 142, + /// + /// - Sparse 0--+255 Dense + /// + HAIR_EYEBROW_DENSITY = 143, + /// + /// - 5 O'Clock Shadow 0--+255 Bushy Hair + /// + HAIR_HAIR_THICKNESS = 144, + /// + /// Saddle Bags - Less Saddle 0--+255 More Saddle + /// + SHAPE_SADDLEBAGS = 145, + /// + /// Taper Back - Wide Back 0--+255 Narrow Back + /// + HAIR_HAIR_TAPER_BACK = 146, + /// + /// Taper Front - Wide Front 0--+255 Narrow Front + /// + HAIR_HAIR_TAPER_FRONT = 147, + /// + /// - Short Neck 0--+255 Long Neck + /// + SHAPE_NECK_LENGTH = 148, + /// + /// Eyebrow Height - Higher 0--+255 Lower + /// + HAIR_LOWER_EYEBROWS = 149, + /// + /// Lower Bridge - Low 0--+255 High + /// + SHAPE_LOWER_BRIDGE_NOSE = 150, + /// + /// Nostril Division - High 0--+255 Low + /// + SHAPE_LOW_SEPTUM_NOSE = 151, + /// + /// Jaw Angle - Low Jaw 0--+255 High Jaw + /// + SHAPE_JAW_ANGLE = 152, + /// + /// Shear Front - Full Front 0--+255 Sheared Front + /// + HAIR_HAIR_SHEAR_FRONT = 153, + /// + /// - Less Volume 0--+255 More Volume + /// + HAIR_HAIR_VOLUME = 154, + /// + /// Lip Cleft Depth - Shallow 0--+255 Deep + /// + SHAPE_LIP_CLEFT_DEEP = 155, + /// + /// Puffy Eyelids - Flat 0--+255 Puffy + /// + SHAPE_PUFFY_LOWER_LIDS = 156, + /// + /// - Sunken Eyes 0--+255 Bugged Eyes + /// + SHAPE_EYE_DEPTH = 157, + /// + /// - Flat Head 0--+255 Long Head + /// + SHAPE_HEAD_LENGTH = 158, + /// + /// - Less Freckles 0--+255 More Freckles + /// + SKIN_BODY_FRECKLES = 159, + /// + /// - Low 0--+255 High + /// + UNDERSHIRT_COLLAR_BACK = 160, + JACKET_COLLAR_BACK_161 = 161, + SHIRT_COLLAR_BACK_162 = 162, + /// + /// - Short Pigtails 0--+255 Long Pigtails + /// + HAIR_PIGTAILS = 163, + /// + /// - Short Ponytail 0--+255 Long Ponytail + /// + HAIR_PONYTAIL = 164, + /// + /// Butt Size - Flat Butt 0--+255 Big Butt + /// + SHAPE_BUTT_SIZE = 165, + /// + /// Ear Tips - Flat 0--+255 Pointy + /// + SHAPE_POINTY_EARS = 166, + /// + /// Lip Ratio - More Upper Lip 0--+255 More Lower Lip + /// + SHAPE_LIP_RATIO = 167, + SHIRT_SLEEVE_LENGTH_168 = 168, + /// + /// - Short 0--+255 Long + /// + SHIRT_SHIRT_BOTTOM = 169, + SHIRT_COLLAR_FRONT_170 = 170, + SHIRT_SHIRT_RED = 171, + SHIRT_SHIRT_GREEN = 172, + SHIRT_SHIRT_BLUE = 173, + PANTS_PANTS_RED = 174, + PANTS_PANTS_GREEN = 175, + PANTS_PANTS_BLUE = 176, + SHOES_SHOES_RED = 177, + SHOES_SHOES_GREEN = 178, + /// + /// - Low 0--+255 High + /// + PANTS_WAIST_HEIGHT = 179, + PANTS_PANTS_LENGTH_180 = 180, + /// + /// Pants Fit - Tight Pants 0--+255 Loose Pants + /// + PANTS_LOOSE_LOWER_CLOTHING = 181, + SHOES_SHOES_BLUE = 182, + SOCKS_SOCKS_RED = 183, + SOCKS_SOCKS_GREEN = 184, + SOCKS_SOCKS_BLUE = 185, + UNDERSHIRT_UNDERSHIRT_RED = 186, + UNDERSHIRT_UNDERSHIRT_GREEN = 187, + UNDERSHIRT_UNDERSHIRT_BLUE = 188, + UNDERPANTS_UNDERPANTS_RED = 189, + UNDERPANTS_UNDERPANTS_GREEN = 190, + UNDERPANTS_UNDERPANTS_BLUE = 191, + GLOVES_GLOVES_RED = 192, + /// + /// Shirt Fit - Tight Shirt 0--+255 Loose Shirt + /// + SHIRT_LOOSE_UPPER_CLOTHING = 193, + GLOVES_GLOVES_GREEN = 194, + GLOVES_GLOVES_BLUE = 195, + JACKET_JACKET_RED = 196, + JACKET_JACKET_GREEN = 197, + JACKET_JACKET_BLUE = 198, + /// + /// Sleeve Looseness - Tight Sleeves 0--+255 Loose Sleeves + /// + SHIRT_SHIRTSLEEVE_FLAIR = 199, + /// + /// Knee Angle - Knock Kneed 0--+255 Bow Legged + /// + SHAPE_BOWED_LEGS = 200, + /// + /// - Short hips 0--+255 Long Hips + /// + SHAPE_HIP_LENGTH = 201, + /// + /// - Fingerless 0--+255 Fingers + /// + GLOVES_GLOVE_FINGERS = 202, + /// + /// bustle skirt - no bustle 0--+255 more bustle + /// + SKIRT_SKIRT_BUSTLE = 203, + /// + /// - Short 0--+255 Long + /// + SKIRT_SKIRT_LENGTH = 204, + /// + /// - Open Front 0--+255 Closed Front + /// + SKIRT_SLIT_FRONT = 205, + /// + /// - Open Back 0--+255 Closed Back + /// + SKIRT_SLIT_BACK = 206, + /// + /// - Open Left 0--+255 Closed Left + /// + SKIRT_SLIT_LEFT = 207, + /// + /// - Open Right 0--+255 Closed Right + /// + SKIRT_SLIT_RIGHT = 208, + /// + /// Skirt Fit - Tight Skirt 0--+255 Poofy Skirt + /// + SKIRT_SKIRT_LOOSENESS = 209, + SHIRT_SHIRT_WRINKLES = 210, + PANTS_PANTS_WRINKLES = 211, + /// + /// Jacket Wrinkles - No Wrinkles 0--+255 Wrinkles + /// + JACKET_JACKET_WRINKLES = 212, + /// + /// Package - Coin Purse 0--+255 Duffle Bag + /// + SHAPE_MALE_PACKAGE = 213, + /// + /// Inner Eye Corner - Corner Down 0--+255 Corner Up + /// + SHAPE_EYELID_INNER_CORNER_UP = 214, + SKIRT_SKIRT_RED = 215, + SKIRT_SKIRT_GREEN = 216, + SKIRT_SKIRT_BLUE = 217, + + /// + /// Avatar Physics section. These are 0 type visual params which get transmitted. + /// + + /// + /// Breast Part 1 + /// + BREAST_PHYSICS_MASS = 218, + BREAST_PHYSICS_GRAVITY = 219, + BREAST_PHYSICS_DRAG = 220, + BREAST_PHYSICS_UPDOWN_MAX_EFFECT = 221, + BREAST_PHYSICS_UPDOWN_SPRING = 222, + BREAST_PHYSICS_UPDOWN_GAIN = 223, + BREAST_PHYSICS_UPDOWN_DAMPING = 224, + BREAST_PHYSICS_INOUT_MAX_EFFECT = 225, + BREAST_PHYSICS_INOUT_SPRING = 226, + BREAST_PHYSICS_INOUT_GAIN = 227, + BREAST_PHYSICS_INOUT_DAMPING = 228, + /// + /// Belly + /// + BELLY_PHYISCS_MASS = 229, + BELLY_PHYSICS_GRAVITY = 230, + BELLY_PHYSICS_DRAG = 231, + BELLY_PHYISCS_UPDOWN_MAX_EFFECT = 232, + BELLY_PHYSICS_UPDOWN_SPRING = 233, + BELLY_PHYSICS_UPDOWN_GAIN = 234, + BELLY_PHYSICS_UPDOWN_DAMPING = 235, + + /// + /// Butt + /// + BUTT_PHYSICS_MASS = 236, + BUTT_PHYSICS_GRAVITY = 237, + BUTT_PHYSICS_DRAG = 238, + BUTT_PHYSICS_UPDOWN_MAX_EFFECT = 239, + BUTT_PHYSICS_UPDOWN_SPRING = 240, + BUTT_PHYSICS_UPDOWN_GAIN = 241, + BUTT_PHYSICS_UPDOWN_DAMPING = 242, + BUTT_PHYSICS_LEFTRIGHT_MAX_EFFECT = 243, + BUTT_PHYSICS_LEFTRIGHT_SPRING = 244, + BUTT_PHYSICS_LEFTRIGHT_GAIN = 245, + BUTT_PHYSICS_LEFTRIGHT_DAMPING = 246, + /// + /// Breast Part 2 + /// + BREAST_PHYSICS_LEFTRIGHT_MAX_EFFECT = 247, + BREAST_PHYSICS_LEFTRIGHT_SPRING= 248, + BREAST_PHYSICS_LEFTRIGHT_GAIN = 249, + BREAST_PHYSICS_LEFTRIGHT_DAMPING = 250 + } + #endregion + } +} diff --git a/OpenSim/Framework/AvatarAttachment.cs b/OpenSim/Framework/AvatarAttachment.cs new file mode 100644 index 0000000000..07dd385f28 --- /dev/null +++ b/OpenSim/Framework/AvatarAttachment.cs @@ -0,0 +1,78 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + public class AvatarAttachment + { + public int AttachPoint; + public UUID ItemID; + public UUID AssetID; + + public AvatarAttachment(AvatarAttachment attach) + { + AttachPoint = attach.AttachPoint; + ItemID = attach.ItemID; + AssetID = attach.AssetID; + } + + public AvatarAttachment(int point, UUID item, UUID asset) + { + AttachPoint = point; + ItemID = item; + AssetID = asset; + } + + public AvatarAttachment(OSDMap args) + { + Unpack(args); + } + + public OSDMap Pack() + { + OSDMap attachdata = new OSDMap(); + attachdata["point"] = OSD.FromInteger(AttachPoint); + attachdata["item"] = OSD.FromUUID(ItemID); + attachdata["asset"] = OSD.FromUUID(AssetID); + + return attachdata; + } + + public void Unpack(OSDMap args) + { + if (args["point"] != null) + AttachPoint = args["point"].AsInteger(); + + ItemID = (args["item"] != null) ? args["item"].AsUUID() : UUID.Zero; + AssetID = (args["asset"] != null) ? args["asset"].AsUUID() : UUID.Zero; + } + } +} diff --git a/OpenSim/Framework/AvatarPickerAvatar.cs b/OpenSim/Framework/AvatarPickerAvatar.cs new file mode 100644 index 0000000000..200c054b5f --- /dev/null +++ b/OpenSim/Framework/AvatarPickerAvatar.cs @@ -0,0 +1,52 @@ +/* + * 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 +{ + /// + /// Avatar returned by the Avatar Picker request + /// + public class AvatarPickerAvatar + { + /// + /// Avatar's Unique ID + /// + public UUID AvatarID; + + /// + /// Avatar's Account first name + /// + public string firstName; + + /// + /// Avatar's Account last name + /// + public string lastName; + } +} diff --git a/OpenSim/Framework/AvatarPickerReplyAgentDataArgs.cs b/OpenSim/Framework/AvatarPickerReplyAgentDataArgs.cs new file mode 100644 index 0000000000..54835da929 --- /dev/null +++ b/OpenSim/Framework/AvatarPickerReplyAgentDataArgs.cs @@ -0,0 +1,48 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Args to return to a client that queries picker data + /// + public class AvatarPickerReplyAgentDataArgs : EventArgs + { + /// + /// Unique Agent ID + /// + public UUID AgentID; + + /// + /// ID of query user submitted + /// + public UUID QueryID; + } +} diff --git a/OpenSim/Framework/AvatarPickerReplyDataArgs.cs b/OpenSim/Framework/AvatarPickerReplyDataArgs.cs new file mode 100644 index 0000000000..0b5bbbc187 --- /dev/null +++ b/OpenSim/Framework/AvatarPickerReplyDataArgs.cs @@ -0,0 +1,39 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class AvatarPickerReplyDataArgs : EventArgs + { + public UUID AvatarID; + public byte[] FirstName; + public byte[] LastName; + } +} diff --git a/OpenSim/Framework/AvatarWearable.cs b/OpenSim/Framework/AvatarWearable.cs new file mode 100644 index 0000000000..8e27596b85 --- /dev/null +++ b/OpenSim/Framework/AvatarWearable.cs @@ -0,0 +1,253 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + public struct WearableItem + { + public UUID ItemID; + public UUID AssetID; + + public WearableItem(UUID itemID, UUID assetID) + { + ItemID = itemID; + AssetID = assetID; + } + } + + public class AvatarWearable + { + // these are guessed at by the list here - + // http://wiki.secondlife.com/wiki/Avatar_Appearance. We'll + // correct them over time for when were are wrong. + public static readonly int BODY = 0; + public static readonly int SKIN = 1; + public static readonly int HAIR = 2; + public static readonly int EYES = 3; + public static readonly int SHIRT = 4; + public static readonly int PANTS = 5; + public static readonly int SHOES = 6; + public static readonly int SOCKS = 7; + public static readonly int JACKET = 8; + public static readonly int GLOVES = 9; + public static readonly int UNDERSHIRT = 10; + public static readonly int UNDERPANTS = 11; + public static readonly int SKIRT = 12; + public static readonly int ALPHA = 13; + public static readonly int TATTOO = 14; + + public static readonly int MAX_WEARABLES = 15; + + public static readonly UUID DEFAULT_BODY_ITEM = new UUID("66c41e39-38f9-f75a-024e-585989bfaba9"); + public static readonly UUID DEFAULT_BODY_ASSET = new UUID("66c41e39-38f9-f75a-024e-585989bfab73"); + + public static readonly UUID DEFAULT_HAIR_ITEM = new UUID("d342e6c1-b9d2-11dc-95ff-0800200c9a66"); + public static readonly UUID DEFAULT_HAIR_ASSET = new UUID("d342e6c0-b9d2-11dc-95ff-0800200c9a66"); + + public static readonly UUID DEFAULT_SKIN_ITEM = new UUID("77c41e39-38f9-f75a-024e-585989bfabc9"); + public static readonly UUID DEFAULT_SKIN_ASSET = new UUID("77c41e39-38f9-f75a-024e-585989bbabbb"); + + public static readonly UUID DEFAULT_SHIRT_ITEM = new UUID("77c41e39-38f9-f75a-0000-585989bf0000"); + public static readonly UUID DEFAULT_SHIRT_ASSET = new UUID("00000000-38f9-1111-024e-222222111110"); + + public static readonly UUID DEFAULT_PANTS_ITEM = new UUID("77c41e39-38f9-f75a-0000-5859892f1111"); + public static readonly UUID DEFAULT_PANTS_ASSET = new UUID("00000000-38f9-1111-024e-222222111120"); + +// public static readonly UUID DEFAULT_ALPHA_ITEM = new UUID("bfb9923c-4838-4d2d-bf07-608c5b1165c8"); +// public static readonly UUID DEFAULT_ALPHA_ASSET = new UUID("1578a2b1-5179-4b53-b618-fe00ca5a5594"); + +// public static readonly UUID DEFAULT_TATTOO_ITEM = new UUID("c47e22bd-3021-4ba4-82aa-2b5cb34d35e1"); +// public static readonly UUID DEFAULT_TATTOO_ASSET = new UUID("00000000-0000-2222-3333-100000001007"); + + protected Dictionary m_items = new Dictionary(); + protected List m_ids = new List(); + + public AvatarWearable() + { + } + + public AvatarWearable(UUID itemID, UUID assetID) + { + Wear(itemID, assetID); + } + + public AvatarWearable(OSDArray args) + { + Unpack(args); + } + + public OSD Pack() + { + OSDArray wearlist = new OSDArray(); + + foreach (UUID id in m_ids) + { + OSDMap weardata = new OSDMap(); + weardata["item"] = OSD.FromUUID(id); + weardata["asset"] = OSD.FromUUID(m_items[id]); + wearlist.Add(weardata); + } + + return wearlist; + } + + public void Unpack(OSDArray args) + { + Clear(); + + foreach (OSDMap weardata in args) + { + Add(weardata["item"].AsUUID(), weardata["asset"].AsUUID()); + } + } + + public int Count + { + get { return m_ids.Count; } + } + + public void Add(UUID itemID, UUID assetID) + { + if (itemID == UUID.Zero) + return; + if (m_items.ContainsKey(itemID)) + { + m_items[itemID] = assetID; + return; + } + if (m_ids.Count >= 5) + return; + + m_ids.Add(itemID); + m_items[itemID] = assetID; + } + + public void Wear(WearableItem item) + { + Wear(item.ItemID, item.AssetID); + } + + public void Wear(UUID itemID, UUID assetID) + { + Clear(); + Add(itemID, assetID); + } + + public void Clear() + { + m_ids.Clear(); + m_items.Clear(); + } + + public void RemoveItem(UUID itemID) + { + if (m_items.ContainsKey(itemID)) + { + m_ids.Remove(itemID); + m_items.Remove(itemID); + } + } + + public void RemoveAsset(UUID assetID) + { + UUID itemID = UUID.Zero; + + foreach (KeyValuePair kvp in m_items) + { + if (kvp.Value == assetID) + { + itemID = kvp.Key; + break; + } + } + + if (itemID != UUID.Zero) + { + m_ids.Remove(itemID); + m_items.Remove(itemID); + } + } + + public WearableItem this [int idx] + { + get + { + if (idx >= m_ids.Count || idx < 0) + return new WearableItem(UUID.Zero, UUID.Zero); + + return new WearableItem(m_ids[idx], m_items[m_ids[idx]]); + } + } + + public UUID GetAsset(UUID itemID) + { + if (!m_items.ContainsKey(itemID)) + return UUID.Zero; + return m_items[itemID]; + } + + public static AvatarWearable[] DefaultWearables + { + get + { + AvatarWearable[] defaultWearables = new AvatarWearable[MAX_WEARABLES]; //should be 15 of these + for (int i = 0; i < MAX_WEARABLES; i++) + { + defaultWearables[i] = new AvatarWearable(); + } + + // Body + defaultWearables[BODY].Add(DEFAULT_BODY_ITEM, DEFAULT_BODY_ASSET); + + // Hair + defaultWearables[HAIR].Add(DEFAULT_HAIR_ITEM, DEFAULT_HAIR_ASSET); + + // Skin + defaultWearables[SKIN].Add(DEFAULT_SKIN_ITEM, DEFAULT_SKIN_ASSET); + + // Shirt + defaultWearables[SHIRT].Add(DEFAULT_SHIRT_ITEM, DEFAULT_SHIRT_ASSET); + + // Pants + defaultWearables[PANTS].Add(DEFAULT_PANTS_ITEM, DEFAULT_PANTS_ASSET); + +// // Alpha +// defaultWearables[ALPHA].Add(DEFAULT_ALPHA_ITEM, DEFAULT_ALPHA_ASSET); + +// // Tattoo +// defaultWearables[TATTOO].Add(DEFAULT_TATTOO_ITEM, DEFAULT_TATTOO_ASSET); + + return defaultWearables; + } + } + } +} diff --git a/OpenSim/Framework/AvatarWearingArgs.cs b/OpenSim/Framework/AvatarWearingArgs.cs new file mode 100644 index 0000000000..d2fab8067c --- /dev/null +++ b/OpenSim/Framework/AvatarWearingArgs.cs @@ -0,0 +1,63 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class AvatarWearingArgs : EventArgs + { + private List m_nowWearing = new List(); + + /// + /// + /// + public List NowWearing + { + get { return m_nowWearing; } + set { m_nowWearing = value; } + } + + #region Nested type: Wearable + + public class Wearable + { + public UUID ItemID = new UUID("00000000-0000-0000-0000-000000000000"); + public byte Type = 0; + + public Wearable(UUID itemId, byte type) + { + ItemID = itemId; + Type = type; + } + } + + #endregion + } +} diff --git a/OpenSim/Framework/BasicDOSProtector.cs b/OpenSim/Framework/BasicDOSProtector.cs new file mode 100644 index 0000000000..89bfa94191 --- /dev/null +++ b/OpenSim/Framework/BasicDOSProtector.cs @@ -0,0 +1,275 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + + public class BasicDOSProtector + { + public enum ThrottleAction + { + DoThrottledMethod, + DoThrow + } + private readonly CircularBuffer _generalRequestTimes; // General request checker + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; // per client request checker + private readonly Dictionary _tempBlocked; // blocked list + private readonly Dictionary _sessions; + private readonly System.Timers.Timer _forgetTimer; // Cleanup timer + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _blockLockSlim = new System.Threading.ReaderWriterLockSlim(); + private readonly System.Threading.ReaderWriterLockSlim _sessionLockSlim = new System.Threading.ReaderWriterLockSlim(); + public BasicDOSProtector(BasicDosProtectorOptions options) + { + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _sessions = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _blockLockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _blockLockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + _sessions.Remove(removes[i]); + } + _blockLockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _blockLockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + + /// + /// Given a string Key, Returns if that context is blocked + /// + /// A Key identifying the context + /// bool Yes or No, True or False for blocked + public bool IsBlocked(string key) + { + bool ret = false; + _blockLockSlim.EnterReadLock(); + ret = _tempBlocked.ContainsKey(key); + _blockLockSlim.ExitReadLock(); + return ret; + } + + /// + /// Process the velocity of this context + /// + /// + /// + /// + public bool Process(string key, string endpoint) + { + if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) + return true; + + string clientstring = key; + + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _blockLockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + + _blockLockSlim.ExitReadLock(); + + lock (_generalRequestTimes) + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_options.MaxConcurrentSessions > 0) + { + int sessionscount = 0; + + _sessionLockSlim.EnterReadLock(); + if (_sessions.ContainsKey(key)) + sessionscount = _sessions[key]; + _sessionLockSlim.ExitReadLock(); + + if (sessionscount > _options.MaxConcurrentSessions) + { + // Add to blocking and cleanup methods + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + { + _tempBlocked.Add(clientstring, + Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds); + _forgetTimer.Enabled = true; + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds based on concurrency, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + } + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + } + + + } + else + ProcessConcurrency(key, endpoint); + } + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(key, endpoint)) + return true; + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + return true; + } + private void ProcessConcurrency(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + _sessions[key] = _sessions[key] + 1; + else + _sessions.Add(key,1); + _sessionLockSlim.ExitWriteLock(); + } + public void ProcessEnd(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + { + _sessions[key]--; + if (_sessions[key] <= 0) + _sessions.Remove(key); + } + else + _sessions.Add(key, 1); + + _sessionLockSlim.ExitWriteLock(); + } + + /// + /// At this point, the rate limiting code needs to track 'per user' velocity. + /// + /// Context Key, string representing a rate limiting context + /// + /// + private bool DeeperInspection(string key, string endpoint) + { + lock (_deeperInspection) + { + string clientstring = key; + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Looks like we're over the limit + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + + } + + + public class BasicDosProtectorOptions + { + public int MaxRequestsInTimeframe; + public TimeSpan RequestTimeSpan; + public TimeSpan ForgetTimeSpan; + public bool AllowXForwardedFor; + public string ReportingName = "BASICDOSPROTECTOR"; + public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod; + public int MaxConcurrentSessions; + } +} diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs new file mode 100644 index 0000000000..d11ad11c17 --- /dev/null +++ b/OpenSim/Framework/BlockingQueue.cs @@ -0,0 +1,148 @@ +/* + * 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.Generic; +using System.Threading; + +namespace OpenSim.Framework +{ + public class BlockingQueue + { + private readonly Queue m_pqueue = new Queue(); + private readonly Queue m_queue = new Queue(); + private readonly object m_queueSync = new object(); + + public void PriorityEnqueue(T value) + { + lock (m_queueSync) + { + m_pqueue.Enqueue(value); + Monitor.Pulse(m_queueSync); + } + } + + public void Enqueue(T value) + { + lock (m_queueSync) + { + m_queue.Enqueue(value); + Monitor.Pulse(m_queueSync); + } + } + + public T Dequeue() + { + lock (m_queueSync) + { + while (m_queue.Count < 1 && m_pqueue.Count < 1) + { + Monitor.Wait(m_queueSync); + } + + if (m_pqueue.Count > 0) + return m_pqueue.Dequeue(); + + if (m_queue.Count > 0) + return m_queue.Dequeue(); + return default(T); + } + } + + public T Dequeue(int msTimeout) + { + lock (m_queueSync) + { + bool success = true; + while (m_queue.Count < 1 && m_pqueue.Count < 1 && success) + { + success = Monitor.Wait(m_queueSync, msTimeout); + } + + if (m_pqueue.Count > 0) + return m_pqueue.Dequeue(); + if (m_queue.Count > 0) + return m_queue.Dequeue(); + return default(T); + } + } + + /// + /// Indicate whether this queue contains the given item. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// + public bool Contains(T item) + { + lock (m_queueSync) + { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return false; + + if (m_pqueue.Contains(item)) + return true; + return m_queue.Contains(item); + } + } + + /// + /// Return a count of the number of requests on this queue. + /// + public int Count() + { + lock (m_queueSync) + return m_queue.Count + m_pqueue.Count; + } + + /// + /// Return the array of items on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// + public T[] GetQueueArray() + { + lock (m_queueSync) + { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return new T[0]; + + return m_queue.ToArray(); + } + } + + public void Clear() + { + lock (m_queueSync) + { + m_pqueue.Clear(); + m_queue.Clear(); + Monitor.Pulse(m_queueSync); + } + } + } +} diff --git a/OpenSim/Framework/Cache.cs b/OpenSim/Framework/Cache.cs new file mode 100644 index 0000000000..31cab4a021 --- /dev/null +++ b/OpenSim/Framework/Cache.cs @@ -0,0 +1,562 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + // The delegate we will use for performing fetch from backing store + // + public delegate Object FetchDelegate(string index); + public delegate bool ExpireDelegate(string index); + + // Strategy + // + // Conservative = Minimize memory. Expire items quickly. + // Balanced = Expire items with few hits quickly. + // Aggressive = Keep cache full. Expire only when over 90% and adding + // + public enum CacheStrategy + { + Conservative = 0, + Balanced = 1, + Aggressive = 2 + } + + // Select classes to store data on different media + // + public enum CacheMedium + { + Memory = 0, + File = 1 + } + + public enum CacheFlags + { + CacheMissing = 1, + AllowUpdate = 2 + } + + // The base class of all cache objects. Implements comparison and sorting + // by the string member. + // + // This is not abstract because we need to instantiate it briefly as a + // method parameter + // + public class CacheItemBase : IEquatable, IComparable + { + public string uuid; + public DateTime entered; + public DateTime lastUsed; + public DateTime expires = new DateTime(0); + public int hits = 0; + + public virtual Object Retrieve() + { + return null; + } + + public virtual void Store(Object data) + { + } + + public CacheItemBase(string index) + { + uuid = index; + entered = DateTime.Now; + lastUsed = entered; + } + + public CacheItemBase(string index, DateTime ttl) + { + uuid = index; + entered = DateTime.Now; + lastUsed = entered; + expires = ttl; + } + + public virtual bool Equals(CacheItemBase item) + { + return uuid == item.uuid; + } + + public virtual int CompareTo(CacheItemBase item) + { + return uuid.CompareTo(item.uuid); + } + + public virtual bool IsLocked() + { + return false; + } + } + + // Simple in-memory storage. Boxes the object and stores it in a variable + // + public class MemoryCacheItem : CacheItemBase + { + private Object m_Data; + + public MemoryCacheItem(string index) : + base(index) + { + } + + public MemoryCacheItem(string index, DateTime ttl) : + base(index, ttl) + { + } + + public MemoryCacheItem(string index, Object data) : + base(index) + { + Store(data); + } + + public MemoryCacheItem(string index, DateTime ttl, Object data) : + base(index, ttl) + { + Store(data); + } + + public override Object Retrieve() + { + return m_Data; + } + + public override void Store(Object data) + { + m_Data = data; + } + } + + // Simple persistent file storage + // + public class FileCacheItem : CacheItemBase + { + public FileCacheItem(string index) : + base(index) + { + } + + public FileCacheItem(string index, DateTime ttl) : + base(index, ttl) + { + } + + public FileCacheItem(string index, Object data) : + base(index) + { + Store(data); + } + + public FileCacheItem(string index, DateTime ttl, Object data) : + base(index, ttl) + { + Store(data); + } + + public override Object Retrieve() + { + //TODO: Add file access code + return null; + } + + public override void Store(Object data) + { + //TODO: Add file access code + } + } + + // The main cache class. This is the class you instantiate to create + // a cache + // + public class Cache + { + /// + /// Must only be accessed under lock. + /// + private List m_Index = new List(); + + /// + /// Must only be accessed under m_Index lock. + /// + private Dictionary m_Lookup = + new Dictionary(); + + private CacheStrategy m_Strategy; + private CacheMedium m_Medium; + private CacheFlags m_Flags = 0; + private int m_Size = 1024; + private TimeSpan m_DefaultTTL = new TimeSpan(0); + public ExpireDelegate OnExpire; + + // Comparison interfaces + // + private class SortLRU : IComparer + { + public int Compare(CacheItemBase a, CacheItemBase b) + { + if (a == null && b == null) + return 0; + if (a == null) + return -1; + if (b == null) + return 1; + + return(a.lastUsed.CompareTo(b.lastUsed)); + } + } + + // Convenience constructors + // + public Cache() + { + m_Strategy = CacheStrategy.Balanced; + m_Medium = CacheMedium.Memory; + m_Flags = 0; + } + + public Cache(CacheMedium medium) : + this(medium, CacheStrategy.Balanced) + { + } + + public Cache(CacheMedium medium, CacheFlags flags) : + this(medium, CacheStrategy.Balanced, flags) + { + } + + public Cache(CacheMedium medium, CacheStrategy strategy) : + this(medium, strategy, 0) + { + } + + public Cache(CacheStrategy strategy, CacheFlags flags) : + this(CacheMedium.Memory, strategy, flags) + { + } + + public Cache(CacheFlags flags) : + this(CacheMedium.Memory, CacheStrategy.Balanced, flags) + { + } + + public Cache(CacheMedium medium, CacheStrategy strategy, + CacheFlags flags) + { + m_Strategy = strategy; + m_Medium = medium; + m_Flags = flags; + } + + // Count of the items currently in cache + // + public int Count + { + get { lock (m_Index) { return m_Index.Count; } } + } + + // Maximum number of items this cache will hold + // + public int Size + { + get { return m_Size; } + set { SetSize(value); } + } + + private void SetSize(int newSize) + { + lock (m_Index) + { + if (Count <= Size) + return; + + m_Index.Sort(new SortLRU()); + m_Index.Reverse(); + + m_Index.RemoveRange(newSize, Count - newSize); + m_Size = newSize; + + m_Lookup.Clear(); + + foreach (CacheItemBase item in m_Index) + m_Lookup[item.uuid] = item; + } + } + + public TimeSpan DefaultTTL + { + get { return m_DefaultTTL; } + set { m_DefaultTTL = value; } + } + + // Get an item from cache. Return the raw item, not it's data + // + protected virtual CacheItemBase GetItem(string index) + { + CacheItemBase item = null; + + lock (m_Index) + { + if (m_Lookup.ContainsKey(index)) + item = m_Lookup[index]; + + if (item == null) + { + Expire(true); + return null; + } + + item.hits++; + item.lastUsed = DateTime.Now; + + Expire(true); + } + + return item; + } + + // Get an item from cache. Do not try to fetch from source if not + // present. Just return null + // + public virtual Object Get(string index) + { + CacheItemBase item = GetItem(index); + + if (item == null) + return null; + + return item.Retrieve(); + } + + // Fetch an object from backing store if not cached, serve from + // cache if it is. + // + public virtual Object Get(string index, FetchDelegate fetch) + { + Object item = Get(index); + if (item != null) + return item; + + Object data = fetch(index); + if (data == null) + { + if ((m_Flags & CacheFlags.CacheMissing) != 0) + { + lock (m_Index) + { + CacheItemBase missing = new CacheItemBase(index); + if (!m_Index.Contains(missing)) + { + m_Index.Add(missing); + m_Lookup[index] = missing; + } + } + } + return null; + } + + Store(index, data); + + return data; + } + + // Find an object in cache by delegate. + // + public Object Find(Predicate d) + { + CacheItemBase item; + + lock (m_Index) + item = m_Index.Find(d); + + if (item == null) + return null; + + return item.Retrieve(); + } + + public virtual void Store(string index, Object data) + { + Type container; + + switch (m_Medium) + { + case CacheMedium.Memory: + container = typeof(MemoryCacheItem); + break; + case CacheMedium.File: + return; + default: + return; + } + + Store(index, data, container); + } + + public virtual void Store(string index, Object data, Type container) + { + Store(index, data, container, new Object[] { index }); + } + + public virtual void Store(string index, Object data, Type container, + Object[] parameters) + { + CacheItemBase item; + + lock (m_Index) + { + Expire(false); + + if (m_Index.Contains(new CacheItemBase(index))) + { + if ((m_Flags & CacheFlags.AllowUpdate) != 0) + { + item = GetItem(index); + + item.hits++; + item.lastUsed = DateTime.Now; + if (m_DefaultTTL.Ticks != 0) + item.expires = DateTime.Now + m_DefaultTTL; + + item.Store(data); + } + return; + } + + item = (CacheItemBase)Activator.CreateInstance(container, + parameters); + + if (m_DefaultTTL.Ticks != 0) + item.expires = DateTime.Now + m_DefaultTTL; + + m_Index.Add(item); + m_Lookup[index] = item; + } + + item.Store(data); + } + + /// + /// Expire items as appropriate. + /// + /// + /// Callers must lock m_Index. + /// + /// + protected virtual void Expire(bool getting) + { + if (getting && (m_Strategy == CacheStrategy.Aggressive)) + return; + + if (m_DefaultTTL.Ticks != 0) + { + DateTime now= DateTime.Now; + + foreach (CacheItemBase item in new List(m_Index)) + { + if (item.expires.Ticks == 0 || + item.expires <= now) + { + m_Index.Remove(item); + m_Lookup.Remove(item.uuid); + } + } + } + + switch (m_Strategy) + { + case CacheStrategy.Aggressive: + if (Count < Size) + return; + + m_Index.Sort(new SortLRU()); + m_Index.Reverse(); + + int target = (int)((float)Size * 0.9); + if (target == Count) // Cover ridiculous cache sizes + return; + + ExpireDelegate doExpire = OnExpire; + + if (doExpire != null) + { + List candidates = + m_Index.GetRange(target, Count - target); + + foreach (CacheItemBase i in candidates) + { + if (doExpire(i.uuid)) + { + m_Index.Remove(i); + m_Lookup.Remove(i.uuid); + } + } + } + else + { + m_Index.RemoveRange(target, Count - target); + + m_Lookup.Clear(); + + foreach (CacheItemBase item in m_Index) + m_Lookup[item.uuid] = item; + } + + break; + + default: + break; + } + } + + public void Invalidate(string uuid) + { + lock (m_Index) + { + if (!m_Lookup.ContainsKey(uuid)) + return; + + CacheItemBase item = m_Lookup[uuid]; + m_Lookup.Remove(uuid); + m_Index.Remove(item); + } + } + + public void Clear() + { + lock (m_Index) + { + m_Index.Clear(); + m_Lookup.Clear(); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/CachedTextureEventArg.cs b/OpenSim/Framework/CachedTextureEventArg.cs new file mode 100644 index 0000000000..239fc566a9 --- /dev/null +++ b/OpenSim/Framework/CachedTextureEventArg.cs @@ -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. + */ + +using System; +using System.Text; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class CachedTextureRequestArg + { + public int BakedTextureIndex; + public UUID WearableHashID; + } + + public class CachedTextureResponseArg + { + public int BakedTextureIndex; + public UUID BakedTextureID; + public String HostName; + } +} diff --git a/OpenSim/Framework/CapsUtil.cs b/OpenSim/Framework/CapsUtil.cs new file mode 100644 index 0000000000..4baf505784 --- /dev/null +++ b/OpenSim/Framework/CapsUtil.cs @@ -0,0 +1,62 @@ +/* + * 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 utility methods + /// + public class CapsUtil + { + /// + /// Generate a CAPS seed path using a previously generated CAPS object path component + /// + /// + /// + public static string GetCapsSeedPath(string capsObjectPath) + { + return "CAPS/" + capsObjectPath + "0000/"; + } + + /// + /// Get a random CAPS object path component that will be used as the identifying part of all future CAPS requests + /// + /// + public static string GetRandomCapsObjectPath() + { + UUID caps = UUID.Random(); + string capsPath = caps.ToString(); + // I'm commenting this, rather than delete, to keep as historical record. + // The caps seed is now a full UUID string that gets added four more digits + // for producing certain CAPs URLs in OpenSim + //capsPath = capsPath.Remove(capsPath.Length - 4, 4); + return capsPath; + } + } +} diff --git a/OpenSim/Framework/ChatTypeEnum.cs b/OpenSim/Framework/ChatTypeEnum.cs new file mode 100644 index 0000000000..b7e4e7cd1a --- /dev/null +++ b/OpenSim/Framework/ChatTypeEnum.cs @@ -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.Framework +{ + public enum ChatTypeEnum + { + Whisper = 0, + Say = 1, + Shout = 2, + // 3 is an obsolete version of Say + StartTyping = 4, + StopTyping = 5, + DebugChannel = 6, + Region = 7, + Owner = 8, + Broadcast = 0xFF + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ChildAgentDataUpdate.cs b/OpenSim/Framework/ChildAgentDataUpdate.cs new file mode 100644 index 0000000000..2a8e67dd61 --- /dev/null +++ b/OpenSim/Framework/ChildAgentDataUpdate.cs @@ -0,0 +1,770 @@ +/* + * 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 OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + // Soon to be dismissed + [Serializable] + public class ChildAgentDataUpdate + { + public Guid ActiveGroupID; + public Guid AgentID; + public bool alwaysrun; + public float AVHeight; + public Vector3 cameraPosition; + public float drawdistance; + public float godlevel; + public uint GroupAccess; + public Vector3 Position; + public ulong regionHandle; + public byte[] throttles; + public Vector3 Velocity; + + public ChildAgentDataUpdate() + { + } + } + + public interface IAgentData + { + UUID AgentID { get; set; } + + OSDMap Pack(); + void Unpack(OSDMap map, IScene scene); + } + + /// + /// Replacement for ChildAgentDataUpdate. Used over RESTComms and LocalComms. + /// + public class AgentPosition : IAgentData + { + private UUID m_id; + public UUID AgentID + { + get { return m_id; } + set { m_id = value; } + } + + public ulong RegionHandle; + public uint CircuitCode; + public UUID SessionID; + + public float Far; + public Vector3 Position; + public Vector3 Velocity; + public Vector3 Center; + public Vector3 Size; + public Vector3 AtAxis; + public Vector3 LeftAxis; + public Vector3 UpAxis; + public bool ChangedGrid; + + // This probably shouldn't be here + public byte[] Throttles; + + + public OSDMap Pack() + { + OSDMap args = new OSDMap(); + args["message_type"] = OSD.FromString("AgentPosition"); + + args["region_handle"] = OSD.FromString(RegionHandle.ToString()); + args["circuit_code"] = OSD.FromString(CircuitCode.ToString()); + args["agent_uuid"] = OSD.FromUUID(AgentID); + args["session_uuid"] = OSD.FromUUID(SessionID); + + args["position"] = OSD.FromString(Position.ToString()); + args["velocity"] = OSD.FromString(Velocity.ToString()); + args["center"] = OSD.FromString(Center.ToString()); + args["size"] = OSD.FromString(Size.ToString()); + args["at_axis"] = OSD.FromString(AtAxis.ToString()); + args["left_axis"] = OSD.FromString(LeftAxis.ToString()); + args["up_axis"] = OSD.FromString(UpAxis.ToString()); + + args["far"] = OSD.FromReal(Far); + args["changed_grid"] = OSD.FromBoolean(ChangedGrid); + + if ((Throttles != null) && (Throttles.Length > 0)) + args["throttles"] = OSD.FromBinary(Throttles); + + return args; + } + + public void Unpack(OSDMap args, IScene scene) + { + if (args.ContainsKey("region_handle")) + UInt64.TryParse(args["region_handle"].AsString(), out RegionHandle); + + if (args["circuit_code"] != null) + UInt32.TryParse((string)args["circuit_code"].AsString(), out CircuitCode); + + if (args["agent_uuid"] != null) + AgentID = args["agent_uuid"].AsUUID(); + + if (args["session_uuid"] != null) + SessionID = args["session_uuid"].AsUUID(); + + if (args["position"] != null) + Vector3.TryParse(args["position"].AsString(), out Position); + + if (args["velocity"] != null) + Vector3.TryParse(args["velocity"].AsString(), out Velocity); + + if (args["center"] != null) + Vector3.TryParse(args["center"].AsString(), out Center); + + if (args["size"] != null) + Vector3.TryParse(args["size"].AsString(), out Size); + + if (args["at_axis"] != null) + Vector3.TryParse(args["at_axis"].AsString(), out AtAxis); + + if (args["left_axis"] != null) + Vector3.TryParse(args["left_axis"].AsString(), out LeftAxis); + + if (args["up_axis"] != null) + Vector3.TryParse(args["up_axis"].AsString(), out UpAxis); + + if (args["changed_grid"] != null) + ChangedGrid = args["changed_grid"].AsBoolean(); + + if (args["far"] != null) + Far = (float)(args["far"].AsReal()); + + if (args["throttles"] != null) + Throttles = args["throttles"].AsBinary(); + } + + /// + /// Soon to be decommissioned + /// + /// + public void CopyFrom(ChildAgentDataUpdate cAgent, UUID sid) + { + AgentID = new UUID(cAgent.AgentID); + SessionID = sid; + + // next: ??? + Size = new Vector3(); + Size.Z = cAgent.AVHeight; + + Center = cAgent.cameraPosition; + Far = cAgent.drawdistance; + Position = cAgent.Position; + RegionHandle = cAgent.regionHandle; + Throttles = cAgent.throttles; + Velocity = cAgent.Velocity; + } + } + + public class AgentGroupData + { + public UUID GroupID; + public ulong GroupPowers; + public bool AcceptNotices; + + public AgentGroupData(UUID id, ulong powers, bool notices) + { + GroupID = id; + GroupPowers = powers; + AcceptNotices = notices; + } + + public AgentGroupData(OSDMap args) + { + UnpackUpdateMessage(args); + } + + public OSDMap PackUpdateMessage() + { + OSDMap groupdata = new OSDMap(); + groupdata["group_id"] = OSD.FromUUID(GroupID); + groupdata["group_powers"] = OSD.FromString(GroupPowers.ToString()); + groupdata["accept_notices"] = OSD.FromBoolean(AcceptNotices); + + return groupdata; + } + + public void UnpackUpdateMessage(OSDMap args) + { + if (args["group_id"] != null) + GroupID = args["group_id"].AsUUID(); + if (args["group_powers"] != null) + UInt64.TryParse((string)args["group_powers"].AsString(), out GroupPowers); + if (args["accept_notices"] != null) + AcceptNotices = args["accept_notices"].AsBoolean(); + } + } + + public class ControllerData + { + public UUID ObjectID; + public UUID ItemID; + public uint IgnoreControls; + public uint EventControls; + + public ControllerData(UUID obj, UUID item, uint ignore, uint ev) + { + ObjectID = obj; + ItemID = item; + IgnoreControls = ignore; + EventControls = ev; + } + + public ControllerData(OSDMap args) + { + UnpackUpdateMessage(args); + } + + public OSDMap PackUpdateMessage() + { + OSDMap controldata = new OSDMap(); + controldata["object"] = OSD.FromUUID(ObjectID); + controldata["item"] = OSD.FromUUID(ItemID); + controldata["ignore"] = OSD.FromInteger(IgnoreControls); + controldata["event"] = OSD.FromInteger(EventControls); + + return controldata; + } + + + public void UnpackUpdateMessage(OSDMap args) + { + if (args["object"] != null) + ObjectID = args["object"].AsUUID(); + if (args["item"] != null) + ItemID = args["item"].AsUUID(); + if (args["ignore"] != null) + IgnoreControls = (uint)args["ignore"].AsInteger(); + if (args["event"] != null) + EventControls = (uint)args["event"].AsInteger(); + } + } + + public class AgentData : IAgentData + { + private UUID m_id; + public UUID AgentID + { + get { return m_id; } + set { m_id = value; } + } + public UUID RegionID; + public uint CircuitCode; + public UUID SessionID; + + public Vector3 Position; + public Vector3 Velocity; + public Vector3 Center; + public Vector3 Size; + public Vector3 AtAxis; + public Vector3 LeftAxis; + public Vector3 UpAxis; + + /// + /// Signal on a V2 teleport that Scene.IncomingChildAgentDataUpdate(AgentData ad) should wait for the + /// scene presence to become root (triggered when the viewer sends a CompleteAgentMovement UDP packet after + /// establishing the connection triggered by it's receipt of a TeleportFinish EQ message). + /// + public bool SenderWantsToWaitForRoot; + + public float Far; + public float Aspect; + //public int[] Throttles; + public byte[] Throttles; + + public uint LocomotionState; + public Quaternion HeadRotation; + public Quaternion BodyRotation; + public uint ControlFlags; + public float EnergyLevel; + public Byte GodLevel; + public bool AlwaysRun; + public UUID PreyAgent; + public Byte AgentAccess; + public UUID ActiveGroupID; + + public AgentGroupData[] Groups; + public Animation[] Anims; + public Animation DefaultAnim = null; + public Animation AnimState = null; + + public UUID GranterID; + public UUID ParentPart; + public Vector3 SitOffset; + + // Appearance + public AvatarAppearance Appearance; + +// DEBUG ON + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); +// DEBUG OFF + +/* + public byte[] AgentTextures; + public byte[] VisualParams; + public UUID[] Wearables; + public AvatarAttachment[] Attachments; +*/ + // Scripted + public ControllerData[] Controllers; + + public string CallbackURI; + + // These two must have the same Count + public List AttachmentObjects; + public List AttachmentObjectStates; + + public virtual OSDMap Pack() + { +// m_log.InfoFormat("[CHILDAGENTDATAUPDATE] Pack data"); + + OSDMap args = new OSDMap(); + args["message_type"] = OSD.FromString("AgentData"); + + args["region_id"] = OSD.FromString(RegionID.ToString()); + args["circuit_code"] = OSD.FromString(CircuitCode.ToString()); + args["agent_uuid"] = OSD.FromUUID(AgentID); + args["session_uuid"] = OSD.FromUUID(SessionID); + + args["position"] = OSD.FromString(Position.ToString()); + args["velocity"] = OSD.FromString(Velocity.ToString()); + args["center"] = OSD.FromString(Center.ToString()); + args["size"] = OSD.FromString(Size.ToString()); + args["at_axis"] = OSD.FromString(AtAxis.ToString()); + args["left_axis"] = OSD.FromString(LeftAxis.ToString()); + args["up_axis"] = OSD.FromString(UpAxis.ToString()); + + //backwards compatibility + args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot); + args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot); + args["far"] = OSD.FromReal(Far); + args["aspect"] = OSD.FromReal(Aspect); + + if ((Throttles != null) && (Throttles.Length > 0)) + args["throttles"] = OSD.FromBinary(Throttles); + + args["locomotion_state"] = OSD.FromString(LocomotionState.ToString()); + args["head_rotation"] = OSD.FromString(HeadRotation.ToString()); + args["body_rotation"] = OSD.FromString(BodyRotation.ToString()); + args["control_flags"] = OSD.FromString(ControlFlags.ToString()); + + args["energy_level"] = OSD.FromReal(EnergyLevel); + args["god_level"] = OSD.FromString(GodLevel.ToString()); + args["always_run"] = OSD.FromBoolean(AlwaysRun); + args["prey_agent"] = OSD.FromUUID(PreyAgent); + args["agent_access"] = OSD.FromString(AgentAccess.ToString()); + + args["active_group_id"] = OSD.FromUUID(ActiveGroupID); + + if ((Groups != null) && (Groups.Length > 0)) + { + OSDArray groups = new OSDArray(Groups.Length); + foreach (AgentGroupData agd in Groups) + groups.Add(agd.PackUpdateMessage()); + args["groups"] = groups; + } + + if ((Anims != null) && (Anims.Length > 0)) + { + OSDArray anims = new OSDArray(Anims.Length); + foreach (Animation aanim in Anims) + anims.Add(aanim.PackUpdateMessage()); + args["animations"] = anims; + } + + if (DefaultAnim != null) + { + args["default_animation"] = DefaultAnim.PackUpdateMessage(); + } + + if (AnimState != null) + { + args["animation_state"] = AnimState.PackUpdateMessage(); + } + + if (Appearance != null) + args["packed_appearance"] = Appearance.Pack(); + + //if ((AgentTextures != null) && (AgentTextures.Length > 0)) + //{ + // OSDArray textures = new OSDArray(AgentTextures.Length); + // foreach (UUID uuid in AgentTextures) + // textures.Add(OSD.FromUUID(uuid)); + // args["agent_textures"] = textures; + //} + + // The code to pack textures, visuals, wearables and attachments + // should be removed; packed appearance contains the full appearance + // This is retained for backward compatibility only + if (Appearance.Texture != null) + { + byte[] rawtextures = Appearance.Texture.GetBytes(); + args["texture_entry"] = OSD.FromBinary(rawtextures); + } + + if ((Appearance.VisualParams != null) && (Appearance.VisualParams.Length > 0)) + args["visual_params"] = OSD.FromBinary(Appearance.VisualParams); + + // We might not pass this in all cases... + if ((Appearance.Wearables != null) && (Appearance.Wearables.Length > 0)) + { + OSDArray wears = new OSDArray(Appearance.Wearables.Length); + foreach (AvatarWearable awear in Appearance.Wearables) + wears.Add(awear.Pack()); + + args["wearables"] = wears; + } + + List attachments = Appearance.GetAttachments(); + if ((attachments != null) && (attachments.Count > 0)) + { + OSDArray attachs = new OSDArray(attachments.Count); + foreach (AvatarAttachment att in attachments) + attachs.Add(att.Pack()); + args["attachments"] = attachs; + } + // End of code to remove + + if ((Controllers != null) && (Controllers.Length > 0)) + { + OSDArray controls = new OSDArray(Controllers.Length); + foreach (ControllerData ctl in Controllers) + controls.Add(ctl.PackUpdateMessage()); + args["controllers"] = controls; + } + + if ((CallbackURI != null) && (!CallbackURI.Equals(""))) + args["callback_uri"] = OSD.FromString(CallbackURI); + + // Attachment objects for fatpack messages + if (AttachmentObjects != null) + { + int i = 0; + OSDArray attObjs = new OSDArray(AttachmentObjects.Count); + foreach (ISceneObject so in AttachmentObjects) + { + OSDMap info = new OSDMap(4); + info["sog"] = OSD.FromString(so.ToXml2()); + info["extra"] = OSD.FromString(so.ExtraToXmlString()); + info["modified"] = OSD.FromBoolean(so.HasGroupChanged); + try + { + info["state"] = OSD.FromString(AttachmentObjectStates[i++]); + } + catch (IndexOutOfRangeException) + { + m_log.WarnFormat("[CHILD AGENT DATA]: scripts list is shorter than object list."); + } + + attObjs.Add(info); + } + args["attach_objects"] = attObjs; + } + + args["parent_part"] = OSD.FromUUID(ParentPart); + args["sit_offset"] = OSD.FromString(SitOffset.ToString()); + + return args; + } + + /// + /// Deserialization of agent data. + /// Avoiding reflection makes it painful to write, but that's the price! + /// + /// + public virtual void Unpack(OSDMap args, IScene scene) + { + //m_log.InfoFormat("[CHILDAGENTDATAUPDATE] Unpack data"); + + if (args.ContainsKey("region_id")) + UUID.TryParse(args["region_id"].AsString(), out RegionID); + + if (args["circuit_code"] != null) + UInt32.TryParse((string)args["circuit_code"].AsString(), out CircuitCode); + + if (args["agent_uuid"] != null) + AgentID = args["agent_uuid"].AsUUID(); + + if (args["session_uuid"] != null) + SessionID = args["session_uuid"].AsUUID(); + + if (args["position"] != null) + Vector3.TryParse(args["position"].AsString(), out Position); + + if (args["velocity"] != null) + Vector3.TryParse(args["velocity"].AsString(), out Velocity); + + if (args["center"] != null) + Vector3.TryParse(args["center"].AsString(), out Center); + + if (args["size"] != null) + Vector3.TryParse(args["size"].AsString(), out Size); + + if (args["at_axis"] != null) + Vector3.TryParse(args["at_axis"].AsString(), out AtAxis); + + if (args["left_axis"] != null) + Vector3.TryParse(args["left_axis"].AsString(), out AtAxis); + + if (args["up_axis"] != null) + Vector3.TryParse(args["up_axis"].AsString(), out AtAxis); + + if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null) + SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean(); + + if (args["far"] != null) + Far = (float)(args["far"].AsReal()); + + if (args["aspect"] != null) + Aspect = (float)args["aspect"].AsReal(); + + if (args["throttles"] != null) + Throttles = args["throttles"].AsBinary(); + + if (args["locomotion_state"] != null) + UInt32.TryParse(args["locomotion_state"].AsString(), out LocomotionState); + + if (args["head_rotation"] != null) + Quaternion.TryParse(args["head_rotation"].AsString(), out HeadRotation); + + if (args["body_rotation"] != null) + Quaternion.TryParse(args["body_rotation"].AsString(), out BodyRotation); + + if (args["control_flags"] != null) + UInt32.TryParse(args["control_flags"].AsString(), out ControlFlags); + + if (args["energy_level"] != null) + EnergyLevel = (float)(args["energy_level"].AsReal()); + + if (args["god_level"] != null) + Byte.TryParse(args["god_level"].AsString(), out GodLevel); + + if (args["always_run"] != null) + AlwaysRun = args["always_run"].AsBoolean(); + + if (args["prey_agent"] != null) + PreyAgent = args["prey_agent"].AsUUID(); + + if (args["agent_access"] != null) + Byte.TryParse(args["agent_access"].AsString(), out AgentAccess); + + if (args["active_group_id"] != null) + ActiveGroupID = args["active_group_id"].AsUUID(); + + if ((args["groups"] != null) && (args["groups"]).Type == OSDType.Array) + { + OSDArray groups = (OSDArray)(args["groups"]); + Groups = new AgentGroupData[groups.Count]; + int i = 0; + foreach (OSD o in groups) + { + if (o.Type == OSDType.Map) + { + Groups[i++] = new AgentGroupData((OSDMap)o); + } + } + } + + if ((args["animations"] != null) && (args["animations"]).Type == OSDType.Array) + { + OSDArray anims = (OSDArray)(args["animations"]); + Anims = new Animation[anims.Count]; + int i = 0; + foreach (OSD o in anims) + { + if (o.Type == OSDType.Map) + { + Anims[i++] = new Animation((OSDMap)o); + } + } + } + + if (args["default_animation"] != null) + { + try + { + DefaultAnim = new Animation((OSDMap)args["default_animation"]); + } + catch + { + DefaultAnim = null; + } + } + + if (args["animation_state"] != null) + { + try + { + AnimState = new Animation((OSDMap)args["animation_state"]); + } + catch + { + AnimState = null; + } + } + + //if ((args["agent_textures"] != null) && (args["agent_textures"]).Type == OSDType.Array) + //{ + // OSDArray textures = (OSDArray)(args["agent_textures"]); + // AgentTextures = new UUID[textures.Count]; + // int i = 0; + // foreach (OSD o in textures) + // AgentTextures[i++] = o.AsUUID(); + //} + + Appearance = new AvatarAppearance(); + + // The code to unpack textures, visuals, wearables and attachments + // should be removed; packed appearance contains the full appearance + // This is retained for backward compatibility only + if (args["texture_entry"] != null) + { + byte[] rawtextures = args["texture_entry"].AsBinary(); + Primitive.TextureEntry textures = new Primitive.TextureEntry(rawtextures,0,rawtextures.Length); + Appearance.SetTextureEntries(textures); + } + + if (args["visual_params"] != null) + Appearance.SetVisualParams(args["visual_params"].AsBinary()); + + if ((args["wearables"] != null) && (args["wearables"]).Type == OSDType.Array) + { + OSDArray wears = (OSDArray)(args["wearables"]); + for (int i = 0; i < wears.Count / 2; i++) + { + AvatarWearable awear = new AvatarWearable((OSDArray)wears[i]); + Appearance.SetWearable(i,awear); + } + } + + if ((args["attachments"] != null) && (args["attachments"]).Type == OSDType.Array) + { + OSDArray attachs = (OSDArray)(args["attachments"]); + foreach (OSD o in attachs) + { + if (o.Type == OSDType.Map) + { + // We know all of these must end up as attachments so we + // append rather than replace to ensure multiple attachments + // per point continues to work +// m_log.DebugFormat("[CHILDAGENTDATAUPDATE]: Appending attachments for {0}", AgentID); + Appearance.AppendAttachment(new AvatarAttachment((OSDMap)o)); + } + } + } + // end of code to remove + + if (args.ContainsKey("packed_appearance") && (args["packed_appearance"]).Type == OSDType.Map) + Appearance = new AvatarAppearance((OSDMap)args["packed_appearance"]); + else + m_log.WarnFormat("[CHILDAGENTDATAUPDATE] No packed appearance"); + + if ((args["controllers"] != null) && (args["controllers"]).Type == OSDType.Array) + { + OSDArray controls = (OSDArray)(args["controllers"]); + Controllers = new ControllerData[controls.Count]; + int i = 0; + foreach (OSD o in controls) + { + if (o.Type == OSDType.Map) + { + Controllers[i++] = new ControllerData((OSDMap)o); + } + } + } + + if (args["callback_uri"] != null) + CallbackURI = args["callback_uri"].AsString(); + + // Attachment objects + if (args["attach_objects"] != null && args["attach_objects"].Type == OSDType.Array) + { + OSDArray attObjs = (OSDArray)(args["attach_objects"]); + AttachmentObjects = new List(); + AttachmentObjectStates = new List(); + foreach (OSD o in attObjs) + { + if (o.Type == OSDType.Map) + { + OSDMap info = (OSDMap)o; + ISceneObject so = scene.DeserializeObject(info["sog"].AsString()); + so.ExtraFromXmlString(info["extra"].AsString()); + so.HasGroupChanged = info["modified"].AsBoolean(); + AttachmentObjects.Add(so); + AttachmentObjectStates.Add(info["state"].AsString()); + } + } + } + + if (args["parent_part"] != null) + ParentPart = args["parent_part"].AsUUID(); + if (args["sit_offset"] != null) + Vector3.TryParse(args["sit_offset"].AsString(), out SitOffset); + } + + public AgentData() + { + } + + public AgentData(Hashtable hash) + { + //UnpackUpdateMessage(hash); + } + + public void Dump() + { + System.Console.WriteLine("------------ AgentData ------------"); + System.Console.WriteLine("UUID: " + AgentID); + System.Console.WriteLine("Region: " + RegionID); + System.Console.WriteLine("Position: " + Position); + } + } + + public class CompleteAgentData : AgentData + { + public override OSDMap Pack() + { + return base.Pack(); + } + + public override void Unpack(OSDMap map, IScene scene) + { + base.Unpack(map, scene); + } + } +} diff --git a/OpenSim/Framework/CircularBuffer.cs b/OpenSim/Framework/CircularBuffer.cs new file mode 100644 index 0000000000..e919337a70 --- /dev/null +++ b/OpenSim/Framework/CircularBuffer.cs @@ -0,0 +1,312 @@ +/* +Copyright (c) 2012, Alex Regueiro +All rights reserved. +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR 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.Threading; + +namespace OpenSim.Framework +{ + public class CircularBuffer : ICollection, IEnumerable, ICollection, IEnumerable + { + private int capacity; + private int size; + private int head; + private int tail; + private T[] buffer; + + [NonSerialized()] + private object syncRoot; + + public CircularBuffer(int capacity) + : this(capacity, false) + { + } + + public CircularBuffer(int capacity, bool allowOverflow) + { + if (capacity < 0) + throw new ArgumentException("Needs to have at least 1","capacity"); + + this.capacity = capacity; + size = 0; + head = 0; + tail = 0; + buffer = new T[capacity]; + AllowOverflow = allowOverflow; + } + + public bool AllowOverflow + { + get; + set; + } + + public int Capacity + { + get { return capacity; } + set + { + if (value == capacity) + return; + + if (value < size) + throw new ArgumentOutOfRangeException("value","Capacity is too small."); + + var dst = new T[value]; + if (size > 0) + CopyTo(dst); + buffer = dst; + + capacity = value; + } + } + + public int Size + { + get { return size; } + } + + public bool Contains(T item) + { + int bufferIndex = head; + var comparer = EqualityComparer.Default; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + if (item == null && buffer[bufferIndex] == null) + return true; + else if ((buffer[bufferIndex] != null) && + comparer.Equals(buffer[bufferIndex], item)) + return true; + } + + return false; + } + + public void Clear() + { + size = 0; + head = 0; + tail = 0; + } + + public int Put(T[] src) + { + return Put(src, 0, src.Length); + } + + public int Put(T[] src, int offset, int count) + { + if (!AllowOverflow && count > capacity - size) + throw new InvalidOperationException("Buffer Overflow"); + + int srcIndex = offset; + for (int i = 0; i < count; i++, tail++, srcIndex++) + { + if (tail == capacity) + tail = 0; + buffer[tail] = src[srcIndex]; + } + size = Math.Min(size + count, capacity); + return count; + } + + public void Put(T item) + { + if (!AllowOverflow && size == capacity) + throw new InvalidOperationException("Buffer Overflow"); + + buffer[tail] = item; + if (++tail == capacity) + tail = 0; + size++; + } + + public void Skip(int count) + { + head += count; + if (head >= capacity) + head -= capacity; + } + + public T[] Get(int count) + { + var dst = new T[count]; + Get(dst); + return dst; + } + + public int Get(T[] dst) + { + return Get(dst, 0, dst.Length); + } + + public int Get(T[] dst, int offset, int count) + { + int realCount = Math.Min(count, size); + int dstIndex = offset; + for (int i = 0; i < realCount; i++, head++, dstIndex++) + { + if (head == capacity) + head = 0; + dst[dstIndex] = buffer[head]; + } + size -= realCount; + return realCount; + } + + public T Get() + { + if (size == 0) + throw new InvalidOperationException("Buffer Empty"); + + var item = buffer[head]; + if (++head == capacity) + head = 0; + size--; + return item; + } + + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + public void CopyTo(T[] array, int arrayIndex) + { + CopyTo(0, array, arrayIndex, size); + } + + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (count > size) + throw new ArgumentOutOfRangeException("count", "Count Too Large"); + + int bufferIndex = head; + for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + array[arrayIndex] = buffer[bufferIndex]; + } + } + + public IEnumerator GetEnumerator() + { + int bufferIndex = head; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + yield return buffer[bufferIndex]; + } + } + + public T[] GetBuffer() + { + return buffer; + } + + public T[] ToArray() + { + var dst = new T[size]; + CopyTo(dst); + return dst; + } + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + void ICollection.Add(T item) + { + Put(item); + } + + bool ICollection.Remove(T item) + { + if (size == 0) + return false; + + Get(); + return true; + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (syncRoot == null) + Interlocked.CompareExchange(ref syncRoot, new object(), null); + return syncRoot; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/Client/IClientChat.cs b/OpenSim/Framework/Client/IClientChat.cs new file mode 100644 index 0000000000..86b1faa8f6 --- /dev/null +++ b/OpenSim/Framework/Client/IClientChat.cs @@ -0,0 +1,40 @@ +/* + * 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.Client +{ + public interface IClientChat + { + event ChatMessage OnChatFromClient; + + void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source, + byte audible); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Client/IClientCore.cs b/OpenSim/Framework/Client/IClientCore.cs new file mode 100644 index 0000000000..1d08fb910f --- /dev/null +++ b/OpenSim/Framework/Client/IClientCore.cs @@ -0,0 +1,44 @@ +/* + * 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.Client +{ + public interface IClientCore + { + bool TryGet(out T iface); + T Get(); + + // Basic Interfaces + UUID AgentId { get; } + + void Disconnect(string reason); + void Disconnect(); + + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Client/IClientIM.cs b/OpenSim/Framework/Client/IClientIM.cs new file mode 100644 index 0000000000..3df86d05b9 --- /dev/null +++ b/OpenSim/Framework/Client/IClientIM.cs @@ -0,0 +1,68 @@ +/* + * 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; + +namespace OpenSim.Framework.Client +{ + public class ClientInstantMessageArgs : EventArgs + { + public IClientCore client; + public string message; + public DateTime time; + public ClientInstantMessageSender sender; + } + + public class ClientInstantMessageSender + { + public UUID ID; + public bool online; + public string name; + public Vector3 position; + public UUID regionID; + } + + public delegate void ClientInstantMessage(Object sender, ClientInstantMessageArgs e); + + public class ClientInstantMessageParms + { + public ClientInstantMessageSender senderInfo; + } + + // Porting Guide from old IM + // SendIM(...) + // Loses FromAgentSession - this should be added by implementers manually. + // + + public interface IClientIM + { + void SendInstantMessage(GridInstantMessage im); + + event ImprovedInstantMessage OnInstantMessage; + } +} diff --git a/OpenSim/Framework/Client/IClientIPEndpoint.cs b/OpenSim/Framework/Client/IClientIPEndpoint.cs new file mode 100644 index 0000000000..2b99bf0a99 --- /dev/null +++ b/OpenSim/Framework/Client/IClientIPEndpoint.cs @@ -0,0 +1,39 @@ +/* + * 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.Net; +using System.Text; + +namespace OpenSim.Framework.Client +{ + public interface IClientIPEndpoint + { + IPAddress EndPoint { get; } + } +} diff --git a/OpenSim/Framework/Client/IClientInventory.cs b/OpenSim/Framework/Client/IClientInventory.cs new file mode 100644 index 0000000000..a6e0510464 --- /dev/null +++ b/OpenSim/Framework/Client/IClientInventory.cs @@ -0,0 +1,40 @@ +/* + * 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.Framework; + +namespace OpenSim.Framework.Client +{ + public interface IClientInventory + { + void SendRemoveInventoryFolders(UUID[] folders); + void SendRemoveInventoryItems(UUID[] folders); + void SendBulkUpdateInventory(InventoryFolderBase[] folders, InventoryItemBase[] items); + } +} diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs new file mode 100644 index 0000000000..98e4465cf8 --- /dev/null +++ b/OpenSim/Framework/ClientInfo.cs @@ -0,0 +1,67 @@ +/* + * 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.Net; + +namespace OpenSim.Framework +{ + public class ClientInfo + { + public readonly DateTime StartedTime = DateTime.Now; + public AgentCircuitData agentcircuit = null; + + public Dictionary needAck; + + public List out_packets = new List(); + public Dictionary pendingAcks = new Dictionary(); + public EndPoint proxyEP; + + public uint sequence; + public byte[] usecircuit; + public EndPoint userEP; + + public int resendThrottle; + public int landThrottle; + public int windThrottle; + public int cloudThrottle; + public int taskThrottle; + public int assetThrottle; + public int textureThrottle; + public int totalThrottle; + + // Used by adaptive only + public int targetThrottle; + + public int maxThrottle; + + public Dictionary SyncRequests = new Dictionary(); + public Dictionary AsyncRequests = new Dictionary(); + public Dictionary GenericRequests = new Dictionary(); + } +} diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs new file mode 100644 index 0000000000..baff2f4354 --- /dev/null +++ b/OpenSim/Framework/ClientManager.cs @@ -0,0 +1,225 @@ +/* + * 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.Net; +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace OpenSim.Framework +{ + /// + /// Maps from client AgentID and RemoteEndPoint values to IClientAPI + /// references for all of the connected clients + /// + public class ClientManager + { + /// A dictionary mapping from + /// to references + private Dictionary m_dict1; + /// A dictionary mapping from + /// to references + private Dictionary m_dict2; + /// An immutable collection of + /// references + private IClientAPI[] m_array; + /// Synchronization object for writing to the collections + private object m_syncRoot = new object(); + + /// Number of clients in the collection + public int Count { get { return m_dict1.Count; } } + + /// + /// Default constructor + /// + public ClientManager() + { + m_dict1 = new Dictionary(); + m_dict2 = new Dictionary(); + m_array = new IClientAPI[0]; + } + + /// + /// Add a client reference to the collection if it does not already + /// exist + /// + /// Reference to the client object + /// True if the client reference was successfully added, + /// otherwise false if the given key already existed in the collection + public bool Add(IClientAPI value) + { + lock (m_syncRoot) + { + if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint)) + return false; + + m_dict1[value.AgentId] = value; + m_dict2[value.RemoteEndPoint] = value; + + IClientAPI[] oldArray = m_array; + int oldLength = oldArray.Length; + + IClientAPI[] newArray = new IClientAPI[oldLength + 1]; + for (int i = 0; i < oldLength; i++) + newArray[i] = oldArray[i]; + newArray[oldLength] = value; + + m_array = newArray; + } + + return true; + } + + /// + /// Remove a client from the collection + /// + /// UUID of the client to remove + /// True if a client was removed, or false if the given UUID + /// was not present in the collection + public bool Remove(UUID key) + { + lock (m_syncRoot) + { + IClientAPI value; + if (m_dict1.TryGetValue(key, out value)) + { + m_dict1.Remove(key); + m_dict2.Remove(value.RemoteEndPoint); + + IClientAPI[] oldArray = m_array; + int oldLength = oldArray.Length; + + IClientAPI[] newArray = new IClientAPI[oldLength - 1]; + int j = 0; + for (int i = 0; i < oldLength; i++) + { + if (oldArray[i] != value) + newArray[j++] = oldArray[i]; + } + + m_array = newArray; + return true; + } + } + + return false; + } + + /// + /// Resets the client collection + /// + public void Clear() + { + lock (m_syncRoot) + { + m_dict1.Clear(); + m_dict2.Clear(); + m_array = new IClientAPI[0]; + } + } + + /// + /// Checks if a UUID is in the collection + /// + /// UUID to check for + /// True if the UUID was found in the collection, otherwise false + public bool ContainsKey(UUID key) + { + return m_dict1.ContainsKey(key); + } + + /// + /// Checks if an endpoint is in the collection + /// + /// Endpoint to check for + /// True if the endpoint was found in the collection, otherwise false + public bool ContainsKey(IPEndPoint key) + { + return m_dict2.ContainsKey(key); + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// UUID of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(UUID key, out IClientAPI value) + { + try { return m_dict1.TryGetValue(key, out value); } + catch (Exception) + { + value = null; + return false; + } + } + + /// + /// Attempts to fetch a value out of the collection + /// + /// Endpoint of the client to retrieve + /// Retrieved client, or null on lookup failure + /// True if the lookup succeeded, otherwise false + public bool TryGetValue(IPEndPoint key, out IClientAPI value) + { + try { return m_dict2.TryGetValue(key, out value); } + catch (Exception) + { + value = null; + return false; + } + } + + /// + /// Performs a given task in parallel for each of the elements in the + /// collection + /// + /// Action to perform on each element + public void ForEach(Action action) + { + IClientAPI[] localArray = m_array; + Parallel.For(0, localArray.Length, + delegate(int i) + { action(localArray[i]); } + ); + } + + /// + /// Performs a given task synchronously for each of the elements in + /// the collection + /// + /// Action to perform on each element + public void ForEachSync(Action action) + { + IClientAPI[] localArray = m_array; + for (int i = 0; i < localArray.Length; i++) + action(localArray[i]); + } + } +} diff --git a/OpenSim/Framework/CnmMemoryCache.cs b/OpenSim/Framework/CnmMemoryCache.cs new file mode 100644 index 0000000000..92af331102 --- /dev/null +++ b/OpenSim/Framework/CnmMemoryCache.cs @@ -0,0 +1,1870 @@ +/* + * 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.Diagnostics; + +namespace OpenSim.Framework +{ + /// + /// Cenome memory based cache to store key/value pairs (elements) limited time and/or limited size. + /// + /// + /// The type of keys in the cache. + /// + /// + /// The type of values in the dictionary. + /// + /// + /// + /// Cenome memory cache stores elements to hash table generations. When new element is being added to cache, and new size would exceed + /// maximal allowed size or maximal amount of allowed element count, then elements in oldest generation are deleted. Last access time + /// is also tracked in generation level - thus it is possible that some elements are staying in cache far beyond their expiration time. + /// If elements in older generations are accessed through method, they are moved to newest generation. + /// + /// + public class CnmMemoryCache : ICnmCache + { + /// + /// Default maximal count. + /// + /// + public const int DefaultMaxCount = 4096; + + /// + /// Default maximal size. + /// + /// + /// + /// 128MB = 128 * 1024^2 = 134 217 728 bytes. + /// + /// + /// + public const long DefaultMaxSize = 134217728; + + /// + /// How many operations between time checks. + /// + private const int DefaultOperationsBetweenTimeChecks = 40; + + /// + /// Default expiration time. + /// + /// + /// + /// 30 minutes. + /// + /// + public static readonly TimeSpan DefaultExpirationTime = TimeSpan.FromMinutes(30.0); + + /// + /// Minimal allowed expiration time. + /// + /// + /// + /// 5 minutes. + /// + /// + public static readonly TimeSpan MinExpirationTime = TimeSpan.FromSeconds(10.0); + + /// + /// Comparer used to compare element keys. + /// + /// + /// Comparer is initialized by constructor. + /// + /// + public readonly IEqualityComparer Comparer; + + /// + /// Expiration time. + /// + private TimeSpan m_expirationTime = DefaultExpirationTime; + + /// + /// Generation bucket count. + /// + private int m_generationBucketCount; + + /// + /// Generation entry count. + /// + private int m_generationElementCount; + + /// + /// Generation max size. + /// + private long m_generationMaxSize; + + /// + /// Maximal allowed count of elements. + /// + private int m_maxCount; + + /// + /// Maximal allowed total size of elements. + /// + private long m_maxElementSize; + + /// + /// Maximal size. + /// + private long m_maxSize; + + /// + /// New generation. + /// + private IGeneration m_newGeneration; + + /// + /// Old generation. + /// + private IGeneration m_oldGeneration; + + /// + /// Operations between time check. + /// + private int m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks; + + /// + /// Synchronization root object, should always be private and exists always + /// + private readonly object m_syncRoot = new object(); + + /// + /// Version of cache. + /// + /// + /// + /// Updated every time when cache has been changed (element removed, expired, added, replaced). + /// + /// + private int m_version; + + /// + /// Initializes a new instance of the class. + /// + public CnmMemoryCache() + : this(DefaultMaxSize) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Maximal cache size. + /// + public CnmMemoryCache(long maximalSize) + : this(maximalSize, DefaultMaxCount) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Maximal cache size. + /// + /// + /// Maximal element count. + /// + public CnmMemoryCache(long maximalSize, int maximalCount) + : this(maximalSize, maximalCount, DefaultExpirationTime) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Maximal cache size. + /// + /// + /// Maximal element count. + /// + /// + /// Elements expiration time. + /// + public CnmMemoryCache(long maximalSize, int maximalCount, TimeSpan expirationTime) + : this(maximalSize, maximalCount, expirationTime, EqualityComparer.Default) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Maximal cache size. + /// + /// + /// Maximal element count. + /// + /// + /// Elements expiration time. + /// + /// + /// Comparer used for comparing elements. + /// + /// + /// is reference. + /// + public CnmMemoryCache(long maximalSize, + int maximalCount, + TimeSpan expirationTime, + IEqualityComparer comparer) + { + if (comparer == null) + throw new ArgumentNullException("comparer"); + + if (expirationTime < MinExpirationTime) + expirationTime = MinExpirationTime; + if (maximalCount < 8) + maximalCount = 8; + if (maximalSize < 8) + maximalSize = 8; + if (maximalCount > maximalSize) + maximalCount = (int) maximalSize; + + Comparer = comparer; + m_expirationTime = expirationTime; + m_maxSize = maximalSize; + m_maxCount = maximalCount; + + Initialize(); + } + + /// + /// Add element to new generation. + /// + /// + /// The bucket index. + /// + /// + /// The element's key. + /// + /// + /// The element's value. + /// + /// + /// The element's size. + /// + protected virtual void AddToNewGeneration(int bucketIndex, TKey key, TValue value, long size) + { + // Add to newest generation + if (!m_newGeneration.Set(bucketIndex, key, value, size)) + { + // Failed to add new generation + RecycleGenerations(); + m_newGeneration.Set(bucketIndex, key, value, size); + } + + m_version++; + } + + /// + /// + /// Get keys bucket index. + /// + /// + /// + /// + /// Key which bucket index is being retrieved. + /// + /// + /// + /// + /// Bucket index. + /// + /// + /// + /// + /// Method uses to calculate hash code. + /// + /// + /// Bucket index is remainder when element key's hash value is divided by bucket count. + /// + /// + /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2. + /// + /// + protected virtual int GetBucketIndex(TKey key) + { + return (Comparer.GetHashCode(key) & 0x7FFFFFFF) % m_generationBucketCount; + } + + /// + /// Purge generation from the cache. + /// + /// + /// The generation that is purged. + /// + protected virtual void PurgeGeneration(IGeneration generation) + { + generation.Clear(); + m_version++; + } + + /// + /// check expired. + /// + private void CheckExpired() + { + // Do this only one in every m_operationsBetweenTimeChecks + // Fetching time is using several millisecons - it is better not to do all time. + m_operationsBetweenTimeChecks--; + if (m_operationsBetweenTimeChecks <= 0) + PurgeExpired(); + } + + /// + /// Initialize cache. + /// + private void Initialize() + { + m_version++; + + m_generationMaxSize = MaxSize / 2; + MaxElementSize = MaxSize / 8; + m_generationElementCount = MaxCount / 2; + + // Buckets need to be prime number to get better spread of hash values + m_generationBucketCount = PrimeNumberHelper.GetPrime(m_generationElementCount); + + m_newGeneration = new HashGeneration(this); + m_oldGeneration = new HashGeneration(this); + m_oldGeneration.MakeOld(); + } + + /// + /// Recycle generations. + /// + private void RecycleGenerations() + { + // Rotate old generation to new generation, new generation to old generation + IGeneration temp = m_newGeneration; + m_newGeneration = m_oldGeneration; + m_newGeneration.Clear(); + m_oldGeneration = temp; + m_oldGeneration.MakeOld(); + } + + #region Nested type: Enumerator + + /// + /// Key and value pair enumerator. + /// + private class Enumerator : IEnumerator> + { + /// + /// Current enumerator. + /// + private int m_currentEnumerator = -1; + + /// + /// Enumerators to different generations. + /// + private readonly IEnumerator>[] m_generationEnumerators = + new IEnumerator>[2]; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The cache. + /// + public Enumerator(CnmMemoryCache cache) + { + m_generationEnumerators[ 0 ] = cache.m_newGeneration.GetEnumerator(); + m_generationEnumerators[ 1 ] = cache.m_oldGeneration.GetEnumerator(); + } + + #region IEnumerator> Members + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + /// + /// The enumerator has reach end of collection or is not called. + /// + public KeyValuePair Current + { + get + { + if (m_currentEnumerator == -1 || m_currentEnumerator >= m_generationEnumerators.Length) + throw new InvalidOperationException(); + + return m_generationEnumerators[ m_currentEnumerator ].Current; + } + } + + /// + /// Gets the current element in the collection. + /// + /// + /// The current element in the collection. + /// + /// + /// The enumerator is positioned before the first element of the collection or after the last element. + /// 2 + object IEnumerator.Current + { + get { return Current; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// 2 + public void Dispose() + { + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// if the enumerator was successfully advanced to the next element; if the enumerator has passed the end of the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + /// 2 + public bool MoveNext() + { + if (m_currentEnumerator == -1) + m_currentEnumerator = 0; + + while (m_currentEnumerator < m_generationEnumerators.Length) + { + if (m_generationEnumerators[ m_currentEnumerator ].MoveNext()) + return true; + + m_currentEnumerator++; + } + + return false; + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + /// 2 + public void Reset() + { + foreach (IEnumerator> enumerator in m_generationEnumerators) + { + enumerator.Reset(); + } + + m_currentEnumerator = -1; + } + + #endregion + } + + #endregion + + #region Nested type: HashGeneration + + /// + /// Hash generation class + /// + /// + /// + /// Current implementation is based to separated chaining with move-to-front heuristics. Hash generations have fixed + /// amount of buckets and it is never rehashed. + /// + /// + /// Read more about hash tables from Wiki article. + /// + /// + /// + private class HashGeneration : IGeneration + { + /// + /// Value indicating whether generation was accessed since last time check. + /// + private bool m_accessedSinceLastTimeCheck; + + /// + /// Index of first element's in element chain. + /// + /// + /// -1 if there is no element in bucket; otherwise first element's index in the element chain. + /// + /// + /// Bucket index is remainder when element key's hash value is divided by bucket count. + /// For example: key's hash is 72, bucket count is 5, element's bucket index is 72 % 5 = 2. + /// + private readonly int[] m_buckets; + + /// + /// Cache object. + /// + private readonly CnmMemoryCache m_cache; + + /// + /// Generation's element array. + /// + /// + private readonly Element[] m_elements; + + /// + /// Generation's expiration time. + /// + private DateTime m_expirationTime1; + + /// + /// Index to first free element. + /// + private int m_firstFreeElement; + + /// + /// Free element count. + /// + /// + /// When generation is cleared or constructed, this is NOT set to element count. + /// This is only tracking elements that are removed and are currently free. + /// + private int m_freeCount; + + /// + /// Is this generation "new generation". + /// + private bool m_newGeneration; + + /// + /// Next unused entry. + /// + private int m_nextUnusedElement; + + /// + /// Size of data stored to generation. + /// + private long m_size; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The cache. + /// + public HashGeneration(CnmMemoryCache cache) + { + m_cache = cache; + m_elements = new Element[m_cache.m_generationElementCount]; + m_buckets = new int[m_cache.m_generationBucketCount]; + Clear(); + } + + /// + /// Find element's index + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// Move element to front of elements. + /// + /// + /// The previous element's index. + /// + /// + /// Element's index, if found from the generation; -1 otherwise (if element is not found the generation). + /// + private int FindElementIndex(int bucketIndex, TKey key, bool moveToFront, out int previousIndex) + { + previousIndex = -1; + int elementIndex = m_buckets[ bucketIndex ]; + while (elementIndex >= 0) + { + if (m_cache.Comparer.Equals(key, m_elements[ elementIndex ].Key)) + { + // Found match + if (moveToFront && previousIndex >= 0) + { + // Move entry to front + m_elements[ previousIndex ].Next = m_elements[ elementIndex ].Next; + m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ]; + m_buckets[ bucketIndex ] = elementIndex; + previousIndex = 0; + } + + return elementIndex; + } + + previousIndex = elementIndex; + elementIndex = m_elements[ elementIndex ].Next; + } + + return -1; + } + + /// + /// Remove element front the generation. + /// + /// + /// The bucket index. + /// + /// + /// The element index. + /// + /// + /// The element's previous index. + /// + private void RemoveElement(int bucketIndex, int entryIndex, int previousIndex) + { + if (previousIndex >= 0) + m_elements[ previousIndex ].Next = m_elements[ entryIndex ].Next; + else + m_buckets[ bucketIndex ] = m_elements[ entryIndex ].Next; + + Size -= m_elements[ entryIndex ].Size; + m_elements[ entryIndex ].Value = default(TValue); + m_elements[ entryIndex ].Key = default(TKey); + + // Add element to free elements list + m_elements[ entryIndex ].Next = m_firstFreeElement; + m_firstFreeElement = entryIndex; + m_freeCount++; + } + + #region Nested type: Element + + /// + /// Element that stores key, next element in chain, size and value. + /// + private struct Element + { + /// + /// Element's key. + /// + public TKey Key; + + /// + /// Next element in chain. + /// + /// + /// When element have value (something is stored to it), this is index of + /// next element with same bucket index. When element is free, this + /// is index of next element in free element's list. + /// + public int Next; + + /// + /// Size of element. + /// + /// + /// 0 if element is free; otherwise larger than 0. + /// + public long Size; + + /// + /// Element's value. + /// + /// + /// It is possible that this value is even when element + /// have value - element's value is then reference. + /// + public TValue Value; + + /// + /// Gets a value indicating whether element is free or have value. + /// + /// + /// when element is free; otherwise . + /// + public bool IsFree + { + get { return Size == 0; } + } + } + + #endregion + + #region Nested type: Enumerator + + /// + /// Key value pair enumerator for object. + /// + private class Enumerator : IEnumerator> + { + /// + /// Current element. + /// + private KeyValuePair m_current; + + /// + /// Current index. + /// + private int m_currentIndex; + + /// + /// Generation that is being enumerated. + /// + private readonly HashGeneration m_generation; + + /// + /// Cache version. + /// + /// + /// When cache is change, version number is changed. + /// + /// + private readonly int m_version; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The generation. + /// + public Enumerator(HashGeneration generation) + { + m_generation = generation; + m_version = m_generation.m_cache.m_version; + } + + #region IEnumerator> Members + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + /// + /// The enumerator has reach end of collection or is not called. + /// + public KeyValuePair Current + { + get + { + if (m_currentIndex == 0 || m_currentIndex >= m_generation.Count) + throw new InvalidOperationException(); + + return m_current; + } + } + + /// + /// Gets the current element in the collection. + /// + /// + /// The current element in the collection. + /// + /// + /// The enumerator is positioned before the first element of the collection or after the last element. + /// 2 + object IEnumerator.Current + { + get { return Current; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// 2 + public void Dispose() + { + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public bool MoveNext() + { + if (m_version != m_generation.m_cache.m_version) + throw new InvalidOperationException(); + + while (m_currentIndex < m_generation.Count) + { + if (m_generation.m_elements[ m_currentIndex ].IsFree) + { + m_currentIndex++; + continue; + } + + m_current = new KeyValuePair(m_generation.m_elements[ m_currentIndex ].Key, + m_generation.m_elements[ m_currentIndex ].Value); + m_currentIndex++; + return true; + } + + m_current = new KeyValuePair(); + return false; + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + /// 2 + public void Reset() + { + if (m_version != m_generation.m_cache.m_version) + throw new InvalidOperationException(); + + m_currentIndex = 0; + } + + #endregion + } + + #endregion + + #region IGeneration Members + + /// + /// Gets or sets a value indicating whether generation was accessed since last time check. + /// + public bool AccessedSinceLastTimeCheck + { + get { return m_accessedSinceLastTimeCheck; } + + set { m_accessedSinceLastTimeCheck = value; } + } + + /// + /// Gets element count in generation. + /// + public int Count + { + get { return m_nextUnusedElement - m_freeCount; } + } + + /// + /// Gets or sets generation's expiration time. + /// + public DateTime ExpirationTime + { + get { return m_expirationTime1; } + + set { m_expirationTime1 = value; } + } + + /// + /// Gets or sets size of data stored to generation. + /// + public long Size + { + get { return m_size; } + + private set { m_size = value; } + } + + /// + /// Clear all elements from the generation and make generation new again. + /// + /// + /// When generation is new, it is allowed to add new elements to it and + /// doesn't remove elements from it. + /// + /// + public void Clear() + { + for (int i = m_buckets.Length - 1 ; i >= 0 ; i--) + { + m_buckets[ i ] = -1; + } + + Array.Clear(m_elements, 0, m_elements.Length); + Size = 0; + m_firstFreeElement = -1; + m_freeCount = 0; + m_nextUnusedElement = 0; + m_newGeneration = true; + ExpirationTime = DateTime.MaxValue; + } + + /// + /// Determines whether the contains an element with the specific key. + /// + /// + /// The bucket index for the to locate in . + /// + /// + /// The key to locate in the . + /// + /// + /// if the contains an element with the ; + /// otherwise . + /// + public bool Contains(int bucketIndex, TKey key) + { + int previousIndex; + if (FindElementIndex(bucketIndex, key, true, out previousIndex) == -1) + return false; + + AccessedSinceLastTimeCheck = true; + return true; + } + + /// + /// Returns an enumerator that iterates through the elements stored . + /// + /// + /// A that can be used to iterate through the . + /// + /// 1 + public IEnumerator> GetEnumerator() + { + return new Enumerator(this); + } + + /// + /// Make from generation old generation. + /// + /// + /// When generation is old, hit removes element from the generation. + /// + /// + public void MakeOld() + { + m_newGeneration = false; + } + + /// + /// Remove element associated with the key from the generation. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// , if remove was successful; otherwise . + /// + public bool Remove(int bucketIndex, TKey key) + { + int previousIndex; + int entryIndex = FindElementIndex(bucketIndex, key, false, out previousIndex); + if (entryIndex != -1) + { + RemoveElement(bucketIndex, entryIndex, previousIndex); + AccessedSinceLastTimeCheck = true; + return true; + } + + return false; + } + + /// + /// Set or add element to generation. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// The element's value. + /// + /// + /// The element's size. + /// + /// + /// , if setting or adding was successful; otherwise . + /// + /// + /// + /// If element was already existing in generation and new element size fits to collection limits, + /// then it's value is replaced with new one and size information is updated. If element didn't + /// exists in generation before, then generation must have empty space for a new element and + /// size must fit generation's limits, before element is added to generation. + /// + /// + public bool Set(int bucketIndex, TKey key, TValue value, long size) + { + Debug.Assert(m_newGeneration, "It is possible to insert new elements only to newest generation."); + Debug.Assert(size > 0, "New element size should be more than 0."); + + int previousIndex; + int elementIndex = FindElementIndex(bucketIndex, key, true, out previousIndex); + if (elementIndex == -1) + { + // New key + if (Size + size > m_cache.m_generationMaxSize || + (m_nextUnusedElement == m_cache.m_generationElementCount && m_freeCount == 0)) + { + // Generation is full + return false; + } + + // Increase size of generation + Size += size; + + // Get first free entry and update free entry list + if (m_firstFreeElement != -1) + { + // There was entry that was removed + elementIndex = m_firstFreeElement; + m_firstFreeElement = m_elements[ elementIndex ].Next; + m_freeCount--; + } + else + { + // No entries removed so far - just take a last one + elementIndex = m_nextUnusedElement; + m_nextUnusedElement++; + } + + Debug.Assert(m_elements[ elementIndex ].IsFree, "Allocated element is not free."); + + // Move new entry to front + m_elements[ elementIndex ].Next = m_buckets[ bucketIndex ]; + m_buckets[ bucketIndex ] = elementIndex; + + // Set key and update count + m_elements[ elementIndex ].Key = key; + } + else + { + // Existing key + if (Size - m_elements[ elementIndex ].Size + size > m_cache.m_generationMaxSize) + { + // Generation is full + // Remove existing element, because generation is going to be recycled to + // old generation and element is stored to new generation + RemoveElement(bucketIndex, elementIndex, previousIndex); + return false; + } + + // Update generation's size + Size = Size - m_elements[ elementIndex ].Size + size; + } + + // Finally set value and size + m_elements[ elementIndex ].Value = value; + m_elements[ elementIndex ].Size = size; + + // Success - key was inserterted to generation + AccessedSinceLastTimeCheck = true; + return true; + } + + /// + /// Try to get element associated with key. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// The element's value. + /// + /// + /// The element's size. + /// + /// + /// , if element was successful retrieved; otherwise . + /// + /// + /// + /// If element is not found from generation then and + /// are set to default value (default(TValue) and 0). + /// + /// + public bool TryGetValue(int bucketIndex, TKey key, out TValue value, out long size) + { + // Find entry index, + int previousIndex; + int elementIndex = FindElementIndex(bucketIndex, key, m_newGeneration, out previousIndex); + if (elementIndex == -1) + { + value = default(TValue); + size = 0; + return false; + } + + value = m_elements[ elementIndex ].Value; + size = m_elements[ elementIndex ].Size; + + if (!m_newGeneration) + { + // Old generation - remove element, because it is moved to new generation + RemoveElement(bucketIndex, elementIndex, previousIndex); + } + + AccessedSinceLastTimeCheck = true; + return true; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + /// 2 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } + + #endregion + + #region Nested type: IGeneration + + /// + /// Cache element generation interface + /// + /// + /// + /// Generation can hold limited count of elements and limited size of data. + /// + /// + /// There are two kind generations: "new generation" and "old generation(s)". All new elements + /// are added to "new generation". + /// + /// + protected interface IGeneration : IEnumerable> + { + /// + /// Gets or sets a value indicating whether generation was accessed since last time check. + /// + bool AccessedSinceLastTimeCheck { get; set; } + + /// + /// Gets element count in generation. + /// + int Count { get; } + + /// + /// Gets or sets generation's expiration time. + /// + DateTime ExpirationTime { get; set; } + + /// + /// Gets size of data stored to generation. + /// + long Size { get; } + + /// + /// Clear all elements from the generation and make generation new again. + /// + /// + /// When generation is new, it is allowed to add new elements to it and + /// doesn't remove elements from it. + /// + /// + void Clear(); + + /// + /// Determines whether the contains an element with the specific key. + /// + /// + /// The bucket index for the to locate in . + /// + /// + /// The key to locate in the . + /// + /// + /// if the contains an element with the ; + /// otherwise . + /// + bool Contains(int bucketIndex, TKey key); + + /// + /// Make from generation old generation. + /// + /// + /// When generation is old, hit removes element from the generation. + /// + /// + void MakeOld(); + + /// + /// Remove element associated with the key from the generation. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// , if remove was successful; otherwise . + /// + bool Remove(int bucketIndex, TKey key); + + /// + /// Set or add element to generation. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// The element's value. + /// + /// + /// The element's size. + /// + /// + /// , if setting or adding was successful; otherwise . + /// + /// + /// + /// If element was already existing in generation and new element size fits to collection limits, + /// then it's value is replaced with new one and size information is updated. If element didn't + /// exists in generation before, then generation must have empty space for a new element and + /// size must fit generation's limits, before element is added to generation. + /// + /// + bool Set(int bucketIndex, TKey key, TValue value, long size); + + /// + /// Try to get element associated with key. + /// + /// + /// The element's bucket index. + /// + /// + /// The element's key. + /// + /// + /// The element's value. + /// + /// + /// The element's size. + /// + /// + /// , if element was successful retrieved; otherwise . + /// + /// + /// + /// If element is not found from generation then and + /// are set to default value (default(TValue) and 0). + /// + /// + bool TryGetValue(int bucketIndex, TKey key, out TValue value, out long size); + } + + #endregion + + #region ICnmCache Members + + /// + /// Gets current count of elements stored to . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + public int Count + { + get { return m_newGeneration.Count + m_oldGeneration.Count; } + } + + /// + /// Gets or sets elements expiration time. + /// + /// + /// Elements expiration time. + /// + /// + /// + /// When element has been stored in longer than + /// and it is not accessed through method or element's value is + /// not replaced by method, then it is automatically removed from the + /// . + /// + /// + /// It is possible that implementation removes element before it's expiration time, + /// because total size or count of elements stored to cache is larger than or . + /// + /// + /// It is also possible that element stays in cache longer than . + /// + /// + /// Calling try to remove all elements that are expired. + /// + /// + /// To disable time limit in cache, set to . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public TimeSpan ExpirationTime + { + get { return m_expirationTime; } + + set + { + if (value < MinExpirationTime) + value = MinExpirationTime; + + if (m_expirationTime == value) + return; + + m_newGeneration.ExpirationTime = (m_newGeneration.ExpirationTime - m_expirationTime) + value; + m_oldGeneration.ExpirationTime = (m_oldGeneration.ExpirationTime - m_expirationTime) + value; + m_expirationTime = value; + + PurgeExpired(); + } + } + + /// + /// Gets a value indicating whether is limiting count of elements. + /// + /// + /// if the count of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + public bool IsCountLimited + { + get { return true; } + } + + /// + /// Gets a value indicating whether is limiting size of elements. + /// + /// + /// if the total size of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + public bool IsSizeLimited + { + get { return true; } + } + + /// + /// Gets a value indicating whether or not access to the is synchronized (thread safe). + /// + /// + /// if access to the is synchronized (thread safe); + /// otherwise, . + /// + /// + /// + /// To get synchronized (thread safe) access to object, use + /// in class + /// to retrieve synchronized wrapper for object. + /// + /// + /// + /// + public bool IsSynchronized + { + get { return false; } + } + + /// + /// Gets a value indicating whether elements stored to have limited inactivity time. + /// + /// + /// if the has a fixed total size of elements; + /// otherwise, . + /// + /// + /// If have limited inactivity time and element is not accessed through + /// or methods in , then element is automatically removed from + /// the cache. Depending on implementation of the , some of the elements may + /// stay longer in cache. + /// + /// + /// + /// + /// + public bool IsTimeLimited + { + get { return ExpirationTime != TimeSpan.MaxValue; } + } + + /// + /// Gets or sets maximal allowed count of elements that can be stored to . + /// + /// + /// , if is not limited by count of elements; + /// otherwise maximal allowed count of elements. + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + public int MaxCount + { + get { return m_maxCount; } + + set + { + if (value < 8) + value = 8; + if (m_maxCount == value) + return; + + m_maxCount = value; + Initialize(); + } + } + + /// + /// Gets maximal allowed element size. + /// + /// + /// Maximal allowed element size. + /// + /// + /// + /// If element's size is larger than , then element is + /// not added to the . + /// + /// + /// + /// + /// + /// + public long MaxElementSize + { + get { return m_maxElementSize; } + + private set { m_maxElementSize = value; } + } + + /// + /// Gets or sets maximal allowed total size for elements stored to . + /// + /// + /// Maximal allowed total size for elements stored to . + /// + /// + /// + /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + public long MaxSize + { + get { return m_maxSize; } + + set + { + if (value < 8) + value = 8; + if (m_maxSize == value) + return; + + m_maxSize = value; + Initialize(); + } + } + + /// + /// Gets total size of elements stored to . + /// + /// + /// Total size of elements stored to . + /// + /// + /// + /// Normally bytes, but can be any suitable unit of measure. + /// + /// + /// Element's size is given when element is added or replaced by method. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + public long Size + { + get { return m_newGeneration.Size + m_oldGeneration.Size; } + } + + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + /// + /// + /// To get synchronized (thread safe) access to , use + /// method to retrieve synchronized wrapper interface to + /// . + /// + /// + /// + /// + public object SyncRoot + { + get { return m_syncRoot; } + } + + /// + /// Removes all elements from the . + /// + /// + /// + /// + /// + /// + public void Clear() + { + m_newGeneration.Clear(); + m_oldGeneration.Clear(); + m_oldGeneration.MakeOld(); + m_version++; + } + + /// + /// Returns an enumerator that iterates through the elements stored to . + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator> GetEnumerator() + { + return new Enumerator(this); + } + + /// + /// Purge expired elements from the . + /// + /// + /// + /// Element becomes expired when last access time to it has been longer time than . + /// + /// + /// Depending on implementation, some of expired elements + /// may stay longer than in the cache. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void PurgeExpired() + { + m_operationsBetweenTimeChecks = DefaultOperationsBetweenTimeChecks; + + if (!IsTimeLimited) + return; + + DateTime now = DateTime.Now; + if (m_newGeneration.AccessedSinceLastTimeCheck) + { + // New generation has been accessed since last check + // Update it's expiration time. + m_newGeneration.ExpirationTime = now + ExpirationTime; + m_newGeneration.AccessedSinceLastTimeCheck = false; + } + else if (m_newGeneration.ExpirationTime < now) + { + // New generation has been expired. + // --> also old generation must be expired. + PurgeGeneration(m_newGeneration); + PurgeGeneration(m_oldGeneration); + return; + } + + if (m_oldGeneration.ExpirationTime < now) + PurgeGeneration(m_oldGeneration); + } + + /// + /// Removes element associated with from the . + /// + /// + /// The key that is associated with element to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public void Remove(TKey key) + { + if (key == null) + throw new ArgumentNullException("key"); + + int bucketIndex = GetBucketIndex(key); + if (!m_newGeneration.Remove(bucketIndex, key)) + { + if (!m_oldGeneration.Remove(bucketIndex, key)) + { + CheckExpired(); + return; + } + } + + CheckExpired(); + m_version++; + } + + /// + /// Removes elements that are associated with one of from the . + /// + /// + /// The keys that are associated with elements to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public void RemoveRange(IEnumerable keys) + { + if (keys == null) + throw new ArgumentNullException("keys"); + + foreach (TKey key in keys) + { + if (key == null) + continue; + + int bucketIndex = GetBucketIndex(key); + if (!m_newGeneration.Remove(bucketIndex, key)) + m_oldGeneration.Remove(bucketIndex, key); + } + + CheckExpired(); + m_version++; + } + + /// + /// Add or replace an element with the provided , and to + /// . + /// + /// + /// The object used as the key of the element. Can't be reference. + /// + /// + /// The object used as the value of the element to add or replace. is allowed. + /// + /// + /// The element's size. Normally bytes, but can be any suitable unit of measure. + /// + /// + /// if element has been added successfully to the ; + /// otherwise . + /// + /// + /// is . + /// + /// + /// The element's is less than 0. + /// + /// + /// + /// If element's is larger than , then element is + /// not added to the , however - possible older element is + /// removed from the . + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool Set(TKey key, TValue value, long size) + { + if (key == null) + throw new ArgumentNullException("key"); + + if (size < 0) + throw new ArgumentOutOfRangeException("size", size, "Value's size can't be less than 0."); + + if (size > MaxElementSize) + { + // Entry size is too big to fit cache - ignore it + Remove(key); + return false; + } + + if (size == 0) + size = 1; + + int bucketIndex = GetBucketIndex(key); + m_oldGeneration.Remove(bucketIndex, key); + AddToNewGeneration(bucketIndex, key, value, size); + CheckExpired(); + + return true; + } + + /// + /// Gets the associated with the specified . + /// + /// + /// if the contains an element with + /// the specified key; otherwise, . + /// + /// + /// The key whose to get. + /// + /// + /// When this method returns, the value associated with the specified , + /// if the is found; otherwise, the + /// default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public bool TryGetValue(TKey key, out TValue value) + { + if (key == null) + throw new ArgumentNullException("key"); + + int bucketIndex = GetBucketIndex(key); + long size; + if (m_newGeneration.TryGetValue(bucketIndex, key, out value, out size)) + { + CheckExpired(); + return true; + } + + if (m_oldGeneration.TryGetValue(bucketIndex, key, out value, out size)) + { + // Move element to new generation + AddToNewGeneration(bucketIndex, key, value, size); + CheckExpired(); + return true; + } + + CheckExpired(); + return false; + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + /// 2 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/CnmSynchronizedCache.cs b/OpenSim/Framework/CnmSynchronizedCache.cs new file mode 100644 index 0000000000..2bafbe98a7 --- /dev/null +++ b/OpenSim/Framework/CnmSynchronizedCache.cs @@ -0,0 +1,747 @@ +/* + * 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.Threading; + +namespace OpenSim.Framework +{ + /// + /// Synchronized Cenome cache wrapper. + /// + /// + /// The type of keys in the cache. + /// + /// + /// The type of values in the cache. + /// + /// + /// + /// Enumerator will block other threads, until enumerator's method is called. + /// "foreach" statement is automatically calling it. + /// + /// + public class CnmSynchronizedCache : ICnmCache + { + /// + /// The cache object. + /// + private readonly ICnmCache m_cache; + + /// + /// Synchronization root. + /// + private readonly object m_syncRoot; + + /// + /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + /// + /// The cache. + /// + private CnmSynchronizedCache(ICnmCache cache) + { + m_cache = cache; + m_syncRoot = m_cache.SyncRoot; + } + + /// + /// Returns a wrapper that is synchronized (thread safe). + /// + /// + /// The to synchronize. + /// + /// + /// A wrapper that is synchronized (thread safe). + /// + /// + /// is null. + /// + public static ICnmCache Synchronized(ICnmCache cache) + { + if (cache == null) + throw new ArgumentNullException("cache"); + return cache.IsSynchronized ? cache : new CnmSynchronizedCache(cache); + } + + #region Nested type: SynchronizedEnumerator + + /// + /// Synchronized enumerator. + /// + private class SynchronizedEnumerator : IEnumerator> + { + /// + /// Enumerator that is being synchronized. + /// + private readonly IEnumerator> m_enumerator; + + /// + /// Synchronization root. + /// + private object m_syncRoot; + + /// + /// Initializes a new instance of the class. + /// + /// + /// The enumerator that is being synchronized. + /// + /// + /// The sync root. + /// + public SynchronizedEnumerator(IEnumerator> enumerator, object syncRoot) + { + m_syncRoot = syncRoot; + m_enumerator = enumerator; + Monitor.Enter(m_syncRoot); + } + + /// + /// Finalizes an instance of the class. + /// + ~SynchronizedEnumerator() + { + Dispose(); + } + + #region IEnumerator> Members + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + /// + /// The element in the collection at the current position of the enumerator. + /// + /// + /// The enumerator has reach end of collection or is not called. + /// + public KeyValuePair Current + { + get { return m_enumerator.Current; } + } + + /// + /// Gets the current element in the collection. + /// + /// + /// The current element in the collection. + /// + /// + /// The enumerator is positioned before the first element of the collection or after the last element. + /// 2 + object IEnumerator.Current + { + get { return Current; } + } + + /// + /// Releases synchronization lock. + /// + public void Dispose() + { + if (m_syncRoot != null) + { + Monitor.Exit(m_syncRoot); + m_syncRoot = null; + } + + m_enumerator.Dispose(); + GC.SuppressFinalize(this); + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public bool MoveNext() + { + return m_enumerator.MoveNext(); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// + /// The collection was modified after the enumerator was created. + /// + public void Reset() + { + m_enumerator.Reset(); + } + + #endregion + } + + #endregion + + #region ICnmCache Members + + /// + /// Gets current count of elements stored to . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + public int Count + { + get + { + lock (m_syncRoot) + { + return m_cache.Count; + } + } + } + + /// + /// Gets or sets elements expiration time. + /// + /// + /// Elements expiration time. + /// + /// + /// + /// When element has been stored in longer than + /// and it is not accessed through method or element's value is + /// not replaced by method, then it is automatically removed from the + /// . + /// + /// + /// It is possible that implementation removes element before it's expiration time, + /// because total size or count of elements stored to cache is larger than or . + /// + /// + /// It is also possible that element stays in cache longer than . + /// + /// + /// Calling try to remove all elements that are expired. + /// + /// + /// To disable time limit in cache, set to . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public TimeSpan ExpirationTime + { + get + { + lock (m_syncRoot) + { + return m_cache.ExpirationTime; + } + } + + set + { + lock (m_syncRoot) + { + m_cache.ExpirationTime = value; + } + } + } + + /// + /// Gets a value indicating whether is limiting count of elements. + /// + /// + /// if the count of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + public bool IsCountLimited + { + get + { + lock (m_syncRoot) + { + return m_cache.IsCountLimited; + } + } + } + + /// + /// Gets a value indicating whether is limiting size of elements. + /// + /// + /// if the total size of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + public bool IsSizeLimited + { + get + { + lock (m_syncRoot) + { + return m_cache.IsSizeLimited; + } + } + } + + /// + /// Gets a value indicating whether or not access to the is synchronized (thread safe). + /// + /// + /// if access to the is synchronized (thread safe); + /// otherwise, . + /// + /// + /// + /// To get synchronized (thread safe) access to object, use + /// in class + /// to retrieve synchronized wrapper for object. + /// + /// + /// + /// + public bool IsSynchronized + { + get { return true; } + } + + /// + /// Gets a value indicating whether elements stored to have limited inactivity time. + /// + /// + /// if the has a fixed total size of elements; + /// otherwise, . + /// + /// + /// If have limited inactivity time and element is not accessed through + /// or methods in , then element is automatically removed from + /// the cache. Depending on implementation of the , some of the elements may + /// stay longer in cache. + /// + /// + /// + /// + /// + public bool IsTimeLimited + { + get + { + lock (m_syncRoot) + { + return m_cache.IsTimeLimited; + } + } + } + + /// + /// Gets or sets maximal allowed count of elements that can be stored to . + /// + /// + /// , if is not limited by count of elements; + /// otherwise maximal allowed count of elements. + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + public int MaxCount + { + get + { + lock (m_syncRoot) + { + return m_cache.MaxCount; + } + } + + set + { + lock (m_syncRoot) + { + m_cache.MaxCount = value; + } + } + } + + /// + /// Gets maximal allowed element size. + /// + /// + /// Maximal allowed element size. + /// + /// + /// + /// If element's size is larger than , then element is + /// not added to the . + /// + /// + /// + /// + /// + /// + public long MaxElementSize + { + get + { + lock (m_syncRoot) + { + return m_cache.MaxElementSize; + } + } + } + + /// + /// Gets or sets maximal allowed total size for elements stored to . + /// + /// + /// Maximal allowed total size for elements stored to . + /// + /// + /// + /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// value is less than 0. + /// + /// + /// + public long MaxSize + { + get + { + lock (m_syncRoot) + { + return m_cache.MaxSize; + } + } + + set + { + lock (m_syncRoot) + { + m_cache.MaxSize = value; + } + } + } + + /// + /// Gets total size of elements stored to . + /// + /// + /// Total size of elements stored to . + /// + /// + /// + /// Normally bytes, but can be any suitable unit of measure. + /// + /// + /// Element's size is given when element is added or replaced by method. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + public long Size + { + get + { + lock (m_syncRoot) + { + return m_cache.Size; + } + } + } + + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + /// + /// + /// To get synchronized (thread safe) access to , use + /// method to retrieve synchronized wrapper interface to + /// . + /// + /// + /// + /// + public object SyncRoot + { + get { return m_syncRoot; } + } + + /// + /// Removes all elements from the . + /// + /// + /// + /// + /// + /// + public void Clear() + { + lock (m_syncRoot) + { + m_cache.Clear(); + } + } + + /// + /// Returns an enumerator that iterates through the elements stored to . + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + public IEnumerator> GetEnumerator() + { + lock (m_syncRoot) + { + return new SynchronizedEnumerator(m_cache.GetEnumerator(), m_syncRoot); + } + } + + /// + /// Purge expired elements from the . + /// + /// + /// + /// Element becomes expired when last access time to it has been longer time than . + /// + /// + /// Depending on implementation, some of expired elements + /// may stay longer than in the cache. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public void PurgeExpired() + { + lock (m_syncRoot) + { + m_cache.PurgeExpired(); + } + } + + /// + /// Removes element associated with from the . + /// + /// + /// The key that is associated with element to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public void Remove(TKey key) + { + lock (m_syncRoot) + { + m_cache.Remove(key); + } + } + + /// + /// Removes elements that are associated with one of from the . + /// + /// + /// The keys that are associated with elements to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public void RemoveRange(IEnumerable keys) + { + lock (m_syncRoot) + { + m_cache.RemoveRange(keys); + } + } + + /// + /// Add or replace an element with the provided , and to + /// . + /// + /// + /// The object used as the key of the element. Can't be reference. + /// + /// + /// The object used as the value of the element to add or replace. is allowed. + /// + /// + /// The element's size. Normally bytes, but can be any suitable unit of measure. + /// + /// + /// if element has been added successfully to the ; + /// otherwise . + /// + /// + /// is . + /// + /// + /// The element's is less than 0. + /// + /// + /// + /// If element's is larger than , then element is + /// not added to the , however - possible older element is + /// removed from the . + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public bool Set(TKey key, TValue value, long size) + { + lock (m_syncRoot) + { + return m_cache.Set(key, value, size); + } + } + + /// + /// Gets the associated with the specified . + /// + /// + /// if the contains an element with + /// the specified key; otherwise, . + /// + /// + /// The key whose to get. + /// + /// + /// When this method returns, the value associated with the specified , + /// if the is found; otherwise, the + /// default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + public bool TryGetValue(TKey key, out TValue value) + { + lock (m_syncRoot) + { + return m_cache.TryGetValue(key, out value); + } + } + + /// + /// Returns an enumerator that iterates through the elements stored to . + /// + /// + /// A that can be used to iterate through the collection. + /// + /// 1 + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/ColliderData.cs b/OpenSim/Framework/ColliderData.cs new file mode 100644 index 0000000000..1b7b68269b --- /dev/null +++ b/OpenSim/Framework/ColliderData.cs @@ -0,0 +1,53 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class DetectedObject + { + public DetectedObject() { } + public UUID groupUUID = UUID.Zero; + public UUID ownerUUID = UUID.Zero; + public UUID keyUUID = UUID.Zero; + public Vector3 posVector = Vector3.Zero; + public Quaternion rotQuat = Quaternion.Identity; + public Vector3 velVector = Vector3.Zero; + public string nameStr = String.Empty; + public int colliderType = 0; + } + + public class ColliderArgs : EventArgs + { + public ColliderArgs() { } + public List Colliders = new List(); + + } +} diff --git a/OpenSim/Framework/Communications/GenericAsyncResult.cs b/OpenSim/Framework/Communications/GenericAsyncResult.cs new file mode 100644 index 0000000000..8e3f62b40e --- /dev/null +++ b/OpenSim/Framework/Communications/GenericAsyncResult.cs @@ -0,0 +1,185 @@ +/* + * 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.Threading; + +namespace OpenSim.Framework.Communications +{ + internal class SimpleAsyncResult : IAsyncResult + { + private readonly AsyncCallback m_callback; + + /// + /// Is process completed? + /// + /// Should really be boolean, but VolatileRead has no boolean method + private byte m_completed; + + /// + /// Did process complete synchronously? + /// + /// I have a hard time imagining a scenario where this is the case, again, same issue about + /// booleans and VolatileRead as m_completed + /// + private byte m_completedSynchronously; + + private readonly object m_asyncState; + private ManualResetEvent m_waitHandle; + private Exception m_exception; + + internal SimpleAsyncResult(AsyncCallback cb, object state) + { + m_callback = cb; + m_asyncState = state; + m_completed = 0; + m_completedSynchronously = 1; + } + + #region IAsyncResult Members + + public object AsyncState + { + get { return m_asyncState; } + } + + public WaitHandle AsyncWaitHandle + { + get + { + if (m_waitHandle == null) + { + bool done = IsCompleted; + ManualResetEvent mre = new ManualResetEvent(done); + if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null) + { + mre.Close(); + } + else + { + if (!done && IsCompleted) + { + m_waitHandle.Set(); + } + } + } + + return m_waitHandle; + } + } + + + public bool CompletedSynchronously + { + get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; } + } + + + public bool IsCompleted + { + get { return Thread.VolatileRead(ref m_completed) == 1; } + } + + #endregion + + #region class Methods + + internal void SetAsCompleted(bool completedSynchronously) + { + m_completed = 1; + if (completedSynchronously) + m_completedSynchronously = 1; + else + m_completedSynchronously = 0; + + SignalCompletion(); + } + + internal void HandleException(Exception e, bool completedSynchronously) + { + m_completed = 1; + if (completedSynchronously) + m_completedSynchronously = 1; + else + m_completedSynchronously = 0; + m_exception = e; + + SignalCompletion(); + } + + private void SignalCompletion() + { + if (m_waitHandle != null) m_waitHandle.Set(); + + if (m_callback != null) m_callback(this); + } + + public void EndInvoke() + { + // This method assumes that only 1 thread calls EndInvoke + if (!IsCompleted) + { + // If the operation isn't done, wait for it + AsyncWaitHandle.WaitOne(); + AsyncWaitHandle.Close(); + m_waitHandle.Close(); + m_waitHandle = null; // Allow early GC + } + + // Operation is done: if an exception occured, throw it + if (m_exception != null) throw m_exception; + } + + #endregion + } + + internal class AsyncResult : SimpleAsyncResult + { + private T m_result = default(T); + + public AsyncResult(AsyncCallback asyncCallback, Object state) : + base(asyncCallback, state) + { + } + + public void SetAsCompleted(T result, bool completedSynchronously) + { + // Save the asynchronous operation's result + m_result = result; + + // Tell the base class that the operation completed + // sucessfully (no exception) + base.SetAsCompleted(completedSynchronously); + } + + public new T EndInvoke() + { + base.EndInvoke(); + return m_result; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Communications/IUserService.cs b/OpenSim/Framework/Communications/IUserService.cs new file mode 100644 index 0000000000..dfa059d929 --- /dev/null +++ b/OpenSim/Framework/Communications/IUserService.cs @@ -0,0 +1,157 @@ +/* + * 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.Services.Interfaces; + +namespace OpenSim.Framework.Communications +{ + public interface IUserService + { + /// + /// Add a temporary user profile. + /// + /// A temporary user profile is one that should exist only for the lifetime of the process. + /// + void AddTemporaryUserProfile(UserProfileData userProfile); + + /// + /// Loads a user profile by name + /// + /// First name + /// Last name + /// A user profile. Returns null if no profile is found + UserProfileData GetUserProfile(string firstName, string lastName); + + /// + /// Loads a user profile from a database by UUID + /// + /// The target UUID + /// A user profile. Returns null if no user profile is found. + UserProfileData GetUserProfile(UUID userId); + + UserProfileData GetUserProfile(Uri uri); + + Uri GetUserUri(UserProfileData userProfile); + + UserAgentData GetAgentByUUID(UUID userId); + + void ClearUserAgent(UUID avatarID); + List GenerateAgentPickerRequestResponse(UUID QueryID, string Query); + + UserProfileData SetupMasterUser(string firstName, string lastName); + UserProfileData SetupMasterUser(string firstName, string lastName, string password); + UserProfileData SetupMasterUser(UUID userId); + + /// + /// Update the user's profile. + /// + /// UserProfileData object with updated data. Should be obtained + /// via a call to GetUserProfile(). + /// true if the update could be applied, false if it could not be applied. + bool UpdateUserProfile(UserProfileData data); + + /// + /// Adds a new friend to the database for XUser + /// + /// The agent that who's friends list is being added to + /// The agent that being added to the friends list of the friends list owner + /// A uint bit vector for set perms that the friend being added has; 0 = none, 1=This friend can see when they sign on, 2 = map, 4 edit objects + void AddNewUserFriend(UUID friendlistowner, UUID friend, uint perms); + + /// + /// Delete friend on friendlistowner's friendlist. + /// + /// The agent that who's friends list is being updated + /// The Ex-friend agent + void RemoveUserFriend(UUID friendlistowner, UUID friend); + + /// + /// Update permissions for friend on friendlistowner's friendlist. + /// + /// The agent that who's friends list is being updated + /// The agent that is getting or loosing permissions + /// A uint bit vector for set perms that the friend being added has; 0 = none, 1=This friend can see when they sign on, 2 = map, 4 edit objects + void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms); + + /// + /// Logs off a user on the user server + /// + /// UUID of the user + /// UUID of the Region + /// regionhandle + /// final position + /// final lookat + void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, Vector3 position, Vector3 lookat); + + /// + /// Logs off a user on the user server (deprecated as of 2008-08-27) + /// + /// UUID of the user + /// UUID of the Region + /// regionhandle + /// final position x + /// final position y + /// final position z + void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, float posx, float posy, float posz); + + /// + /// Returns a list of FriendsListItems that describe the friends and permissions in the friend relationship + /// for UUID friendslistowner + /// + /// + /// The agent for whom we're retreiving the friends Data. + /// + /// A List of FriendListItems that contains info about the user's friends. + /// Always returns a list even if the user has no friends + /// + List GetUserFriendList(UUID friendlistowner); + + // This probably shouldn't be here, it belongs to IAuthentication + // But since Scenes only have IUserService references, I'm placing it here for now. + bool VerifySession(UUID userID, UUID sessionID); + + /// + /// Authenticate a user by their password. + /// + /// + /// This is used by callers outside the login process that want to + /// verify a user who has given their password. + /// + /// This should probably also be in IAuthentication but is here for the same reasons as VerifySession() is + /// + /// + /// + /// + bool AuthenticateUserByPassword(UUID userID, string password); + + // Temporary Hack until we move everything to the new service model + void SetInventoryService(IInventoryService invService); + } +} diff --git a/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs new file mode 100644 index 0000000000..070d106252 --- /dev/null +++ b/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs @@ -0,0 +1,66 @@ +/* + * 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.Communications.Limit +{ + /// + /// Interface for strategies that can limit requests from the client. Currently only used in the + /// texture modules to deal with repeated requests for certain textures. However, limiting strategies + /// could be used with other requests. + /// + public interface IRequestLimitStrategy + { + /// + /// Should the request be allowed? If the id is not monitored, then the request is always allowed. + /// Otherwise, the strategy criteria will be applied. + /// + /// + /// + bool AllowRequest(TId id); + + /// + /// Has the request been refused just once? + /// + /// False if the request has not yet been refused, or if the request has been refused more + /// than once. + bool IsFirstRefusal(TId id); + + /// + /// Start monitoring for future AllowRequest calls. If the id is already monitored, then monitoring + /// continues. + /// + /// + void MonitorRequests(TId id); + + /// + /// Is the id being monitored? + /// + /// + /// + bool IsMonitoringRequests(TId id); + } +} diff --git a/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs new file mode 100644 index 0000000000..76726531a8 --- /dev/null +++ b/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs @@ -0,0 +1,40 @@ +/* + * 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.Communications.Limit +{ + /// + /// Strategy which polices no limits + /// + public class NullLimitStrategy : IRequestLimitStrategy + { + public bool AllowRequest(TId id) { return true; } + public bool IsFirstRefusal(TId id) { return false; } + public void MonitorRequests(TId id) { /* intentionally blank */ } + public bool IsMonitoringRequests(TId id) { return false; } + } +} diff --git a/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs new file mode 100644 index 0000000000..44dd5927a3 --- /dev/null +++ b/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs @@ -0,0 +1,109 @@ +/* + * 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.Generic; + +namespace OpenSim.Framework.Communications.Limit +{ + /// + /// Limit requests by discarding them after they've been repeated a certain number of times. + /// + public class RepeatLimitStrategy : IRequestLimitStrategy + { + /// + /// Record each asset request that we're notified about. + /// + private readonly Dictionary requestCounts = new Dictionary(); + + /// + /// The maximum number of requests that can be made before we drop subsequent requests. + /// + private readonly int m_maxRequests; + public int MaxRequests + { + get { return m_maxRequests; } + } + + /// + /// The maximum number of requests that may be served before all further + /// requests are dropped. + public RepeatLimitStrategy(int maxRequests) + { + m_maxRequests = maxRequests; + } + + /// + /// + /// + public bool AllowRequest(TId id) + { + if (requestCounts.ContainsKey(id)) + { + requestCounts[id] += 1; + + if (requestCounts[id] > m_maxRequests) + { + return false; + } + } + + return true; + } + + /// + /// + /// + public bool IsFirstRefusal(TId id) + { + if (requestCounts.ContainsKey(id) && m_maxRequests + 1 == requestCounts[id]) + { + return true; + } + + return false; + } + + /// + /// + /// + public void MonitorRequests(TId id) + { + if (!IsMonitoringRequests(id)) + { + requestCounts.Add(id, 1); + } + } + + /// + /// + /// + public bool IsMonitoringRequests(TId id) + { + return requestCounts.ContainsKey(id); + } + } +} diff --git a/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs new file mode 100644 index 0000000000..7ac8293b77 --- /dev/null +++ b/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs @@ -0,0 +1,140 @@ +/* + * 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; + +namespace OpenSim.Framework.Communications.Limit +{ + /// + /// Limit requests by discarding repeat attempts that occur within a given time period + /// + /// XXX Don't use this for limiting texture downloading, at least not until we better handle multiple requests + /// for the same texture at different resolutions. + /// + public class TimeLimitStrategy : IRequestLimitStrategy + { + /// + /// Record the time at which an asset request occurs. + /// + private readonly Dictionary requests = new Dictionary(); + + /// + /// The minimum time period between which requests for the same data will be serviced. + /// + private readonly TimeSpan m_repeatPeriod; + public TimeSpan RepeatPeriod + { + get { return m_repeatPeriod; } + } + + /// + /// + public TimeLimitStrategy(TimeSpan repeatPeriod) + { + m_repeatPeriod = repeatPeriod; + } + + /// + /// + /// + public bool AllowRequest(TId id) + { + if (IsMonitoringRequests(id)) + { + DateTime now = DateTime.Now; + TimeSpan elapsed = now - requests[id].Time; + + if (elapsed < RepeatPeriod) + { + requests[id].Refusals += 1; + return false; + } + + requests[id].Time = now; + } + + return true; + } + + /// + /// + /// + public bool IsFirstRefusal(TId id) + { + if (IsMonitoringRequests(id)) + { + if (1 == requests[id].Refusals) + { + return true; + } + } + + return false; + } + + /// + /// + /// + public void MonitorRequests(TId id) + { + if (!IsMonitoringRequests(id)) + { + requests.Add(id, new Request(DateTime.Now)); + } + } + + /// + /// + /// + public bool IsMonitoringRequests(TId id) + { + return requests.ContainsKey(id); + } + } + + /// + /// Private request details. + /// + class Request + { + /// + /// Time of last request + /// + public DateTime Time; + + /// + /// Number of refusals associated with this request + /// + public int Refusals; + + public Request(DateTime time) + { + Time = time; + } + } +} diff --git a/OpenSim/Framework/Communications/OutboundUrlFilter.cs b/OpenSim/Framework/Communications/OutboundUrlFilter.cs new file mode 100644 index 0000000000..8b572d1c58 --- /dev/null +++ b/OpenSim/Framework/Communications/OutboundUrlFilter.cs @@ -0,0 +1,256 @@ +/* + * 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.Reflection; +using log4net; +using LukeSkywalker.IPNetwork; +using Nini.Config; + +namespace OpenSim.Framework.Communications +{ + public class OutboundUrlFilter + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name { get; private set; } + + private List m_blacklistNetworks; + private List m_blacklistEndPoints; + + private List m_blacklistExceptionNetworks; + private List m_blacklistExceptionEndPoints; + + public OutboundUrlFilter( + string name, + List blacklistNetworks, List blacklistEndPoints, + List blacklistExceptionNetworks, List blacklistExceptionEndPoints) + { + Name = name; + + m_blacklistNetworks = blacklistNetworks; + m_blacklistEndPoints = blacklistEndPoints; + m_blacklistExceptionNetworks = blacklistExceptionNetworks; + m_blacklistExceptionEndPoints = blacklistExceptionEndPoints; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the filter for logging purposes. + /// Filter configuration + public OutboundUrlFilter(string name, IConfigSource config) + { + Name = name; + + string configBlacklist + = "0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32"; + string configBlacklistExceptions = ""; + + IConfig networkConfig = config.Configs["Network"]; + + if (networkConfig != null) + { + configBlacklist = networkConfig.GetString("OutboundDisallowForUserScripts", configBlacklist); + configBlacklistExceptions + = networkConfig.GetString("OutboundDisallowForUserScriptsExcept", configBlacklistExceptions); + } + + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScripts for {0} is [{1}]", Name, configBlacklist); + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScriptsExcept for {0} is [{1}]", Name, configBlacklistExceptions); + + OutboundUrlFilter.ParseConfigList( + configBlacklist, Name, out m_blacklistNetworks, out m_blacklistEndPoints); + OutboundUrlFilter.ParseConfigList( + configBlacklistExceptions, Name, out m_blacklistExceptionNetworks, out m_blacklistExceptionEndPoints); + } + + private static void ParseConfigList( + string fullConfigEntry, string filterName, out List networks, out List endPoints) + { + // Parse blacklist + string[] configBlacklistEntries + = fullConfigEntry.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + configBlacklistEntries = configBlacklistEntries.Select(e => e.Trim()).ToArray(); + + networks = new List(); + endPoints = new List(); + + foreach (string configEntry in configBlacklistEntries) + { + if (configEntry.Contains("/")) + { + IPNetwork network; + + if (!IPNetwork.TryParse(configEntry, out network)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: Entry [{0}] is invalid network for {1}", configEntry, filterName); + + continue; + } + + networks.Add(network); + } + else + { + Uri configEntryUri; + + if (!Uri.TryCreate("http://" + configEntry, UriKind.Absolute, out configEntryUri)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: EndPoint entry [{0}] is invalid endpoint for {1}", + configEntry, filterName); + + continue; + } + + IPAddress[] addresses = Dns.GetHostAddresses(configEntryUri.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}] in config", addr); + + IPEndPoint configEntryEp = new IPEndPoint(addr, configEntryUri.Port); + endPoints.Add(configEntryEp); + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Added blacklist exception [{0}]", configEntryEp); + } + } + } + } + } + + /// + /// Determines if an url is in a list of networks and endpoints. + /// + /// + /// IP address + /// + /// Networks. + /// End points. + /// Filter name. + private static bool IsInNetwork( + IPAddress addr, int port, List networks, List endPoints, string filterName) + { + foreach (IPNetwork ipn in networks) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}] against network [{1}]", addr, ipn); + + if (IPNetwork.Contains(ipn, addr)) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}] in network [{1}]", addr, ipn); + + return true; + } + } + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foreach (IPEndPoint ep in endPoints) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}:{1}] against endpoint [{2}]", +// addr, port, ep); + + if (addr.Equals(ep.Address) && port == ep.Port) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}:{1}] in endpoint [{2}]", addr, port, ep); + + return true; + } + } + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Did not find [{0}:{1}] in list", addr, port); + + return false; + } + + /// + /// Checks whether the given url is allowed by the filter. + /// + /// + public bool CheckAllowed(Uri url) + { + bool allowed = true; + + // Check that we are permitted to make calls to this endpoint. + bool foundIpv4Address = false; + + IPAddress[] addresses = Dns.GetHostAddresses(url.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foundIpv4Address = true; + + // Check blacklist + if (OutboundUrlFilter.IsInNetwork(addr, url.Port, m_blacklistNetworks, m_blacklistEndPoints, Name)) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in blacklist for {1}", url, Name); + + // Check blacklist exceptions + allowed + = OutboundUrlFilter.IsInNetwork( + addr, url.Port, m_blacklistExceptionNetworks, m_blacklistExceptionEndPoints, Name); + +// if (allowed) +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in whitelist for {1}", url, Name); + } + } + + // Found at least one address in a blacklist and not a blacklist exception + if (!allowed) + return false; +// else +// m_log.DebugFormat("[OUTBOUND URL FILTER]: URL [{0}] not in blacklist for {1}", url, Name); + } + + // We do not know how to handle IPv6 securely yet. + if (!foundIpv4Address) + return false; + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Allowing request [{0}]", url); + + return allowed; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b398167211 --- /dev/null +++ b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs @@ -0,0 +1,65 @@ +/* + * 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.Reflection; +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.Framework.Communications")] +[assembly : AssemblyDescription("")] +[assembly : AssemblyConfiguration("")] +[assembly : AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("OpenSim")] +[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("13e7c396-78a9-4a5c-baf2-6f980ea75d95")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly : AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs new file mode 100644 index 0000000000..6f517b6f87 --- /dev/null +++ b/OpenSim/Framework/Communications/RestClient.cs @@ -0,0 +1,522 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using System.Web; +using log4net; + +using OpenSim.Framework.ServiceAuth; + +namespace OpenSim.Framework.Communications +{ + /// + /// Implementation of a generic REST client + /// + /// + /// This class is a generic implementation of a REST (Representational State Transfer) web service. This + /// class is designed to execute both synchronously and asynchronously. + /// + /// Internally the implementation works as a two stage asynchronous web-client. + /// When the request is initiated, RestClient will query asynchronously for for a web-response, + /// sleeping until the initial response is returned by the server. Once the initial response is retrieved + /// the second stage of asynchronous requests will be triggered, in an attempt to read of the response + /// object into a memorystream as a sequence of asynchronous reads. + /// + /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing + /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be + /// invoked by the caller in either synchronous mode or asynchronous modes. + /// + public class RestClient : IDisposable + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // private string realuri; + + #region member variables + + /// + /// The base Uri of the web-service e.g. http://www.google.com + /// + private string _url; + + /// + /// Path elements of the query + /// + private List _pathElements = new List(); + + /// + /// Parameter elements of the query, e.g. min=34 + /// + private Dictionary _parameterElements = new Dictionary(); + + /// + /// Request method. E.g. GET, POST, PUT or DELETE + /// + private string _method; + + /// + /// Temporary buffer used to store bytes temporarily as they come in from the server + /// + private byte[] _readbuf; + + /// + /// MemoryStream representing the resulting resource + /// + private Stream _resource; + + /// + /// WebRequest object, held as a member variable + /// + private HttpWebRequest _request; + + /// + /// WebResponse object, held as a member variable, so we can close it + /// + private HttpWebResponse _response; + + /// + /// This flag will help block the main synchroneous method, in case we run in synchroneous mode + /// + //public static ManualResetEvent _allDone = new ManualResetEvent(false); + + /// + /// Default time out period + /// + //private const int DefaultTimeout = 10*1000; // 10 seconds timeout + + /// + /// Default Buffer size of a block requested from the web-server + /// + private const int BufferSize = 4096; // Read blocks of 4 KB. + + + /// + /// if an exception occours during async processing, we need to save it, so it can be + /// rethrown on the primary thread; + /// + private Exception _asyncException; + + #endregion member variables + + #region constructors + + /// + /// Instantiate a new RestClient + /// + /// Web-service to query, e.g. http://osgrid.org:8003 + public RestClient(string url) + { + _url = url; + _readbuf = new byte[BufferSize]; + _resource = new MemoryStream(); + _request = null; + _response = null; + _lock = new object(); + } + + private object _lock; + + #endregion constructors + + + #region Dispose + + private bool disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing) + { + _resource.Dispose(); + } + + disposed = true; + } + + #endregion Dispose + + + /// + /// Add a path element to the query, e.g. assets + /// + /// path entry + public void AddResourcePath(string element) + { + if (isSlashed(element)) + _pathElements.Add(element.Substring(0, element.Length - 1)); + else + _pathElements.Add(element); + } + + /// + /// Add a query parameter to the Url + /// + /// Name of the parameter, e.g. min + /// Value of the parameter, e.g. 42 + public void AddQueryParameter(string name, string value) + { + try + { + _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value)); + } + catch (ArgumentException) + { + m_log.Error("[REST]: Query parameter " + name + " is already added."); + } + catch (Exception e) + { + m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); + } + } + + /// + /// Add a query parameter to the Url + /// + /// Name of the parameter, e.g. min + public void AddQueryParameter(string name) + { + try + { + _parameterElements.Add(HttpUtility.UrlEncode(name), null); + } + catch (ArgumentException) + { + m_log.Error("[REST]: Query parameter " + name + " is already added."); + } + catch (Exception e) + { + m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); + } + } + + /// + /// Web-Request method, e.g. GET, PUT, POST, DELETE + /// + public string RequestMethod + { + get { return _method; } + set { _method = value; } + } + + /// + /// True if string contains a trailing slash '/' + /// + /// string to be examined + /// true if slash is present + private static bool isSlashed(string s) + { + return s.Substring(s.Length - 1, 1) == "/"; + } + + /// + /// Build a Uri based on the initial Url, path elements and parameters + /// + /// fully constructed Uri + private Uri buildUri() + { + StringBuilder sb = new StringBuilder(); + sb.Append(_url); + + foreach (string e in _pathElements) + { + sb.Append("/"); + sb.Append(e); + } + + bool firstElement = true; + foreach (KeyValuePair kv in _parameterElements) + { + if (firstElement) + { + sb.Append("?"); + firstElement = false; + } + else + sb.Append("&"); + + sb.Append(kv.Key); + if (!string.IsNullOrEmpty(kv.Value)) + { + sb.Append("="); + sb.Append(kv.Value); + } + } + // realuri = sb.ToString(); + //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri); + return new Uri(sb.ToString()); + } + + #region Async communications with server + + /// + /// Async method, invoked when a block of data has been received from the service + /// + /// + private void StreamIsReadyDelegate(IAsyncResult ar) + { + try + { + Stream s = (Stream) ar.AsyncState; + int read = s.EndRead(ar); + + if (read > 0) + { + _resource.Write(_readbuf, 0, read); + // IAsyncResult asynchronousResult = + // s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); + s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); + + // TODO! Implement timeout, without killing the server + //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + } + else + { + s.Close(); + //_allDone.Set(); + } + } + catch (Exception e) + { + //_allDone.Set(); + _asyncException = e; + } + } + + #endregion Async communications with server + + /// + /// Perform a synchronous request + /// + public Stream Request() + { + return Request(null); + } + + /// + /// Perform a synchronous request + /// + public Stream Request(IServiceAuth auth) + { + lock (_lock) + { + _request = (HttpWebRequest) WebRequest.Create(buildUri()); + _request.KeepAlive = false; + _request.ContentType = "application/xml"; + _request.Timeout = 200000; + _request.Method = RequestMethod; + _asyncException = null; + if (auth != null) + auth.AddAuthorization(_request.Headers); + + int reqnum = WebUtil.RequestNumber++; + if (WebUtil.DebugLevel >= 3) + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); + +// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); + + try + { + using (_response = (HttpWebResponse) _request.GetResponse()) + { + using (Stream src = _response.GetResponseStream()) + { + int length = src.Read(_readbuf, 0, BufferSize); + while (length > 0) + { + _resource.Write(_readbuf, 0, length); + length = src.Read(_readbuf, 0, BufferSize); + } + + // TODO! Implement timeout, without killing the server + // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted + //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + + // _allDone.WaitOne(); + } + } + } + catch (WebException e) + { + using (HttpWebResponse errorResponse = e.Response as HttpWebResponse) + { + if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode) + { + // This is often benign. E.g., requesting a missing asset will return 404. + m_log.DebugFormat("[REST CLIENT] Resource not found (404): {0}", _request.Address.ToString()); + } + else + { + m_log.Error(string.Format("[REST CLIENT] Error fetching resource from server: {0} ", _request.Address.ToString()), e); + } + } + + return null; + } + + if (_asyncException != null) + throw _asyncException; + + if (_resource != null) + { + _resource.Flush(); + _resource.Seek(0, SeekOrigin.Begin); + } + + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, _resource); + + return _resource; + } + } + + public Stream Request(Stream src, IServiceAuth auth) + { + _request = (HttpWebRequest) WebRequest.Create(buildUri()); + _request.KeepAlive = false; + _request.ContentType = "application/xml"; + _request.Timeout = 900000; + _request.Method = RequestMethod; + _asyncException = null; + _request.ContentLength = src.Length; + if (auth != null) + auth.AddAuthorization(_request.Headers); + + src.Seek(0, SeekOrigin.Begin); + + int reqnum = WebUtil.RequestNumber++; + if (WebUtil.DebugLevel >= 3) + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail(string.Format("SEND {0}: ", reqnum), src); + + Stream dst = _request.GetRequestStream(); + + byte[] buf = new byte[1024]; + int length = src.Read(buf, 0, 1024); + while (length > 0) + { + dst.Write(buf, 0, length); + length = src.Read(buf, 0, 1024); + } + + try + { + _response = (HttpWebResponse)_request.GetResponse(); + } + catch (WebException e) + { + m_log.WarnFormat("[REST]: Request {0} {1} failed with status {2} and message {3}", + RequestMethod, _request.RequestUri, e.Status, e.Message); + return null; + } + catch (Exception e) + { + m_log.WarnFormat( + "[REST]: Request {0} {1} failed with exception {2} {3}", + RequestMethod, _request.RequestUri, e.Message, e.StackTrace); + return null; + } + + if (WebUtil.DebugLevel >= 5) + { + using (Stream responseStream = _response.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(responseStream)) + { + string responseStr = reader.ReadToEnd(); + WebUtil.LogResponseDetail(reqnum, responseStr); + } + } + } + + _response.Close(); + +// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); + + // TODO! Implement timeout, without killing the server + // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted + //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + + return null; + } + + #region Async Invocation + + public IAsyncResult BeginRequest(AsyncCallback callback, object state) + { + /// + /// In case, we are invoked asynchroneously this object will keep track of the state + /// + AsyncResult ar = new AsyncResult(callback, state); + Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest"); + return ar; + } + + public Stream EndRequest(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult) asyncResult; + + // Wait for operation to complete, then return result or + // throw exception + return ar.EndInvoke(); + } + + private void RequestHelper(Object asyncResult) + { + // We know that it's really an AsyncResult object + AsyncResult ar = (AsyncResult) asyncResult; + try + { + // Perform the operation; if sucessful set the result + Stream s = Request(null); + ar.SetAsCompleted(s, false); + } + catch (Exception e) + { + // If operation fails, set the exception + ar.HandleException(e, false); + } + } + + #endregion Async Invocation + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Communications/XMPP/XmppError.cs b/OpenSim/Framework/Communications/XMPP/XmppError.cs new file mode 100644 index 0000000000..3d36e9c8cf --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppError.cs @@ -0,0 +1,39 @@ +/* + * 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.Xml.Serialization; + +namespace OpenSim.Framework.Communications.XMPP +{ + [XmlRoot("error")] + public class XmppErrorStanza + { + public XmppErrorStanza() + { + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs new file mode 100644 index 0000000000..12263f4e89 --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs @@ -0,0 +1,60 @@ +/* + * 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.Xml.Serialization; + +namespace OpenSim.Framework.Communications.XMPP +{ + /// + /// An IQ needs to have one of the follow types set. + /// + public enum XmppIqType + { + [XmlEnum("set")] set, + [XmlEnum("get")] get, + [XmlEnum("result")] result, + [XmlEnum("error")] error, + } + + /// + /// XmppIqStanza needs to be subclassed as the query content is + /// specific to the query type. + /// + [XmlRoot("iq")] + public abstract class XmppIqStanza: XmppStanza + { + /// + /// IQ type: one of set, get, result, error + /// + [XmlAttribute("type")] + public XmppIqType Type; + + public XmppIqStanza(): base() + { + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs new file mode 100644 index 0000000000..1e8c33e52d --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs @@ -0,0 +1,93 @@ +/* + * 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.Xml.Serialization; + +namespace OpenSim.Framework.Communications.XMPP +{ + /// + /// Message types. + /// + public enum XmppMessageType + { + [XmlEnum("chat")] chat, + [XmlEnum("error")] error, + [XmlEnum("groupchat")] groupchat, + [XmlEnum("headline")] headline, + [XmlEnum("normal")] normal, + } + + /// + /// Message body. + /// + public class XmppMessageBody + { + [XmlText] + public string Text; + + public XmppMessageBody() + { + } + + public XmppMessageBody(string message) + { + Text = message; + } + + new public string ToString() + { + return Text; + } + } + + [XmlRoot("message")] + public class XmppMessageStanza: XmppStanza + { + /// + /// IQ type: one of set, get, result, error + /// + [XmlAttribute("type")] + public XmppMessageType MessageType; + + // [XmlAttribute("error")] + // public XmppError Error; + + [XmlElement("body")] + public XmppMessageBody Body; + + public XmppMessageStanza() : base() + { + } + + public XmppMessageStanza(string fromJid, string toJid, XmppMessageType mType, string message) : + base(fromJid, toJid) + { + MessageType = mType; + Body = new XmppMessageBody(message); + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs new file mode 100644 index 0000000000..4d45ac0663 --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs @@ -0,0 +1,69 @@ +/* + * 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.Xml.Serialization; + +namespace OpenSim.Framework.Communications.XMPP +{ + /// + /// Message types. + /// + public enum XmppPresenceType + { + [XmlEnum("unavailable")] unavailable, + [XmlEnum("subscribe")] subscribe, + [XmlEnum("subscribed")] subscribed, + [XmlEnum("unsubscribe")] unsubscribe, + [XmlEnum("unsubscribed")] unsubscribed, + [XmlEnum("probe")] probe, + [XmlEnum("error")] error, + } + + + [XmlRoot("message")] + public class XmppPresenceStanza: XmppStanza + { + /// + /// IQ type: one of set, get, result, error + /// + [XmlAttribute("type")] + public XmppPresenceType PresenceType; + + // [XmlAttribute("error")] + // public XmppError Error; + + public XmppPresenceStanza() : base() + { + } + + public XmppPresenceStanza(string fromJid, string toJid, XmppPresenceType pType) : + base(fromJid, toJid) + { + PresenceType = pType; + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs b/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs new file mode 100644 index 0000000000..e37ef2853d --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs @@ -0,0 +1,79 @@ +/* + * 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.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Communications.XMPP +{ + public class XmppSerializer + { + // private static readonly ILog _log = + // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // need to do it this way, as XmlSerializer(type, extratypes) + // does not work on mono (at least). + private Dictionary _serializerForType = new Dictionary(); + private Dictionary _serializerForName = new Dictionary(); + private XmlSerializerNamespaces _xmlNs; + private string _defaultNS; + + public XmppSerializer(bool server) + { + _xmlNs = new XmlSerializerNamespaces(); + _xmlNs.Add(String.Empty, String.Empty); + if (server) + _defaultNS = "jabber:server"; + else + _defaultNS = "jabber:client"; + + // TODO: do this via reflection + _serializerForType[typeof(XmppMessageStanza)] = _serializerForName["message"] = + new XmlSerializer(typeof(XmppMessageStanza), _defaultNS); + } + + public void Serialize(XmlWriter xw, object o) + { + if (!_serializerForType.ContainsKey(o.GetType())) + throw new ArgumentException(String.Format("no serializer available for type {0}", o.GetType())); + + _serializerForType[o.GetType()].Serialize(xw, o, _xmlNs); + } + + public object Deserialize(XmlReader xr) + { + // position on next element + xr.Read(); + if (!_serializerForName.ContainsKey(xr.LocalName)) + throw new ArgumentException(String.Format("no serializer available for name {0}", xr.LocalName)); + + return _serializerForName[xr.LocalName].Deserialize(xr); + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppStanza.cs new file mode 100644 index 0000000000..5312a31bb6 --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppStanza.cs @@ -0,0 +1,70 @@ +/* + * 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.Framework.Communications.XMPP +{ + public abstract class XmppStanza + { + /// + /// counter used for generating ID + /// + [XmlIgnore] + private static ulong _ctr = 0; + + /// + /// recipient JID + /// + [XmlAttribute("to")] + public string ToJid; + + /// + /// sender JID + /// + [XmlAttribute("from")] + public string FromJid; + + /// + /// unique ID. + /// + [XmlAttribute("id")] + public string MessageId; + + public XmppStanza() + { + } + + public XmppStanza(string fromJid, string toJid) + { + ToJid = toJid; + FromJid = fromJid; + MessageId = String.Format("OpenSim_{0}{1}", DateTime.UtcNow.Ticks, _ctr++); + } + } +} diff --git a/OpenSim/Framework/Communications/XMPP/XmppWriter.cs b/OpenSim/Framework/Communications/XMPP/XmppWriter.cs new file mode 100644 index 0000000000..415d808c2f --- /dev/null +++ b/OpenSim/Framework/Communications/XMPP/XmppWriter.cs @@ -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. + */ + +using System.IO; +using System.Text; +using System.Xml; +using IOStream = System.IO.Stream; + +namespace OpenSim.Framework.Communications.XMPP +{ + public class XMPPWriter: XmlTextWriter + { + public XMPPWriter(TextWriter textWriter) : base(textWriter) + { + } + + public XMPPWriter(IOStream stream) : this(stream, Util.UTF8) + { + } + + public XMPPWriter(IOStream stream, Encoding enc) : base(stream, enc) + { + } + + public override void WriteStartDocument() + { + } + + public override void WriteStartDocument(bool standalone) + { + } + } +} diff --git a/OpenSim/Framework/ConfigSettings.cs b/OpenSim/Framework/ConfigSettings.cs new file mode 100644 index 0000000000..108a3e4407 --- /dev/null +++ b/OpenSim/Framework/ConfigSettings.cs @@ -0,0 +1,39 @@ +/* + * 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 +{ + public class ConfigSettings + { + public string PhysicsEngine { get; set; } + public string MeshEngineName { get; set; } + public string ClientstackDll { get; set; } + public string LibrariesXMLFile { get; set; } + + public const uint DefaultRegionHttpPort = 9000; + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ConfigurationOption.cs b/OpenSim/Framework/ConfigurationOption.cs new file mode 100644 index 0000000000..b1bde86fbf --- /dev/null +++ b/OpenSim/Framework/ConfigurationOption.cs @@ -0,0 +1,74 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class ConfigurationOption + { + #region Delegates + + public delegate bool ConfigurationOptionShouldBeAsked(string configuration_key); + + #endregion + + #region ConfigurationTypes enum + + public enum ConfigurationTypes + { + TYPE_STRING, + TYPE_STRING_NOT_EMPTY, + TYPE_UINT16, + TYPE_UINT32, + TYPE_UINT64, + TYPE_INT16, + TYPE_INT32, + TYPE_INT64, + TYPE_IP_ADDRESS, + TYPE_CHARACTER, + TYPE_BOOLEAN, + TYPE_BYTE, + TYPE_UUID, + TYPE_UUID_NULL_FREE, + TYPE_Vector3, + TYPE_FLOAT, + TYPE_DOUBLE + } ; + + #endregion + + public string configurationDefault = String.Empty; + + public string configurationKey = String.Empty; + public string configurationQuestion = String.Empty; + + public ConfigurationTypes configurationType = ConfigurationTypes.TYPE_STRING; + public bool configurationUseDefaultNoPrompt = false; + public ConfigurationOptionShouldBeAsked shouldIBeAsked; //Should I be asked now? Based on previous answers + } +} diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs new file mode 100644 index 0000000000..67af47190c --- /dev/null +++ b/OpenSim/Framework/Console/AssemblyInfo.cs @@ -0,0 +1,58 @@ +/* + * 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.Reflection; +using System.Runtime.InteropServices; + +// Information about this assembly is defined by the following +// attributes. +// +// change them to the information which is associated with the assembly +// you compile. + +[assembly : AssemblyTitle("ServerConsole")] +[assembly : AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly : AssemblyProduct("ServerConsole")] +[assembly: AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// This sets the default COM visibility of types in the assembly to invisible. +// If you need to expose a type to COM, use [ComVisible(true)] on that type. + +[assembly : ComVisible(false)] + +// The assembly version has following format : +// +// Major.Minor.Build.Revision +// +// You can specify all values by your own or you can build default build and revision +// numbers with the '*' character (the default): + +[assembly : AssemblyVersion("0.8.2.*")] diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs new file mode 100644 index 0000000000..0f68afe7d4 --- /dev/null +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -0,0 +1,793 @@ +/* + * 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.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using log4net; +using OpenSim.Framework; + +namespace OpenSim.Framework.Console +{ + public class Commands : ICommands + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Encapsulates a command that can be invoked from the console + /// + private class CommandInfo + { + /// + /// The module from which this command comes + /// + public string module; + + /// + /// Whether the module is shared + /// + public bool shared; + + /// + /// Very short BNF description + /// + public string help_text; + + /// + /// Longer one line help text + /// + public string long_help; + + /// + /// Full descriptive help for this command + /// + public string descriptive_help; + + /// + /// The method to invoke for this command + /// + public List fn; + } + + public const string GeneralHelpText + = "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n"; + + public const string ItemHelpText += @"For more information, type 'help all' to get a list of all commands, + or type help ' where is one of the following:"; + + /// + /// Commands organized by keyword in a tree + /// + private Dictionary tree = + new Dictionary(); + + /// + /// Commands organized by module + /// + private Dictionary> m_modulesCommands = new Dictionary>(); + + /// + /// Get help for the given help string + /// + /// Parsed parts of the help string. If empty then general help is returned. + /// + public List GetHelp(string[] cmd) + { + List help = new List(); + List helpParts = new List(cmd); + + // Remove initial help keyword + helpParts.RemoveAt(0); + + help.Add(""); // Will become a newline. + + // General help + if (helpParts.Count == 0) + { + help.Add(GeneralHelpText); + help.Add(ItemHelpText); + help.AddRange(CollectModulesHelp(tree)); + } + else if (helpParts.Count == 1 && helpParts[0] == "all") + { + help.AddRange(CollectAllCommandsHelp()); + } + else + { + help.AddRange(CollectHelp(helpParts)); + } + + help.Add(""); // Will become a newline. + + return help; + } + + /// + /// Collects the help from all commands and return in alphabetical order. + /// + /// + private List CollectAllCommandsHelp() + { + List help = new List(); + + lock (m_modulesCommands) + { + foreach (List commands in m_modulesCommands.Values) + { + var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help)); + help.AddRange(ourHelpText); + } + } + + help.Sort(); + + return help; + } + + /// + /// See if we can find the requested command in order to display longer help + /// + /// + /// + private List CollectHelp(List helpParts) + { + string originalHelpRequest = string.Join(" ", helpParts.ToArray()); + List help = new List(); + + // Check modules first to see if we just need to display a list of those commands + if (TryCollectModuleHelp(originalHelpRequest, help)) + { + help.Insert(0, ItemHelpText); + return help; + } + + Dictionary dict = tree; + while (helpParts.Count > 0) + { + string helpPart = helpParts[0]; + + if (!dict.ContainsKey(helpPart)) + break; + + //m_log.Debug("Found {0}", helpParts[0]); + + if (dict[helpPart] is Dictionary) + dict = (Dictionary)dict[helpPart]; + + helpParts.RemoveAt(0); + } + + // There was a command for the given help string + if (dict.ContainsKey(String.Empty)) + { + CommandInfo commandInfo = (CommandInfo)dict[String.Empty]; + help.Add(commandInfo.help_text); + help.Add(commandInfo.long_help); + + string descriptiveHelp = commandInfo.descriptive_help; + + // If we do have some descriptive help then insert a spacing line before for readability. + if (descriptiveHelp != string.Empty) + help.Add(string.Empty); + + help.Add(commandInfo.descriptive_help); + } + else + { + help.Add(string.Format("No help is available for {0}", originalHelpRequest)); + } + + return help; + } + + /// + /// Try to collect help for the given module if that module exists. + /// + /// + /// /param> + /// true if there was the module existed, false otherwise. + private bool TryCollectModuleHelp(string moduleName, List helpText) + { + lock (m_modulesCommands) + { + foreach (string key in m_modulesCommands.Keys) + { + // Allow topic help requests to succeed whether they are upper or lowercase. + if (moduleName.ToLower() == key.ToLower()) + { + List commands = m_modulesCommands[key]; + var ourHelpText = commands.ConvertAll(c => string.Format("{0} - {1}", c.help_text, c.long_help)); + ourHelpText.Sort(); + helpText.AddRange(ourHelpText); + + return true; + } + } + + return false; + } + } + + private List CollectModulesHelp(Dictionary dict) + { + lock (m_modulesCommands) + { + List helpText = new List(m_modulesCommands.Keys); + helpText.Sort(); + return helpText; + } + } + +// private List CollectHelp(Dictionary dict) +// { +// List result = new List(); +// +// foreach (KeyValuePair kvp in dict) +// { +// if (kvp.Value is Dictionary) +// { +// result.AddRange(CollectHelp((Dictionary)kvp.Value)); +// } +// else +// { +// if (((CommandInfo)kvp.Value).long_help != String.Empty) +// result.Add(((CommandInfo)kvp.Value).help_text+" - "+ +// ((CommandInfo)kvp.Value).long_help); +// } +// } +// return result; +// } + + /// + /// Add a command to those which can be invoked from the console. + /// + /// + /// + /// + /// + /// + public void AddCommand(string module, bool shared, string command, + string help, string longhelp, CommandDelegate fn) + { + AddCommand(module, shared, command, help, longhelp, String.Empty, fn); + } + + /// + /// Add a command to those which can be invoked from the console. + /// + /// + /// + /// + /// + /// + /// + public void AddCommand(string module, bool shared, string command, + string help, string longhelp, string descriptivehelp, + CommandDelegate fn) + { + string[] parts = Parser.Parse(command); + + Dictionary current = tree; + + foreach (string part in parts) + { + if (current.ContainsKey(part)) + { + if (current[part] is Dictionary) + current = (Dictionary)current[part]; + else + return; + } + else + { + current[part] = new Dictionary(); + current = (Dictionary)current[part]; + } + } + + CommandInfo info; + + if (current.ContainsKey(String.Empty)) + { + info = (CommandInfo)current[String.Empty]; + if (!info.shared && !info.fn.Contains(fn)) + info.fn.Add(fn); + + return; + } + + info = new CommandInfo(); + info.module = module; + info.shared = shared; + info.help_text = help; + info.long_help = longhelp; + info.descriptive_help = descriptivehelp; + info.fn = new List(); + info.fn.Add(fn); + current[String.Empty] = info; + + // Now add command to modules dictionary + lock (m_modulesCommands) + { + List commands; + if (m_modulesCommands.ContainsKey(module)) + { + commands = m_modulesCommands[module]; + } + else + { + commands = new List(); + m_modulesCommands[module] = commands; + } + +// m_log.DebugFormat("[COMMAND CONSOLE]: Adding to category {0} command {1}", module, command); + commands.Add(info); + } + } + + public string[] FindNextOption(string[] cmd, bool term) + { + Dictionary current = tree; + + int remaining = cmd.Length; + + foreach (string s in cmd) + { + remaining--; + + List found = new List(); + + foreach (string opt in current.Keys) + { + if (remaining > 0 && opt == s) + { + found.Clear(); + found.Add(opt); + break; + } + if (opt.StartsWith(s)) + { + found.Add(opt); + } + } + + if (found.Count == 1 && (remaining != 0 || term)) + { + current = (Dictionary)current[found[0]]; + } + else if (found.Count > 0) + { + return found.ToArray(); + } + else + { + break; +// return new string[] {""}; + } + } + + if (current.Count > 1) + { + List choices = new List(); + + bool addcr = false; + foreach (string s in current.Keys) + { + if (s == String.Empty) + { + CommandInfo ci = (CommandInfo)current[String.Empty]; + if (ci.fn.Count != 0) + addcr = true; + } + else + choices.Add(s); + } + if (addcr) + choices.Add(""); + return choices.ToArray(); + } + + if (current.ContainsKey(String.Empty)) + return new string[] { "Command help: "+((CommandInfo)current[String.Empty]).help_text}; + + return new string[] { new List(current.Keys)[0] }; + } + + private CommandInfo ResolveCommand(string[] cmd, out string[] result) + { + result = cmd; + int index = -1; + + Dictionary current = tree; + + foreach (string s in cmd) + { + index++; + + List found = new List(); + + foreach (string opt in current.Keys) + { + if (opt == s) + { + found.Clear(); + found.Add(opt); + break; + } + if (opt.StartsWith(s)) + { + found.Add(opt); + } + } + + if (found.Count == 1) + { + result[index] = found[0]; + current = (Dictionary)current[found[0]]; + } + else if (found.Count > 0) + { + return null; + } + else + { + break; + } + } + + if (current.ContainsKey(String.Empty)) + return (CommandInfo)current[String.Empty]; + + return null; + } + + public bool HasCommand(string command) + { + string[] result; + return ResolveCommand(Parser.Parse(command), out result) != null; + } + + public string[] Resolve(string[] cmd) + { + string[] result; + CommandInfo ci = ResolveCommand(cmd, out result); + + if (ci == null) + return new string[0]; + + if (ci.fn.Count == 0) + return new string[0]; + + foreach (CommandDelegate fn in ci.fn) + { + if (fn != null) + fn(ci.module, result); + else + return new string[0]; + } + + return result; + } + + public XmlElement GetXml(XmlDocument doc) + { + CommandInfo help = (CommandInfo)((Dictionary)tree["help"])[String.Empty]; + ((Dictionary)tree["help"]).Remove(string.Empty); + if (((Dictionary)tree["help"]).Count == 0) + tree.Remove("help"); + + CommandInfo quit = (CommandInfo)((Dictionary)tree["quit"])[String.Empty]; + ((Dictionary)tree["quit"]).Remove(string.Empty); + if (((Dictionary)tree["quit"]).Count == 0) + tree.Remove("quit"); + + XmlElement root = doc.CreateElement("", "HelpTree", ""); + + ProcessTreeLevel(tree, root, doc); + + if (!tree.ContainsKey("help")) + tree["help"] = (object) new Dictionary(); + ((Dictionary)tree["help"])[String.Empty] = help; + + if (!tree.ContainsKey("quit")) + tree["quit"] = (object) new Dictionary(); + ((Dictionary)tree["quit"])[String.Empty] = quit; + + return root; + } + + private void ProcessTreeLevel(Dictionary level, XmlElement xml, XmlDocument doc) + { + foreach (KeyValuePair kvp in level) + { + if (kvp.Value is Dictionary) + { + XmlElement next = doc.CreateElement("", "Level", ""); + next.SetAttribute("Name", kvp.Key); + + xml.AppendChild(next); + + ProcessTreeLevel((Dictionary)kvp.Value, next, doc); + } + else + { + CommandInfo c = (CommandInfo)kvp.Value; + + XmlElement cmd = doc.CreateElement("", "Command", ""); + + XmlElement e; + + e = doc.CreateElement("", "Module", ""); + cmd.AppendChild(e); + e.AppendChild(doc.CreateTextNode(c.module)); + + e = doc.CreateElement("", "Shared", ""); + cmd.AppendChild(e); + e.AppendChild(doc.CreateTextNode(c.shared.ToString())); + + e = doc.CreateElement("", "HelpText", ""); + cmd.AppendChild(e); + e.AppendChild(doc.CreateTextNode(c.help_text)); + + e = doc.CreateElement("", "LongHelp", ""); + cmd.AppendChild(e); + e.AppendChild(doc.CreateTextNode(c.long_help)); + + e = doc.CreateElement("", "Description", ""); + cmd.AppendChild(e); + e.AppendChild(doc.CreateTextNode(c.descriptive_help)); + + xml.AppendChild(cmd); + } + } + } + + public void FromXml(XmlElement root, CommandDelegate fn) + { + CommandInfo help = (CommandInfo)((Dictionary)tree["help"])[String.Empty]; + ((Dictionary)tree["help"]).Remove(string.Empty); + if (((Dictionary)tree["help"]).Count == 0) + tree.Remove("help"); + + CommandInfo quit = (CommandInfo)((Dictionary)tree["quit"])[String.Empty]; + ((Dictionary)tree["quit"]).Remove(string.Empty); + if (((Dictionary)tree["quit"]).Count == 0) + tree.Remove("quit"); + + tree.Clear(); + + ReadTreeLevel(tree, root, fn); + + if (!tree.ContainsKey("help")) + tree["help"] = (object) new Dictionary(); + ((Dictionary)tree["help"])[String.Empty] = help; + + if (!tree.ContainsKey("quit")) + tree["quit"] = (object) new Dictionary(); + ((Dictionary)tree["quit"])[String.Empty] = quit; + } + + private void ReadTreeLevel(Dictionary level, XmlNode node, CommandDelegate fn) + { + Dictionary next; + string name; + + XmlNodeList nodeL = node.ChildNodes; + XmlNodeList cmdL; + CommandInfo c; + + foreach (XmlNode part in nodeL) + { + switch (part.Name) + { + case "Level": + name = ((XmlElement)part).GetAttribute("Name"); + next = new Dictionary(); + level[name] = next; + ReadTreeLevel(next, part, fn); + break; + case "Command": + cmdL = part.ChildNodes; + c = new CommandInfo(); + foreach (XmlNode cmdPart in cmdL) + { + switch (cmdPart.Name) + { + case "Module": + c.module = cmdPart.InnerText; + break; + case "Shared": + c.shared = Convert.ToBoolean(cmdPart.InnerText); + break; + case "HelpText": + c.help_text = cmdPart.InnerText; + break; + case "LongHelp": + c.long_help = cmdPart.InnerText; + break; + case "Description": + c.descriptive_help = cmdPart.InnerText; + break; + } + } + c.fn = new List(); + c.fn.Add(fn); + level[String.Empty] = c; + break; + } + } + } + } + + public class Parser + { + // If an unquoted portion ends with an element matching this regex + // and the next element contains a space, then we have stripped + // embedded quotes that should not have been stripped + private static Regex optionRegex = new Regex("^--[a-zA-Z0-9-]+=$"); + + public static string[] Parse(string text) + { + List result = new List(); + + int index; + + string[] unquoted = text.Split(new char[] {'"'}); + + for (index = 0 ; index < unquoted.Length ; index++) + { + if (index % 2 == 0) + { + string[] words = unquoted[index].Split(new char[] {' '}); + + bool option = false; + foreach (string w in words) + { + if (w != String.Empty) + { + if (optionRegex.Match(w) == Match.Empty) + option = false; + else + option = true; + result.Add(w); + } + } + // The last item matched the regex, put the quotes back + if (option) + { + // If the line ended with it, don't do anything + if (index < (unquoted.Length - 1)) + { + // Get and remove the option name + string optionText = result[result.Count - 1]; + result.RemoveAt(result.Count - 1); + + // Add the quoted value back + optionText += "\"" + unquoted[index + 1] + "\""; + + // Push the result into our return array + result.Add(optionText); + + // Skip the already used value + index++; + } + } + } + else + { + result.Add(unquoted[index]); + } + } + + return result.ToArray(); + } + } + + /// + /// A console that processes commands internally + /// + public class CommandConsole : ConsoleBase, ICommandConsole + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public event OnOutputDelegate OnOutput; + + public ICommands Commands { get; private set; } + + public CommandConsole(string defaultPrompt) : base(defaultPrompt) + { + Commands = new Commands(); + + Commands.AddCommand( + "Help", false, "help", "help []", + "Display help on a particular command or on a list of commands in a category", Help); + } + + private void Help(string module, string[] cmd) + { + List help = Commands.GetHelp(cmd); + + foreach (string s in help) + Output(s); + } + + protected void FireOnOutput(string text) + { + OnOutputDelegate onOutput = OnOutput; + if (onOutput != null) + onOutput(text); + } + + /// + /// Display a command prompt on the console and wait for user input + /// + public void Prompt() + { + string line = ReadLine(DefaultPrompt + "# ", true, true); + + if (line != String.Empty) + Output("Invalid command"); + } + + public void RunCommand(string cmd) + { + string[] parts = Parser.Parse(cmd); + Commands.Resolve(parts); + } + + public override string ReadLine(string p, bool isCommand, bool e) + { + System.Console.Write("{0}", p); + string cmdinput = System.Console.ReadLine(); + + if (isCommand) + { + string[] cmd = Commands.Resolve(Parser.Parse(cmdinput)); + + if (cmd.Length != 0) + { + int i; + + for (i=0 ; i < cmd.Length ; i++) + { + if (cmd[i].Contains(" ")) + cmd[i] = "\"" + cmd[i] + "\""; + } + return String.Empty; + } + } + return cmdinput; + } + } +} diff --git a/OpenSim/Framework/Console/ConsoleBase.cs b/OpenSim/Framework/Console/ConsoleBase.cs new file mode 100755 index 0000000000..2d8e723af2 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleBase.cs @@ -0,0 +1,180 @@ +/* + * 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.Diagnostics; +using System.Reflection; +using System.Text; +using System.Threading; +using log4net; + +namespace OpenSim.Framework.Console +{ + public class ConsoleBase + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string prompt = "# "; + + public object ConsoleScene { get; set; } + + public string DefaultPrompt { get; set; } + + public ConsoleBase(string defaultPrompt) + { + DefaultPrompt = defaultPrompt; + } + + public virtual void LockOutput() + { + } + + public virtual void UnlockOutput() + { + } + + public virtual void Output(string text, string level) + { + Output(text); + } + + public virtual void Output(string text) + { + System.Console.WriteLine(text); + } + + public virtual void OutputFormat(string format, params object[] components) + { + Output(string.Format(format, components)); + } + + public string CmdPrompt(string p) + { + return ReadLine(String.Format("{0}: ", p), false, true); + } + + public string CmdPrompt(string p, string def) + { + string ret = ReadLine(String.Format("{0} [{1}]: ", p, def), false, true); + if (ret == String.Empty) + ret = def; + + return ret; + } + + public string CmdPrompt(string p, List excludedCharacters) + { + bool itisdone = false; + string ret = String.Empty; + while (!itisdone) + { + itisdone = true; + ret = CmdPrompt(p); + + foreach (char c in excludedCharacters) + { + if (ret.Contains(c.ToString())) + { + System.Console.WriteLine("The character \"" + c.ToString() + "\" is not permitted."); + itisdone = false; + } + } + } + + return ret; + } + + public string CmdPrompt(string p, string def, List excludedCharacters) + { + bool itisdone = false; + string ret = String.Empty; + while (!itisdone) + { + itisdone = true; + ret = CmdPrompt(p, def); + + if (ret == String.Empty) + { + ret = def; + } + else + { + foreach (char c in excludedCharacters) + { + if (ret.Contains(c.ToString())) + { + System.Console.WriteLine("The character \"" + c.ToString() + "\" is not permitted."); + itisdone = false; + } + } + } + } + + return ret; + } + + // Displays a command prompt and returns a default value, user may only enter 1 of 2 options + public string CmdPrompt(string prompt, string defaultresponse, List options) + { + bool itisdone = false; + string optstr = String.Empty; + foreach (string s in options) + optstr += " " + s; + + string temp = CmdPrompt(prompt, defaultresponse); + while (itisdone == false) + { + if (options.Contains(temp)) + { + itisdone = true; + } + else + { + System.Console.WriteLine("Valid options are" + optstr); + temp = CmdPrompt(prompt, defaultresponse); + } + } + return temp; + } + + // Displays a prompt and waits for the user to enter a string, then returns that string + // (Done with no echo and suitable for passwords) + public string PasswdPrompt(string p) + { + return ReadLine(String.Format("{0}: ", p), false, false); + } + + public virtual string ReadLine(string p, bool isCommand, bool e) + { + System.Console.Write("{0}", p); + string cmdinput = System.Console.ReadLine(); + + return cmdinput; + } + } +} diff --git a/OpenSim/Framework/Console/ConsoleDisplayList.cs b/OpenSim/Framework/Console/ConsoleDisplayList.cs new file mode 100644 index 0000000000..68855091d4 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleDisplayList.cs @@ -0,0 +1,112 @@ +/* + * 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.Text; + +namespace OpenSim.Framework.Console +{ + /// + /// Used to generated a formatted table for the console. + /// + /// + /// Currently subject to change. If you use this, be prepared to change your code when this class changes. + /// + public class ConsoleDisplayList + { + /// + /// The default divider between key and value for a list item. + /// + public const string DefaultKeyValueDivider = " : "; + + /// + /// The divider used between key and value for a list item. + /// + public string KeyValueDivider { get; set; } + + /// + /// Table rows + /// + public List> Rows { get; private set; } + + /// + /// Number of spaces to indent the list. + /// + public int Indent { get; set; } + + public ConsoleDisplayList() + { + Rows = new List>(); + KeyValueDivider = DefaultKeyValueDivider; + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + AddToStringBuilder(sb); + return sb.ToString(); + } + + public void AddToStringBuilder(StringBuilder sb) + { + string formatString = GetFormatString(); +// System.Console.WriteLine("FORMAT STRING [{0}]", formatString); + + // rows + foreach (KeyValuePair row in Rows) + sb.AppendFormat(formatString, row.Key, row.Value); + } + + /// + /// Gets the format string for the table. + /// + private string GetFormatString() + { + StringBuilder formatSb = new StringBuilder(); + + int longestKey = -1; + + foreach (KeyValuePair row in Rows) + if (row.Key.Length > longestKey) + longestKey = row.Key.Length; + + formatSb.Append(' ', Indent); + + // Can only do left formatting for now + formatSb.AppendFormat("{{0,-{0}}}{1}{{1}}\n", longestKey, KeyValueDivider); + + return formatSb.ToString(); + } + + public void AddRow(object key, object value) + { + Rows.Add(new KeyValuePair(key.ToString(), value.ToString())); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/ConsoleDisplayTable.cs b/OpenSim/Framework/Console/ConsoleDisplayTable.cs new file mode 100644 index 0000000000..711a337f81 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleDisplayTable.cs @@ -0,0 +1,155 @@ +/* + * 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.Text; + +namespace OpenSim.Framework.Console +{ + /// + /// Used to generated a formatted table for the console. + /// + /// + /// Currently subject to change. If you use this, be prepared to change your code when this class changes. + /// + public class ConsoleDisplayTable + { + /// + /// Default number of spaces between table columns. + /// + public const int DefaultTableSpacing = 2; + + /// + /// Table columns. + /// + public List Columns { get; private set; } + + /// + /// Table rows + /// + public List Rows { get; private set; } + + /// + /// Number of spaces to indent the whole table. + /// + public int Indent { get; set; } + + /// + /// Spacing between table columns + /// + public int TableSpacing { get; set; } + + public ConsoleDisplayTable() + { + TableSpacing = DefaultTableSpacing; + Columns = new List(); + Rows = new List(); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + AddToStringBuilder(sb); + return sb.ToString(); + } + + public void AddColumn(string name, int width) + { + Columns.Add(new ConsoleDisplayTableColumn(name, width)); + } + + public void AddRow(params object[] cells) + { + Rows.Add(new ConsoleDisplayTableRow(cells)); + } + + public void AddToStringBuilder(StringBuilder sb) + { + string formatString = GetFormatString(); +// System.Console.WriteLine("FORMAT STRING [{0}]", formatString); + + // columns + sb.AppendFormat(formatString, Columns.ConvertAll(c => c.Header).ToArray()); + + // rows + foreach (ConsoleDisplayTableRow row in Rows) + sb.AppendFormat(formatString, row.Cells.ToArray()); + } + + /// + /// Gets the format string for the table. + /// + private string GetFormatString() + { + StringBuilder formatSb = new StringBuilder(); + + formatSb.Append(' ', Indent); + + for (int i = 0; i < Columns.Count; i++) + { + if (i != 0) + formatSb.Append(' ', TableSpacing); + + // Can only do left formatting for now + formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width); + } + + formatSb.Append('\n'); + + return formatSb.ToString(); + } + } + + public struct ConsoleDisplayTableColumn + { + public string Header { get; set; } + public int Width { get; set; } + + public ConsoleDisplayTableColumn(string header, int width) : this() + { + Header = header; + Width = width; + } + } + + public struct ConsoleDisplayTableRow + { + public List Cells { get; private set; } + + public ConsoleDisplayTableRow(List cells) : this() + { + Cells = cells; + } + + public ConsoleDisplayTableRow(params object[] cells) : this() + { + Cells = new List(cells); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/ConsoleDisplayUtil.cs b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs new file mode 100644 index 0000000000..6417663c39 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs @@ -0,0 +1,48 @@ +/* + * 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; + +namespace OpenSim.Framework.Console +{ + /// + /// This will be a set of typical column sizes to allow greater consistency between console commands. + /// + public static class ConsoleDisplayUtil + { + 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; + + public const int UuidSize = 36; + public const int VectorSize = 15; + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/ConsolePluginCommand.cs b/OpenSim/Framework/Console/ConsolePluginCommand.cs new file mode 100755 index 0000000000..f4d3687450 --- /dev/null +++ b/OpenSim/Framework/Console/ConsolePluginCommand.cs @@ -0,0 +1,139 @@ +/* + * 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; + +namespace OpenSim.Framework.Console +{ + public delegate void ConsoleCommand(string[] comParams); + + /// + /// Holder object for a new console plugin command + /// + /// Override the methods like Run and IsHelpfull (but the defaults might work ok.) + /// + public class ConsolePluginCommand + { + /// + /// command delegate used in running + /// + private ConsoleCommand m_commandDelegate; + /// + /// help text displayed + /// + private string m_helpText; + /// + /// command in the form of "showme new commands" + /// + private string[] m_cmdText; + + /// + /// Construct a new ConsolePluginCommand + /// + /// for use with OpenSim.RegisterConsolePluginCommand(myCmd); + /// + /// + /// in the form of "showme new commands" + /// ommand delegate used in running + /// the text displayed in "help showme new commands" + public ConsolePluginCommand(string command, ConsoleCommand dlg, string help) + { + m_cmdText = command.Split(new char[] { ' ' }); + m_commandDelegate = dlg; + m_helpText = help; + } + + /// + /// Returns the match length this command has upon the 'cmdWithParams' + /// At least a higher number for "show plugin status" then "show" would return + /// This is used to have multi length command verbs + /// + /// @see OopenSim.RunPluginCommands + /// It will only run the one with the highest number + /// + /// + public int matchLength(string cmdWithParams) + { + // QUESTION: have a case insensitive flag? + cmdWithParams = cmdWithParams.ToLower().Trim(); + string matchText = String.Join(" ",m_cmdText).ToLower().Trim(); + if (cmdWithParams.StartsWith(matchText)) + { + // QUESTION Instead return cmdText.Length; ? + return matchText.Length; + } + return 0; + } + + /// + /// Run the delegate the incomming string may contain the command, if so, it is chopped off the cmdParams[] + /// + public void Run(string cmd, string[] cmdParams) + { + int skipParams = 0; + if (m_cmdText.Length > 1) + { + int currentParam = 1; + while (currentParam < m_cmdText.Length) + { + if (cmdParams[skipParams].ToLower().Equals(m_cmdText[currentParam].ToLower())) + { + skipParams++; + } + currentParam++; + } + + } + string[] sendCmdParams = cmdParams; + if (skipParams > 0) + { + sendCmdParams = new string[cmdParams.Length-skipParams]; + for (int i=0;i + /// Shows help information on the console's Notice method + /// + public void ShowHelp(ConsoleBase console) + { + console.Output(String.Join(" ", m_cmdText) + " - " + m_helpText + "\n"); + } + + /// + /// return true if the ShowHelp(..) method might be helpfull + /// + public bool IsHelpfull(string cmdWithParams) + { + cmdWithParams = cmdWithParams.ToLower(); + return cmdWithParams.Contains(String.Join(" ", m_cmdText).ToLower()) || m_helpText.ToLower().Contains(cmdWithParams); + } + } +} diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs new file mode 100644 index 0000000000..44f6dc193b --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -0,0 +1,387 @@ +/* + * 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.Linq; +using System.Reflection; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Framework.Console +{ + public class ConsoleUtil + { + // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const int LocalIdNotFound = 0; + + /// + /// Used by modules to display stock co-ordinate help, though possibly this should be under some general section + /// rather than in each help summary. + /// + public const string CoordHelp + = @"Each component of the coord is comma separated. There must be no spaces between the commas. + If you don't care about the z component you can simply omit it. + If you don't care about the x or y components then you can leave them blank (though a comma is still required) + If you want to specify the maximum value of a component then you can use ~ instead of a number + If you want to specify the minimum value of a component then you can use -~ instead of a number + e.g. + show object pos 20,20,20 to 40,40,40 + delete object pos 20,20 to 40,40 + show object pos ,20,20 to ,40,40 + delete object pos ,,30 to ,,~ + show object pos ,,-~ to ,,30"; + + public const string MinRawConsoleVectorValue = "-~"; + public const string MaxRawConsoleVectorValue = "~"; + + public const string VectorSeparator = ","; + public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray(); + + /// + /// Check if the given file path exists. + /// + /// If not, warning is printed to the given console. + /// true if the file does not exist, false otherwise. + /// + /// + public static bool CheckFileDoesNotExist(ICommandConsole console, string path) + { + if (File.Exists(path)) + { + console.OutputFormat("File {0} already exists. Please move or remove it.", path); + return false; + } + + return true; + } + + /// + /// Try to parse a console UUID from the console. + /// + /// + /// Will complain to the console if parsing fails. + /// + /// + /// If null then no complaint is printed. + /// + /// + public static bool TryParseConsoleUuid(ICommandConsole console, string rawUuid, out UUID uuid) + { + if (!UUID.TryParse(rawUuid, out uuid)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid uuid", rawUuid); + + return false; + } + + return true; + } + + public static bool TryParseConsoleLocalId(ICommandConsole console, string rawLocalId, out uint localId) + { + if (!uint.TryParse(rawLocalId, out localId)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid local id", localId); + + return false; + } + + if (localId == 0) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid local id - it must be greater than 0", localId); + + return false; + } + + return true; + } + + /// + /// Tries to parse the input as either a UUID or a local ID. + /// + /// true if parsing succeeded, false otherwise. + /// + /// + /// + /// + /// Will be set to ConsoleUtil.LocalIdNotFound if parsing result was a UUID or no parse succeeded. + /// + public static bool TryParseConsoleId(ICommandConsole console, string rawId, out UUID uuid, out uint localId) + { + if (TryParseConsoleUuid(null, rawId, out uuid)) + { + localId = LocalIdNotFound; + return true; + } + + if (TryParseConsoleLocalId(null, rawId, out localId)) + { + return true; + } + + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid UUID or local id", rawId); + + return false; + } + + /// + /// Convert a console input to a bool, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b) + { + if (!bool.TryParse(rawConsoleString, out b)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a true or false value", rawConsoleString); + + return false; + } + + return true; + } + + /// + /// Convert a console input to an int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i) + { + if (!int.TryParse(rawConsoleInt, out i)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid integer", rawConsoleInt); + + return false; + } + + return true; + } + + /// + /// Convert a console input to a float, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleFloat(ICommandConsole console, string rawConsoleInput, out float i) + { + if (!float.TryParse(rawConsoleInput, out i)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid float", rawConsoleInput); + + return false; + } + + return true; + } + + /// + /// Convert a console input to a double, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleDouble(ICommandConsole console, string rawConsoleInput, out double i) + { + if (!double.TryParse(rawConsoleInput, out i)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid double", rawConsoleInput); + + return false; + } + + return true; + } + + /// + /// Convert a console integer to a natural int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleNaturalInt(ICommandConsole console, string rawConsoleInt, out int i) + { + if (TryParseConsoleInt(console, rawConsoleInt, out i)) + { + if (i < 0) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a positive integer", rawConsoleInt); + + return false; + } + + return true; + } + + return false; + } + + /// + /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 + /// + /// /param> + /// + /// + public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector) + { + return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector); + } + + /// + /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3 + /// + /// /param> + /// + /// + public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector) + { + return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector); + } + + /// + /// Convert a vector input from the console to an OpenMetaverse.Vector3 + /// + /// + /// A string in the form ,, where there is no space between values. + /// Any component can be missing (e.g. ,,40). blankComponentFunc is invoked to replace the blank with a suitable value + /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40,30 or 40) + /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue + /// Other than that, component values must be numeric. + /// + /// + /// Behaviour if component is blank. If null then conversion fails on a blank component. + /// + /// + /// + public static bool TryParseConsoleVector( + string rawConsoleVector, Func blankComponentFunc, out Vector3 vector) + { + return Vector3.TryParse(CookVector(rawConsoleVector, 3, blankComponentFunc), out vector); + } + + /// + /// Convert a vector input from the console to an OpenMetaverse.Vector2 + /// + /// + /// A string in the form , where there is no space between values. + /// Any component can be missing (e.g. ,40). blankComponentFunc is invoked to replace the blank with a suitable value + /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40) + /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue + /// Other than that, component values must be numeric. + /// + /// + /// Behaviour if component is blank. If null then conversion fails on a blank component. + /// + /// + /// + public static bool TryParseConsole2DVector( + string rawConsoleVector, Func blankComponentFunc, out Vector2 vector) + { + // We don't use Vector2.TryParse() for now because for some reason it expects an input with 3 components + // rather than 2. + string cookedVector = CookVector(rawConsoleVector, 2, blankComponentFunc); + + if (cookedVector == null) + { + vector = Vector2.Zero; + + return false; + } + else + { + string[] cookedComponents = cookedVector.Split(VectorSeparatorChars); + + vector = new Vector2(float.Parse(cookedComponents[0]), float.Parse(cookedComponents[1])); + + return true; + } + + //return Vector2.TryParse(CookVector(rawConsoleVector, 2, blankComponentFunc), out vector); + } + + /// + /// Convert a raw console vector into a vector that can be be parsed by the relevant OpenMetaverse.TryParse() + /// + /// + /// + /// + /// null if conversion was not possible + private static string CookVector( + string rawConsoleVector, int dimensions, Func blankComponentFunc) + { + List components = rawConsoleVector.Split(VectorSeparatorChars).ToList(); + + if (components.Count < 1 || components.Count > dimensions) + return null; + + if (components.Count < dimensions) + { + if (blankComponentFunc == null) + return null; + else + for (int i = components.Count; i < dimensions; i++) + components.Add(""); + } + + List cookedComponents + = components.ConvertAll( + c => + { + if (c == "") + return blankComponentFunc.Invoke(c); + else if (c == MaxRawConsoleVectorValue) + return float.MaxValue.ToString(); + else if (c == MinRawConsoleVectorValue) + return float.MinValue.ToString(); + else + return c; + }); + + return string.Join(VectorSeparator, cookedComponents.ToArray()); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs new file mode 100644 index 0000000000..28293c0dd0 --- /dev/null +++ b/OpenSim/Framework/Console/LocalConsole.cs @@ -0,0 +1,585 @@ +/* + * 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.Diagnostics; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.IO; +using Nini.Config; +using log4net; + +namespace OpenSim.Framework.Console +{ + /// + /// A console that uses cursor control and color + /// + public class LocalConsole : CommandConsole + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private string m_historyPath; + private bool m_historyEnable; + + // private readonly object m_syncRoot = new object(); + private const string LOGLEVEL_NONE = "(none)"; + + // Used to extract categories for colourization. + private Regex m_categoryRegex + = new Regex( + @"^(?.*?)\[(?[^\]]+)\]:?(?.*)", RegexOptions.Singleline | RegexOptions.Compiled); + + private int m_cursorYPosition = -1; + private int m_cursorXPosition = 0; + private StringBuilder m_commandLine = new StringBuilder(); + private bool m_echo = true; + private List m_history = new List(); + + private static readonly ConsoleColor[] Colors = { + // the dark colors don't seem to be visible on some black background terminals like putty :( + //ConsoleColor.DarkBlue, + //ConsoleColor.DarkGreen, + //ConsoleColor.DarkCyan, + //ConsoleColor.DarkMagenta, + //ConsoleColor.DarkYellow, + ConsoleColor.Gray, + //ConsoleColor.DarkGray, + ConsoleColor.Blue, + ConsoleColor.Green, + ConsoleColor.Cyan, + ConsoleColor.Magenta, + ConsoleColor.Yellow + }; + + private static ConsoleColor DeriveColor(string input) + { + // it is important to do Abs, hash values can be negative + return Colors[(Math.Abs(input.ToUpper().GetHashCode()) % Colors.Length)]; + } + + public LocalConsole(string defaultPrompt, IConfig startupConfig = null) : base(defaultPrompt) + { + + if (startupConfig == null) return; + + m_historyEnable = startupConfig.GetBoolean("ConsoleHistoryFileEnabled", false); + if (!m_historyEnable) + { + m_log.Info("[LOCAL CONSOLE]: Persistent command line history from file is Disabled"); + return; + } + + string m_historyFile = startupConfig.GetString("ConsoleHistoryFile", "OpenSimConsoleHistory.txt"); + int m_historySize = startupConfig.GetInt("ConsoleHistoryFileLines", 100); + m_historyPath = Path.GetFullPath(Path.Combine(Util.configDir(), m_historyFile)); + m_log.InfoFormat("[LOCAL CONSOLE]: Persistent command line history is Enabled, up to {0} lines from file {1}", m_historySize, m_historyPath); + + if (File.Exists(m_historyPath)) + { + using (StreamReader history_file = new StreamReader(m_historyPath)) + { + string line; + while ((line = history_file.ReadLine()) != null) + { + m_history.Add(line); + } + } + + if (m_history.Count > m_historySize) + { + while (m_history.Count > m_historySize) + m_history.RemoveAt(0); + + using (StreamWriter history_file = new StreamWriter(m_historyPath)) + { + foreach (string line in m_history) + { + history_file.WriteLine(line); + } + } + } + m_log.InfoFormat("[LOCAL CONSOLE]: Read {0} lines of command line history from file {1}", m_history.Count, m_historyPath); + } + else + { + m_log.InfoFormat("[LOCAL CONSOLE]: Creating new empty command line history file {0}", m_historyPath); + File.Create(m_historyPath).Dispose(); + } + } + + private void AddToHistory(string text) + { + while (m_history.Count >= 100) + m_history.RemoveAt(0); + + m_history.Add(text); + if (m_historyEnable) + { + File.AppendAllText(m_historyPath, text + Environment.NewLine); + } + } + + /// + /// Set the cursor row. + /// + /// + /// + /// Row to set. If this is below 0, then the row is set to 0. If it is equal to the buffer height or greater + /// then it is set to one less than the height. + /// + /// + /// The new cursor row. + /// + private int SetCursorTop(int top) + { + // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try + // to set a cursor row position with a currently invalid column, mono will throw an exception. + // Therefore, we need to make sure that the column position is valid first. + int left = System.Console.CursorLeft; + + if (left < 0) + { + System.Console.CursorLeft = 0; + } + else + { + int bufferWidth = System.Console.BufferWidth; + + // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) + if (bufferWidth > 0 && left >= bufferWidth) + System.Console.CursorLeft = bufferWidth - 1; + } + + if (top < 0) + { + top = 0; + } + else + { + int bufferHeight = System.Console.BufferHeight; + + // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) + if (bufferHeight > 0 && top >= bufferHeight) + top = bufferHeight - 1; + } + + System.Console.CursorTop = top; + + return top; + } + + /// + /// Set the cursor column. + /// + /// + /// + /// Column to set. If this is below 0, then the column is set to 0. If it is equal to the buffer width or greater + /// then it is set to one less than the width. + /// + /// + /// The new cursor column. + /// + private int SetCursorLeft(int left) + { + // From at least mono 2.4.2.3, window resizing can give mono an invalid row and column values. If we try + // to set a cursor column position with a currently invalid row, mono will throw an exception. + // Therefore, we need to make sure that the row position is valid first. + int top = System.Console.CursorTop; + + if (top < 0) + { + System.Console.CursorTop = 0; + } + else + { + int bufferHeight = System.Console.BufferHeight; + // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) + if (bufferHeight > 0 && top >= bufferHeight) + System.Console.CursorTop = bufferHeight - 1; + } + + if (left < 0) + { + left = 0; + } + else + { + int bufferWidth = System.Console.BufferWidth; + + // On Mono 2.4.2.3 (and possibly above), the buffer value is sometimes erroneously zero (Mantis 4657) + if (bufferWidth > 0 && left >= bufferWidth) + left = bufferWidth - 1; + } + + System.Console.CursorLeft = left; + + return left; + } + + private void Show() + { + lock (m_commandLine) + { + if (m_cursorYPosition == -1 || System.Console.BufferWidth == 0) + return; + + int xc = prompt.Length + m_cursorXPosition; + int new_x = xc % System.Console.BufferWidth; + int new_y = m_cursorYPosition + xc / System.Console.BufferWidth; + int end_y = m_cursorYPosition + (m_commandLine.Length + prompt.Length) / System.Console.BufferWidth; + + if (end_y >= System.Console.BufferHeight) // wrap + { + m_cursorYPosition--; + new_y--; + SetCursorLeft(0); + SetCursorTop(System.Console.BufferHeight - 1); + System.Console.WriteLine(" "); + } + + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + SetCursorLeft(0); + + if (m_echo) + System.Console.Write("{0}{1}", prompt, m_commandLine); + else + System.Console.Write("{0}", prompt); + + SetCursorTop(new_y); + SetCursorLeft(new_x); + } + } + + public override void LockOutput() + { + Monitor.Enter(m_commandLine); + try + { + if (m_cursorYPosition != -1) + { + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + System.Console.CursorLeft = 0; + + int count = m_commandLine.Length + prompt.Length; + + while (count-- > 0) + System.Console.Write(" "); + + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + SetCursorLeft(0); + } + } + catch (Exception) + { + } + } + + public override void UnlockOutput() + { + if (m_cursorYPosition != -1) + { + m_cursorYPosition = System.Console.CursorTop; + Show(); + } + Monitor.Exit(m_commandLine); + } + + private void WriteColorText(ConsoleColor color, string sender) + { + try + { + lock (this) + { + try + { + System.Console.ForegroundColor = color; + System.Console.Write(sender); + System.Console.ResetColor(); + } + catch (ArgumentNullException) + { + // Some older systems dont support coloured text. + System.Console.WriteLine(sender); + } + } + } + catch (ObjectDisposedException) + { + } + } + + private void WriteLocalText(string text, string level) + { + string outText = text; + + if (level != LOGLEVEL_NONE) + { + MatchCollection matches = m_categoryRegex.Matches(text); + + if (matches.Count == 1) + { + outText = matches[0].Groups["End"].Value; + System.Console.Write(matches[0].Groups["Front"].Value); + + System.Console.Write("["); + WriteColorText(DeriveColor(matches[0].Groups["Category"].Value), + matches[0].Groups["Category"].Value); + System.Console.Write("]:"); + } + else + { + outText = outText.Trim(); + } + } + + if (level == "error") + WriteColorText(ConsoleColor.Red, outText); + else if (level == "warn") + WriteColorText(ConsoleColor.Yellow, outText); + else + System.Console.Write(outText); + + System.Console.WriteLine(); + } + + public override void Output(string text) + { + Output(text, LOGLEVEL_NONE); + } + + public override void Output(string text, string level) + { + FireOnOutput(text); + + lock (m_commandLine) + { + if (m_cursorYPosition == -1) + { + WriteLocalText(text, level); + + return; + } + + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + SetCursorLeft(0); + + int count = m_commandLine.Length + prompt.Length; + + while (count-- > 0) + System.Console.Write(" "); + + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + SetCursorLeft(0); + + WriteLocalText(text, level); + + m_cursorYPosition = System.Console.CursorTop; + + Show(); + } + } + + private bool ContextHelp() + { + string[] words = Parser.Parse(m_commandLine.ToString()); + + bool trailingSpace = m_commandLine.ToString().EndsWith(" "); + + // Allow ? through while typing a URI + // + if (words.Length > 0 && words[words.Length-1].StartsWith("http") && !trailingSpace) + return false; + + string[] opts = Commands.FindNextOption(words, trailingSpace); + + if (opts[0].StartsWith("Command help:")) + Output(opts[0]); + else + Output(String.Format("Options: {0}", String.Join(" ", opts))); + + return true; + } + + public override string ReadLine(string p, bool isCommand, bool e) + { + m_cursorXPosition = 0; + prompt = p; + m_echo = e; + int historyLine = m_history.Count; + + SetCursorLeft(0); // Needed for mono + System.Console.Write(" "); // Needed for mono + + lock (m_commandLine) + { + m_cursorYPosition = System.Console.CursorTop; + m_commandLine.Remove(0, m_commandLine.Length); + } + + while (true) + { + Show(); + + ConsoleKeyInfo key = System.Console.ReadKey(true); + char enteredChar = key.KeyChar; + + if (!Char.IsControl(enteredChar)) + { + if (m_cursorXPosition >= 318) + continue; + + if (enteredChar == '?' && isCommand) + { + if (ContextHelp()) + continue; + } + + m_commandLine.Insert(m_cursorXPosition, enteredChar); + m_cursorXPosition++; + } + else + { + switch (key.Key) + { + case ConsoleKey.Backspace: + if (m_cursorXPosition == 0) + break; + m_commandLine.Remove(m_cursorXPosition-1, 1); + m_cursorXPosition--; + + SetCursorLeft(0); + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + + if (m_echo) + System.Console.Write("{0}{1} ", prompt, m_commandLine); + else + System.Console.Write("{0}", prompt); + + break; + case ConsoleKey.Delete: + if (m_cursorXPosition == m_commandLine.Length) + break; + + m_commandLine.Remove(m_cursorXPosition, 1); + + SetCursorLeft(0); + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + + if (m_echo) + System.Console.Write("{0}{1} ", prompt, m_commandLine); + else + System.Console.Write("{0}", prompt); + + break; + case ConsoleKey.End: + m_cursorXPosition = m_commandLine.Length; + break; + case ConsoleKey.Home: + m_cursorXPosition = 0; + break; + case ConsoleKey.UpArrow: + if (historyLine < 1) + break; + historyLine--; + LockOutput(); + m_commandLine.Remove(0, m_commandLine.Length); + m_commandLine.Append(m_history[historyLine]); + m_cursorXPosition = m_commandLine.Length; + UnlockOutput(); + break; + case ConsoleKey.DownArrow: + if (historyLine >= m_history.Count) + break; + historyLine++; + LockOutput(); + if (historyLine == m_history.Count) + { + m_commandLine.Remove(0, m_commandLine.Length); + } + else + { + m_commandLine.Remove(0, m_commandLine.Length); + m_commandLine.Append(m_history[historyLine]); + } + m_cursorXPosition = m_commandLine.Length; + UnlockOutput(); + break; + case ConsoleKey.LeftArrow: + if (m_cursorXPosition > 0) + m_cursorXPosition--; + break; + case ConsoleKey.RightArrow: + if (m_cursorXPosition < m_commandLine.Length) + m_cursorXPosition++; + break; + case ConsoleKey.Enter: + SetCursorLeft(0); + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + + System.Console.WriteLine(); + //Show(); + + lock (m_commandLine) + { + m_cursorYPosition = -1; + } + + string commandLine = m_commandLine.ToString(); + + if (isCommand) + { + string[] cmd = Commands.Resolve(Parser.Parse(commandLine)); + + if (cmd.Length != 0) + { + int index; + + for (index=0 ; index < cmd.Length ; index++) + { + if (cmd[index].Contains(" ")) + cmd[index] = "\"" + cmd[index] + "\""; + } + AddToHistory(String.Join(" ", cmd)); + return String.Empty; + } + } + + // If we're not echoing to screen (e.g. a password) then we probably don't want it in history + if (m_echo && commandLine != "") + AddToHistory(commandLine); + + return commandLine; + default: + break; + } + } + } + } + } +} diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs new file mode 100644 index 0000000000..1a142df016 --- /dev/null +++ b/OpenSim/Framework/Console/MockConsole.cs @@ -0,0 +1,89 @@ +/* + * 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.Threading; +using System.Collections.Generic; +using System.Text; +using System.Xml; + +namespace OpenSim.Framework.Console +{ + /// + /// This is a Fake console that's used when setting up the Scene in Unit Tests + /// Don't use this except for Unit Testing or you're in for a world of hurt when the + /// sim gets to ReadLine + /// + public class MockConsole : ICommandConsole + { +#pragma warning disable 0067 + public event OnOutputDelegate OnOutput; +#pragma warning restore 0067 + + private MockCommands m_commands = new MockCommands(); + + public ICommands Commands { get { return m_commands; } } + + public string DefaultPrompt { get; set; } + + public void Prompt() {} + + public void RunCommand(string cmd) {} + + public string ReadLine(string p, bool isCommand, bool e) { return ""; } + + public object ConsoleScene { + get { return null; } + set {} + } + + public void Output(string text, string level) {} + public void Output(string text) {} + public void OutputFormat(string format, params object[] components) {} + + public string CmdPrompt(string p) { return ""; } + public string CmdPrompt(string p, string def) { return ""; } + public string CmdPrompt(string p, List excludedCharacters) { return ""; } + public string CmdPrompt(string p, string def, List excludedCharacters) { return ""; } + + public string CmdPrompt(string prompt, string defaultresponse, List options) { return ""; } + + public string PasswdPrompt(string p) { return ""; } + } + + public class MockCommands : ICommands + { + public void FromXml(XmlElement root, CommandDelegate fn) {} + public List GetHelp(string[] cmd) { return null; } + public void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn) {} + public void AddCommand(string module, bool shared, string command, string help, string longhelp, string descriptivehelp, CommandDelegate fn) {} + public string[] FindNextOption(string[] cmd, bool term) { return null; } + public bool HasCommand(string cmd) { return false; } + public string[] Resolve(string[] cmd) { return null; } + public XmlElement GetXml(XmlDocument doc) { return null; } + } +} diff --git a/OpenSim/Framework/Console/OpenSimAppender.cs b/OpenSim/Framework/Console/OpenSimAppender.cs new file mode 100644 index 0000000000..72a251e9b1 --- /dev/null +++ b/OpenSim/Framework/Console/OpenSimAppender.cs @@ -0,0 +1,86 @@ +/* + * 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 log4net.Appender; +using log4net.Core; + +namespace OpenSim.Framework.Console +{ + /// + /// Writes log information out onto the console + /// + public class OpenSimAppender : AnsiColorTerminalAppender + { + private ConsoleBase m_console = null; + + public ConsoleBase Console + { + get { return m_console; } + set { m_console = value; } + } + + override protected void Append(LoggingEvent le) + { + if (m_console != null) + m_console.LockOutput(); + + string loggingMessage = RenderLoggingEvent(le); + + try + { + if (m_console != null) + { + string level = "normal"; + + if (le.Level == Level.Error) + level = "error"; + else if (le.Level == Level.Warn) + level = "warn"; + + m_console.Output(loggingMessage, level); + } + else + { + if (!loggingMessage.EndsWith("\n")) + System.Console.WriteLine(loggingMessage); + else + System.Console.Write(loggingMessage); + } + } + catch (Exception e) + { + System.Console.WriteLine("Couldn't write out log message: {0}", e.ToString()); + } + finally + { + if (m_console != null) + m_console.UnlockOutput(); + } + } + } +} diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs new file mode 100644 index 0000000000..8ad7b0daa9 --- /dev/null +++ b/OpenSim/Framework/Console/RemoteConsole.cs @@ -0,0 +1,513 @@ +/* + * 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.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using OpenMetaverse; +using Nini.Config; +using OpenSim.Framework.Servers.HttpServer; +using log4net; + +namespace OpenSim.Framework.Console +{ + public class ConsoleConnection + { + public int last; + public long lastLineSeen; + public bool newConnection = true; + } + + // A console that uses REST interfaces + // + public class RemoteConsole : CommandConsole + { + private IHttpServer m_Server = null; + private IConfigSource m_Config = null; + + private List m_Scrollback = new List(); + private ManualResetEvent m_DataEvent = new ManualResetEvent(false); + private List m_InputData = new List(); + private long m_LineNumber = 0; + private Dictionary m_Connections = + new Dictionary(); + private string m_UserName = String.Empty; + private string m_Password = String.Empty; + private string m_AllowedOrigin = String.Empty; + + public RemoteConsole(string defaultPrompt) : base(defaultPrompt) + { + } + + public void ReadConfig(IConfigSource config) + { + m_Config = config; + + IConfig netConfig = m_Config.Configs["Network"]; + if (netConfig == null) + return; + + m_UserName = netConfig.GetString("ConsoleUser", String.Empty); + m_Password = netConfig.GetString("ConsolePass", String.Empty); + m_AllowedOrigin = netConfig.GetString("ConsoleAllowedOrigin", String.Empty); + } + + public void SetServer(IHttpServer server) + { + m_Server = server; + + m_Server.AddHTTPHandler("/StartSession/", HandleHttpStartSession); + m_Server.AddHTTPHandler("/CloseSession/", HandleHttpCloseSession); + m_Server.AddHTTPHandler("/SessionCommand/", HandleHttpSessionCommand); + } + + public override void Output(string text, string level) + { + lock (m_Scrollback) + { + while (m_Scrollback.Count >= 1000) + m_Scrollback.RemoveAt(0); + m_LineNumber++; + m_Scrollback.Add(String.Format("{0}", m_LineNumber)+":"+level+":"+text); + } + FireOnOutput(text.Trim()); + System.Console.WriteLine(text.Trim()); + } + + public override void Output(string text) + { + Output(text, "normal"); + } + + public override string ReadLine(string p, bool isCommand, bool e) + { + if (isCommand) + Output("+++"+p); + else + Output("-++"+p); + + m_DataEvent.WaitOne(); + + string cmdinput; + + lock (m_InputData) + { + if (m_InputData.Count == 0) + { + m_DataEvent.Reset(); + return ""; + } + + cmdinput = m_InputData[0]; + m_InputData.RemoveAt(0); + if (m_InputData.Count == 0) + m_DataEvent.Reset(); + + } + + if (isCommand) + { + string[] cmd = Commands.Resolve(Parser.Parse(cmdinput)); + + if (cmd.Length != 0) + { + int i; + + for (i=0 ; i < cmd.Length ; i++) + { + if (cmd[i].Contains(" ")) + cmd[i] = "\"" + cmd[i] + "\""; + } + return String.Empty; + } + } + return cmdinput; + } + + private Hashtable CheckOrigin(Hashtable result) + { + if (!string.IsNullOrEmpty(m_AllowedOrigin)) + result["access_control_allow_origin"] = m_AllowedOrigin; + return result; + } + /* TODO: Figure out how PollServiceHTTPHandler can access the request headers + * in order to use m_AllowedOrigin as a regular expression + private Hashtable CheckOrigin(Hashtable headers, Hashtable result) + { + if (!string.IsNullOrEmpty(m_AllowedOrigin)) + { + if (headers.ContainsKey("origin")) + { + string origin = headers["origin"].ToString(); + if (Regex.IsMatch(origin, m_AllowedOrigin)) + result["access_control_allow_origin"] = origin; + } + } + return result; + } + */ + + private void DoExpire() + { + List expired = new List(); + + lock (m_Connections) + { + foreach (KeyValuePair kvp in m_Connections) + { + if (System.Environment.TickCount - kvp.Value.last > 500000) + expired.Add(kvp.Key); + } + + foreach (UUID id in expired) + { + m_Connections.Remove(id); + CloseConnection(id); + } + } + } + + private Hashtable HandleHttpStartSession(Hashtable request) + { + DoExpire(); + + Hashtable post = DecodePostString(request["body"].ToString()); + Hashtable reply = new Hashtable(); + + reply["str_response_string"] = ""; + reply["int_response_code"] = 401; + reply["content_type"] = "text/plain"; + + if (m_UserName == String.Empty) + return reply; + + if (post["USER"] == null || post["PASS"] == null) + return reply; + + if (m_UserName != post["USER"].ToString() || + m_Password != post["PASS"].ToString()) + { + return reply; + } + + ConsoleConnection c = new ConsoleConnection(); + c.last = System.Environment.TickCount; + c.lastLineSeen = 0; + + UUID sessionID = UUID.Random(); + + lock (m_Connections) + { + m_Connections[sessionID] = c; + } + + string uri = "/ReadResponses/" + sessionID.ToString() + "/"; + + m_Server.AddPollServiceHTTPHandler( + uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout + + XmlDocument xmldoc = new XmlDocument(); + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", + ""); + + xmldoc.AppendChild(rootElement); + + XmlElement id = xmldoc.CreateElement("", "SessionID", ""); + id.AppendChild(xmldoc.CreateTextNode(sessionID.ToString())); + + rootElement.AppendChild(id); + + XmlElement prompt = xmldoc.CreateElement("", "Prompt", ""); + prompt.AppendChild(xmldoc.CreateTextNode(DefaultPrompt)); + + rootElement.AppendChild(prompt); + + rootElement.AppendChild(MainConsole.Instance.Commands.GetXml(xmldoc)); + + reply["str_response_string"] = xmldoc.InnerXml; + reply["int_response_code"] = 200; + reply["content_type"] = "text/xml"; + reply = CheckOrigin(reply); + + return reply; + } + + private Hashtable HandleHttpCloseSession(Hashtable request) + { + DoExpire(); + + Hashtable post = DecodePostString(request["body"].ToString()); + Hashtable reply = new Hashtable(); + + reply["str_response_string"] = ""; + reply["int_response_code"] = 404; + reply["content_type"] = "text/plain"; + + if (post["ID"] == null) + return reply; + + UUID id; + if (!UUID.TryParse(post["ID"].ToString(), out id)) + return reply; + + lock (m_Connections) + { + if (m_Connections.ContainsKey(id)) + { + m_Connections.Remove(id); + CloseConnection(id); + } + } + + XmlDocument xmldoc = new XmlDocument(); + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", + ""); + + xmldoc.AppendChild(rootElement); + + XmlElement res = xmldoc.CreateElement("", "Result", ""); + res.AppendChild(xmldoc.CreateTextNode("OK")); + + rootElement.AppendChild(res); + + reply["str_response_string"] = xmldoc.InnerXml; + reply["int_response_code"] = 200; + reply["content_type"] = "text/xml"; + reply = CheckOrigin(reply); + + return reply; + } + + private Hashtable HandleHttpSessionCommand(Hashtable request) + { + DoExpire(); + + Hashtable post = DecodePostString(request["body"].ToString()); + Hashtable reply = new Hashtable(); + + reply["str_response_string"] = ""; + reply["int_response_code"] = 404; + reply["content_type"] = "text/plain"; + + if (post["ID"] == null) + return reply; + + UUID id; + if (!UUID.TryParse(post["ID"].ToString(), out id)) + return reply; + + lock (m_Connections) + { + if (!m_Connections.ContainsKey(id)) + return reply; + } + + if (post["COMMAND"] == null) + return reply; + + lock (m_InputData) + { + m_DataEvent.Set(); + m_InputData.Add(post["COMMAND"].ToString()); + } + + XmlDocument xmldoc = new XmlDocument(); + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", + ""); + + xmldoc.AppendChild(rootElement); + + XmlElement res = xmldoc.CreateElement("", "Result", ""); + res.AppendChild(xmldoc.CreateTextNode("OK")); + + rootElement.AppendChild(res); + + reply["str_response_string"] = xmldoc.InnerXml; + reply["int_response_code"] = 200; + reply["content_type"] = "text/xml"; + reply = CheckOrigin(reply); + + return reply; + } + + private Hashtable DecodePostString(string data) + { + Hashtable result = new Hashtable(); + + string[] terms = data.Split(new char[] {'&'}); + + foreach (string term in terms) + { + string[] elems = term.Split(new char[] {'='}); + if (elems.Length == 0) + continue; + + string name = System.Web.HttpUtility.UrlDecode(elems[0]); + string value = String.Empty; + + if (elems.Length > 1) + value = System.Web.HttpUtility.UrlDecode(elems[1]); + + result[name] = value; + } + + return result; + } + + public void CloseConnection(UUID id) + { + try + { + string uri = "/ReadResponses/" + id.ToString() + "/"; + + m_Server.RemovePollServiceHTTPHandler("", uri); + } + catch (Exception) + { + } + } + + private bool HasEvents(UUID RequestID, UUID sessionID) + { + ConsoleConnection c = null; + + lock (m_Connections) + { + if (!m_Connections.ContainsKey(sessionID)) + return false; + c = m_Connections[sessionID]; + } + c.last = System.Environment.TickCount; + if (c.lastLineSeen < m_LineNumber) + return true; + return false; + } + + private Hashtable GetEvents(UUID RequestID, UUID sessionID) + { + ConsoleConnection c = null; + + lock (m_Connections) + { + if (!m_Connections.ContainsKey(sessionID)) + return NoEvents(RequestID, UUID.Zero); + c = m_Connections[sessionID]; + } + c.last = System.Environment.TickCount; + if (c.lastLineSeen >= m_LineNumber) + return NoEvents(RequestID, UUID.Zero); + + Hashtable result = new Hashtable(); + + XmlDocument xmldoc = new XmlDocument(); + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", + ""); + + if (c.newConnection) + { + c.newConnection = false; + Output("+++" + DefaultPrompt); + } + + lock (m_Scrollback) + { + long startLine = m_LineNumber - m_Scrollback.Count; + long sendStart = startLine; + if (sendStart < c.lastLineSeen) + sendStart = c.lastLineSeen; + + for (long i = sendStart ; i < m_LineNumber ; i++) + { + XmlElement res = xmldoc.CreateElement("", "Line", ""); + long line = i + 1; + res.SetAttribute("Number", line.ToString()); + res.AppendChild(xmldoc.CreateTextNode(m_Scrollback[(int)(i - startLine)])); + + rootElement.AppendChild(res); + } + } + c.lastLineSeen = m_LineNumber; + + xmldoc.AppendChild(rootElement); + + result["str_response_string"] = xmldoc.InnerXml; + result["int_response_code"] = 200; + result["content_type"] = "application/xml"; + result["keepalive"] = false; + result["reusecontext"] = false; + result = CheckOrigin(result); + + return result; + } + + private Hashtable NoEvents(UUID RequestID, UUID id) + { + Hashtable result = new Hashtable(); + + XmlDocument xmldoc = new XmlDocument(); + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + xmldoc.AppendChild(xmlnode); + XmlElement rootElement = xmldoc.CreateElement("", "ConsoleSession", + ""); + + xmldoc.AppendChild(rootElement); + + result["str_response_string"] = xmldoc.InnerXml; + result["int_response_code"] = 200; + result["content_type"] = "text/xml"; + result["keepalive"] = false; + result["reusecontext"] = false; + result = CheckOrigin(result); + + return result; + } + } +} diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs new file mode 100644 index 0000000000..3ba264c7af --- /dev/null +++ b/OpenSim/Framework/Constants.cs @@ -0,0 +1,102 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class Constants + { + // 'RegionSize' is the legacy region size. + // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionInfo.RegionSize[XYZ] as a region might not + // be the legacy region size. + public const uint RegionSize = 256; + public const uint RegionHeight = 4096; + // This could be a parameters but, really, a region of greater than this is pretty unmanageable + public const uint MaximumRegionSize = 8192; + + // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum + public const int MinRegionSize = 16; + public const int TerrainPatchSize = 16; + + public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f"; + + public enum EstateAccessCodex : uint + { + AccessOptions = 1, + AllowedGroups = 2, + EstateBans = 4, + EstateManagers = 8 + } + + [Flags]public enum TeleportFlags : uint + { + /// No flags set, or teleport failed + Default = 0, + /// Set when newbie leaves help island for first time + SetHomeToTarget = 1 << 0, + /// + SetLastToTarget = 1 << 1, + /// Via Lure + ViaLure = 1 << 2, + /// Via Landmark + ViaLandmark = 1 << 3, + /// Via Location + ViaLocation = 1 << 4, + /// Via Home + ViaHome = 1 << 5, + /// Via Telehub + ViaTelehub = 1 << 6, + /// Via Login + ViaLogin = 1 << 7, + /// Linden Summoned + ViaGodlikeLure = 1 << 8, + /// Linden Forced me + Godlike = 1 << 9, + /// + NineOneOne = 1 << 10, + /// Agent Teleported Home via Script + DisableCancel = 1 << 11, + /// + ViaRegionID = 1 << 12, + /// + IsFlying = 1 << 13, + /// + ResetHome = 1 << 14, + /// forced to new location for example when avatar is banned or ejected + ForceRedirect = 1 << 15, + /// Teleport Finished via a Lure + FinishedViaLure = 1 << 26, + /// Finished, Sim Changed + FinishedViaNewSim = 1 << 28, + /// Finished, Same Sim + FinishedViaSameSim = 1 << 29, + /// Agent coming into the grid from another grid + ViaHGLogin = 1 << 30 + } + + } +} diff --git a/OpenSim/Framework/Culture.cs b/OpenSim/Framework/Culture.cs new file mode 100644 index 0000000000..3d78fac2d4 --- /dev/null +++ b/OpenSim/Framework/Culture.cs @@ -0,0 +1,56 @@ +/* + * 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.Globalization; +using System.Threading; + +namespace OpenSim.Framework +{ + public class Culture + { + private static readonly CultureInfo m_cultureInfo = new CultureInfo("en-US", false); + + public static NumberFormatInfo NumberFormatInfo + { + get { return m_cultureInfo.NumberFormat; } + } + + public static IFormatProvider FormatProvider + { + get { return m_cultureInfo; } + } + + /// + /// Set Culture to en-US to make string processing of numbers simpler. + /// + public static void SetCurrentCulture() + { + Thread.CurrentThread.CurrentCulture = m_cultureInfo; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs new file mode 100644 index 0000000000..4995a92de0 --- /dev/null +++ b/OpenSim/Framework/DAMap.cs @@ -0,0 +1,328 @@ +/* + * 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.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// This class stores and retrieves dynamic attributes. + /// + /// + /// Modules that want to use dynamic attributes need to do so in a private data store + /// which is accessed using a unique name. DAMap provides access to the data stores, + /// each of which is an OSDMap. Modules are free to store any type of data they want + /// within their data store. However, avoid storing large amounts of data because that + /// would slow down database access. + /// + public class DAMap : IXmlSerializable + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly int MIN_NAMESPACE_LENGTH = 4; + + private OSDMap m_map = new OSDMap(); + + // WARNING: this is temporary for experimentation only, it will be removed!!!! + public OSDMap TopLevelMap + { + get { return m_map; } + set { m_map = value; } + } + + public XmlSchema GetSchema() { return null; } + + public static DAMap FromXml(string rawXml) + { + DAMap map = new DAMap(); + map.ReadXml(rawXml); + return map; + } + + public void ReadXml(XmlReader reader) + { + ReadXml(reader.ReadInnerXml()); + } + + public void ReadXml(string rawXml) + { + // System.Console.WriteLine("Trying to deserialize [{0}]", rawXml); + + lock (this) + { + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(rawXml); + SanitiseMap(this); + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteRaw(ToXml()); + } + + public string ToXml() + { + lock (this) + return OSDParser.SerializeLLSDXmlString(m_map); + } + + public void CopyFrom(DAMap other) + { + // Deep copy + + string data = null; + lock (other) + { + if (other.CountNamespaces > 0) + { + data = OSDParser.SerializeLLSDXmlString(other.m_map); + } + } + + lock (this) + { + if (data == null) + Clear(); + else + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(data); + } + } + + /// + /// Sanitise the map to remove any namespaces or stores that are not OSDMap. + /// + /// + /// + public static void SanitiseMap(DAMap daMap) + { + List keysToRemove = null; + + OSDMap namespacesMap = daMap.m_map; + + foreach (string key in namespacesMap.Keys) + { +// Console.WriteLine("Processing ns {0}", key); + if (!(namespacesMap[key] is OSDMap)) + { + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); + } + } + + if (keysToRemove != null) + { + foreach (string key in keysToRemove) + { +// Console.WriteLine ("Removing bad ns {0}", key); + namespacesMap.Remove(key); + } + } + + foreach (OSD nsOsd in namespacesMap.Values) + { + OSDMap nsOsdMap = (OSDMap)nsOsd; + keysToRemove = null; + + foreach (string key in nsOsdMap.Keys) + { + if (!(nsOsdMap[key] is OSDMap)) + { + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); + } + } + + if (keysToRemove != null) + foreach (string key in keysToRemove) + nsOsdMap.Remove(key); + } + } + + /// + /// Get the number of namespaces + /// + public int CountNamespaces { get { lock (this) { return m_map.Count; } } } + + /// + /// Get the number of stores. + /// + public int CountStores + { + get + { + int count = 0; + + lock (this) + { + foreach (OSD osdNamespace in m_map) + { + count += ((OSDMap)osdNamespace).Count; + } + } + + return count; + } + } + + /// + /// Retrieve a Dynamic Attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the stored data, or null if not found + public OSDMap GetStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD store; + + if (((OSDMap)namespaceOsd).TryGetValue(storeName, out store)) + return (OSDMap)store; + } + } + + return null; + } + + /// + /// Saves a Dynamic attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the data to store + public void SetStore(string ns, string storeName, OSDMap store) + { + ValidateNamespace(ns); + OSDMap nsMap; + + lock (this) + { + if (!m_map.ContainsKey(ns)) + { + nsMap = new OSDMap(); + m_map[ns] = nsMap; + } + + nsMap = (OSDMap)m_map[ns]; + +// m_log.DebugFormat("[DA MAP]: Setting store to {0}:{1}", ns, storeName); + nsMap[storeName] = store; + } + } + + /// + /// Validate the key used for storing separate data stores. + /// + /// + public static void ValidateNamespace(string ns) + { + if (ns.Length < MIN_NAMESPACE_LENGTH) + throw new Exception("Minimum namespace length is " + MIN_NAMESPACE_LENGTH); + } + + public bool ContainsStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + return ((OSDMap)namespaceOsd).ContainsKey(storeName); + } + } + + return false; + } + + public bool TryGetStore(string ns, string storeName, out OSDMap store) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD storeOsd; + + bool result = ((OSDMap)namespaceOsd).TryGetValue(storeName, out storeOsd); + store = (OSDMap)storeOsd; + + return result; + } + } + + store = null; + return false; + } + + public void Clear() + { + lock (this) + m_map.Clear(); + } + + public bool RemoveStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSDMap namespaceOsdMap = (OSDMap)namespaceOsd; + namespaceOsdMap.Remove(storeName); + + // Don't keep empty namespaces around + if (namespaceOsdMap.Count <= 0) + m_map.Remove(ns); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/DOMap.cs b/OpenSim/Framework/DOMap.cs new file mode 100644 index 0000000000..f5b650b375 --- /dev/null +++ b/OpenSim/Framework/DOMap.cs @@ -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.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// This class stores and retrieves dynamic objects. + /// + /// + /// Experimental - DO NOT USE. Does not yet have namespace support. + /// + public class DOMap + { + private IDictionary m_map; + + public void Add(string ns, string objName, object dynObj) + { + DAMap.ValidateNamespace(ns); + + lock (this) + { + if (m_map == null) + m_map = new Dictionary(); + + m_map.Add(objName, dynObj); + } + } + + public bool ContainsKey(string key) + { + return Get(key) != null; + } + + /// + /// Get a dynamic object + /// + /// + /// Not providing an index method so that users can't casually overwrite each other's objects. + /// + /// + public object Get(string key) + { + lock (this) + { + if (m_map == null) + return null; + else + return m_map[key]; + } + } + + public bool Remove(string key) + { + lock (this) + { + if (m_map == null) + return false; + else + return m_map.Remove(key); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs new file mode 100644 index 0000000000..9056548a51 --- /dev/null +++ b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2008, openmetaverse.org, http://opensimulator.org/ + * All rights reserved. + * + * - 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. + * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading; +using System.Collections.Generic; + +namespace OpenSim.Framework +{ + /// + /// A double dictionary that is thread abort safe. + /// + /// + /// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within + /// a finally section (which can't be interrupted by Thread.Abort()). + /// + public class DoubleDictionaryThreadAbortSafe + { + Dictionary Dictionary1; + Dictionary Dictionary2; + ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); + + public DoubleDictionaryThreadAbortSafe() + { + Dictionary1 = new Dictionary(); + Dictionary2 = new Dictionary(); + } + + public DoubleDictionaryThreadAbortSafe(int capacity) + { + Dictionary1 = new Dictionary(capacity); + Dictionary2 = new Dictionary(capacity); + } + + public void Add(TKey1 key1, TKey2 key2, TValue value) + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterWriteLock(); + gotLock = true; + } + + if (Dictionary1.ContainsKey(key1)) + { + if (!Dictionary2.ContainsKey(key2)) + throw new ArgumentException("key1 exists in the dictionary but not key2"); + } + else if (Dictionary2.ContainsKey(key2)) + { + if (!Dictionary1.ContainsKey(key1)) + throw new ArgumentException("key2 exists in the dictionary but not key1"); + } + + Dictionary1[key1] = value; + Dictionary2[key2] = value; + } + finally + { + if (gotLock) + rwLock.ExitWriteLock(); + } + } + + public bool Remove(TKey1 key1, TKey2 key2) + { + bool success; + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterWriteLock(); + gotLock = true; + } + + Dictionary1.Remove(key1); + success = Dictionary2.Remove(key2); + } + finally + { + if (gotLock) + rwLock.ExitWriteLock(); + } + + return success; + } + + public bool Remove(TKey1 key1) + { + bool found = false; + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterWriteLock(); + gotLock = true; + } + + // This is an O(n) operation! + TValue value; + if (Dictionary1.TryGetValue(key1, out value)) + { + foreach (KeyValuePair kvp in Dictionary2) + { + if (kvp.Value.Equals(value)) + { + Dictionary1.Remove(key1); + Dictionary2.Remove(kvp.Key); + found = true; + break; + } + } + } + } + finally + { + if (gotLock) + rwLock.ExitWriteLock(); + } + + return found; + } + + public bool Remove(TKey2 key2) + { + bool found = false; + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterWriteLock(); + gotLock = true; + } + + // This is an O(n) operation! + TValue value; + if (Dictionary2.TryGetValue(key2, out value)) + { + foreach (KeyValuePair kvp in Dictionary1) + { + if (kvp.Value.Equals(value)) + { + Dictionary2.Remove(key2); + Dictionary1.Remove(kvp.Key); + found = true; + break; + } + } + } + } + finally + { + if (gotLock) + rwLock.ExitWriteLock(); + } + + return found; + } + + public void Clear() + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterWriteLock(); + gotLock = true; + } + + Dictionary1.Clear(); + Dictionary2.Clear(); + } + finally + { + if (gotLock) + rwLock.ExitWriteLock(); + } + } + + public int Count + { + get { return Dictionary1.Count; } + } + + public bool ContainsKey(TKey1 key) + { + return Dictionary1.ContainsKey(key); + } + + public bool ContainsKey(TKey2 key) + { + return Dictionary2.ContainsKey(key); + } + + public bool TryGetValue(TKey1 key, out TValue value) + { + bool success; + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + success = Dictionary1.TryGetValue(key, out value); + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + + return success; + } + + public bool TryGetValue(TKey2 key, out TValue value) + { + bool success; + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + success = Dictionary2.TryGetValue(key, out value); + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + + return success; + } + + public void ForEach(Action action) + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + foreach (TValue value in Dictionary1.Values) + action(value); + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + } + + public void ForEach(Action> action) + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + foreach (KeyValuePair entry in Dictionary1) + action(entry); + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + } + + public void ForEach(Action> action) + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + foreach (KeyValuePair entry in Dictionary2) + action(entry); + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + } + + public TValue FindValue(Predicate predicate) + { + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + foreach (TValue value in Dictionary1.Values) + { + if (predicate(value)) + return value; + } + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + + return default(TValue); + } + + public IList FindAll(Predicate predicate) + { + IList list = new List(); + bool gotLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterReadLock(); + gotLock = true; + } + + foreach (TValue value in Dictionary1.Values) + { + if (predicate(value)) + list.Add(value); + } + } + finally + { + if (gotLock) + rwLock.ExitReadLock(); + } + + return list; + } + + public int RemoveAll(Predicate predicate) + { + IList list = new List(); + bool gotUpgradeableLock = false; + + try + { + // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing + // the acquision inside the main try. The inner finally block is needed because thread aborts cannot + // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly). + try {} + finally + { + rwLock.EnterUpgradeableReadLock(); + gotUpgradeableLock = true; + } + + foreach (KeyValuePair kvp in Dictionary1) + { + if (predicate(kvp.Value)) + list.Add(kvp.Key); + } + + IList list2 = new List(list.Count); + foreach (KeyValuePair kvp in Dictionary2) + { + if (predicate(kvp.Value)) + list2.Add(kvp.Key); + } + + bool gotWriteLock = false; + + try + { + try {} + finally + { + rwLock.EnterUpgradeableReadLock(); + gotWriteLock = true; + } + + for (int i = 0; i < list.Count; i++) + Dictionary1.Remove(list[i]); + + for (int i = 0; i < list2.Count; i++) + Dictionary2.Remove(list2[i]); + } + finally + { + if (gotWriteLock) + rwLock.ExitWriteLock(); + } + } + finally + { + if (gotUpgradeableLock) + rwLock.ExitUpgradeableReadLock(); + } + + return list.Count; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/EstateBan.cs b/OpenSim/Framework/EstateBan.cs new file mode 100644 index 0000000000..ebed794619 --- /dev/null +++ b/OpenSim/Framework/EstateBan.cs @@ -0,0 +1,164 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class EstateBan + { + private uint m_estateID = 1; + /// + /// ID of the estate this ban limits access to. + /// + public uint EstateID + { + get + { + return m_estateID; + } + set + { + m_estateID = value; + } + } + + private UUID m_bannedUserID = UUID.Zero; + /// + /// ID of the banned user. + /// + public UUID BannedUserID + { + get + { + return m_bannedUserID; + } + set + { + m_bannedUserID = value; + } + } + + private string m_bannedHostAddress = string.Empty; + /// + /// IP address or domain name of the banned client. + /// + public string BannedHostAddress + { + get + { + return m_bannedHostAddress; + } + set + { + m_bannedHostAddress = value; + } + } + + private string m_bannedHostIPMask = string.Empty; + /// + /// IP address mask for banning group of client hosts. + /// + public string BannedHostIPMask + { + get + { + return m_bannedHostIPMask; + } + set + { + m_bannedHostIPMask = value; + } + } + + private string m_bannedHostNameMask = string.Empty; + /// + /// Domain name mask for banning group of client hosts. + /// + public string BannedHostNameMask + { + get + { + return m_bannedHostNameMask; + } + set + { + m_bannedHostNameMask = value; + } + } + + public EstateBan() { } + + public Dictionary ToMap() + { + Dictionary map = new Dictionary(); + PropertyInfo[] properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + map[p.Name] = p.GetValue(this, null); + + return map; + } + + public EstateBan(Dictionary map) + { + foreach (KeyValuePair kvp in map) + { + PropertyInfo p = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance); + if (p == null) + continue; + object value = p.GetValue(this, null); + if (value is String) + p.SetValue(this, map[p.Name], null); + else if (value is UInt32) + p.SetValue(this, UInt32.Parse((string)map[p.Name]), null); + else if (value is Boolean) + p.SetValue(this, Boolean.Parse((string)map[p.Name]), null); + else if (value is UUID) + p.SetValue(this, UUID.Parse((string)map[p.Name]), null); + } + } + + + /// + /// For debugging + /// + /// + public override string ToString() + { + Dictionary map = ToMap(); + string result = string.Empty; + foreach (KeyValuePair kvp in map) + result += string.Format("{0}: {1} {2}", kvp.Key, kvp.Value, Environment.NewLine); + + return result; + } + } +} diff --git a/OpenSim/Framework/EstateSettings.cs b/OpenSim/Framework/EstateSettings.cs new file mode 100644 index 0000000000..4df7860e04 --- /dev/null +++ b/OpenSim/Framework/EstateSettings.cs @@ -0,0 +1,530 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class EstateSettings + { + // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public delegate void SaveDelegate(EstateSettings rs); + + public event SaveDelegate OnSave; + + // Only the client uses these + // + private uint m_EstateID = 0; + + public uint EstateID + { + get { return m_EstateID; } + set { m_EstateID = value; } + } + + private string m_EstateName = "My Estate"; + + public string EstateName + { + get { return m_EstateName; } + set { m_EstateName = value; } + } + + private bool m_AllowLandmark = true; + + public bool AllowLandmark + { + get { return m_AllowLandmark; } + set { m_AllowLandmark = value; } + } + + private bool m_AllowParcelChanges = true; + + public bool AllowParcelChanges + { + get { return m_AllowParcelChanges; } + set { m_AllowParcelChanges = value; } + } + + private bool m_AllowSetHome = true; + + public bool AllowSetHome + { + get { return m_AllowSetHome; } + set { m_AllowSetHome = value; } + } + + private uint m_ParentEstateID = 1; + + public uint ParentEstateID + { + get { return m_ParentEstateID; } + set { m_ParentEstateID = value; } + } + + private float m_BillableFactor = 0.0f; + + public float BillableFactor + { + get { return m_BillableFactor; } + set { m_BillableFactor = value; } + } + + private int m_PricePerMeter = 1; + + public int PricePerMeter + { + get { return m_PricePerMeter; } + set { m_PricePerMeter = value; } + } + + private int m_RedirectGridX = 0; + + public int RedirectGridX + { + get { return m_RedirectGridX; } + set { m_RedirectGridX = value; } + } + + private int m_RedirectGridY = 0; + + public int RedirectGridY + { + get { return m_RedirectGridY; } + set { m_RedirectGridY = value; } + } + + // Used by the sim + // + private bool m_UseGlobalTime = true; + + public bool UseGlobalTime + { + get { return m_UseGlobalTime; } + set { m_UseGlobalTime = value; } + } + + private bool m_FixedSun = false; + + public bool FixedSun + { + get { return m_FixedSun; } + set { m_FixedSun = value; } + } + + private double m_SunPosition = 0.0; + + public double SunPosition + { + get { return m_SunPosition; } + set { m_SunPosition = value; } + } + + private bool m_AllowVoice = true; + + public bool AllowVoice + { + get { return m_AllowVoice; } + set { m_AllowVoice = value; } + } + + private bool m_AllowDirectTeleport = true; + + public bool AllowDirectTeleport + { + get { return m_AllowDirectTeleport; } + set { m_AllowDirectTeleport = value; } + } + + private bool m_DenyAnonymous = false; + + public bool DenyAnonymous + { + get { return m_DenyAnonymous; } + set { m_DenyAnonymous = value; } + } + + private bool m_DenyIdentified = false; + + public bool DenyIdentified + { + get { return m_DenyIdentified; } + set { m_DenyIdentified = value; } + } + + private bool m_DenyTransacted = false; + + public bool DenyTransacted + { + get { return m_DenyTransacted; } + set { m_DenyTransacted = value; } + } + + private bool m_AbuseEmailToEstateOwner = false; + + public bool AbuseEmailToEstateOwner + { + get { return m_AbuseEmailToEstateOwner; } + set { m_AbuseEmailToEstateOwner = value; } + } + + private bool m_BlockDwell = false; + + public bool BlockDwell + { + get { return m_BlockDwell; } + set { m_BlockDwell = value; } + } + + private bool m_EstateSkipScripts = false; + + public bool EstateSkipScripts + { + get { return m_EstateSkipScripts; } + set { m_EstateSkipScripts = value; } + } + + private bool m_ResetHomeOnTeleport = false; + + public bool ResetHomeOnTeleport + { + get { return m_ResetHomeOnTeleport; } + set { m_ResetHomeOnTeleport = value; } + } + + private bool m_TaxFree = false; + + public bool TaxFree + { + get { return m_TaxFree; } + set { m_TaxFree = value; } + } + + private bool m_PublicAccess = true; + + public bool PublicAccess + { + get { return m_PublicAccess; } + set { m_PublicAccess = value; } + } + + private string m_AbuseEmail = String.Empty; + + public string AbuseEmail + { + get { return m_AbuseEmail; } + set { m_AbuseEmail= value; } + } + + private UUID m_EstateOwner = UUID.Zero; + + public UUID EstateOwner + { + get { return m_EstateOwner; } + set { m_EstateOwner = value; } + } + + private bool m_DenyMinors = false; + + public bool DenyMinors + { + get { return m_DenyMinors; } + set { m_DenyMinors = value; } + } + + // All those lists... + // + private List l_EstateManagers = new List(); + + public UUID[] EstateManagers + { + get { return l_EstateManagers.ToArray(); } + set { l_EstateManagers = new List(value); } + } + + private List l_EstateBans = new List(); + + public EstateBan[] EstateBans + { + get { return l_EstateBans.ToArray(); } + set { l_EstateBans = new List(value); } + } + + private List l_EstateAccess = new List(); + + public UUID[] EstateAccess + { + get { return l_EstateAccess.ToArray(); } + set { l_EstateAccess = new List(value); } + } + + private List l_EstateGroups = new List(); + + public UUID[] EstateGroups + { + get { return l_EstateGroups.ToArray(); } + set { l_EstateGroups = new List(value); } + } + + public EstateSettings() + { + } + + public void Save() + { + if (OnSave != null) + OnSave(this); + } + + public void AddEstateUser(UUID avatarID) + { + if (avatarID == UUID.Zero) + return; + if (!l_EstateAccess.Contains(avatarID)) + l_EstateAccess.Add(avatarID); + } + + public void RemoveEstateUser(UUID avatarID) + { + if (l_EstateAccess.Contains(avatarID)) + l_EstateAccess.Remove(avatarID); + } + + public void AddEstateGroup(UUID avatarID) + { + if (avatarID == UUID.Zero) + return; + if (!l_EstateGroups.Contains(avatarID)) + l_EstateGroups.Add(avatarID); + } + + public void RemoveEstateGroup(UUID avatarID) + { + if (l_EstateGroups.Contains(avatarID)) + l_EstateGroups.Remove(avatarID); + } + + public void AddEstateManager(UUID avatarID) + { + if (avatarID == UUID.Zero) + return; + if (!l_EstateManagers.Contains(avatarID)) + l_EstateManagers.Add(avatarID); + } + + public void RemoveEstateManager(UUID avatarID) + { + if (l_EstateManagers.Contains(avatarID)) + l_EstateManagers.Remove(avatarID); + } + + public bool IsEstateManagerOrOwner(UUID avatarID) + { + if (IsEstateOwner(avatarID)) + return true; + + return l_EstateManagers.Contains(avatarID); + } + + public bool IsEstateOwner(UUID avatarID) + { + if (avatarID == m_EstateOwner) + return true; + + return false; + } + + public bool IsBanned(UUID avatarID) + { + foreach (EstateBan ban in l_EstateBans) + if (ban.BannedUserID == avatarID) + return true; + return false; + } + + public void AddBan(EstateBan ban) + { + if (ban == null) + return; + if (!IsBanned(ban.BannedUserID)) + l_EstateBans.Add(ban); + } + + public void ClearBans() + { + l_EstateBans.Clear(); + } + + public void RemoveBan(UUID avatarID) + { + foreach (EstateBan ban in new List(l_EstateBans)) + if (ban.BannedUserID == avatarID) + l_EstateBans.Remove(ban); + } + + public bool HasAccess(UUID user) + { + if (IsEstateManagerOrOwner(user)) + return true; + + return l_EstateAccess.Contains(user); + } + + public void SetFromFlags(ulong regionFlags) + { + ResetHomeOnTeleport = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport) == (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport); + BlockDwell = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.BlockDwell) == (ulong)OpenMetaverse.RegionFlags.BlockDwell); + AllowLandmark = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowLandmark) == (ulong)OpenMetaverse.RegionFlags.AllowLandmark); + AllowParcelChanges = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges) == (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges); + AllowSetHome = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowSetHome) == (ulong)OpenMetaverse.RegionFlags.AllowSetHome); + } + + public bool GroupAccess(UUID groupID) + { + return l_EstateGroups.Contains(groupID); + } + + public Dictionary ToMap() + { + Dictionary map = new Dictionary(); + PropertyInfo[] properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + { + // EstateBans is a complex type, let's treat it as special + if (p.Name == "EstateBans") + continue; + + object value = p.GetValue(this, null); + if (value != null) + { + if (p.PropertyType.IsArray) // of UUIDs + { + if (((Array)value).Length > 0) + { + string[] args = new string[((Array)value).Length]; + int index = 0; + foreach (object o in (Array)value) + args[index++] = o.ToString(); + map[p.Name] = String.Join(",", args); + } + } + else // simple types + map[p.Name] = value; + } + } + + // EstateBans are special + if (EstateBans.Length > 0) + { + Dictionary bans = new Dictionary(); + int i = 0; + foreach (EstateBan ban in EstateBans) + bans["ban" + i++] = ban.ToMap(); + map["EstateBans"] = bans; + } + + return map; + } + + /// + /// For debugging + /// + /// + public override string ToString() + { + Dictionary map = ToMap(); + String result = String.Empty; + + foreach (KeyValuePair kvp in map) + { + if (kvp.Key == "EstateBans") + { + result += "EstateBans:" + Environment.NewLine; + foreach (KeyValuePair ban in (Dictionary)kvp.Value) + result += ban.Value.ToString(); + } + else + result += string.Format("{0}: {1} {2}", kvp.Key, kvp.Value.ToString(), Environment.NewLine); + } + + return result; + } + + public EstateSettings(Dictionary map) + { + foreach (KeyValuePair kvp in map) + { + PropertyInfo p = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance); + if (p == null) + continue; + + // EstateBans is a complex type, let's treat it as special + if (p.Name == "EstateBans") + continue; + + if (p.PropertyType.IsArray) + { + string[] elements = ((string)map[p.Name]).Split(new char[] { ',' }); + UUID[] uuids = new UUID[elements.Length]; + int i = 0; + foreach (string e in elements) + uuids[i++] = new UUID(e); + p.SetValue(this, uuids, null); + } + else + { + object value = p.GetValue(this, null); + if (value is String) + p.SetValue(this, map[p.Name], null); + else if (value is UInt32) + p.SetValue(this, UInt32.Parse((string)map[p.Name]), null); + else if (value is Boolean) + p.SetValue(this, Boolean.Parse((string)map[p.Name]), null); + else if (value is UUID) + p.SetValue(this, UUID.Parse((string)map[p.Name]), null); + } + } + + // EstateBans are special + if (map.ContainsKey("EstateBans")) + { + var banData = ((Dictionary)map["EstateBans"]).Values; + EstateBan[] bans = new EstateBan[banData.Count]; + int b = 0; + foreach (Dictionary ban in banData) + bans[b++] = new EstateBan(ban); + PropertyInfo bansProperty = this.GetType().GetProperty("EstateBans", BindingFlags.Public | BindingFlags.Instance); + bansProperty.SetValue(this, bans, null); + } + } + } +} diff --git a/OpenSim/Framework/EventData.cs b/OpenSim/Framework/EventData.cs new file mode 100644 index 0000000000..d58ea19d99 --- /dev/null +++ b/OpenSim/Framework/EventData.cs @@ -0,0 +1,53 @@ +/* + * 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 +{ + public enum EventDataFlags + { + Mature = 1 << 0, + } + + public class EventData + { + public uint eventID; + public string creator; + public string name; + public string category; + public string description; + public string date; + public uint dateUTC; + public uint duration; + public uint cover; + public uint amount; + public string simName; + public Vector3 globalPos; + public uint eventFlags; + } +} diff --git a/OpenSim/Framework/ExtraPhysicsData.cs b/OpenSim/Framework/ExtraPhysicsData.cs new file mode 100644 index 0000000000..9e7334f0da --- /dev/null +++ b/OpenSim/Framework/ExtraPhysicsData.cs @@ -0,0 +1,50 @@ +/* + * 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 +{ + public enum PhysShapeType : byte + { + prim = 0, + none = 1, + convex = 2, + + invalid = 255 // use to mark invalid data in ExtraPhysicsData + } + + public struct ExtraPhysicsData + { + public float Density; + public float GravitationModifier; + public float Friction; + public float Bounce; + public PhysShapeType PhysShapeType; + + } +} diff --git a/OpenSim/Framework/ForeignUserProfileData.cs b/OpenSim/Framework/ForeignUserProfileData.cs new file mode 100644 index 0000000000..2beaf80f80 --- /dev/null +++ b/OpenSim/Framework/ForeignUserProfileData.cs @@ -0,0 +1,77 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class ForeignUserProfileData : UserProfileData + { + /// + /// The address of the users home sim, used for foreigners. + /// + private string _userUserServerURI = String.Empty; + + /// + /// The address of the users home sim, used for foreigners. + /// + private string _userHomeAddress = String.Empty; + + /// + /// The port of the users home sim, used for foreigners. + /// + private string _userHomePort = String.Empty; + /// + /// The remoting port of the users home sim, used for foreigners. + /// + private string _userHomeRemotingPort = String.Empty; + + public string UserServerURI + { + get { return _userUserServerURI; } + set { _userUserServerURI = value; } + } + + public string UserHomeAddress + { + get { return _userHomeAddress; } + set { _userHomeAddress = value; } + } + + public string UserHomePort + { + get { return _userHomePort; } + set { _userHomePort = value; } + } + + public string UserHomeRemotingPort + { + get { return _userHomeRemotingPort; } + set { _userHomeRemotingPort = value; } + } + } +} diff --git a/OpenSim/Framework/FriendListItem.cs b/OpenSim/Framework/FriendListItem.cs new file mode 100644 index 0000000000..a02ec7f088 --- /dev/null +++ b/OpenSim/Framework/FriendListItem.cs @@ -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. + */ + +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class FriendListItem + { + public UUID Friend; + public UUID FriendListOwner; + + // These are what the list owner gives the friend permission to do + public uint FriendListOwnerPerms; + + // These are what the friend gives the listowner permission to do + public uint FriendPerms; + } +} diff --git a/OpenSim/Framework/GcNotify.cs b/OpenSim/Framework/GcNotify.cs new file mode 100644 index 0000000000..14a22a6104 --- /dev/null +++ b/OpenSim/Framework/GcNotify.cs @@ -0,0 +1,62 @@ +/* + * 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 log4net; + +public class GcNotify +{ + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static bool Enabled + { + get { return s_initialized; } + set + { + if (!s_initialized && value) + new GcNotify(); + + s_initialized = value; + } + } + + private static bool s_initialized = false; + + private GcNotify() {} + + ~GcNotify() + { + if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) + { + m_log.DebugFormat("[GC NOTIFY]: Garbage collection triggered."); + + if (Enabled) + new GcNotify(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/GridInstantMessage.cs b/OpenSim/Framework/GridInstantMessage.cs new file mode 100644 index 0000000000..da3690c93b --- /dev/null +++ b/OpenSim/Framework/GridInstantMessage.cs @@ -0,0 +1,113 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + [Serializable] + public class GridInstantMessage + { + public Guid fromAgentID; + public string fromAgentName; + public Guid toAgentID; + public byte dialog; + public bool fromGroup; + public string message; + public Guid imSessionID; + public byte offline; + public Vector3 Position; + public byte[] binaryBucket; + + public uint ParentEstateID; + public Guid RegionID; + public uint timestamp; + + public GridInstantMessage() + { + binaryBucket = new byte[0]; + } + + public GridInstantMessage(GridInstantMessage im, bool addTimestamp) + { + fromAgentID = im.fromAgentID; + fromAgentName = im.fromAgentName; + toAgentID = im.toAgentID; + dialog = im.dialog; + fromGroup = im.fromGroup; + message = im.message; + imSessionID = im.imSessionID; + offline = im.offline; + Position = im.Position; + binaryBucket = im.binaryBucket; + RegionID = im.RegionID; + + if (addTimestamp) + timestamp = (uint)Util.UnixTimeSinceEpoch(); + } + + public GridInstantMessage(IScene scene, UUID _fromAgentID, + string _fromAgentName, UUID _toAgentID, + byte _dialog, bool _fromGroup, string _message, + UUID _imSessionID, bool _offline, Vector3 _position, + byte[] _binaryBucket, bool addTimestamp) + { + fromAgentID = _fromAgentID.Guid; + fromAgentName = _fromAgentName; + toAgentID = _toAgentID.Guid; + dialog = _dialog; + fromGroup = _fromGroup; + message = _message; + imSessionID = _imSessionID.Guid; + if (_offline) + offline = 1; + else + offline = 0; + Position = _position; + binaryBucket = _binaryBucket; + + if (scene != null) + { + ParentEstateID = scene.RegionInfo.EstateSettings.ParentEstateID; + RegionID = scene.RegionInfo.RegionSettings.RegionUUID.Guid; + } + + if (addTimestamp) + timestamp = (uint)Util.UnixTimeSinceEpoch(); + } + + public GridInstantMessage(IScene scene, UUID _fromAgentID, + string _fromAgentName, UUID _toAgentID, byte _dialog, + string _message, bool _offline, + Vector3 _position) : this(scene, _fromAgentID, _fromAgentName, + _toAgentID, _dialog, false, _message, + _fromAgentID ^ _toAgentID, _offline, _position, new byte[0], true) + { + } + } +} diff --git a/OpenSim/Framework/GroupData.cs b/OpenSim/Framework/GroupData.cs new file mode 100644 index 0000000000..815946cac1 --- /dev/null +++ b/OpenSim/Framework/GroupData.cs @@ -0,0 +1,162 @@ +/* + * 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 +{ + public class GroupRecord + { + public UUID GroupID; + public string GroupName; + public bool AllowPublish = true; + public bool MaturePublish = true; + public string Charter; + public UUID FounderID = UUID.Zero; + public UUID GroupPicture = UUID.Zero; + public int MembershipFee = 0; + public bool OpenEnrollment = true; + public UUID OwnerRoleID = UUID.Zero; + public bool ShowInList = false; + } + + public class GroupMembershipData + { + // Group base data + public UUID GroupID; + public string GroupName; + public bool AllowPublish = true; + public bool MaturePublish = true; + public string Charter; + public UUID FounderID = UUID.Zero; + public UUID GroupPicture = UUID.Zero; + public int MembershipFee = 0; + public bool OpenEnrollment = true; + public bool ShowInList = true; + + // Per user data + public bool AcceptNotices = true; + public int Contribution = 0; + public ulong GroupPowers = 0; + public bool Active = false; + public UUID ActiveRole = UUID.Zero; + public bool ListInProfile = false; + public string GroupTitle; + } + + public struct GroupTitlesData + { + public string Name; + public UUID UUID; + public bool Selected; + } + + public struct GroupProfileData + { + public UUID GroupID; + public string Name; + public string Charter; + public bool ShowInList; + public string MemberTitle; + public ulong PowersMask; + public UUID InsigniaID; + public UUID FounderID; + public int MembershipFee; + public bool OpenEnrollment; + public int Money; + public int GroupMembershipCount; + public int GroupRolesCount; + public bool AllowPublish; + public bool MaturePublish; + public UUID OwnerRole; + } + + public struct GroupMembersData + { + public UUID AgentID; + public int Contribution; + public string OnlineStatus; + public ulong AgentPowers; + public string Title; + public bool IsOwner; + public bool ListInProfile; + public bool AcceptNotices; + } + + public struct GroupRolesData + { + public UUID RoleID; + public string Name; + public string Title; + public string Description; + public ulong Powers; + public int Members; + } + + public struct GroupRoleMembersData + { + public UUID RoleID; + public UUID MemberID; + } + + public struct GroupNoticeData + { + public UUID NoticeID; + public uint Timestamp; + public string FromName; + public string Subject; + public bool HasAttachment; + public byte AssetType; + } + + public struct GroupVoteHistory + { + public string VoteID; + public string VoteInitiator; + public string Majority; + public string Quorum; + public string TerseDateID; + public string StartDateTime; + public string EndDateTime; + public string VoteType; + public string VoteResult; + public string ProposalText; + } + + public struct GroupActiveProposals + { + public string VoteID; + public string VoteInitiator; + public string Majority; + public string Quorum; + public string TerseDateID; + public string StartDateTime; + public string EndDateTime; + public string ProposalText; + } +} diff --git a/OpenSim/Framework/IAssetLoader.cs b/OpenSim/Framework/IAssetLoader.cs new file mode 100644 index 0000000000..7612af358a --- /dev/null +++ b/OpenSim/Framework/IAssetLoader.cs @@ -0,0 +1,36 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public interface IAssetLoader + { + void ForEachDefaultXmlAsset(string assetSetFilename, Action action); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs new file mode 100644 index 0000000000..e36edb247d --- /dev/null +++ b/OpenSim/Framework/IClientAPI.cs @@ -0,0 +1,1483 @@ +/* + * 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.Net; +using OpenMetaverse; +using OpenMetaverse.Packets; + +namespace OpenSim.Framework +{ + #region Client API Delegate definitions + + public delegate void ViewerEffectEventHandler(IClientAPI sender, List args); + + public delegate void ChatMessage(Object sender, OSChatMessage e); + + public delegate void GenericMessage(Object sender, string method, List args); + + public delegate void TextureRequest(Object sender, TextureRequestArgs e); + + public delegate void AvatarNowWearing(IClientAPI sender, AvatarWearingArgs e); + + public delegate void ImprovedInstantMessage(IClientAPI remoteclient, GridInstantMessage im); + + public delegate void RezObject(IClientAPI remoteClient, UUID itemID, Vector3 RayEnd, Vector3 RayStart, + UUID RayTargetID, byte BypassRayCast, bool RayEndIsIntersection, + bool RezSelected, bool RemoveItem, UUID fromTaskID); + + public delegate ISceneEntity RezSingleAttachmentFromInv(IClientAPI remoteClient, UUID itemID, uint AttachmentPt); + + public delegate void RezMultipleAttachmentsFromInv(IClientAPI remoteClient, List> rezlist ); + + public delegate void ObjectAttach( + IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent); + + public delegate void ModifyTerrain(UUID user, + float height, float seconds, byte size, byte action, float north, float west, float south, float east, + UUID agentId); + + public delegate void NetworkStats(int inPackets, int outPackets, int unAckedBytes); + + public delegate void CachedTextureRequest(IClientAPI remoteClient, int serial, List cachedTextureRequest); + + public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 AvSize, WearableCacheItem[] CacheItems); + + public delegate void StartAnim(IClientAPI remoteClient, UUID animID); + + public delegate void StopAnim(IClientAPI remoteClient, UUID animID); + + public delegate void LinkObjects(IClientAPI remoteClient, uint parent, List children); + + public delegate void DelinkObjects(List primIds, IClientAPI client); + + public delegate void RequestMapBlocks(IClientAPI remoteClient, int minX, int minY, int maxX, int maxY, uint flag); + + public delegate void RequestMapName(IClientAPI remoteClient, string mapName, uint flags); + + public delegate void TeleportLocationRequest( + IClientAPI remoteClient, ulong regionHandle, Vector3 position, Vector3 lookAt, uint flags); + + public delegate void TeleportLandmarkRequest( + IClientAPI remoteClient, AssetLandmark lm); + + public delegate void TeleportCancel(IClientAPI remoteClient); + + public delegate void DisconnectUser(); + + public delegate void RequestAvatarProperties(IClientAPI remoteClient, UUID avatarID); + + public delegate void UpdateAvatarProperties(IClientAPI remoteClient, UserProfileData ProfileData); + + public delegate void SetAlwaysRun(IClientAPI remoteClient, bool SetAlwaysRun); + + public delegate void GenericCall1(IClientAPI remoteClient); + + public delegate void GenericCall2(); + + // really don't want to be passing packets in these events, so this is very temporary. + public delegate void GenericCall4(Packet packet, IClientAPI remoteClient); + + public delegate void DeRezObject( + IClientAPI remoteClient, List localIDs, UUID groupID, DeRezAction action, UUID destinationID); + + public delegate void GenericCall5(IClientAPI remoteClient, bool status); + + public delegate void GenericCall7(IClientAPI remoteClient, uint localID, string message); + + public delegate void UpdateShape(UUID agentID, uint localID, UpdateShapeArgs shapeBlock); + + public delegate void ObjectExtraParams(UUID agentID, uint localID, ushort type, bool inUse, byte[] data); + + public delegate void ObjectSelect(uint localID, IClientAPI remoteClient); + + public delegate void ObjectRequest(uint localID, IClientAPI remoteClient); + + public delegate void RequestObjectPropertiesFamily( + IClientAPI remoteClient, UUID AgentID, uint RequestFlags, UUID TaskID); + + public delegate void ObjectDeselect(uint localID, IClientAPI remoteClient); + + public delegate void ObjectDrop(uint localID, IClientAPI remoteClient); + + public delegate void UpdatePrimFlags( + uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, ExtraPhysicsData PhysData, IClientAPI remoteClient); + + public delegate void UpdatePrimTexture(uint localID, byte[] texture, IClientAPI remoteClient); + + public delegate void UpdateVector(uint localID, Vector3 pos, IClientAPI remoteClient); + + public delegate void UpdatePrimRotation(uint localID, Quaternion rot, IClientAPI remoteClient); + + public delegate void UpdatePrimSingleRotation(uint localID, Quaternion rot, IClientAPI remoteClient); + + public delegate void UpdatePrimSingleRotationPosition(uint localID, Quaternion rot, Vector3 pos, IClientAPI remoteClient); + + public delegate void UpdatePrimGroupRotation(uint localID, Vector3 pos, Quaternion rot, IClientAPI remoteClient); + + public delegate void ObjectDuplicate(uint localID, Vector3 offset, uint dupeFlags, UUID AgentID, UUID GroupID); + + public delegate void ObjectDuplicateOnRay(uint localID, uint dupeFlags, UUID AgentID, UUID GroupID, + UUID RayTargetObj, Vector3 RayEnd, Vector3 RayStart, + bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, + bool CopyRotates); + + public delegate void StatusChange(bool status); + + public delegate void NewAvatar(IClientAPI remoteClient, UUID agentID, bool status); + + public delegate void UpdateAgent(IClientAPI remoteClient, AgentUpdateArgs agentData); + + public delegate void AgentRequestSit(IClientAPI remoteClient, UUID agentID, UUID targetID, Vector3 offset); + + public delegate void AgentSit(IClientAPI remoteClient, UUID agentID); + + public delegate void LandUndo(IClientAPI remoteClient); + + public delegate void AvatarPickerRequest(IClientAPI remoteClient, UUID agentdata, UUID queryID, string UserQuery); + + public delegate void GrabObject( + uint localID, Vector3 pos, IClientAPI remoteClient, List surfaceArgs); + + public delegate void DeGrabObject( + uint localID, IClientAPI remoteClient, List surfaceArgs); + + public delegate void MoveObject( + UUID objectID, Vector3 offset, Vector3 grapPos, IClientAPI remoteClient, List surfaceArgs); + + public delegate void SpinStart(UUID objectID, IClientAPI remoteClient); + public delegate void SpinObject(UUID objectID, Quaternion rotation, IClientAPI remoteClient); + public delegate void SpinStop(UUID objectID, IClientAPI remoteClient); + + public delegate void ParcelAccessListRequest( + UUID agentID, UUID sessionID, uint flags, int sequenceID, int landLocalID, IClientAPI remote_client); + + public delegate void ParcelAccessListUpdateRequest(UUID agentID, uint flags, + int landLocalID, UUID transactionID, int sequenceID, + int sections, List entries, + IClientAPI remote_client); + + public delegate void ParcelPropertiesRequest( + int start_x, int start_y, int end_x, int end_y, int sequence_id, bool snap_selection, IClientAPI remote_client); + + public delegate void ParcelDivideRequest(int west, int south, int east, int north, IClientAPI remote_client); + + public delegate void ParcelJoinRequest(int west, int south, int east, int north, IClientAPI remote_client); + + public delegate void ParcelPropertiesUpdateRequest(LandUpdateArgs args, int local_id, IClientAPI remote_client); + + public delegate void ParcelSelectObjects(int land_local_id, int request_type, List returnIDs, IClientAPI remote_client); + + public delegate void ParcelObjectOwnerRequest(int local_id, IClientAPI remote_client); + + public delegate void ParcelAbandonRequest(int local_id, IClientAPI remote_client); + + public delegate void ParcelGodForceOwner(int local_id, UUID ownerID, IClientAPI remote_client); + + public delegate void ParcelReclaim(int local_id, IClientAPI remote_client); + + public delegate void ParcelReturnObjectsRequest( + int local_id, uint return_type, UUID[] agent_ids, UUID[] selected_ids, IClientAPI remote_client); + + public delegate void ParcelDeedToGroup(int local_id, UUID group_id, IClientAPI remote_client); + + public delegate void EstateOwnerMessageRequest( + UUID AgentID, UUID SessionID, UUID TransactionID, UUID Invoice, byte[] Method, byte[][] Parameters, + IClientAPI remote_client); + + public delegate void RegionInfoRequest(IClientAPI remote_client); + + public delegate void EstateCovenantRequest(IClientAPI remote_client); + + public delegate void UUIDNameRequest(UUID id, IClientAPI remote_client); + + public delegate void AddNewPrim( + UUID ownerID, UUID groupID, Vector3 RayEnd, Quaternion rot, PrimitiveBaseShape shape, byte bypassRaycast, Vector3 RayStart, + UUID RayTargetID, + byte RayEndIsIntersection); + + public delegate void RequestGodlikePowers( + UUID AgentID, UUID SessionID, UUID token, bool GodLike, IClientAPI remote_client); + + public delegate void GodKickUser( + UUID GodAgentID, UUID GodSessionID, UUID AgentID, uint kickflags, byte[] reason); + + public delegate void CreateInventoryFolder( + IClientAPI remoteClient, UUID folderID, ushort folderType, string folderName, UUID parentID); + + public delegate void UpdateInventoryFolder( + IClientAPI remoteClient, UUID folderID, ushort type, string name, UUID parentID); + + public delegate void MoveInventoryFolder( + IClientAPI remoteClient, UUID folderID, UUID parentID); + + public delegate void CreateNewInventoryItem( + IClientAPI remoteClient, UUID transActionID, UUID folderID, uint callbackID, string description, string name, + sbyte invType, sbyte type, byte wearableType, uint nextOwnerMask, int creationDate); + + public delegate void LinkInventoryItem( + IClientAPI remoteClient, UUID transActionID, UUID folderID, uint callbackID, string description, string name, + sbyte invType, sbyte type, UUID olditemID); + + public delegate void FetchInventoryDescendents( + IClientAPI remoteClient, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder); + + public delegate void PurgeInventoryDescendents( + IClientAPI remoteClient, UUID folderID); + + public delegate void FetchInventory(IClientAPI remoteClient, UUID itemID, UUID ownerID); + + public delegate void RequestTaskInventory(IClientAPI remoteClient, uint localID); + +/* public delegate void UpdateInventoryItem( + IClientAPI remoteClient, UUID transactionID, UUID itemID, string name, string description, + uint nextOwnerMask);*/ + + public delegate void UpdateInventoryItem( + IClientAPI remoteClient, UUID transactionID, UUID itemID, InventoryItemBase itemUpd); + + public delegate void CopyInventoryItem( + IClientAPI remoteClient, uint callbackID, UUID oldAgentID, UUID oldItemID, UUID newFolderID, + string newName); + + public delegate void MoveInventoryItem( + IClientAPI remoteClient, List items); + + public delegate void RemoveInventoryItem( + IClientAPI remoteClient, List itemIDs); + + public delegate void RemoveInventoryFolder( + IClientAPI remoteClient, List folderIDs); + + public delegate void RequestAsset(IClientAPI remoteClient, RequestAssetArgs transferRequest); + + public delegate void AbortXfer(IClientAPI remoteClient, ulong xferID); + + public delegate void RezScript(IClientAPI remoteClient, InventoryItemBase item, UUID transactionID, uint localID); + + public delegate void UpdateTaskInventory( + IClientAPI remoteClient, UUID transactionID, TaskInventoryItem item, uint localID); + + public delegate void MoveTaskInventory(IClientAPI remoteClient, UUID folderID, uint localID, UUID itemID); + + public delegate void RemoveTaskInventory(IClientAPI remoteClient, UUID itemID, uint localID); + + public delegate void UDPAssetUploadRequest( + IClientAPI remoteClient, UUID assetID, UUID transaction, sbyte type, byte[] data, bool storeLocal, + bool tempFile); + + public delegate void XferReceive(IClientAPI remoteClient, ulong xferID, uint packetID, byte[] data); + + public delegate void RequestXfer(IClientAPI remoteClient, ulong xferID, string fileName); + + public delegate void ConfirmXfer(IClientAPI remoteClient, ulong xferID, uint packetID); + + public delegate void FriendActionDelegate( + IClientAPI remoteClient, UUID transactionID, List callingCardFolders); + + public delegate void FriendshipTermination(IClientAPI remoteClient, UUID ExID); + + public delegate void MoneyTransferRequest( + UUID sourceID, UUID destID, int amount, int transactionType, string description); + + public delegate void ParcelBuy(UUID agentId, UUID groupId, bool final, bool groupOwned, + bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, + bool authenticated); + + // We keep all this information for fraud purposes in the future. + public delegate void MoneyBalanceRequest(IClientAPI remoteClient, UUID agentID, UUID sessionID, UUID TransactionID); + + public delegate void ObjectPermissions( + IClientAPI controller, UUID agentID, UUID sessionID, byte field, uint localId, uint mask, byte set); + + public delegate void EconomyDataRequest(IClientAPI client); + + public delegate void ObjectIncludeInSearch(IClientAPI remoteClient, bool IncludeInSearch, uint localID); + + public delegate void ScriptAnswer(IClientAPI remoteClient, UUID objectID, UUID itemID, int answer); + + public delegate void RequestPayPrice(IClientAPI remoteClient, UUID objectID); + + public delegate void ObjectSaleInfo( + IClientAPI remoteClient, UUID agentID, UUID sessionID, uint localID, byte saleType, int salePrice); + + public delegate void ObjectBuy( + IClientAPI remoteClient, UUID agentID, UUID sessionID, UUID groupID, UUID categoryID, uint localID, + byte saleType, int salePrice); + + public delegate void BuyObjectInventory( + IClientAPI remoteClient, UUID agentID, UUID sessionID, UUID objectID, UUID itemID, UUID folderID); + + public delegate void ForceReleaseControls(IClientAPI remoteClient, UUID agentID); + + public delegate void GodLandStatRequest( + int parcelID, uint reportType, uint requestflags, string filter, IClientAPI remoteClient); + + //Estate Requests + public delegate void DetailedEstateDataRequest(IClientAPI remoteClient, UUID invoice); + + public delegate void SetEstateFlagsRequest( + bool blockTerraform, bool noFly, bool allowDamage, bool blockLandResell, int maxAgents, float objectBonusFactor, + int matureLevel, bool restrictPushObject, bool allowParcelChanges); + + public delegate void SetEstateTerrainBaseTexture(IClientAPI remoteClient, int corner, UUID side); + + public delegate void SetEstateTerrainDetailTexture(IClientAPI remoteClient, int corner, UUID side); + + public delegate void SetEstateTerrainTextureHeights(IClientAPI remoteClient, int corner, float lowVal, float highVal + ); + + public delegate void CommitEstateTerrainTextureRequest(IClientAPI remoteClient); + + public delegate void SetRegionTerrainSettings( + float waterHeight, float terrainRaiseLimit, float terrainLowerLimit, bool estateSun, bool fixedSun, + float sunHour, bool globalSun, bool estateFixed, float estateSunHour); + + public delegate void EstateChangeInfo(IClientAPI client, UUID invoice, UUID senderID, UInt32 param1, UInt32 param2); + + public delegate void EstateManageTelehub(IClientAPI client, UUID invoice, UUID senderID, string cmd, UInt32 param1); + + public delegate void RequestTerrain(IClientAPI remoteClient, string clientFileName); + + public delegate void BakeTerrain(IClientAPI remoteClient); + + + public delegate void EstateRestartSimRequest(IClientAPI remoteClient, int secondsTilReboot); + + public delegate void EstateChangeCovenantRequest(IClientAPI remoteClient, UUID newCovenantID); + + public delegate void UpdateEstateAccessDeltaRequest( + IClientAPI remote_client, UUID invoice, int estateAccessType, UUID user); + + public delegate void SimulatorBlueBoxMessageRequest( + IClientAPI remoteClient, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message); + + public delegate void EstateBlueBoxMessageRequest( + IClientAPI remoteClient, UUID invoice, UUID senderID, UUID sessionID, string senderName, string message); + + public delegate void EstateDebugRegionRequest( + IClientAPI remoteClient, UUID invoice, UUID senderID, bool scripted, bool collisionEvents, bool physics); + + public delegate void EstateTeleportOneUserHomeRequest( + IClientAPI remoteClient, UUID invoice, UUID senderID, UUID prey); + + public delegate void EstateTeleportAllUsersHomeRequest(IClientAPI remoteClient, UUID invoice, UUID senderID); + + public delegate void RegionHandleRequest(IClientAPI remoteClient, UUID regionID); + + public delegate void ParcelInfoRequest(IClientAPI remoteClient, UUID parcelID); + + public delegate void ScriptReset(IClientAPI remoteClient, UUID objectID, UUID itemID); + + public delegate void GetScriptRunning(IClientAPI remoteClient, UUID objectID, UUID itemID); + + public delegate void SetScriptRunning(IClientAPI remoteClient, UUID objectID, UUID itemID, bool running); + + public delegate void ActivateGesture(IClientAPI client, UUID gestureid, UUID assetId); + + public delegate void DeactivateGesture(IClientAPI client, UUID gestureid); + + public delegate void TerrainUnacked(IClientAPI remoteClient, int patchX, int patchY); + + public delegate void ObjectOwner(IClientAPI remoteClient, UUID ownerID, UUID groupID, List localIDs); + + public delegate void DirPlacesQuery( + IClientAPI remoteClient, UUID queryID, string queryText, int queryFlags, int category, string simName, + int queryStart); + + public delegate void DirFindQuery( + IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, int queryStart); + + public delegate void DirLandQuery( + IClientAPI remoteClient, UUID queryID, uint queryFlags, uint searchType, int price, int area, int queryStart); + + public delegate void DirPopularQuery(IClientAPI remoteClient, UUID queryID, uint queryFlags); + + public delegate void DirClassifiedQuery( + IClientAPI remoteClient, UUID queryID, string queryText, uint queryFlags, uint category, int queryStart); + + public delegate void EventInfoRequest(IClientAPI remoteClient, uint eventID); + + public delegate void ParcelSetOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime); + + public delegate void MapItemRequest( + IClientAPI remoteClient, uint flags, uint EstateID, bool godlike, uint itemtype, ulong regionhandle); + + public delegate void OfferCallingCard(IClientAPI remoteClient, UUID destID, UUID transactionID); + + public delegate void AcceptCallingCard(IClientAPI remoteClient, UUID transactionID, UUID folderID); + + public delegate void DeclineCallingCard(IClientAPI remoteClient, UUID transactionID); + + public delegate void SoundTrigger( + UUID soundId, UUID ownerid, UUID objid, UUID parentid, double Gain, Vector3 Position, UInt64 Handle, float radius); + + public delegate void StartLure(byte lureType, string message, UUID targetID, IClientAPI client); + public delegate void TeleportLureRequest(UUID lureID, uint teleportFlags, IClientAPI client); + + public delegate void ClassifiedInfoRequest(UUID classifiedID, IClientAPI client); + public delegate void ClassifiedInfoUpdate(UUID classifiedID, uint category, string name, string description, UUID parcelID, uint parentEstate, UUID snapshotID, Vector3 globalPos, byte classifiedFlags, int price, IClientAPI client); + public delegate void ClassifiedDelete(UUID classifiedID, IClientAPI client); + + public delegate void EventNotificationAddRequest(uint EventID, IClientAPI client); + public delegate void EventNotificationRemoveRequest(uint EventID, IClientAPI client); + + public delegate void EventGodDelete(uint eventID, UUID queryID, string queryText, uint queryFlags, int queryStart, IClientAPI client); + + public delegate void ParcelDwellRequest(int localID, IClientAPI client); + + public delegate void UserInfoRequest(IClientAPI client); + public delegate void UpdateUserInfo(bool imViaEmail, bool visible, IClientAPI client); + public delegate void RetrieveInstantMessages(IClientAPI client); + public delegate void PickDelete(IClientAPI client, UUID pickID); + public delegate void PickGodDelete(IClientAPI client, UUID agentID, UUID pickID, UUID queryID); + public delegate void PickInfoUpdate(IClientAPI client, UUID pickID, UUID creatorID, bool topPick, string name, string desc, UUID snapshotID, int sortOrder, bool enabled); + public delegate void AvatarNotesUpdate(IClientAPI client, UUID targetID, string notes); + public delegate void MuteListRequest(IClientAPI client, uint muteCRC); + public delegate void AvatarInterestUpdate(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); + public delegate void GrantUserFriendRights(IClientAPI client, UUID target, int rights); + public delegate void PlacesQuery(UUID QueryID, UUID TransactionID, string QueryText, uint QueryFlags, byte Category, string SimName, IClientAPI client); + + public delegate void AgentFOV(IClientAPI client, float verticalAngle); + + public delegate void MuteListEntryUpdate(IClientAPI client, UUID MuteID, string Name, int Flags,UUID AgentID); + + public delegate void MuteListEntryRemove(IClientAPI client, UUID MuteID, string Name, UUID AgentID); + + public delegate void AvatarInterestReply(IClientAPI client,UUID target, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages); + + public delegate void FindAgentUpdate(IClientAPI client, UUID hunter, UUID target); + + public delegate void TrackAgentUpdate(IClientAPI client, UUID hunter, UUID target); + + public delegate void FreezeUserUpdate(IClientAPI client, UUID parcelowner,uint flags, UUID target); + + public delegate void EjectUserUpdate(IClientAPI client, UUID parcelowner,uint flags, UUID target); + + public delegate void NewUserReport(IClientAPI client, string regionName,UUID abuserID, byte catagory, byte checkflags, string details, UUID objectID, Vector3 postion, byte reportType ,UUID screenshotID, string Summary, UUID reporter); + + public delegate void GodUpdateRegionInfoUpdate(IClientAPI client, float BillableFactor, ulong EstateID, ulong RegionFlags, byte[] SimName,int RedirectX, int RedirectY); + + public delegate void GodlikeMessage(IClientAPI client, UUID requester, byte[] Method, byte[] Parameter); + + public delegate void SaveStateHandler(IClientAPI client,UUID agentID); + + public delegate void GroupAccountSummaryRequest(IClientAPI client,UUID agentID, UUID groupID); + + public delegate void GroupAccountDetailsRequest(IClientAPI client,UUID agentID, UUID groupID, UUID transactionID, UUID sessionID); + + public delegate void GroupAccountTransactionsRequest(IClientAPI client,UUID agentID, UUID groupID, UUID transactionID, UUID sessionID); + + public delegate void ParcelBuyPass(IClientAPI client, UUID agentID, int ParcelLocalID); + + public delegate void ParcelGodMark(IClientAPI client, UUID agentID, int ParcelLocalID); + + public delegate void GroupActiveProposalsRequest(IClientAPI client,UUID agentID, UUID groupID, UUID transactionID, UUID sessionID); + + public delegate void GroupVoteHistoryRequest(IClientAPI client,UUID agentID, UUID groupID, UUID transactionID, UUID sessionID); + + + public delegate void SimWideDeletesDelegate(IClientAPI client,UUID agentID, int flags, UUID targetID); + + public delegate void SendPostcard(IClientAPI client); + + #endregion + + public struct DirPlacesReplyData + { + public UUID parcelID; + public string name; + public bool forSale; + public bool auction; + public float dwell; + public uint Status; + } + + public struct DirPeopleReplyData + { + public UUID agentID; + public string firstName; + public string lastName; + public string group; + public bool online; + public int reputation; + } + + public struct DirEventsReplyData + { + public UUID ownerID; + public string name; + public uint eventID; + public string date; + public uint unixTime; + public uint eventFlags; + public uint Status; + } + + public struct DirGroupsReplyData + { + public UUID groupID; + public string groupName; + public int members; + public float searchOrder; + } + + public struct DirClassifiedReplyData + { + public UUID classifiedID; + public string name; + public byte classifiedFlags; + public uint creationDate; + public uint expirationDate; + public int price; + public uint Status; + } + + public struct DirLandReplyData + { + public UUID parcelID; + public string name; + public bool auction; + public bool forSale; + public int salePrice; + public int actualArea; + } + + public struct DirPopularReplyData + { + public UUID parcelID; + public string name; + public float dwell; + } + + public class IEntityUpdate + { + private ISceneEntity m_entity; + private uint m_flags; + private int m_updateTime; + + public ISceneEntity Entity + { + get { return m_entity; } + } + + public uint Flags + { + get { return m_flags; } + } + + public int UpdateTime + { + get { return m_updateTime; } + } + + public virtual void Update(IEntityUpdate update) + { + m_flags |= update.Flags; + + // Use the older of the updates as the updateTime + if (Util.EnvironmentTickCountCompare(UpdateTime, update.UpdateTime) > 0) + m_updateTime = update.UpdateTime; + } + + public IEntityUpdate(ISceneEntity entity, uint flags) + { + m_entity = entity; + m_flags = flags; + m_updateTime = Util.EnvironmentTickCount(); + } + + public IEntityUpdate(ISceneEntity entity, uint flags, Int32 updateTime) + { + m_entity = entity; + m_flags = flags; + m_updateTime = updateTime; + } + } + + public class EntityUpdate : IEntityUpdate + { + private float m_timeDilation; + + public float TimeDilation + { + get { return m_timeDilation; } + } + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation) + : base(entity, (uint)flags) + { + // Flags = flags; + m_timeDilation = timedilation; + } + + public EntityUpdate(ISceneEntity entity, PrimUpdateFlags flags, float timedilation, Int32 updateTime) + : base(entity,(uint)flags,updateTime) + { + m_timeDilation = timedilation; + } + } + + public class PlacesReplyData + { + public UUID OwnerID; + public string Name; + public string Desc; + public int ActualArea; + public int BillableArea; + public byte Flags; + public uint GlobalX; + public uint GlobalY; + public uint GlobalZ; + public string SimName; + public UUID SnapshotID; + public uint Dwell; + public int Price; + } + + /// + /// Specifies the fields that have been changed when sending a prim or + /// avatar update + /// + [Flags] + public enum PrimUpdateFlags : uint + { + None = 0, + AttachmentPoint = 1 << 0, + Material = 1 << 1, + ClickAction = 1 << 2, + Scale = 1 << 3, + ParentID = 1 << 4, + PrimFlags = 1 << 5, + PrimData = 1 << 6, + MediaURL = 1 << 7, + ScratchPad = 1 << 8, + Textures = 1 << 9, + TextureAnim = 1 << 10, + NameValue = 1 << 11, + Position = 1 << 12, + Rotation = 1 << 13, + Velocity = 1 << 14, + Acceleration = 1 << 15, + AngularVelocity = 1 << 16, + CollisionPlane = 1 << 17, + Text = 1 << 18, + Particles = 1 << 19, + ExtraData = 1 << 20, + Sound = 1 << 21, + Joint = 1 << 22, + FullUpdate = UInt32.MaxValue + } + + public static class PrimUpdateFlagsExtensions + { + public static bool HasFlag(this PrimUpdateFlags updateFlags, PrimUpdateFlags flag) + { + return (updateFlags & flag) == flag; + } + } + + public interface IClientAPI + { + Vector3 StartPos { get; set; } + + UUID AgentId { get; } + + /// + /// The scene agent for this client. This will only be set if the client has an agent in a scene (i.e. if it + /// is connected). + /// + ISceneAgent SceneAgent { get; set; } + + UUID SessionId { get; } + + UUID SecureSessionId { get; } + + UUID ActiveGroupId { get; } + + string ActiveGroupName { get; } + + ulong ActiveGroupPowers { get; } + + ulong GetGroupPowers(UUID groupID); + + bool IsGroupMember(UUID GroupID); + + string FirstName { get; } + + string LastName { get; } + + IScene Scene { get; } + + // [Obsolete("LLClientView Specific - Replace with ???")] + int NextAnimationSequenceNumber { get; } + + /// + /// Returns the full name of the agent/avatar represented by this client + /// + string Name { get; } + + /// + /// True if the client is active (sending and receiving new UDP messages). False if the client is being closed. + /// + bool IsActive { get; set; } + + /// + /// Set if the client is closing due to a logout request + /// + /// + /// Do not use this flag if you want to know if the client is closing, since it will not be set in other + /// circumstances (e.g. if a child agent is closed or the agent is kicked off the simulator). Use IsActive + /// instead with a IClientAPI.SceneAgent.IsChildAgent check if necessary. + /// + /// Only set for root agents. + /// + bool IsLoggingOut { get; set; } + + bool SendLogoutPacketWhenClosing { set; } + + // [Obsolete("LLClientView Specific - Circuits are unique to LLClientView")] + uint CircuitCode { get; } + + IPEndPoint RemoteEndPoint { get; } + + event GenericMessage OnGenericMessage; + + // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")] + event ImprovedInstantMessage OnInstantMessage; + // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments. Rename OnChat.")] + event ChatMessage OnChatFromClient; + // [Obsolete("LLClientView Specific - Replace with more bare-bones arguments.")] + event TextureRequest OnRequestTexture; + // [Obsolete("LLClientView Specific - Remove bitbuckets. Adam, can you be more specific here.. as I don't see any bit buckets.")] + event RezObject OnRezObject; + // [Obsolete("LLClientView Specific - Replace with more suitable arguments.")] + event ModifyTerrain OnModifyTerrain; + event BakeTerrain OnBakeTerrain; + event EstateChangeInfo OnEstateChangeInfo; + event EstateManageTelehub OnEstateManageTelehub; + // [Obsolete("LLClientView Specific.")] + event CachedTextureRequest OnCachedTextureRequest; + event SetAppearance OnSetAppearance; + // [Obsolete("LLClientView Specific - Replace and rename OnAvatarUpdate. Difference from SetAppearance?")] + event AvatarNowWearing OnAvatarNowWearing; + event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; + event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; + event UUIDNameRequest OnDetachAttachmentIntoInv; + event ObjectAttach OnObjectAttach; + event ObjectDeselect OnObjectDetach; + event ObjectDrop OnObjectDrop; + event StartAnim OnStartAnim; + event StopAnim OnStopAnim; + event LinkObjects OnLinkObjects; + event DelinkObjects OnDelinkObjects; + event RequestMapBlocks OnRequestMapBlocks; + event RequestMapName OnMapNameRequest; + event TeleportLocationRequest OnTeleportLocationRequest; + event DisconnectUser OnDisconnectUser; + event RequestAvatarProperties OnRequestAvatarProperties; + event SetAlwaysRun OnSetAlwaysRun; + event TeleportLandmarkRequest OnTeleportLandmarkRequest; + event TeleportCancel OnTeleportCancel; + event DeRezObject OnDeRezObject; + event Action OnRegionHandShakeReply; + event GenericCall1 OnRequestWearables; + event Action OnCompleteMovementToRegion; + + /// + /// Called when an AgentUpdate message is received and before OnAgentUpdate. + /// + /// + /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates. + /// + event UpdateAgent OnPreAgentUpdate; + + /// + /// Called when an AgentUpdate message is received and after OnPreAgentUpdate. + /// + /// + /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates. + /// + event UpdateAgent OnAgentUpdate; + + event UpdateAgent OnAgentCameraUpdate; + + event AgentRequestSit OnAgentRequestSit; + event AgentSit OnAgentSit; + event AvatarPickerRequest OnAvatarPickerRequest; + event Action OnRequestAvatarsData; + event AddNewPrim OnAddPrim; + + event FetchInventory OnAgentDataUpdateRequest; + event TeleportLocationRequest OnSetStartLocationRequest; + + event RequestGodlikePowers OnRequestGodlikePowers; + event GodKickUser OnGodKickUser; + + event ObjectDuplicate OnObjectDuplicate; + event ObjectDuplicateOnRay OnObjectDuplicateOnRay; + event GrabObject OnGrabObject; + event DeGrabObject OnDeGrabObject; + event MoveObject OnGrabUpdate; + event SpinStart OnSpinStart; + event SpinObject OnSpinUpdate; + event SpinStop OnSpinStop; + + event UpdateShape OnUpdatePrimShape; + event ObjectExtraParams OnUpdateExtraParams; + event ObjectRequest OnObjectRequest; + event ObjectSelect OnObjectSelect; + event ObjectDeselect OnObjectDeselect; + event GenericCall7 OnObjectDescription; + event GenericCall7 OnObjectName; + event GenericCall7 OnObjectClickAction; + event GenericCall7 OnObjectMaterial; + event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; + event UpdatePrimFlags OnUpdatePrimFlags; + event UpdatePrimTexture OnUpdatePrimTexture; + event UpdateVector OnUpdatePrimGroupPosition; + event UpdateVector OnUpdatePrimSinglePosition; + event UpdatePrimRotation OnUpdatePrimGroupRotation; + event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; + event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; + event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; + event UpdateVector OnUpdatePrimScale; + event UpdateVector OnUpdatePrimGroupScale; + event StatusChange OnChildAgentStatus; + event GenericCall2 OnStopMovement; + event Action OnRemoveAvatar; + event ObjectPermissions OnObjectPermissions; + + event CreateNewInventoryItem OnCreateNewInventoryItem; + event LinkInventoryItem OnLinkInventoryItem; + event CreateInventoryFolder OnCreateNewInventoryFolder; + event UpdateInventoryFolder OnUpdateInventoryFolder; + event MoveInventoryFolder OnMoveInventoryFolder; + event FetchInventoryDescendents OnFetchInventoryDescendents; + event PurgeInventoryDescendents OnPurgeInventoryDescendents; + event FetchInventory OnFetchInventory; + event RequestTaskInventory OnRequestTaskInventory; + event UpdateInventoryItem OnUpdateInventoryItem; + event CopyInventoryItem OnCopyInventoryItem; + event MoveInventoryItem OnMoveInventoryItem; + event RemoveInventoryFolder OnRemoveInventoryFolder; + event RemoveInventoryItem OnRemoveInventoryItem; + event UDPAssetUploadRequest OnAssetUploadRequest; + event XferReceive OnXferReceive; + event RequestXfer OnRequestXfer; + event ConfirmXfer OnConfirmXfer; + event AbortXfer OnAbortXfer; + event RezScript OnRezScript; + event UpdateTaskInventory OnUpdateTaskInventory; + event MoveTaskInventory OnMoveTaskItem; + event RemoveTaskInventory OnRemoveTaskItem; + event RequestAsset OnRequestAsset; + + event UUIDNameRequest OnNameFromUUIDRequest; + + event ParcelAccessListRequest OnParcelAccessListRequest; + event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; + event ParcelPropertiesRequest OnParcelPropertiesRequest; + event ParcelDivideRequest OnParcelDivideRequest; + event ParcelJoinRequest OnParcelJoinRequest; + event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; + event ParcelSelectObjects OnParcelSelectObjects; + event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; + event ParcelAbandonRequest OnParcelAbandonRequest; + event ParcelGodForceOwner OnParcelGodForceOwner; + event ParcelReclaim OnParcelReclaim; + event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; + event ParcelDeedToGroup OnParcelDeedToGroup; + event RegionInfoRequest OnRegionInfoRequest; + event EstateCovenantRequest OnEstateCovenantRequest; + + event FriendActionDelegate OnApproveFriendRequest; + event FriendActionDelegate OnDenyFriendRequest; + event FriendshipTermination OnTerminateFriendship; + + // Financial packets + event MoneyTransferRequest OnMoneyTransferRequest; + event EconomyDataRequest OnEconomyDataRequest; + + event MoneyBalanceRequest OnMoneyBalanceRequest; + event UpdateAvatarProperties OnUpdateAvatarProperties; + event ParcelBuy OnParcelBuy; + event RequestPayPrice OnRequestPayPrice; + event ObjectSaleInfo OnObjectSaleInfo; + event ObjectBuy OnObjectBuy; + event BuyObjectInventory OnBuyObjectInventory; + + event RequestTerrain OnRequestTerrain; + + event RequestTerrain OnUploadTerrain; + + event ObjectIncludeInSearch OnObjectIncludeInSearch; + + event UUIDNameRequest OnTeleportHomeRequest; + + event ScriptAnswer OnScriptAnswer; + + event AgentSit OnUndo; + event AgentSit OnRedo; + event LandUndo OnLandUndo; + + event ForceReleaseControls OnForceReleaseControls; + event GodLandStatRequest OnLandStatRequest; + + event DetailedEstateDataRequest OnDetailedEstateDataRequest; + event SetEstateFlagsRequest OnSetEstateFlagsRequest; + event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; + event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; + event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; + event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; + event SetRegionTerrainSettings OnSetRegionTerrainSettings; + event EstateRestartSimRequest OnEstateRestartSimRequest; + event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; + event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; + event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; + event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; + event EstateDebugRegionRequest OnEstateDebugRegionRequest; + event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; + event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; + event UUIDNameRequest OnUUIDGroupNameRequest; + + event RegionHandleRequest OnRegionHandleRequest; + event ParcelInfoRequest OnParcelInfoRequest; + + event RequestObjectPropertiesFamily OnObjectGroupRequest; + event ScriptReset OnScriptReset; + event GetScriptRunning OnGetScriptRunning; + event SetScriptRunning OnSetScriptRunning; + event Action OnAutoPilotGo; + + event TerrainUnacked OnUnackedTerrain; + event ActivateGesture OnActivateGesture; + event DeactivateGesture OnDeactivateGesture; + event ObjectOwner OnObjectOwner; + + event DirPlacesQuery OnDirPlacesQuery; + event DirFindQuery OnDirFindQuery; + event DirLandQuery OnDirLandQuery; + event DirPopularQuery OnDirPopularQuery; + event DirClassifiedQuery OnDirClassifiedQuery; + event EventInfoRequest OnEventInfoRequest; + event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; + + event MapItemRequest OnMapItemRequest; + + event OfferCallingCard OnOfferCallingCard; + event AcceptCallingCard OnAcceptCallingCard; + event DeclineCallingCard OnDeclineCallingCard; + event SoundTrigger OnSoundTrigger; + + event StartLure OnStartLure; + event TeleportLureRequest OnTeleportLureRequest; + event NetworkStats OnNetworkStatsUpdate; + + event ClassifiedInfoRequest OnClassifiedInfoRequest; + event ClassifiedInfoUpdate OnClassifiedInfoUpdate; + event ClassifiedDelete OnClassifiedDelete; + event ClassifiedDelete OnClassifiedGodDelete; + + event EventNotificationAddRequest OnEventNotificationAddRequest; + event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; + event EventGodDelete OnEventGodDelete; + + event ParcelDwellRequest OnParcelDwellRequest; + + event UserInfoRequest OnUserInfoRequest; + event UpdateUserInfo OnUpdateUserInfo; + + event RetrieveInstantMessages OnRetrieveInstantMessages; + + event PickDelete OnPickDelete; + event PickGodDelete OnPickGodDelete; + event PickInfoUpdate OnPickInfoUpdate; + event AvatarNotesUpdate OnAvatarNotesUpdate; + event AvatarInterestUpdate OnAvatarInterestUpdate; + event GrantUserFriendRights OnGrantUserRights; + + event MuteListRequest OnMuteListRequest; + + event PlacesQuery OnPlacesQuery; + + event FindAgentUpdate OnFindAgent; + event TrackAgentUpdate OnTrackAgent; + event NewUserReport OnUserReport; + event SaveStateHandler OnSaveState; + event GroupAccountSummaryRequest OnGroupAccountSummaryRequest; + event GroupAccountDetailsRequest OnGroupAccountDetailsRequest; + event GroupAccountTransactionsRequest OnGroupAccountTransactionsRequest; + event FreezeUserUpdate OnParcelFreezeUser; + event EjectUserUpdate OnParcelEjectUser; + event ParcelBuyPass OnParcelBuyPass; + event ParcelGodMark OnParcelGodMark; + event GroupActiveProposalsRequest OnGroupActiveProposalsRequest; + event GroupVoteHistoryRequest OnGroupVoteHistoryRequest; + event SimWideDeletesDelegate OnSimWideDeletes; + event SendPostcard OnSendPostcard; + event MuteListEntryUpdate OnUpdateMuteListEntry; + event MuteListEntryRemove OnRemoveMuteListEntry; + event GodlikeMessage onGodlikeMessage; + event GodUpdateRegionInfoUpdate OnGodUpdateRegionInfoUpdate; + + /// + /// Set the debug level at which packet output should be printed to console. + /// + int DebugPacketLevel { get; set; } + + void InPacket(object NewPack); + void ProcessInPacket(Packet NewPack); + + /// + /// Close this client + /// + void Close(); + + /// + /// Close this client + /// + /// + /// If true, attempts the close without checking active status. You do not want to try this except as a last + /// ditch attempt where Active == false but the ScenePresence still exists. + /// + void Close(bool force); + + void Kick(string message); + + /// + /// Start processing for this client. + /// + void Start(); + + void Stop(); + + // void ActivateGesture(UUID assetId, UUID gestureId); + + /// + /// Tell this client what items it should be wearing now + /// + void SendWearables(AvatarWearable[] wearables, int serial); + + /// + /// Send information about the given agent's appearance to another client. + /// + /// The id of the agent associated with the appearance + /// + /// + void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry); + + void SendCachedTextureResponse(ISceneEntity avatar, int serial, List cachedTextures); + + void SendStartPingCheck(byte seq); + + /// + /// Tell the client that an object has been deleted + /// + /// + void SendKillObject(List localID); + + void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs); + void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args); + + /// + /// Send chat to the viewer. + /// + /// + /// + /// + /// + /// + /// + /// + /// + void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source, + byte audible); + + void SendInstantMessage(GridInstantMessage im); + + void SendGenericMessage(string method, UUID invoice, List message); + void SendGenericMessage(string method, UUID invoice, List message); + + void SendLayerData(float[] map); + void SendLayerData(int px, int py, float[] map); + + void SendWindData(Vector2[] windSpeeds); + void SendCloudData(float[] cloudCover); + + /// + /// Sent when an agent completes its movement into a region. + /// + /// + /// This packet marks completion of the arrival of a root avatar in a region, whether through login, region + /// crossing or direct teleport. + /// + void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look); + + void InformClientOfNeighbour(ulong neighbourHandle, IPEndPoint neighbourExternalEndPoint); + + /// + /// Return circuit information for this client. + /// + /// + AgentCircuitData RequestClientInfo(); + + void CrossRegion(ulong newRegionHandle, Vector3 pos, Vector3 lookAt, IPEndPoint newRegionExternalEndPoint, + string capsURL); + + void SendMapBlock(List mapBlocks, uint flag); + void SendLocalTeleport(Vector3 position, Vector3 lookAt, uint flags); + + void SendRegionTeleport(ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, uint locationID, + uint flags, string capsURL); + + void SendTeleportFailed(string reason); + void SendTeleportStart(uint flags); + void SendTeleportProgress(uint flags, string message); + + void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance, int transactionType, UUID sourceID, bool sourceIsGroup, UUID destID, bool destIsGroup, int amount, string item); + + void SendPayPrice(UUID objectID, int[] payPrice); + + void SendCoarseLocationUpdate(List users, List CoarseLocations); + + void SetChildAgentThrottle(byte[] throttle); + + void SendAvatarDataImmediate(ISceneEntity avatar); + + /// + /// Send a positional, velocity, etc. update to the viewer for a given entity. + /// + /// + /// + void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags); + + void ReprioritizeUpdates(); + void FlushPrimUpdates(); + + void SendInventoryFolderDetails(UUID ownerID, UUID folderID, List items, + List folders, int version, bool fetchFolders, + bool fetchItems); + + void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item); + + /// + /// Tell the client that we have created the item it requested. + /// + /// + void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackId); + + void SendRemoveInventoryItem(UUID itemID); + + void SendTakeControls(int controls, bool passToAgent, bool TakeControls); + + void SendTaskInventory(UUID taskID, short serial, byte[] fileName); + + void SendTelehubInfo(UUID ObjectID, string ObjectName, Vector3 ObjectPos, Quaternion ObjectRot, List SpawnPoint); + + /// + /// Used by the server to inform the client of new inventory items and folders. + /// + /// + /// If the node is a folder then the contents will be transferred + /// (including all descendent folders) as well as the folder itself. + /// + /// + void SendBulkUpdateInventory(InventoryNodeBase node); + + void SendXferPacket(ulong xferID, uint packet, byte[] data); + + void SendAbortXferPacket(ulong xferID); + + void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, + int PriceGroupCreate, int PriceObjectClaim, float PriceObjectRent, + float PriceObjectScaleFactor, + int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, + int PricePublicObjectDecay, + int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, + float TeleportPriceExponent); + + void SendAvatarPickerReply(AvatarPickerReplyAgentDataArgs AgentData, List Data); + + void SendAgentDataUpdate(UUID agentid, UUID activegroupid, string firstname, string lastname, ulong grouppowers, + string groupname, string grouptitle); + + void SendPreLoadSound(UUID objectID, UUID ownerID, UUID soundID); + void SendPlayAttachedSound(UUID soundID, UUID objectID, UUID ownerID, float gain, byte flags); + + void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, + float gain); + + void SendAttachedSoundGainChange(UUID objectID, float gain); + + void SendNameReply(UUID profileId, string firstname, string lastname); + void SendAlertMessage(string message); + + void SendAgentAlertMessage(string message, bool modal); + void SendLoadURL(string objectname, UUID objectID, UUID ownerID, bool groupOwned, string message, string url); + + /// + /// Open a dialog box on the client. + /// + /// + /// + /// /param> + /// + /// + /// + /// + /// + /// + void SendDialog(string objectname, UUID objectID, UUID ownerID, string ownerFirstName, string ownerLastName, string msg, UUID textureID, int ch, + string[] buttonlabels); + + /// + /// Update the client as to where the sun is currently located. + /// + /// + /// + /// Seconds since Unix Epoch 01/01/1970 00:00:00 + /// + /// + /// The orbital position is given in radians, and must be "adjusted" for the linden client, see LLClientView + void SendSunPos(Vector3 sunPos, Vector3 sunVel, ulong CurrentTime, uint SecondsPerSunCycle, uint SecondsPerYear, + float OrbitalPosition); + + void SendViewerEffect(ViewerEffectPacket.EffectBlock[] effectBlocks); + void SendViewerTime(int phase); + + void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, string flAbout, + uint flags, UUID flImageID, UUID imageID, string profileURL, UUID partnerID); + + void SendScriptQuestion(UUID taskID, string taskName, string ownerName, UUID itemID, int question); + void SendHealth(float health); + + + void SendEstateList(UUID invoice, int code, UUID[] Data, uint estateID); + + void SendBannedUserList(UUID invoice, EstateBan[] banlist, uint estateID); + + void SendRegionInfoToEstateMenu(RegionInfoForEstateMenuArgs args); + void SendEstateCovenantInformation(UUID covenant); + + void SendDetailedEstateData(UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, + uint sunPosition, UUID covenant, uint covenantChanged, string abuseEmail, UUID estateOwner); + + /// + /// Send land properties to the client. + /// + /// + /// + /// + /// + /// /param> + /// + /// + void SendLandProperties(int sequence_id, bool snap_selection, int request_result, ILandObject lo, + float simObjectBonusFactor, int parcelObjectCapacity, int simObjectCapacity, + uint regionFlags); + + void SendLandAccessListData(List accessList, uint accessFlag, int localLandID); + void SendForceClientSelectObjects(List objectIDs); + void SendCameraConstraint(Vector4 ConstraintPlane); + void SendLandObjectOwners(LandData land, List groups, Dictionary ownersAndCount); + void SendLandParcelOverlay(byte[] data, int sequence_id); + + #region Parcel Methods + + void SendParcelMediaCommand(uint flags, ParcelMediaCommandEnum command, float time); + + void SendParcelMediaUpdate(string mediaUrl, UUID mediaTextureID, + byte autoScale, string mediaType, string mediaDesc, int mediaWidth, int mediaHeight, + byte mediaLoop); + + #endregion + + void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID); + void SendConfirmXfer(ulong xferID, uint PacketID); + void SendXferRequest(ulong XferID, short AssetType, UUID vFileID, byte FilePath, byte[] FileName); + + void SendInitiateDownload(string simFileName, string clientFileName); + + /// + /// Send the first part of a texture. For sufficiently small textures, this may be the only packet. + /// + /// + /// + /// + /// + /// + void SendImageFirstPart(ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec); + + /// + /// Send the next packet for a series of packets making up a single texture, + /// as established by SendImageFirstPart() + /// + /// + /// + /// + void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData); + + /// + /// Tell the client that the requested texture cannot be found + /// + void SendImageNotFound(UUID imageid); + + void SendShutdownConnectionNotice(); + + /// + /// Send statistical information about the sim to the client. + /// + /// + void SendSimStats(SimStats stats); + + void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags); + + void SendObjectPropertiesReply(ISceneEntity Entity); + + void SendPartPhysicsProprieties(ISceneEntity Entity); + + void SendAgentOffline(UUID[] agentIDs); + + void SendAgentOnline(UUID[] agentIDs); + + void SendSitResponse(UUID TargetID, Vector3 OffsetPos, Quaternion SitOrientation, bool autopilot, + Vector3 CameraAtOffset, Vector3 CameraEyeOffset, bool ForceMouseLook); + + void SendAdminResponse(UUID Token, uint AdminLevel); + + void SendGroupMembership(GroupMembershipData[] GroupMembership); + + void SendGroupNameReply(UUID groupLLUID, string GroupName); + + void SendJoinGroupReply(UUID groupID, bool success); + + void SendEjectGroupMemberReply(UUID agentID, UUID groupID, bool success); + + void SendLeaveGroupReply(UUID groupID, bool success); + + void SendCreateGroupReply(UUID groupID, bool success, string message); + + void SendLandStatReply(uint reportType, uint requestFlags, uint resultCount, LandStatReportItem[] lsrpia); + + void SendScriptRunningReply(UUID objectID, UUID itemID, bool running); + + void SendAsset(AssetRequestToClient req); + + void SendTexture(AssetBase TextureAsset); + + byte[] GetThrottlesPacked(float multiplier); + + event ViewerEffectEventHandler OnViewerEffect; + event Action OnLogout; + event Action OnConnectionClosed; + + void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message); + + void SendLogoutPacket(); + + // WARNING WARNING WARNING + // + // The two following methods are EXCLUSIVELY for the load balancer. + // they cause a MASSIVE performance hit! + // + ClientInfo GetClientInfo(); + void SetClientInfo(ClientInfo info); + + void SetClientOption(string option, string value); + string GetClientOption(string option); + + void SendSetFollowCamProperties(UUID objectID, SortedDictionary parameters); + void SendClearFollowCamProperties(UUID objectID); + + void SendRegionHandle(UUID regoinID, ulong handle); + void SendParcelInfo(RegionInfo info, LandData land, UUID parcelID, uint x, uint y); + void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, Vector3 lookAt); + + void SendDirPlacesReply(UUID queryID, DirPlacesReplyData[] data); + void SendDirPeopleReply(UUID queryID, DirPeopleReplyData[] data); + void SendDirEventsReply(UUID queryID, DirEventsReplyData[] data); + void SendDirGroupsReply(UUID queryID, DirGroupsReplyData[] data); + void SendDirClassifiedReply(UUID queryID, DirClassifiedReplyData[] data); + void SendDirLandReply(UUID queryID, DirLandReplyData[] data); + void SendDirPopularReply(UUID queryID, DirPopularReplyData[] data); + void SendEventInfoReply(EventData info); + + void SendMapItemReply(mapItemReply[] replies, uint mapitemtype, uint flags); + + void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data); + void SendOfferCallingCard(UUID srcID, UUID transactionID); + void SendAcceptCallingCard(UUID transactionID); + void SendDeclineCallingCard(UUID transactionID); + + void SendTerminateFriend(UUID exFriendID); + + void SendAvatarClassifiedReply(UUID targetID, UUID[] classifiedID, string[] name); + void SendClassifiedInfoReply(UUID classifiedID, UUID creatorID, uint creationDate, uint expirationDate, uint category, string name, string description, UUID parcelID, uint parentEstate, UUID snapshotID, string simName, Vector3 globalPos, string parcelName, byte classifiedFlags, int price); + + void SendAgentDropGroup(UUID groupID); + void RefreshGroupMembership(); + void SendAvatarNotesReply(UUID targetID, string text); + void SendAvatarPicksReply(UUID targetID, Dictionary picks); + void SendPickInfoReply(UUID pickID,UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled); + + void SendAvatarClassifiedReply(UUID targetID, Dictionary classifieds); + + void SendParcelDwellReply(int localID, UUID parcelID, float dwell); + + void SendUserInfoReply(bool imViaEmail, bool visible, string email); + + void SendUseCachedMuteList(); + + void SendMuteListUpdate(string filename); + + void SendGroupActiveProposals(UUID groupID, UUID transactionID, GroupActiveProposals[] Proposals); + + void SendGroupVoteHistory(UUID groupID, UUID transactionID, GroupVoteHistory[] Votes); + + bool AddGenericPacketHandler(string MethodName, GenericMessage handler); + + void SendRebakeAvatarTextures(UUID textureID); + + void SendAvatarInterestsReply(UUID avatarID, uint wantMask, string wantText, uint skillsMask, string skillsText, string languages); + + void SendGroupAccountingDetails(IClientAPI sender,UUID groupID, UUID transactionID, UUID sessionID, int amt); + + void SendGroupAccountingSummary(IClientAPI sender,UUID groupID, uint moneyAmt, int totalTier, int usedTier); + + void SendGroupTransactionsSummaryDetails(IClientAPI sender,UUID groupID, UUID transactionID, UUID sessionID,int amt); + + void SendChangeUserRights(UUID agentID, UUID friendID, int rights); + void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId); + + void SendAgentTerseUpdate(ISceneEntity presence); + + void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data); + } +} diff --git a/OpenSim/Framework/ICnmCache.cs b/OpenSim/Framework/ICnmCache.cs new file mode 100644 index 0000000000..27b9c56318 --- /dev/null +++ b/OpenSim/Framework/ICnmCache.cs @@ -0,0 +1,441 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Represent generic cache to store key/value pairs (elements) limited by time, size and count of elements. + /// + /// + /// The type of keys in the cache. + /// + /// + /// The type of values in the cache. + /// + /// + /// + /// Cache store limitations: + /// + /// + /// + /// Limitation + /// Description + /// + /// + /// Time + /// + /// Element that is not accessed through or in last are + /// removed from the cache automatically. Depending on implementation of the cache some of elements may stay longer in cache. + /// returns , if cache is limited by time. + /// + /// + /// + /// Count + /// + /// When adding an new element to cache that already have of elements, cache will remove less recently + /// used element(s) from the cache, until element fits to cache. + /// returns , if cache is limiting element count. + /// + /// + /// + /// Size + /// + /// + /// When adding an new element to cache that already have of elements, cache will remove less recently + /// used element(s) from the cache, until element fits to cache. + /// returns , if cache is limiting total size of elements. + /// Normally size is bytes used by element in the cache. But it can be any other suitable unit of measure. + /// + /// + /// + /// + /// + public interface ICnmCache : IEnumerable> + { + /// + /// Gets current count of elements stored to . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + int Count { get; } + + /// + /// Gets or sets elements expiration time. + /// + /// + /// Elements expiration time. + /// + /// + /// + /// When element has been stored in longer than + /// and it is not accessed through method or element's value is + /// not replaced by method, then it is automatically removed from the + /// . + /// + /// + /// It is possible that implementation removes element before it's expiration time, + /// because total size or count of elements stored to cache is larger than or . + /// + /// + /// It is also possible that element stays in cache longer than . + /// + /// + /// Calling try to remove all elements that are expired. + /// + /// + /// To disable time limit in cache, set to . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + TimeSpan ExpirationTime { get; set; } + + /// + /// Gets a value indicating whether or not access to the is synchronized (thread safe). + /// + /// + /// if access to the is synchronized (thread safe); + /// otherwise, . + /// + /// + /// + /// To get synchronized (thread safe) access to object, use + /// in class + /// to retrieve synchronized wrapper for object. + /// + /// + /// + /// + bool IsSynchronized { get; } + + /// + /// Gets a value indicating whether is limiting count of elements. + /// + /// + /// if the count of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + bool IsCountLimited { get; } + + /// + /// Gets a value indicating whether is limiting size of elements. + /// + /// + /// if the total size of elements is limited; + /// otherwise, . + /// + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + bool IsSizeLimited { get; } + + /// + /// Gets a value indicating whether elements stored to have limited inactivity time. + /// + /// + /// if the has a fixed total size of elements; + /// otherwise, . + /// + /// + /// If have limited inactivity time and element is not accessed through + /// or methods in , then element is automatically removed from + /// the cache. Depending on implementation of the , some of the elements may + /// stay longer in cache. + /// + /// + /// + /// + /// + bool IsTimeLimited { get; } + + /// + /// Gets or sets maximal allowed count of elements that can be stored to . + /// + /// + /// , if is not limited by count of elements; + /// otherwise maximal allowed count of elements. + /// + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + int MaxCount { get; set; } + + /// + /// Gets maximal allowed element size. + /// + /// + /// Maximal allowed element size. + /// + /// + /// + /// If element's size is larger than , then element is + /// not added to the . + /// + /// + /// + /// + /// + /// + long MaxElementSize { get; } + + /// + /// Gets or sets maximal allowed total size for elements stored to . + /// + /// + /// Maximal allowed total size for elements stored to . + /// + /// + /// + /// Normally size is total bytes used by elements in the cache. But it can be any other suitable unit of measure. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// value is less than 0. + /// + /// + /// + long MaxSize { get; set; } + + /// + /// Gets total size of elements stored to . + /// + /// + /// Total size of elements stored to . + /// + /// + /// + /// Normally bytes, but can be any suitable unit of measure. + /// + /// + /// Element's size is given when element is added or replaced by method. + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + long Size { get; } + + /// + /// Gets an object that can be used to synchronize access to the . + /// + /// + /// An object that can be used to synchronize access to the . + /// + /// + /// + /// To get synchronized (thread safe) access to , use + /// method to retrieve synchronized wrapper interface to + /// . + /// + /// + /// + /// + object SyncRoot { get; } + + /// + /// Removes all elements from the . + /// + /// + /// + /// + /// + /// + void Clear(); + + /// + /// Purge expired elements from the . + /// + /// + /// + /// Element becomes expired when last access time to it has been longer time than . + /// + /// + /// Depending on implementation, some of expired elements + /// may stay longer than in the cache. + /// + /// + /// + /// + /// + /// + /// + /// + /// + void PurgeExpired(); + + /// + /// Removes element associated with from the . + /// + /// + /// The key that is associated with element to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + void Remove(TKey key); + + /// + /// Removes elements that are associated with one of from the . + /// + /// + /// The keys that are associated with elements to remove from the . + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + void RemoveRange(IEnumerable keys); + + /// + /// Add or replace an element with the provided , and to + /// . + /// + /// + /// The object used as the key of the element. Can't be reference. + /// + /// + /// The object used as the value of the element to add or replace. is allowed. + /// + /// + /// The element's size. Normally bytes, but can be any suitable unit of measure. + /// + /// + /// if element has been added successfully to the ; + /// otherwise . + /// + /// + /// is . + /// + /// + /// The element's is less than 0. + /// + /// + /// + /// If element's is larger than , then element is + /// not added to the , however - possible older element is + /// removed from the . + /// + /// + /// When adding an new element to that is limiting total size of elements, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// When adding an new element to that is limiting element count, + /// will remove less recently used elements until it can fit an new element. + /// + /// + /// + /// + /// + /// + /// + /// + /// + bool Set(TKey key, TValue value, long size); + + /// + /// Gets the associated with the specified . + /// + /// + /// if the contains an element with + /// the specified key; otherwise, . + /// + /// + /// The key whose to get. + /// + /// + /// When this method returns, the value associated with the specified , + /// if the is found; otherwise, the + /// default value for the type of the parameter. This parameter is passed uninitialized. + /// + /// + /// is . + /// + /// + /// + /// + /// + /// + bool TryGetValue(TKey key, out TValue value); + } +} diff --git a/OpenSim/Framework/ICommandConsole.cs b/OpenSim/Framework/ICommandConsole.cs new file mode 100644 index 0000000000..3db27652e9 --- /dev/null +++ b/OpenSim/Framework/ICommandConsole.cs @@ -0,0 +1,106 @@ +/* + * 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.Xml; + +namespace OpenSim.Framework +{ + public delegate void CommandDelegate(string module, string[] cmd); + + public interface ICommands + { + void FromXml(XmlElement root, CommandDelegate fn); + + /// + /// Get help for the given help string + /// + /// Parsed parts of the help string. If empty then general help is returned. + /// + List GetHelp(string[] cmd); + + /// + /// Add a command to those which can be invoked from the console. + /// + /// + /// + /// + /// + /// + void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn); + + /// + /// Add a command to those which can be invoked from the console. + /// + /// + /// + /// + /// + /// + /// + void AddCommand(string module, bool shared, string command, + string help, string longhelp, string descriptivehelp, + CommandDelegate fn); + + /// + /// Has the given command already been registered? + /// + /// + /// Command. + bool HasCommand(string command); + + string[] FindNextOption(string[] command, bool term); + + string[] Resolve(string[] command); + + XmlElement GetXml(XmlDocument doc); + } + + public delegate void OnOutputDelegate(string message); + + public interface ICommandConsole : IConsole + { + event OnOutputDelegate OnOutput; + + ICommands Commands { get; } + + /// + /// The default prompt text. + /// + string DefaultPrompt { get; set; } + + /// + /// Display a command prompt on the console and wait for user input + /// + void Prompt(); + + void RunCommand(string cmd); + + string ReadLine(string p, bool isCommand, bool e); + } +} diff --git a/OpenSim/Framework/IConsole.cs b/OpenSim/Framework/IConsole.cs new file mode 100644 index 0000000000..79560d805b --- /dev/null +++ b/OpenSim/Framework/IConsole.cs @@ -0,0 +1,53 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public interface IConsole + { + object ConsoleScene { get; set; } + + void Output(string text, string level); + void Output(string text); + void OutputFormat(string format, params object[] components); + + string CmdPrompt(string p); + string CmdPrompt(string p, string def); + string CmdPrompt(string p, List excludedCharacters); + string CmdPrompt(string p, string def, List excludedCharacters); + + // Displays a command prompt and returns a default value, user may only enter 1 of 2 options + string CmdPrompt(string prompt, string defaultresponse, List options); + + // Displays a prompt and waits for the user to enter a string, then returns that string + // (Done with no echo and suitable for passwords) + string PasswdPrompt(string p); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IGenericConfig.cs b/OpenSim/Framework/IGenericConfig.cs new file mode 100644 index 0000000000..aafa112efa --- /dev/null +++ b/OpenSim/Framework/IGenericConfig.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + public interface IGenericConfig + { + void SetFileName(string fileName); + void LoadData(); + void LoadDataFromString(string data); + string GetAttribute(string attributeName); + bool SetAttribute(string attributeName, string attributeValue); + void Commit(); + void Close(); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IImprovedAssetCache.cs b/OpenSim/Framework/IImprovedAssetCache.cs new file mode 100644 index 0000000000..a853e9015d --- /dev/null +++ b/OpenSim/Framework/IImprovedAssetCache.cs @@ -0,0 +1,64 @@ +/* + * 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 OpenSim.Framework; + +namespace OpenSim.Framework +{ + public interface IImprovedAssetCache + { + /// + /// Cache the specified asset. + /// + /// + void Cache(AssetBase asset); + + /// + /// Get an asset by its id. + /// + /// + /// null if the asset does not exist. + AssetBase Get(string id); + + /// + /// Check whether an asset with the specified id exists in the cache. + /// + /// + bool Check(string id); + + /// + /// Expire an asset from the cache. + /// + /// + void Expire(string id); + + /// + /// Clear the cache. + /// + void Clear(); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ILandChannel.cs b/OpenSim/Framework/ILandChannel.cs new file mode 100644 index 0000000000..c46c03c4d0 --- /dev/null +++ b/OpenSim/Framework/ILandChannel.cs @@ -0,0 +1,97 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.Framework.Interfaces +{ + public interface ILandChannel + { + /// + /// Get all parcels + /// + /// + List AllParcels(); + + /// + /// Get the parcel at the specified point + /// + /// Value between 0 - 256 on the x axis of the point + /// Value between 0 - 256 on the y axis of the point + /// Land object at the point supplied + ILandObject GetLandObject(int x, int y); + + /// + /// Get the parcel at the specified point + /// + /// Value between 0 - 256 on the x axis of the point + /// Value between 0 - 256 on the y axis of the point + /// Land object at the point supplied + ILandObject GetLandObject(float x, float y); + + /// + /// Get the parcel at the specified point + /// + /// Vector where x and y components are between 0 and 256. z component is ignored. + /// Land object at the point supplied + ILandObject GetLandObject(Vector3 position); + + /// + /// Get the parcels near the specified point + /// + /// + /// + List ParcelsNearPoint(Vector3 position); + + /// + /// Get the parcel given the land's local id. + /// + /// + /// + ILandObject GetLandObject(int localID); + + /// + /// Clear the land channel of all parcels. + /// + /// + /// If true, set up a default parcel covering the whole region owned by the estate owner. + /// + void Clear(bool setupDefaultParcel); + + bool IsForcefulBansAllowed(); + void UpdateLandObject(int localID, LandData data); + void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient); + void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); + void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); + void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime); + + void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id); + void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id); + } +} diff --git a/OpenSim/Framework/ILandObject.cs b/OpenSim/Framework/ILandObject.cs new file mode 100644 index 0000000000..8465c86e11 --- /dev/null +++ b/OpenSim/Framework/ILandObject.cs @@ -0,0 +1,143 @@ +/* + * 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.Generic; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public delegate int overrideParcelMaxPrimCountDelegate(ILandObject obj); + public delegate int overrideSimulatorMaxPrimCountDelegate(ILandObject obj); + + public interface ILandObject + { + int GetParcelMaxPrimCount(); + int GetSimulatorMaxPrimCount(); + int GetPrimsFree(); + Dictionary GetLandObjectOwners(); + + LandData LandData { get; set; } + bool[,] LandBitmap { get; set; } + UUID RegionUUID { get; } + + /// + /// Prim counts for this land object. + /// + IPrimCounts PrimCounts { get; set; } + + /// + /// The start point for the land object. This is the western-most point as one scans land working from + /// north to south. + /// + Vector3 StartPoint { get; } + + /// + /// The end point for the land object. This is the eastern-most point as one scans land working from + /// south to north. + /// + Vector3 EndPoint { get; } + + bool ContainsPoint(int x, int y); + + ILandObject Copy(); + + void SendLandUpdateToAvatarsOverMe(); + + void SendLandProperties(int sequence_id, bool snap_selection, int request_result, IClientAPI remote_client); + void UpdateLandProperties(LandUpdateArgs args, IClientAPI remote_client); + bool IsEitherBannedOrRestricted(UUID avatar); + bool IsBannedFromLand(UUID avatar); + bool CanBeOnThisLand(UUID avatar, float posHeight); + bool IsRestrictedFromLand(UUID avatar); + bool IsInLandAccessList(UUID avatar); + void SendLandUpdateToClient(IClientAPI remote_client); + void SendLandUpdateToClient(bool snap_selection, IClientAPI remote_client); + List CreateAccessListArrayByFlag(AccessList flag); + void SendAccessList(UUID agentID, UUID sessionID, uint flags, int sequenceID, IClientAPI remote_client); + void UpdateAccessList(uint flags, UUID transactionID, int sequenceID, int sections, List entries, IClientAPI remote_client); + void UpdateLandBitmapByteArray(); + void SetLandBitmapFromByteArray(); + bool[,] GetLandBitmap(); + void ForceUpdateLandInfo(); + void SetLandBitmap(bool[,] bitmap); + + /// + /// Get a land bitmap that would cover an entire region. + /// + /// The bitmap created. + bool[,] BasicFullRegionLandBitmap(); + + /// + /// Create a square land bitmap. + /// + /// + /// Land co-ordinates are zero indexed. The inputs are treated as points. So if you want to create a bitmap + /// that covers an entire 256 x 256m region apart from a strip of land on the east, then you would need to + /// specify start_x = 0, start_y = 0, end_x = 252 (or anything up to 255), end_y = 256. + /// + /// At the moment, the smallest parcel of land is 4m x 4m, so if the + /// region is 256 x 256m (the SL size), the bitmap returned will start at (0,0) and end at (63,63). + /// + /// + /// + /// + /// + /// The bitmap created. + bool[,] GetSquareLandBitmap(int start_x, int start_y, int end_x, int end_y); + + bool[,] ModifyLandBitmapSquare(bool[,] land_bitmap, int start_x, int start_y, int end_x, int end_y, bool set_value); + bool[,] MergeLandBitmaps(bool[,] bitmap_base, bool[,] bitmap_add); + void SendForceObjectSelect(int local_id, int request_type, List returnIDs, IClientAPI remote_client); + void SendLandObjectOwners(IClientAPI remote_client); + void ReturnLandObjects(uint type, UUID[] owners, UUID[] tasks, IClientAPI remote_client); + void ResetOverMeRecord(); + void UpdateLandSold(UUID avatarID, UUID groupID, bool groupOwned, uint AuctionID, int claimprice, int area); + + void DeedToGroup(UUID groupID); + + void SetParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); + void SetSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); + + /// + /// Set the media url for this land parcel + /// + /// + void SetMediaUrl(string url); + + /// + /// Set the music url for this land parcel + /// + /// + void SetMusicUrl(string url); + + /// + /// Get the music url for this land parcel + /// + /// The music url. + string GetMusicUrl(); + } +} diff --git a/OpenSim/Framework/IMoneyModule.cs b/OpenSim/Framework/IMoneyModule.cs new file mode 100644 index 0000000000..52f3e83854 --- /dev/null +++ b/OpenSim/Framework/IMoneyModule.cs @@ -0,0 +1,50 @@ +/* + * 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 +{ + public delegate void ObjectPaid(UUID objectID, UUID agentID, int amount); + public interface IMoneyModule + { + bool ObjectGiveMoney(UUID objectID, UUID fromID, UUID toID, + int amount); + + int GetBalance(UUID agentID); + bool UploadCovered(UUID agentID, int amount); + bool AmountCovered(UUID agentID, int amount); + void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type); + void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type, string extraData); + void ApplyUploadCharge(UUID agentID, int amount, string text); + + int UploadCharge { get; } + int GroupCreationCharge { get; } + + event ObjectPaid OnObjectPaid; + } +} diff --git a/OpenSim/Framework/IPeople.cs b/OpenSim/Framework/IPeople.cs new file mode 100644 index 0000000000..8d274d0eb0 --- /dev/null +++ b/OpenSim/Framework/IPeople.cs @@ -0,0 +1,49 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class UserData + { + public UUID Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string HomeURL { get; set; } + public Dictionary ServerURLs { get; set; } + public bool IsUnknownUser { get; set; } + public bool HasGridUserTried { get; set; } + } + + public interface IPeople + { + List GetUserData(string query, int page_size, int page_number); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IPlugin.cs b/OpenSim/Framework/IPlugin.cs new file mode 100644 index 0000000000..52ddd647a6 --- /dev/null +++ b/OpenSim/Framework/IPlugin.cs @@ -0,0 +1,80 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Exception thrown if Initialise has been called, but failed. + /// + public class PluginNotInitialisedException : Exception + { + public PluginNotInitialisedException () : base() {} + public PluginNotInitialisedException (string msg) : base(msg) {} + public PluginNotInitialisedException (string msg, Exception e) : base(msg, e) {} + } + + /// + /// This interface, describes a generic plugin + /// + public interface IPlugin : IDisposable + { + /// + /// Returns the plugin version + /// + /// Plugin version in MAJOR.MINOR.REVISION.BUILD format + string Version { get; } + + /// + /// Returns the plugin name + /// + /// Plugin name, eg MySQL User Provider + string Name { get; } + + /// + /// Default-initialises the plugin + /// + void Initialise(); + } + + /// + /// Any plugins which need to pass parameters to their initialisers must + /// inherit this class and use it to set the PluginLoader Initialiser property + /// + public class PluginInitialiserBase + { + // this would be a lot simpler if C# supported currying or typedefs + + // default initialisation + public virtual void Initialise (IPlugin plugin) + { + plugin.Initialise(); + } + } + +} diff --git a/OpenSim/Framework/IPrimCounts.cs b/OpenSim/Framework/IPrimCounts.cs new file mode 100644 index 0000000000..3e12348e1b --- /dev/null +++ b/OpenSim/Framework/IPrimCounts.cs @@ -0,0 +1,74 @@ +/* + * 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 +{ + public interface IPrimCounts + { + /// + /// Parcel owner owned prims + /// + int Owner { get; } + + /// + /// Parcel group owned prims + /// + int Group { get; } + + /// + /// Prims owned by others (not parcel owner or parcel group). + /// + int Others { get; } + + /// + /// Selected prims + /// + int Selected { get; } + + /// + /// Total prims on the parcel. + /// + int Total { get; } + + /// + /// Prims on the simulator that are owned by the parcel owner, even if they are in other parcels. + /// + int Simulator { get; } + + /// + /// Prims per individual users. + /// + IUserPrimCounts Users { get; } + } + + public interface IUserPrimCounts + { + int this[UUID agentID] { get; } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IRegionCreator.cs b/OpenSim/Framework/IRegionCreator.cs new file mode 100644 index 0000000000..350120d829 --- /dev/null +++ b/OpenSim/Framework/IRegionCreator.cs @@ -0,0 +1,39 @@ +/* + * 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.Text; + +namespace OpenSim.Framework +{ + public delegate void NewRegionCreated(IScene scene); + public interface IRegionCreator + { + event NewRegionCreated OnNewRegionCreated; + } +} diff --git a/OpenSim/Framework/IRegionLoader.cs b/OpenSim/Framework/IRegionLoader.cs new file mode 100644 index 0000000000..c566fc7916 --- /dev/null +++ b/OpenSim/Framework/IRegionLoader.cs @@ -0,0 +1,37 @@ +/* + * 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 Nini.Config; + +namespace OpenSim.Framework +{ + public interface IRegionLoader + { + void SetIniConfigSource(IConfigSource configSource); + RegionInfo[] LoadRegions(); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IRegistryCore.cs b/OpenSim/Framework/IRegistryCore.cs new file mode 100644 index 0000000000..a94b65d529 --- /dev/null +++ b/OpenSim/Framework/IRegistryCore.cs @@ -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. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Framework +{ + public interface IRegistryCore + { + T Get(); + void RegisterInterface(T iface); + bool TryGet(out T iface); + + void StackModuleInterface(M mod); + T[] RequestModuleInterfaces(); + } +} diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs new file mode 100644 index 0000000000..e1b6d1e1ad --- /dev/null +++ b/OpenSim/Framework/IScene.cs @@ -0,0 +1,147 @@ +/* + * 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; +//using OpenSim.Framework.Console; +using Nini.Config; + +namespace OpenSim.Framework +{ + public delegate void restart(RegionInfo thisRegion); + + public enum RegionStatus : int + { + Down = 0, + Up = 1, + Crashed = 2, + Starting = 3, + }; + + /// + /// Indicate what action to take on an object derez request + /// + public enum DeRezAction : byte + { + SaveToExistingUserInventoryItem = 0, + TakeCopy = 1, + Take = 4, + GodTakeCopy = 5, + Delete = 6, + Return = 9 + }; + + public interface IScene + { + /// + /// The name of this scene. + /// + string Name { get; } + + RegionInfo RegionInfo { get; } + RegionStatus RegionStatus { get; set; } + + IConfigSource Config { get; } + + /// + /// Are logins enabled on this simulator? + /// + bool LoginsEnabled { get; set; } + + /// + /// Is this region ready for use? + /// + /// + /// This does not mean that logins are enabled, merely that they can be. + /// + bool Ready { get; set; } + + float TimeDilation { get; } + + bool AllowScriptCrossings { get; } + + event restart OnRestart; + + /// + /// Add a new agent with an attached client. All agents except initial login clients will starts off as a child agent + /// - the later agent crossing will promote it to a root agent. + /// + /// + /// The type of agent to add. + /// + /// The scene agent if the new client was added or if an agent that already existed. + ISceneAgent AddNewAgent(IClientAPI client, PresenceType type); + + /// + /// Tell a single agent to disconnect from the region. + /// + /// + /// + /// Force the agent to close even if it might be in the middle of some other operation. You do not want to + /// force unless you are absolutely sure that the agent is dead and a normal close is not working. + /// + bool CloseAgent(UUID agentID, bool force); + + void Restart(); + + string GetSimulatorVersion(); + + bool TryGetScenePresence(UUID agentID, out object scenePresence); + + /// + /// Register an interface to a region module. This allows module methods to be called directly as + /// well as via events. If there is already a module registered for this interface, it is not replaced + /// (is this the best behaviour?) + /// + /// + void RegisterModuleInterface(M mod); + + void StackModuleInterface(M mod); + + /// + /// For the given interface, retrieve the region module which implements it. + /// + /// null if there is no registered module implementing that interface + T RequestModuleInterface(); + + /// + /// For the given interface, retrieve an array of region modules that implement it. + /// + /// an empty array if there are no registered modules implementing that interface + T[] RequestModuleInterfaces(); + +// void AddCommand(object module, string command, string shorthelp, string longhelp, CommandDelegate callback); + + ISceneObject DeserializeObject(string representation); + + bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); + + /// + /// Start the scene and associated scripts within it. + /// + void Start(); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ISceneAgent.cs b/OpenSim/Framework/ISceneAgent.cs new file mode 100644 index 0000000000..ca1399c784 --- /dev/null +++ b/OpenSim/Framework/ISceneAgent.cs @@ -0,0 +1,87 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// An agent in the scene. + /// + /// + /// Interface is a work in progress. Please feel free to add other required properties and methods. + /// + public interface ISceneAgent : ISceneEntity + { + /// + /// The client controlling this presence + /// + IClientAPI ControllingClient { get; } + + /// + /// What type of presence is this? User, NPC, etc. + /// + PresenceType PresenceType { get; } + + /// + /// If true, then the agent has no avatar in the scene. + /// The agent exists to relay data from a region that neighbours the current position of the user's avatar. + /// Occasionally data is relayed, such as which a user clicks an item in a neighbouring region. + /// + bool IsChildAgent { get; } + + /// + /// Avatar appearance data. + /// + /// + // Because appearance setting is in a module, we actually need + // to give it access to our appearance directly, otherwise we + // get a synchronization issue. + /// + AvatarAppearance Appearance { get; set; } + + /// + /// Set if initial data about the scene (avatars, objects) has been sent to the client. + /// + bool SentInitialDataToClient { get; } + + /// + /// Send initial scene data to the client controlling this agent + /// + /// + /// This includes scene object data and the appearance data of other avatars. + /// + void SendInitialDataToClient(); + + /// + /// Direction in which the scene presence is looking. + /// + /// Will be Vector3.Zero for a child agent. + Vector3 Lookat { get; } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ISceneEntity.cs b/OpenSim/Framework/ISceneEntity.cs new file mode 100644 index 0000000000..a9f21d258c --- /dev/null +++ b/OpenSim/Framework/ISceneEntity.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + public interface ISceneEntity + { + string Name { get; set; } + UUID UUID { get; } + uint LocalId { get; } + + Vector3 AbsolutePosition { get; } + } +} diff --git a/OpenSim/Framework/ISceneObject.cs b/OpenSim/Framework/ISceneObject.cs new file mode 100644 index 0000000000..754b77b4a2 --- /dev/null +++ b/OpenSim/Framework/ISceneObject.cs @@ -0,0 +1,52 @@ +/* + * 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.Xml; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public interface ISceneObject + { + string Name { get; } + + UUID UUID { get; } + + /// + /// The owner of this object. + /// + UUID OwnerID { get; set; } + + ISceneObject CloneForNewScene(); + string ToXml2(); + string ExtraToXmlString(); + void ExtraFromXmlString(string xmlstr); + string GetStateSnapshot(); + void SetState(string xmlstr, IScene s); + bool HasGroupChanged { get; set; } + } +} diff --git a/OpenSim/Framework/InventoryCollection.cs b/OpenSim/Framework/InventoryCollection.cs new file mode 100644 index 0000000000..59655eb3a7 --- /dev/null +++ b/OpenSim/Framework/InventoryCollection.cs @@ -0,0 +1,44 @@ +/* + * 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.Generic; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + /// + /// Used to serialize a whole inventory for transfer over the network. + /// + public class InventoryCollection + { + public List Folders; + public List Items; + public UUID OwnerID; + public UUID FolderID; + public int Version; + } +} diff --git a/OpenSim/Framework/InventoryFolderBase.cs b/OpenSim/Framework/InventoryFolderBase.cs new file mode 100644 index 0000000000..fb66056fba --- /dev/null +++ b/OpenSim/Framework/InventoryFolderBase.cs @@ -0,0 +1,102 @@ +/* + * 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 +{ + /// + /// User inventory folder + /// + public class InventoryFolderBase : InventoryNodeBase + { + public static readonly string ROOT_FOLDER_NAME = "My Inventory"; + public static readonly string SUITCASE_FOLDER_NAME = "My Suitcase"; + + /// + /// The folder this folder is contained in + /// + private UUID _parentID; + + /// + /// Type of items normally stored in this folder + /// + private short _type; + + /// + /// This is used to denote the version of the client, needed + /// because of the changes clients have with inventory from + /// time to time (1.19.1 caused us some fits there). + /// + private ushort _version; + + public virtual UUID ParentID + { + get { return _parentID; } + set { _parentID = value; } + } + + public virtual short Type + { + get { return _type; } + set { _type = value; } + } + + public virtual ushort Version + { + get { return _version; } + set { _version = value; } + } + + public InventoryFolderBase() + { + } + + public InventoryFolderBase(UUID id) : this() + { + ID = id; + } + + public InventoryFolderBase(UUID id, UUID owner) : this(id) + { + Owner = owner; + } + + public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent) : this(id, owner) + { + Name = name; + ParentID = parent; + } + + public InventoryFolderBase( + UUID id, string name, UUID owner, short type, UUID parent, ushort version) : this(id, name, owner, parent) + { + Type = type; + Version = version; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/InventoryFolderImpl.cs b/OpenSim/Framework/InventoryFolderImpl.cs new file mode 100644 index 0000000000..139776bb39 --- /dev/null +++ b/OpenSim/Framework/InventoryFolderImpl.cs @@ -0,0 +1,472 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class InventoryFolderImpl : InventoryFolderBase + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly string PATH_DELIMITER = "/"; + + /// + /// Items that are contained in this folder + /// + public Dictionary Items = new Dictionary(); + + /// + /// Child folders that are contained in this folder + /// + protected Dictionary m_childFolders = new Dictionary(); + + // Constructors + public InventoryFolderImpl(InventoryFolderBase folderbase) + { + Owner = folderbase.Owner; + ID = folderbase.ID; + Name = folderbase.Name; + ParentID = folderbase.ParentID; + Type = folderbase.Type; + Version = folderbase.Version; + } + + public InventoryFolderImpl() + { + } + + /// + /// Create a new subfolder. + /// + /// + /// + /// + /// The newly created subfolder. Returns null if the folder already exists + public InventoryFolderImpl CreateChildFolder(UUID folderID, string folderName, ushort type) + { + lock (m_childFolders) + { + if (!m_childFolders.ContainsKey(folderID)) + { + InventoryFolderImpl subFold = new InventoryFolderImpl(); + subFold.Name = folderName; + subFold.ID = folderID; + subFold.Type = (short)type; + subFold.ParentID = this.ID; + subFold.Owner = Owner; + m_childFolders.Add(subFold.ID, subFold); + + return subFold; + } + } + + return null; + } + + /// + /// Add a folder that already exists. + /// + /// + public void AddChildFolder(InventoryFolderImpl folder) + { + lock (m_childFolders) + { + folder.ParentID = ID; + m_childFolders[folder.ID] = folder; + } + } + + /// + /// Does this folder contain the given child folder? + /// + /// + /// + public bool ContainsChildFolder(UUID folderID) + { + return m_childFolders.ContainsKey(folderID); + } + + /// + /// Get a child folder + /// + /// + /// The folder if it exists, null if it doesn't + public InventoryFolderImpl GetChildFolder(UUID folderID) + { + InventoryFolderImpl folder = null; + + lock (m_childFolders) + { + m_childFolders.TryGetValue(folderID, out folder); + } + + return folder; + } + + /// + /// Removes the given child subfolder. + /// + /// + /// + /// The folder removed, or null if the folder was not present. + /// + public InventoryFolderImpl RemoveChildFolder(UUID folderID) + { + InventoryFolderImpl removedFolder = null; + + lock (m_childFolders) + { + if (m_childFolders.ContainsKey(folderID)) + { + removedFolder = m_childFolders[folderID]; + m_childFolders.Remove(folderID); + } + } + + return removedFolder; + } + + /// + /// Delete all the folders and items in this folder. + /// + public void Purge() + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + folder.Purge(); + } + + m_childFolders.Clear(); + Items.Clear(); + } + + /// + /// Returns the item if it exists in this folder or in any of this folder's descendant folders + /// + /// + /// null if the item is not found + public InventoryItemBase FindItem(UUID itemID) + { + lock (Items) + { + if (Items.ContainsKey(itemID)) + { + return Items[itemID]; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryItemBase item = folder.FindItem(itemID); + + if (item != null) + { + return item; + } + } + } + + return null; + } + + public InventoryItemBase FindAsset(UUID assetID) + { + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { + if (item.AssetID == assetID) + return item; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryItemBase item = folder.FindAsset(assetID); + + if (item != null) + { + return item; + } + } + } + + return null; + } + + /// + /// Deletes an item if it exists in this folder or any children + /// + /// + /// + public bool DeleteItem(UUID itemID) + { + bool found = false; + + lock (Items) + { + if (Items.ContainsKey(itemID)) + { + Items.Remove(itemID); + return true; + } + } + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + found = folder.DeleteItem(itemID); + + if (found == true) + { + break; + } + } + } + + return found; + } + + /// + /// Returns the folder requested if it is this folder or is a descendent of this folder. The search is depth + /// first. + /// + /// The requested folder if it exists, null if it does not. + public InventoryFolderImpl FindFolder(UUID folderID) + { + if (folderID == ID) + return this; + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + InventoryFolderImpl returnFolder = folder.FindFolder(folderID); + + if (returnFolder != null) + return returnFolder; + } + } + + return null; + } + + /// + /// Look through all child subfolders for a folder marked as one for a particular asset type, and return it. + /// + /// + /// Returns null if no such folder is found + public InventoryFolderImpl FindFolderForType(int type) + { + lock (m_childFolders) + { + foreach (InventoryFolderImpl f in m_childFolders.Values) + { + if (f.Type == type) + return f; + } + } + + return null; + } + + /// + /// Find a folder given a PATH_DELIMITER delimited path starting from this folder + /// + /// + /// This method does not handle paths that contain multiple delimitors + /// + /// FIXME: We do not yet handle situations where folders have the same name. We could handle this by some + /// XPath like expression + /// + /// FIXME: Delimitors which occur in names themselves are not currently escapable. + /// + /// + /// The path to the required folder. + /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. + /// + /// null if the folder is not found + public InventoryFolderImpl FindFolderByPath(string path) + { + if (path == string.Empty) + return this; + + path = path.Trim(); + + if (path == PATH_DELIMITER) + return this; + + string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + if (folder.Name == components[0]) + if (components.Length > 1) + return folder.FindFolderByPath(components[1]); + else + return folder; + } + } + + // We didn't find a folder with the given name + return null; + } + + /// + /// Find an item given a PATH_DELIMITOR delimited path starting from this folder. + /// + /// This method does not handle paths that contain multiple delimitors + /// + /// FIXME: We do not yet handle situations where folders or items have the same name. We could handle this by some + /// XPath like expression + /// + /// FIXME: Delimitors which occur in names themselves are not currently escapable. + /// + /// + /// The path to the required item. + /// + /// null if the item is not found + public InventoryItemBase FindItemByPath(string path) + { + string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); + + if (components.Length == 1) + { + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { + if (item.Name == components[0]) + return item; + } + } + } + else + { + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + if (folder.Name == components[0]) + return folder.FindItemByPath(components[1]); + } + } + } + + // We didn't find an item or intermediate folder with the given name + return null; + } + + /// + /// Return a copy of the list of child items in this folder. The items themselves are the originals. + /// + public List RequestListOfItems() + { + List itemList = new List(); + + lock (Items) + { + foreach (InventoryItemBase item in Items.Values) + { +// m_log.DebugFormat( +// "[INVENTORY FOLDER IMPL]: Returning item {0} {1}, OwnerPermissions {2:X}", +// item.Name, item.ID, item.CurrentPermissions); + + itemList.Add(item); + } + } + + //m_log.DebugFormat("[INVENTORY FOLDER IMPL]: Found {0} items", itemList.Count); + + return itemList; + } + + /// + /// Return a copy of the list of child folders in this folder. The folders themselves are the originals. + /// + public List RequestListOfFolders() + { + List folderList = new List(); + + lock (m_childFolders) + { + foreach (InventoryFolderBase folder in m_childFolders.Values) + { + folderList.Add(folder); + } + } + + return folderList; + } + + public List RequestListOfFolderImpls() + { + List folderList = new List(); + + lock (m_childFolders) + { + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + folderList.Add(folder); + } + } + + return folderList; + } + + /// + /// The total number of items in this folder and in the immediate child folders (though not from other + /// descendants). + /// + public int TotalCount + { + get + { + int total = Items.Count; + + foreach (InventoryFolderImpl folder in m_childFolders.Values) + { + total = total + folder.TotalCount; + } + + return total; + } + } + } +} diff --git a/OpenSim/Framework/InventoryItemBase.cs b/OpenSim/Framework/InventoryItemBase.cs new file mode 100644 index 0000000000..f9fd752370 --- /dev/null +++ b/OpenSim/Framework/InventoryItemBase.cs @@ -0,0 +1,419 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Inventory Item - contains all the properties associated with an individual inventory piece. + /// + public class InventoryItemBase : InventoryNodeBase, ICloneable + { + /// + /// The inventory type of the item. This is slightly different from the asset type in some situations. + /// + public int InvType + { + get + { + return m_invType; + } + + set + { + m_invType = value; + } + } + protected int m_invType; + + /// + /// The folder this item is contained in + /// + public UUID Folder + { + get + { + return m_folder; + } + + set + { + m_folder = value; + } + } + protected UUID m_folder; + + /// + /// The creator of this item + /// + public string CreatorId + { + get + { + return m_creatorId; + } + + set + { + m_creatorId = value; + + if ((m_creatorId == null) || !UUID.TryParse(m_creatorId, out m_creatorIdAsUuid)) + m_creatorIdAsUuid = UUID.Zero; + } + } + protected string m_creatorId; + + /// + /// The CreatorId expressed as a UUID. + /// + public UUID CreatorIdAsUuid + { + get + { + if (UUID.Zero == m_creatorIdAsUuid) + { + UUID.TryParse(CreatorId, out m_creatorIdAsUuid); + } + + return m_creatorIdAsUuid; + } + } + protected UUID m_creatorIdAsUuid = UUID.Zero; + + /// + /// Extended creator information of the form ; + /// + public string CreatorData // = ; + { + get { return m_creatorData; } + set { m_creatorData = value; } + } + protected string m_creatorData = string.Empty; + + /// + /// Used by the DB layer to retrieve / store the entire user identification. + /// The identification can either be a simple UUID or a string of the form + /// uuid[;profile_url[;name]] + /// + public string CreatorIdentification + { + get + { + if (!string.IsNullOrEmpty(m_creatorData)) + return m_creatorId + ';' + m_creatorData; + else + return m_creatorId; + } + set + { + if ((value == null) || (value != null && value == string.Empty)) + { + m_creatorData = string.Empty; + return; + } + + if (!value.Contains(";")) // plain UUID + { + m_creatorId = value; + } + else // [;[;name]] + { + string name = "Unknown User"; + string[] parts = value.Split(';'); + if (parts.Length >= 1) + m_creatorId = parts[0]; + if (parts.Length >= 2) + m_creatorData = parts[1]; + if (parts.Length >= 3) + name = parts[2]; + + m_creatorData += ';' + name; + } + } + } + + /// + /// The description of the inventory item (must be less than 64 characters) + /// + public string Description + { + get + { + return m_description; + } + + set + { + m_description = value; + } + } + protected string m_description = String.Empty; + + /// + /// + /// + public uint NextPermissions + { + get + { + return m_nextPermissions; + } + + set + { + m_nextPermissions = value; + } + } + protected uint m_nextPermissions; + + /// + /// A mask containing permissions for the current owner (cannot be enforced) + /// + public uint CurrentPermissions + { + get + { + return m_currentPermissions; + } + + set + { + m_currentPermissions = value; + } + } + protected uint m_currentPermissions; + + /// + /// + /// + public uint BasePermissions + { + get + { + return m_basePermissions; + } + + set + { + m_basePermissions = value; + } + } + protected uint m_basePermissions; + + /// + /// + /// + public uint EveryOnePermissions + { + get + { + return m_everyonePermissions; + } + + set + { + m_everyonePermissions = value; + } + } + protected uint m_everyonePermissions; + + /// + /// + /// + public uint GroupPermissions + { + get + { + return m_groupPermissions; + } + + set + { + m_groupPermissions = value; + } + } + protected uint m_groupPermissions; + + /// + /// This is an enumerated value determining the type of asset (eg Notecard, Sound, Object, etc) + /// + public int AssetType + { + get + { + return m_assetType; + } + + set + { + m_assetType = value; + } + } + protected int m_assetType; + + /// + /// The UUID of the associated asset on the asset server + /// + public UUID AssetID + { + get + { + return m_assetID; + } + + set + { + m_assetID = value; + } + } + protected UUID m_assetID; + + /// + /// + /// + public UUID GroupID + { + get + { + return m_groupID; + } + + set + { + m_groupID = value; + } + } + protected UUID m_groupID; + + /// + /// + /// + public bool GroupOwned + { + get + { + return m_groupOwned; + } + + set + { + m_groupOwned = value; + } + } + protected bool m_groupOwned; + + /// + /// + /// + public int SalePrice + { + get + { + return m_salePrice; + } + + set + { + m_salePrice = value; + } + } + protected int m_salePrice; + + /// + /// + /// + public byte SaleType + { + get + { + return m_saleType; + } + + set + { + m_saleType = value; + } + } + protected byte m_saleType; + + /// + /// + /// + public uint Flags + { + get + { + return m_flags; + } + + set + { + m_flags = value; + } + } + protected uint m_flags; + + /// + /// + /// + public int CreationDate + { + get + { + return m_creationDate; + } + + set + { + m_creationDate = value; + } + } + protected int m_creationDate = (int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + + public InventoryItemBase() + { + } + + public InventoryItemBase(UUID id) + { + ID = id; + } + + public InventoryItemBase(UUID id, UUID owner) + { + ID = id; + Owner = owner; + } + + public object Clone() + { + return MemberwiseClone(); + } + } +} diff --git a/OpenSim/Framework/InventoryNodeBase.cs b/OpenSim/Framework/InventoryNodeBase.cs new file mode 100644 index 0000000000..31c3fd1840 --- /dev/null +++ b/OpenSim/Framework/InventoryNodeBase.cs @@ -0,0 +1,67 @@ +/* + * 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 +{ + /// + /// Common base class for inventory nodes of different types (files, folders, etc.) + /// + public class InventoryNodeBase + { + /// + /// The name of the node (64 characters or less) + /// + public virtual string Name + { + get { return m_name; } + set { m_name = value; } + } + private string m_name = string.Empty; + + /// + /// A UUID containing the ID for the inventory node itself + /// + public UUID ID + { + get { return m_id; } + set { m_id = value; } + } + private UUID m_id; + + /// + /// The agent who's inventory this is contained by + /// + public virtual UUID Owner + { + get { return m_owner; } + set { m_owner = value; } + } + private UUID m_owner; + } +} diff --git a/OpenSim/Framework/LandData.cs b/OpenSim/Framework/LandData.cs new file mode 100644 index 0000000000..fc02f33fdf --- /dev/null +++ b/OpenSim/Framework/LandData.cs @@ -0,0 +1,813 @@ +/* + * 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.Xml; +using System.Xml.Serialization; + +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class LandAccessEntry + { + public UUID AgentID; + public int Expires; + public AccessList Flags; + } + + /// + /// Details of a Parcel of land + /// + public class LandData + { + // use only one serializer to give the runtime a chance to + // optimize it (it won't do that if you use a new instance + // every time) + private static XmlSerializer serializer = new XmlSerializer(typeof(LandData)); + + private Vector3 _AABBMax = new Vector3(); + private Vector3 _AABBMin = new Vector3(); + private int _area = 0; + private uint _auctionID = 0; //Unemplemented. If set to 0, not being auctioned + private UUID _authBuyerID = UUID.Zero; //Unemplemented. Authorized Buyer's UUID + private ParcelCategory _category = ParcelCategory.None; //Unemplemented. Parcel's chosen category + private int _claimDate = 0; + private int _claimPrice = 0; //Unemplemented + private UUID _globalID = UUID.Zero; + private UUID _groupID = UUID.Zero; + private bool _isGroupOwned = false; + private byte[] _bitmap = new byte[512]; + private string _description = String.Empty; + + private uint _flags = (uint)ParcelFlags.AllowFly | (uint)ParcelFlags.AllowLandmark | + (uint)ParcelFlags.AllowAPrimitiveEntry | + (uint)ParcelFlags.AllowDeedToGroup | (uint)ParcelFlags.AllowTerraform | + (uint)ParcelFlags.CreateObjects | (uint)ParcelFlags.AllowOtherScripts | + (uint)ParcelFlags.SoundLocal | (uint)ParcelFlags.AllowVoiceChat; + + private byte _landingType = 0; + private string _name = "Your Parcel"; + private ParcelStatus _status = ParcelStatus.Leased; + private int _localID = 0; + private byte _mediaAutoScale = 0; + private UUID _mediaID = UUID.Zero; + private string _mediaURL = String.Empty; + private string _musicURL = String.Empty; + private UUID _ownerID = UUID.Zero; + private List _parcelAccessList = new List(); + private float _passHours = 0; + private int _passPrice = 0; + private int _salePrice = 0; //Unemeplemented. Parcels price. + private int _simwideArea = 0; + private int _simwidePrims = 0; + private UUID _snapshotID = UUID.Zero; + private Vector3 _userLocation = new Vector3(); + private Vector3 _userLookAt = new Vector3(); + private int _otherCleanTime = 0; + private string _mediaType = "none/none"; + private string _mediaDescription = ""; + private int _mediaHeight = 0; + private int _mediaWidth = 0; + private bool _mediaLoop = false; + private bool _obscureMusic = false; + private bool _obscureMedia = false; + private float _dwell = 0; + + /// + /// Traffic count of parcel + /// + [XmlIgnore] + public float Dwell + { + get + { + return _dwell; + } + set + { + _dwell = value; + } + } + + /// + /// Whether to obscure parcel media URL + /// + [XmlIgnore] + public bool ObscureMedia + { + get + { + return _obscureMedia; + } + set + { + _obscureMedia = value; + } + } + + /// + /// Whether to obscure parcel music URL + /// + [XmlIgnore] + public bool ObscureMusic + { + get + { + return _obscureMusic; + } + set + { + _obscureMusic = value; + } + } + + /// + /// Whether to loop parcel media + /// + [XmlIgnore] + public bool MediaLoop + { + get + { + return _mediaLoop; + } + set + { + _mediaLoop = value; + } + } + + /// + /// Height of parcel media render + /// + [XmlIgnore] + public int MediaHeight + { + get + { + return _mediaHeight; + } + set + { + _mediaHeight = value; + } + } + + /// + /// Width of parcel media render + /// + [XmlIgnore] + public int MediaWidth + { + get + { + return _mediaWidth; + } + set + { + _mediaWidth = value; + } + } + + /// + /// Upper corner of the AABB for the parcel + /// + [XmlIgnore] + public Vector3 AABBMax + { + get + { + return _AABBMax; + } + set + { + _AABBMax = value; + } + } + /// + /// Lower corner of the AABB for the parcel + /// + [XmlIgnore] + public Vector3 AABBMin + { + get + { + return _AABBMin; + } + set + { + _AABBMin = value; + } + } + + /// + /// Area in meters^2 the parcel contains + /// + public int Area + { + get + { + return _area; + } + set + { + _area = value; + } + } + + /// + /// ID of auction (3rd Party Integration) when parcel is being auctioned + /// + public uint AuctionID + { + get + { + return _auctionID; + } + set + { + _auctionID = value; + } + } + + /// + /// UUID of authorized buyer of parcel. This is UUID.Zero if anyone can buy it. + /// + public UUID AuthBuyerID + { + get + { + return _authBuyerID; + } + set + { + _authBuyerID = value; + } + } + + /// + /// Category of parcel. Used for classifying the parcel in classified listings + /// + public ParcelCategory Category + { + get + { + return _category; + } + set + { + _category = value; + } + } + + /// + /// Date that the current owner purchased or claimed the parcel + /// + public int ClaimDate + { + get + { + return _claimDate; + } + set + { + _claimDate = value; + } + } + + /// + /// The last price that the parcel was sold at + /// + public int ClaimPrice + { + get + { + return _claimPrice; + } + set + { + _claimPrice = value; + } + } + + /// + /// Global ID for the parcel. (3rd Party Integration) + /// + public UUID GlobalID + { + get + { + return _globalID; + } + set + { + _globalID = value; + } + } + + /// + /// Unique ID of the Group that owns + /// + public UUID GroupID + { + get + { + return _groupID; + } + set + { + _groupID = value; + } + } + + /// + /// Returns true if the Land Parcel is owned by a group + /// + public bool IsGroupOwned + { + get + { + return _isGroupOwned; + } + set + { + _isGroupOwned = value; + } + } + + /// + /// jp2 data for the image representative of the parcel in the parcel dialog + /// + public byte[] Bitmap + { + get + { + return _bitmap; + } + set + { + _bitmap = value; + } + } + + /// + /// Parcel Description + /// + public string Description + { + get + { + return _description; + } + set + { + _description = value; + } + } + + /// + /// Parcel settings. Access flags, Fly, NoPush, Voice, Scripts allowed, etc. ParcelFlags + /// + public uint Flags + { + get + { + return _flags; + } + set + { + _flags = value; + } + } + + /// + /// Determines if people are able to teleport where they please on the parcel or if they + /// get constrainted to a specific point on teleport within the parcel + /// + public byte LandingType + { + get + { + return _landingType; + } + set + { + _landingType = value; + } + } + + /// + /// Parcel Name + /// + public string Name + { + get + { + return _name; + } + set + { + _name = value; + } + } + + /// + /// Status of Parcel, Leased, Abandoned, For Sale + /// + public ParcelStatus Status + { + get + { + return _status; + } + set + { + _status = value; + } + } + + /// + /// Internal ID of the parcel. Sometimes the client will try to use this value + /// + public int LocalID + { + get + { + return _localID; + } + set + { + _localID = value; + } + } + + /// + /// Determines if we scale the media based on the surface it's on + /// + public byte MediaAutoScale + { + get + { + return _mediaAutoScale; + } + set + { + _mediaAutoScale = value; + } + } + + /// + /// Texture Guid to replace with the output of the media stream + /// + public UUID MediaID + { + get + { + return _mediaID; + } + set + { + _mediaID = value; + } + } + + /// + /// URL to the media file to display + /// + public string MediaURL + { + get + { + return _mediaURL; + } + set + { + _mediaURL = value; + } + } + + public string MediaType + { + get + { + return _mediaType; + } + set + { + _mediaType = value; + } + } + + /// + /// URL to the shoutcast music stream to play on the parcel + /// + public string MusicURL + { + get + { + return _musicURL; + } + set + { + _musicURL = value; + } + } + + /// + /// Owner Avatar or Group of the parcel. Naturally, all land masses must be + /// owned by someone + /// + public UUID OwnerID + { + get + { + return _ownerID; + } + set + { + _ownerID = value; + } + } + + /// + /// List of access data for the parcel. User data, some bitflags, and a time + /// + public List ParcelAccessList + { + get + { + return _parcelAccessList; + } + set + { + _parcelAccessList = value; + } + } + + /// + /// How long in hours a Pass to the parcel is given + /// + public float PassHours + { + get + { + return _passHours; + } + set + { + _passHours = value; + } + } + + /// + /// Price to purchase a Pass to a restricted parcel + /// + public int PassPrice + { + get + { + return _passPrice; + } + set + { + _passPrice = value; + } + } + + /// + /// When the parcel is being sold, this is the price to purchase the parcel + /// + public int SalePrice + { + get + { + return _salePrice; + } + set + { + _salePrice = value; + } + } + + /// + /// Number of meters^2 in the Simulator + /// + [XmlIgnore] + public int SimwideArea + { + get + { + return _simwideArea; + } + set + { + _simwideArea = value; + } + } + + /// + /// Number of SceneObjectPart in the Simulator + /// + [XmlIgnore] + public int SimwidePrims + { + get + { + return _simwidePrims; + } + set + { + _simwidePrims = value; + } + } + + /// + /// ID of the snapshot used in the client parcel dialog of the parcel + /// + public UUID SnapshotID + { + get + { + return _snapshotID; + } + set + { + _snapshotID = value; + } + } + + /// + /// When teleporting is restricted to a certain point, this is the location + /// that the user will be redirected to + /// + public Vector3 UserLocation + { + get + { + return _userLocation; + } + set + { + _userLocation = value; + } + } + + /// + /// When teleporting is restricted to a certain point, this is the rotation + /// that the user will be positioned + /// + public Vector3 UserLookAt + { + get + { + return _userLookAt; + } + set + { + _userLookAt = value; + } + } + + /// + /// Autoreturn number of minutes to return SceneObjectGroup that are owned by someone who doesn't own + /// the parcel and isn't set to the same 'group' as the parcel. + /// + public int OtherCleanTime + { + get + { + return _otherCleanTime; + } + set + { + _otherCleanTime = value; + } + } + + /// + /// parcel media description + /// + public string MediaDescription + { + get + { + return _mediaDescription; + } + set + { + _mediaDescription = value; + } + } + + public LandData() + { + _globalID = UUID.Random(); + } + + /// + /// Make a new copy of the land data + /// + /// + public LandData Copy() + { + LandData landData = new LandData(); + + landData._AABBMax = _AABBMax; + landData._AABBMin = _AABBMin; + landData._area = _area; + landData._auctionID = _auctionID; + landData._authBuyerID = _authBuyerID; + landData._category = _category; + landData._claimDate = _claimDate; + landData._claimPrice = _claimPrice; + landData._globalID = _globalID; + landData._groupID = _groupID; + landData._isGroupOwned = _isGroupOwned; + landData._localID = _localID; + landData._landingType = _landingType; + landData._mediaAutoScale = _mediaAutoScale; + landData._mediaID = _mediaID; + landData._mediaURL = _mediaURL; + landData._musicURL = _musicURL; + landData._ownerID = _ownerID; + landData._bitmap = (byte[])_bitmap.Clone(); + landData._description = _description; + landData._flags = _flags; + landData._name = _name; + landData._status = _status; + landData._passHours = _passHours; + landData._passPrice = _passPrice; + landData._salePrice = _salePrice; + landData._snapshotID = _snapshotID; + landData._userLocation = _userLocation; + landData._userLookAt = _userLookAt; + landData._otherCleanTime = _otherCleanTime; + landData._mediaType = _mediaType; + landData._mediaDescription = _mediaDescription; + landData._mediaWidth = _mediaWidth; + landData._mediaHeight = _mediaHeight; + landData._mediaLoop = _mediaLoop; + landData._obscureMusic = _obscureMusic; + landData._obscureMedia = _obscureMedia; + landData._simwideArea = _simwideArea; + landData._simwidePrims = _simwidePrims; + landData._dwell = _dwell; + + landData._parcelAccessList.Clear(); + foreach (LandAccessEntry entry in _parcelAccessList) + { + LandAccessEntry newEntry = new LandAccessEntry(); + newEntry.AgentID = entry.AgentID; + newEntry.Flags = entry.Flags; + newEntry.Expires = entry.Expires; + + landData._parcelAccessList.Add(newEntry); + } + + return landData; + } + + public void ToXml(XmlWriter xmlWriter) + { + serializer.Serialize(xmlWriter, this); + } + + /// + /// Restore a LandData object from the serialized xml representation. + /// + /// + /// + public static LandData FromXml(XmlReader xmlReader) + { + LandData land = (LandData)serializer.Deserialize(xmlReader); + + return land; + } + } +} diff --git a/OpenSim/Framework/LandStatReportItem.cs b/OpenSim/Framework/LandStatReportItem.cs new file mode 100644 index 0000000000..9f92ab4b8a --- /dev/null +++ b/OpenSim/Framework/LandStatReportItem.cs @@ -0,0 +1,50 @@ +/* + * 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 +{ + public class LandStatReportItem + { + public float LocationX; + public float LocationY; + public float LocationZ; + public string OwnerName; + public float Score; + public UUID TaskID; + public uint TaskLocalID; + public string TaskName; + + public LandStatReportItem() + { + + } + + } +} diff --git a/OpenSim/Framework/LandUpdateArgs.cs b/OpenSim/Framework/LandUpdateArgs.cs new file mode 100644 index 0000000000..7d6c4f2dab --- /dev/null +++ b/OpenSim/Framework/LandUpdateArgs.cs @@ -0,0 +1,60 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class LandUpdateArgs : EventArgs + { + public UUID AuthBuyerID; + public ParcelCategory Category; + public string Desc; + public UUID GroupID; + public byte LandingType; + public byte MediaAutoScale; + public UUID MediaID; + public string MediaURL; + public string MusicURL; + public string Name; + public uint ParcelFlags; + public float PassHours; + public int PassPrice; + public int SalePrice; + public UUID SnapshotID; + public Vector3 UserLocation; + public Vector3 UserLookAt; + public string MediaType; + public string MediaDescription; + public int MediaHeight; + public int MediaWidth; + public bool MediaLoop; + public bool ObscureMusic; + public bool ObscureMedia; + } +} diff --git a/OpenSim/Framework/Lazy.cs b/OpenSim/Framework/Lazy.cs new file mode 100644 index 0000000000..91de4bdd1d --- /dev/null +++ b/OpenSim/Framework/Lazy.cs @@ -0,0 +1,236 @@ +// +// Lazy.cs +// +// Authors: +// Zoltan Varga (vargaz@gmail.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright (C) 2009 Novell +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.Serialization; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Threading; +using System.Diagnostics; + +namespace OpenSim.Framework +{ + public enum LazyThreadSafetyMode + { + None, + PublicationOnly, + ExecutionAndPublication + } + + [SerializableAttribute] + [ComVisibleAttribute(false)] + [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] + public class Lazy + { + T value; + bool inited; + LazyThreadSafetyMode mode; + Func factory; + object monitor; + Exception exception; + + public Lazy() + : this(LazyThreadSafetyMode.ExecutionAndPublication) + { + } + + public Lazy(Func valueFactory) + : this(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication) + { + } + + public Lazy(bool isThreadSafe) + : this(() => Activator.CreateInstance(), isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) + { + } + + public Lazy(Func valueFactory, bool isThreadSafe) + : this(valueFactory, isThreadSafe ? LazyThreadSafetyMode.ExecutionAndPublication : LazyThreadSafetyMode.None) + { + } + + public Lazy(LazyThreadSafetyMode mode) + : this(() => Activator.CreateInstance(), mode) + { + } + + public Lazy(Func valueFactory, LazyThreadSafetyMode mode) + { + if (valueFactory == null) + throw new ArgumentNullException("valueFactory"); + this.factory = valueFactory; + if (mode != LazyThreadSafetyMode.None) + monitor = new object(); + this.mode = mode; + } + + // Don't trigger expensive initialization + [DebuggerBrowsable(DebuggerBrowsableState.Never)] + public T Value + { + get + { + if (inited) + return value; + if (exception != null) + throw exception; + + return InitValue(); + } + } + + T InitValue() + { + switch (mode) + { + case LazyThreadSafetyMode.None: + { + var init_factory = factory; + if (init_factory == null) + throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch (Exception ex) + { + exception = ex; + throw; + } + break; + } + case LazyThreadSafetyMode.PublicationOnly: + { + var init_factory = factory; + T v; + + //exceptions are ignored + if (init_factory != null) + v = init_factory(); + else + v = default(T); + + lock (monitor) + { + if (inited) + return value; + value = v; + Thread.MemoryBarrier(); + inited = true; + factory = null; + } + break; + } + case LazyThreadSafetyMode.ExecutionAndPublication: + { + lock (monitor) + { + if (inited) + return value; + + if (factory == null) + throw exception = new InvalidOperationException("The initialization function tries to access Value on this instance"); + + var init_factory = factory; + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch (Exception ex) + { + exception = ex; + throw; + } + } + break; + } + default: + throw new InvalidOperationException("Invalid LazyThreadSafetyMode " + mode); + } + + if (monitor == null) + { + value = factory(); + inited = true; + } + else + { + lock (monitor) + { + if (inited) + return value; + + if (factory == null) + throw new InvalidOperationException("The initialization function tries to access Value on this instance"); + + var init_factory = factory; + try + { + factory = null; + T v = init_factory(); + value = v; + Thread.MemoryBarrier(); + inited = true; + } + catch + { + factory = init_factory; + throw; + } + } + } + + return value; + } + + public bool IsValueCreated + { + get + { + return inited; + } + } + + public override string ToString() + { + if (inited) + return value.ToString(); + else + return "Value is not created"; + } + } +} diff --git a/OpenSim/Framework/Location.cs b/OpenSim/Framework/Location.cs new file mode 100644 index 0000000000..0b888347e7 --- /dev/null +++ b/OpenSim/Framework/Location.cs @@ -0,0 +1,109 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + [Serializable] + public class Location : ICloneable + { + private readonly uint m_x; + private readonly uint m_y; + + public Location(uint x, uint y) + { + m_x = x; + m_y = y; + } + + public Location(ulong regionHandle) + { + m_x = (uint)(regionHandle >> 32); + m_y = (uint)(regionHandle & (ulong)uint.MaxValue); + } + + public ulong RegionHandle + { + get { return Utils.UIntsToLong(m_x, m_y); } + } + + public uint X + { + get { return m_x; } + } + + public uint Y + { + get { return m_y; } + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(obj, this)) + return true; + + if (obj is Location) + { + return Equals((Location) obj); + } + + return base.Equals(obj); + } + + public bool Equals(Location loc) + { + return loc.X == X && loc.Y == Y; + } + + public bool Equals(int x, int y) + { + return X == x && y == Y; + } + + public static bool operator ==(Location o, object o2) + { + return o.Equals(o2); + } + + public static bool operator !=(Location o, object o2) + { + return !o.Equals(o2); + } + + public override int GetHashCode() + { + return X.GetHashCode() ^ Y.GetHashCode(); + } + + public object Clone() + { + return new Location(X, Y); + } + } +} diff --git a/OpenSim/Framework/LocklessQueue.cs b/OpenSim/Framework/LocklessQueue.cs new file mode 100644 index 0000000000..84f887cc4d --- /dev/null +++ b/OpenSim/Framework/LocklessQueue.cs @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2009, openmetaverse.org + * All rights reserved. + * + * - 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. + * - Neither the name of the openmetaverse.org 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading; + +namespace OpenSim.Framework +{ + public sealed class LocklessQueue + { + private sealed class SingleLinkNode + { + public SingleLinkNode Next; + public T Item; + } + + SingleLinkNode head; + SingleLinkNode tail; + int count; + + public int Count { get { return count; } } + + public LocklessQueue() + { + Init(); + } + + public void Enqueue(T item) + { + SingleLinkNode oldTail = null; + SingleLinkNode oldTailNext; + + SingleLinkNode newNode = new SingleLinkNode(); + newNode.Item = item; + + bool newNodeWasAdded = false; + + while (!newNodeWasAdded) + { + oldTail = tail; + oldTailNext = oldTail.Next; + + if (tail == oldTail) + { + if (oldTailNext == null) + newNodeWasAdded = CAS(ref tail.Next, null, newNode); + else + CAS(ref tail, oldTail, oldTailNext); + } + } + + CAS(ref tail, oldTail, newNode); + Interlocked.Increment(ref count); + } + + public bool Dequeue(out T item) + { + item = default(T); + SingleLinkNode oldHead = null; + bool haveAdvancedHead = false; + + while (!haveAdvancedHead) + { + oldHead = head; + SingleLinkNode oldTail = tail; + SingleLinkNode oldHeadNext = oldHead.Next; + + if (oldHead == head) + { + if (oldHead == oldTail) + { + if (oldHeadNext == null) + return false; + + CAS(ref tail, oldTail, oldHeadNext); + } + else + { + item = oldHeadNext.Item; + haveAdvancedHead = CAS(ref head, oldHead, oldHeadNext); + if (haveAdvancedHead) + { + oldHeadNext.Item = default(T); + oldHead.Next = null; + } + } + } + } + + Interlocked.Decrement(ref count); + return true; + } + + public void Clear() + { + // ugly + T item; + while(count > 0) + Dequeue(out item); + Init(); + } + + private void Init() + { + count = 0; + head = tail = new SingleLinkNode(); + } + + private static bool CAS(ref SingleLinkNode location, SingleLinkNode comparand, SingleLinkNode newValue) + { + return + (object)comparand == + (object)Interlocked.CompareExchange(ref location, newValue, comparand); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Login.cs b/OpenSim/Framework/Login.cs new file mode 100644 index 0000000000..54a6654685 --- /dev/null +++ b/OpenSim/Framework/Login.cs @@ -0,0 +1,52 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class Login + { + public UUID Agent; + public UUID BaseFolder; + public string CapsPath = String.Empty; + public uint CircuitCode; + public string First = "Test"; + public UUID InventoryFolder; + public string Last = "User"; + public UUID SecureSession = UUID.Zero; + public UUID Session; + public Vector3 StartPos; + public AvatarAppearance Appearance; + + public Login() + { + StartPos = new Vector3(128, 128, 70); + } + } +} diff --git a/OpenSim/Framework/MainConsole.cs b/OpenSim/Framework/MainConsole.cs new file mode 100644 index 0000000000..4527b68a75 --- /dev/null +++ b/OpenSim/Framework/MainConsole.cs @@ -0,0 +1,40 @@ +/* + * 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 +{ + public class MainConsole + { + private static ICommandConsole instance; + + public static ICommandConsole Instance + { + get { return instance; } + set { instance = value; } + } + } +} diff --git a/OpenSim/Framework/MapAndArray.cs b/OpenSim/Framework/MapAndArray.cs new file mode 100644 index 0000000000..c98d3ccd6f --- /dev/null +++ b/OpenSim/Framework/MapAndArray.cs @@ -0,0 +1,189 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Stores two synchronized collections: a mutable dictionary and an + /// immutable array. Slower inserts/removes than a normal dictionary, + /// but provides safe iteration while maintaining fast hash lookups + /// + /// Key type to use for hash lookups + /// Value type to store + public sealed class MapAndArray + { + private Dictionary m_dict; + private TValue[] m_array; + private object m_syncRoot = new object(); + + /// Number of values currently stored in the collection + public int Count { get { return m_array.Length; } } + /// NOTE: This collection is thread safe. You do not need to + /// acquire a lock to add, remove, or enumerate entries. This + /// synchronization object should only be locked for larger + /// transactions + public object SyncRoot { get { return m_syncRoot; } } + + /// + /// Constructor + /// + public MapAndArray() + { + m_dict = new Dictionary(); + m_array = new TValue[0]; + } + + /// + /// Constructor + /// + /// Initial capacity of the dictionary + public MapAndArray(int capacity) + { + m_dict = new Dictionary(capacity); + m_array = new TValue[0]; + } + + /// + /// Adds a key/value pair to the collection, or updates an existing key + /// with a new value + /// + /// Key to add or update + /// Value to add + /// True if a new key was added, false if an existing key was + /// updated + public bool AddOrReplace(TKey key, TValue value) + { + lock (m_syncRoot) + { + bool containedKey = m_dict.ContainsKey(key); + + m_dict[key] = value; + CreateArray(); + + return !containedKey; + } + } + + /// + /// Adds a key/value pair to the collection. This will throw an + /// exception if the key is already present in the collection + /// + /// Key to add or update + /// Value to add + /// Index of the inserted item + public int Add(TKey key, TValue value) + { + lock (m_syncRoot) + { + m_dict.Add(key, value); + CreateArray(); + return m_array.Length; + } + } + + /// + /// Removes a key/value pair from the collection + /// + /// Key to remove + /// True if the key was found and removed, otherwise false + public bool Remove(TKey key) + { + lock (m_syncRoot) + { + bool removed = m_dict.Remove(key); + CreateArray(); + + return removed; + } + } + + /// + /// Determines whether the collections contains a specified key + /// + /// Key to search for + /// True if the key was found, otherwise false + public bool ContainsKey(TKey key) + { + lock (m_syncRoot) + return m_dict.ContainsKey(key); + } + + /// + /// Gets the value associated with the specified key + /// + /// Key of the value to get + /// Will contain the value associated with the + /// given key if the key is found. If the key is not found it will + /// contain the default value for the type of the value parameter + /// True if the key was found and a value was retrieved, + /// otherwise false + public bool TryGetValue(TKey key, out TValue value) + { + lock (m_syncRoot) + return m_dict.TryGetValue(key, out value); + } + + /// + /// Clears all key/value pairs from the collection + /// + public void Clear() + { + lock (m_syncRoot) + { + m_dict = new Dictionary(); + m_array = new TValue[0]; + } + } + + /// + /// Gets a reference to the immutable array of values stored in this + /// collection. This array is thread safe for iteration + /// + /// A thread safe reference ton an array of all of the stored + /// values + public TValue[] GetArray() + { + return m_array; + } + + private void CreateArray() + { + // Rebuild the array from the dictionary. This method must be + // called from inside a lock + TValue[] array = new TValue[m_dict.Count]; + int i = 0; + + foreach (TValue value in m_dict.Values) + array[i++] = value; + + m_array = array; + } + } +} diff --git a/OpenSim/Framework/MapBlockData.cs b/OpenSim/Framework/MapBlockData.cs new file mode 100644 index 0000000000..4bee4995f3 --- /dev/null +++ b/OpenSim/Framework/MapBlockData.cs @@ -0,0 +1,66 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + public class MapBlockData + { + public byte Access; + public byte Agents; + public UUID MapImageId; + public String Name; + public uint RegionFlags; + public byte WaterHeight; + public ushort X; + public ushort Y; + public ushort SizeX; + public ushort SizeY; + + public MapBlockData() + { + } + + public OSDMap ToOSD() + { + OSDMap map = new OSDMap(); + map["X"] = X; + map["Y"] = Y; + map["SizeX"] = SizeX; + map["SizeY"] = SizeY; + map["Name"] = Name; + map["Access"] = Access; + map["RegionFlags"] = RegionFlags; + map["WaterHeight"] = WaterHeight; + map["MapImageID"] = MapImageId; + return map; + } + } +} diff --git a/OpenSim/Framework/MapItemReplyStruct.cs b/OpenSim/Framework/MapItemReplyStruct.cs new file mode 100644 index 0000000000..c8693ae4d2 --- /dev/null +++ b/OpenSim/Framework/MapItemReplyStruct.cs @@ -0,0 +1,74 @@ +/* + * 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; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + public struct mapItemReply + { + public uint x; + public uint y; + public UUID id; + public int Extra; + public int Extra2; + public string name; + + public mapItemReply(uint pX, uint pY, UUID pId, string pName, int pExt1, int pExt2) + { + x = pX; + y = pY; + id = pId; + name = pName; + Extra = pExt1; + Extra2 = pExt2; + } + + public OSDMap ToOSD() + { + OSDMap map = new OSDMap(); + map["X"] = OSD.FromInteger((int)x); + map["Y"] = OSD.FromInteger((int)y); + map["ID"] = OSD.FromUUID(id); + map["Name"] = OSD.FromString(name); + map["Extra"] = OSD.FromInteger(Extra); + map["Extra2"] = OSD.FromInteger(Extra2); + return map; + } + + public void FromOSD(OSDMap map) + { + x = (uint) map["X"].AsInteger(); + y = (uint) map["Y"].AsInteger(); + id = map["ID"].AsUUID(); + Extra = map["Extra"].AsInteger(); + Extra2 = map["Extra2"].AsInteger(); + name = map["Name"].AsString(); + } + } +} diff --git a/OpenSim/Framework/MetricsCollector.cs b/OpenSim/Framework/MetricsCollector.cs new file mode 100644 index 0000000000..c8f4a33260 --- /dev/null +++ b/OpenSim/Framework/MetricsCollector.cs @@ -0,0 +1,223 @@ +using System; +using System.Diagnostics; + +namespace OpenSim.Framework +{ + /// + /// A MetricsCollector for 'long' values. + /// + public class MetricsCollectorLong : MetricsCollector + { + public MetricsCollectorLong(int windowSize, int numBuckets) + : base(windowSize, numBuckets) + { + } + + protected override long GetZero() { return 0; } + + protected override long Add(long a, long b) { return a + b; } + } + + + /// + /// A MetricsCollector for time spans. + /// + public class MetricsCollectorTime : MetricsCollectorLong + { + public MetricsCollectorTime(int windowSize, int numBuckets) + : base(windowSize, numBuckets) + { + } + + public void AddSample(Stopwatch timer) + { + long ticks = timer.ElapsedTicks; + if (ticks > 0) + AddSample(ticks); + } + + public TimeSpan GetSumTime() + { + return TimeSpan.FromMilliseconds((GetSum() * 1000) / Stopwatch.Frequency); + } + } + + + struct MetricsBucket + { + public T value; + public int count; + } + + + /// + /// Collects metrics in a sliding window. + /// + /// + /// MetricsCollector provides the current Sum of the metrics that it collects. It can easily be extended + /// to provide the Average, too. It uses a sliding window to keep these values current. + /// + /// This class is not thread-safe. + /// + /// Subclass MetricsCollector to have it use a concrete value type. Override the abstract methods. + /// + public abstract class MetricsCollector + { + private int bucketSize; // e.g. 3,000 ms + + private MetricsBucket[] buckets; + + private int NumBuckets { get { return buckets.Length; } } + + + // The number of the current bucket, if we had an infinite number of buckets and didn't have to wrap around + long curBucketGlobal; + + // The total of all the buckets + T totalSum; + int totalCount; + + + /// + /// Returns the default (zero) value. + /// + /// + protected abstract T GetZero(); + + /// + /// Adds two values. + /// + protected abstract T Add(T a, T b); + + + /// + /// Creates a MetricsCollector. + /// + /// The period of time over which to collect the metrics, in ms. E.g.: 30,000. + /// The number of buckets to divide the samples into. E.g.: 10. Using more buckets + /// smooths the jarring that occurs whenever we drop an old bucket, but uses more memory. + public MetricsCollector(int windowSize, int numBuckets) + { + bucketSize = windowSize / numBuckets; + buckets = new MetricsBucket[numBuckets]; + Reset(); + } + + public void Reset() + { + ZeroBuckets(0, NumBuckets); + curBucketGlobal = GetNow() / bucketSize; + totalSum = GetZero(); + totalCount = 0; + } + + public void AddSample(T sample) + { + MoveWindow(); + + int curBucket = (int)(curBucketGlobal % NumBuckets); + buckets[curBucket].value = Add(buckets[curBucket].value, sample); + buckets[curBucket].count++; + + totalSum = Add(totalSum, sample); + totalCount++; + } + + /// + /// Returns the total values in the collection window. + /// + public T GetSum() + { + // It might have been a while since we last added a sample, so we may need to adjust the window + MoveWindow(); + + return totalSum; + } + + /// + /// Returns the current time in ms. + /// + private long GetNow() + { + return DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + } + + /// + /// Clears the values in buckets [offset, offset+num) + /// + private void ZeroBuckets(int offset, int num) + { + for (int i = 0; i < num; i++) + { + buckets[offset + i].value = GetZero(); + buckets[offset + i].count = 0; + } + } + + /// + /// Adjusts the buckets so that the "current bucket" corresponds to the current time. + /// This may require dropping old buckets. + /// + /// + /// This method allows for the possibility that we don't get new samples for each bucket, so the + /// new bucket may be some distance away from the last used bucket. + /// + private void MoveWindow() + { + long newBucketGlobal = GetNow() / bucketSize; + long bucketsDistance = newBucketGlobal - curBucketGlobal; + + if (bucketsDistance == 0) + { + // We're still on the same bucket as before + return; + } + + if (bucketsDistance >= NumBuckets) + { + // Discard everything + Reset(); + return; + } + + int curBucket = (int)(curBucketGlobal % NumBuckets); + int newBucket = (int)(newBucketGlobal % NumBuckets); + + + // Clear all the buckets in this range: (cur, new] + int numToClear = (int)bucketsDistance; + + if (curBucket < NumBuckets - 1) + { + // Clear buckets at the end of the window + int num = Math.Min((int)bucketsDistance, NumBuckets - (curBucket + 1)); + ZeroBuckets(curBucket + 1, num); + numToClear -= num; + } + + if (numToClear > 0) + { + // Clear buckets at the beginning of the window + ZeroBuckets(0, numToClear); + } + + // Move the "current bucket" pointer + curBucketGlobal = newBucketGlobal; + + RecalcTotal(); + } + + private void RecalcTotal() + { + totalSum = GetZero(); + totalCount = 0; + + for (int i = 0; i < NumBuckets; i++) + { + totalSum = Add(totalSum, buckets[i].value); + totalCount += buckets[i].count; + } + } + + } +} diff --git a/OpenSim/Framework/MinHeap.cs b/OpenSim/Framework/MinHeap.cs new file mode 100644 index 0000000000..99ac25d09d --- /dev/null +++ b/OpenSim/Framework/MinHeap.cs @@ -0,0 +1,406 @@ +/* + * 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.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace OpenSim.Framework +{ + public interface IHandle { } + + [Serializable, ComVisible(false)] + public class MinHeap : ICollection, ICollection + { + private class Handle : IHandle + { + internal int index = -1; + internal MinHeap heap = null; + + internal void Clear() + { + this.index = -1; + this.heap = null; + } + } + + private struct HeapItem + { + internal T value; + internal Handle handle; + + internal HeapItem(T value, Handle handle) + { + this.value = value; + this.handle = handle; + } + + internal void Clear() + { + if (this.handle != null) + this.handle.Clear(); + ClearRef(); + } + + internal void ClearRef() + { + this.value = default(T); + this.handle = null; + } + } + + public const int DEFAULT_CAPACITY = 4; + + private HeapItem[] items; + private int size; + private object sync_root; + private int version; + + private Comparison comparison; + + public MinHeap() : this(DEFAULT_CAPACITY, Comparer.Default) { } + public MinHeap(int capacity) : this(capacity, Comparer.Default) { } + public MinHeap(IComparer comparer) : this(DEFAULT_CAPACITY, comparer) { } + public MinHeap(int capacity, IComparer comparer) : + this(capacity, new Comparison(comparer.Compare)) { } + public MinHeap(Comparison comparison) : this(DEFAULT_CAPACITY, comparison) { } + public MinHeap(int capacity, Comparison comparison) + { + this.items = new HeapItem[capacity]; + this.comparison = comparison; + this.size = this.version = 0; + } + + public int Count { get { return this.size; } } + + public bool IsReadOnly { get { return false; } } + + public bool IsSynchronized { get { return false; } } + + public T this[IHandle key] + { + get + { + Handle handle = ValidateThisHandle(key); + return this.items[handle.index].value; + } + + set + { + Handle handle = ValidateThisHandle(key); + this.items[handle.index].value = value; + if (!BubbleUp(handle.index)) + BubbleDown(handle.index); + } + } + + public object SyncRoot + { + get + { + if (this.sync_root == null) + Interlocked.CompareExchange(ref this.sync_root, new object(), null); + return this.sync_root; + } + } + + private Handle ValidateHandle(IHandle ihandle) + { + if (ihandle == null) + throw new ArgumentNullException("handle"); + Handle handle = ihandle as Handle; + if (handle == null) + throw new InvalidOperationException("handle is not valid"); + return handle; + } + + private Handle ValidateThisHandle(IHandle ihandle) + { + Handle handle = ValidateHandle(ihandle); + if (!object.ReferenceEquals(handle.heap, this)) + throw new InvalidOperationException("handle is not valid for this heap"); + if (handle.index < 0) + throw new InvalidOperationException("handle is not associated to a value"); + return handle; + } + + private void Set(HeapItem item, int index) + { + this.items[index] = item; + if (item.handle != null) + item.handle.index = index; + } + + private bool BubbleUp(int index) + { + HeapItem item = this.items[index]; + int current, parent; + + for (current = index, parent = (current - 1) / 2; + (current > 0) && (this.comparison(this.items[parent].value, item.value)) > 0; + current = parent, parent = (current - 1) / 2) + { + Set(this.items[parent], current); + } + + if (current != index) + { + Set(item, current); + ++this.version; + return true; + } + return false; + } + + private void BubbleDown(int index) + { + HeapItem item = this.items[index]; + int current, child; + + for (current = index, child = (2 * current) + 1; + current < this.size / 2; + current = child, child = (2 * current) + 1) + { + if ((child < this.size - 1) && this.comparison(this.items[child].value, this.items[child + 1].value) > 0) + ++child; + if (this.comparison(this.items[child].value, item.value) >= 0) + break; + Set(this.items[child], current); + } + + if (current != index) + { + Set(item, current); + ++this.version; + } + } + + public bool TryGetValue(IHandle key, out T value) + { + Handle handle = ValidateHandle(key); + if (handle.index > -1) + { + value = this.items[handle.index].value; + return true; + } + value = default(T); + return false; + } + + public bool ContainsHandle(IHandle ihandle) + { + Handle handle = ValidateHandle(ihandle); + return object.ReferenceEquals(handle.heap, this) && handle.index > -1; + } + + public void Add(T value, ref IHandle handle) + { + if (handle == null) + handle = new Handle(); + Add(value, handle); + } + + public void Add(T value, IHandle ihandle) + { + if (this.size == this.items.Length) + { + int capacity = (int)((this.items.Length * 200L) / 100L); + if (capacity < (this.items.Length + DEFAULT_CAPACITY)) + capacity = this.items.Length + DEFAULT_CAPACITY; + Array.Resize(ref this.items, capacity); + } + + Handle handle = null; + if (ihandle != null) + { + handle = ValidateHandle(ihandle); + handle.heap = this; + } + + HeapItem item = new MinHeap.HeapItem(value, handle); + + Set(item, this.size); + BubbleUp(this.size++); + } + + public void Add(T value) + { + Add(value, null); + } + + public T Min() + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + + return this.items[0].value; + } + + public void Clear() + { + for (int index = 0; index < this.size; ++index) + this.items[index].Clear(); + this.size = 0; + ++this.version; + } + + public void TrimExcess() + { + int length = (int)(this.items.Length * 0.9); + if (this.size < length) + Array.Resize(ref this.items, Math.Min(this.size, DEFAULT_CAPACITY)); + } + + private void RemoveAt(int index) + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + if (index >= this.size) + throw new ArgumentOutOfRangeException("index"); + + this.items[index].Clear(); + if (--this.size > 0 && index != this.size) + { + Set(this.items[this.size], index); + this.items[this.size].ClearRef(); + if (!BubbleUp(index)) + BubbleDown(index); + } + } + + public T RemoveMin() + { + if (this.size == 0) + throw new InvalidOperationException("Heap is empty"); + + HeapItem item = this.items[0]; + RemoveAt(0); + return item.value; + } + + public T Remove(IHandle ihandle) + { + Handle handle = ValidateThisHandle(ihandle); + HeapItem item = this.items[handle.index]; + RemoveAt(handle.index); + return item.value; + } + + private int GetIndex(T value) + { + EqualityComparer comparer = EqualityComparer.Default; + int index; + + for (index = 0; index < this.size; ++index) + { + if (comparer.Equals(this.items[index].value, value)) + return index; + } + return -1; + } + + public bool Contains(T value) + { + return GetIndex(value) != -1; + } + + public bool Remove(T value) + { + int index = GetIndex(value); + if (index != -1) + { + RemoveAt(index); + return true; + } + return false; + } + + public void CopyTo(T[] array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Rank != 1) + throw new ArgumentException("Multidimensional array not supported"); + if (array.GetLowerBound(0) != 0) + throw new ArgumentException("Non-zero lower bound array not supported"); + + int length = array.Length; + if ((index < 0) || (index > length)) + throw new ArgumentOutOfRangeException("index"); + if ((length - index) < this.size) + throw new ArgumentException("Not enough space available in array starting at index"); + + for (int i = 0; i < this.size; ++i) + array[index + i] = this.items[i].value; + } + + public void CopyTo(Array array, int index) + { + if (array == null) + throw new ArgumentNullException("array"); + if (array.Rank != 1) + throw new ArgumentException("Multidimensional array not supported"); + if (array.GetLowerBound(0) != 0) + throw new ArgumentException("Non-zero lower bound array not supported"); + + int length = array.Length; + if ((index < 0) || (index > length)) + throw new ArgumentOutOfRangeException("index"); + if ((length - index) < this.size) + throw new ArgumentException("Not enough space available in array starting at index"); + + try + { + for (int i = 0; i < this.size; ++i) + array.SetValue(this.items[i].value, index + i); + } + catch (ArrayTypeMismatchException) + { + throw new ArgumentException("Invalid array type"); + } + } + + public IEnumerator GetEnumerator() + { + int version = this.version; + + for (int index = 0; index < this.size; ++index) + { + if (version != this.version) + throw new InvalidOperationException("Heap was modified while enumerating"); + yield return this.items[index].value; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs new file mode 100644 index 0000000000..6a0f676b42 --- /dev/null +++ b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs @@ -0,0 +1,130 @@ +/* + * 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.Timers; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Asset service statistics collection + /// + public class AssetStatsCollector : BaseStatsCollector + { + private Timer ageStatsTimer = new Timer(24 * 60 * 60 * 1000); + private DateTime startTime = DateTime.Now; + + private long assetRequestsToday; + private long assetRequestsNotFoundToday; + private long assetRequestsYesterday; + private long assetRequestsNotFoundYesterday; + + public long AssetRequestsToday { get { return assetRequestsToday; } } + public long AssetRequestsNotFoundToday { get { return assetRequestsNotFoundToday; } } + public long AssetRequestsYesterday { get { return assetRequestsYesterday; } } + public long AssetRequestsNotFoundYesterday { get { return assetRequestsNotFoundYesterday; } } + + public AssetStatsCollector() + { + ageStatsTimer.Elapsed += new ElapsedEventHandler(OnAgeing); + ageStatsTimer.Enabled = true; + } + + private void OnAgeing(object source, ElapsedEventArgs e) + { + assetRequestsYesterday = assetRequestsToday; + + // There is a possibility that an asset request could occur between the execution of these + // two statements. But we're better off without the synchronization overhead. + assetRequestsToday = 0; + + assetRequestsNotFoundYesterday = assetRequestsNotFoundToday; + assetRequestsNotFoundToday = 0; + } + + /// + /// Record that an asset request failed to find an asset + /// + public void AddNotFoundRequest() + { + assetRequestsNotFoundToday++; + } + + /// + /// Record that a request was made to the asset server + /// + public void AddRequest() + { + assetRequestsToday++; + } + + /// + /// Report back collected statistical information. + /// + /// + override public string Report() + { + double elapsedHours = (DateTime.Now - startTime).TotalHours; + if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero + + long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours); + long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0); + + return string.Format( +@"Asset requests today : {0} ({1} per hour) of which {2} were not found +Asset requests yesterday : {3} ({4} per hour) of which {5} were not found", + AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday, + AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday); + } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + double elapsedHours = (DateTime.Now - startTime).TotalHours; + if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero + + long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours); + long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0); + + OSDMap ret = new OSDMap(); + ret.Add("AssetRequestsToday", OSD.FromLong(AssetRequestsToday)); + ret.Add("AssetRequestsTodayPerHour", OSD.FromLong(assetRequestsTodayPerHour)); + ret.Add("AssetRequestsNotFoundToday", OSD.FromLong(AssetRequestsNotFoundToday)); + ret.Add("AssetRequestsYesterday", OSD.FromLong(AssetRequestsYesterday)); + ret.Add("AssetRequestsYesterdayPerHour", OSD.FromLong(assetRequestsYesterdayPerHour)); + ret.Add("AssetRequestsNotFoundYesterday", OSD.FromLong(assetRequestsNotFoundYesterday)); + + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs new file mode 100644 index 0000000000..20495f6e95 --- /dev/null +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -0,0 +1,78 @@ +/* + * 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.Diagnostics; +using System.Text; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Statistics which all collectors are interested in reporting + /// + public class BaseStatsCollector : IStatsCollector + { + public virtual string Report() + { + StringBuilder sb = new StringBuilder(Environment.NewLine); + sb.Append("MEMORY STATISTICS"); + sb.Append(Environment.NewLine); + + sb.AppendFormat( + "Heap allocated to OpenSim : {0} MB\n", + Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); + + sb.AppendFormat( + "Last heap allocation rate : {0} MB/s\n", + Math.Round((MemoryWatchdog.LastHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); + + sb.AppendFormat( + "Average heap allocation rate: {0} MB/s\n", + Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); + + sb.AppendFormat( + "Process memory : {0} MB\n", + Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024.0 / 1024.0)); + + return sb.ToString(); + } + + public virtual string XReport(string uptime, string version) + { + return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ; + } + + public virtual OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("TotalMemory", new OSDReal(Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/Checks/Check.cs b/OpenSim/Framework/Monitoring/Checks/Check.cs new file mode 100644 index 0000000000..594386a93f --- /dev/null +++ b/OpenSim/Framework/Monitoring/Checks/Check.cs @@ -0,0 +1,118 @@ +/* + * 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.Text; + +namespace OpenSim.Framework.Monitoring +{ + public class Check + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + /// + /// Action used to check whether alert should go off. + /// + /// + /// Should return true if check passes. False otherwise. + /// + public Func CheckFunc { get; private set; } + + /// + /// Message from the last failure, if any. If there is no message or no failure then will be null. + /// + /// + /// Should be set by the CheckFunc when applicable. + /// + public string LastFailureMessage { get; set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + + public Check( + string shortName, + string name, + string description, + string category, + string container, + Func checkFunc, + StatVerbosity verbosity) + { + if (ChecksManager.SubCommands.Contains(category)) + throw new Exception( + string.Format("Alert cannot be in category '{0}' since this is reserved for a subcommand", category)); + + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + throw new Exception(string.Format("Alert name {0} cannot contain character {1}", shortName, c)); + } + + ShortName = shortName; + Name = name; + Description = description; + Category = category; + Container = container; + CheckFunc = checkFunc; + Verbosity = verbosity; + } + + public bool CheckIt() + { + return CheckFunc(this); + } + + public virtual string ToConsoleString() + { + return string.Format( + "{0}.{1}.{2} - {3}", + Category, + Container, + ShortName, + Description); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/ChecksManager.cs b/OpenSim/Framework/Monitoring/ChecksManager.cs new file mode 100644 index 0000000000..e4a7f8ca2f --- /dev/null +++ b/OpenSim/Framework/Monitoring/ChecksManager.cs @@ -0,0 +1,262 @@ +/* + * 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 log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Static class used to register/deregister checks on runtime conditions. + /// + public static class ChecksManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Subcommand used to list other stats. + public const string ListSubCommand = "list"; + + // All subcommands + public static HashSet SubCommands = new HashSet { ListSubCommand }; + + /// + /// Checks categorized by category/container/shortname + /// + /// + /// Do not add or remove directly from this dictionary. + /// + public static SortedDictionary>> RegisteredChecks + = new SortedDictionary>>(); + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "show checks", + "show checks", + "Show checks configured for this server", + "If no argument is specified then info on all checks will be shown.\n" + + "'list' argument will show check categories.\n" + + "THIS FACILITY IS EXPERIMENTAL", + HandleShowchecksCommand); + } + + public static void HandleShowchecksCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length > 2) + { + foreach (string name in cmd.Skip(2)) + { + string[] components = name.Split('.'); + + string categoryName = components[0]; +// string containerName = components.Length > 1 ? components[1] : null; + + if (categoryName == ListSubCommand) + { + con.Output("check categories available are:"); + + foreach (string category in RegisteredChecks.Keys) + con.OutputFormat(" {0}", category); + } +// else +// { +// SortedDictionary> category; +// if (!Registeredchecks.TryGetValue(categoryName, out category)) +// { +// con.OutputFormat("No such category as {0}", categoryName); +// } +// else +// { +// if (String.IsNullOrEmpty(containerName)) +// { +// OutputConfiguredToConsole(con, category); +// } +// else +// { +// SortedDictionary container; +// if (category.TryGetValue(containerName, out container)) +// { +// OutputContainerChecksToConsole(con, container); +// } +// else +// { +// con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); +// } +// } +// } +// } + } + } + else + { + OutputAllChecksToConsole(con); + } + } + + /// + /// Registers a statistic. + /// + /// + /// + public static bool RegisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + // Check name is not unique across category/container/shortname key. + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + if (TryGetCheckParents(check, out category, out container)) + return false; + + // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. + // This means that we don't need to lock or copy them on iteration, which will be a much more + // common operation after startup. + if (container != null) + newContainer = new SortedDictionary(container); + else + newContainer = new SortedDictionary(); + + if (category != null) + newCategory = new SortedDictionary>(category); + else + newCategory = new SortedDictionary>(); + + newContainer[check.ShortName] = check; + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + } + + return true; + } + + /// + /// Deregister an check + /// > + /// + /// + public static bool DeregisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + if (!TryGetCheckParents(check, out category, out container)) + return false; + + newContainer = new SortedDictionary(container); + newContainer.Remove(check.ShortName); + + newCategory = new SortedDictionary>(category); + newCategory.Remove(check.Container); + + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + + return true; + } + } + + public static bool TryGetCheckParents( + Check check, + out SortedDictionary> category, + out SortedDictionary container) + { + category = null; + container = null; + + lock (RegisteredChecks) + { + if (RegisteredChecks.TryGetValue(check.Category, out category)) + { + if (category.TryGetValue(check.Container, out container)) + { + if (container.ContainsKey(check.ShortName)) + return true; + } + } + } + + return false; + } + + public static void CheckChecks() + { + lock (RegisteredChecks) + { + foreach (SortedDictionary> category in RegisteredChecks.Values) + { + foreach (SortedDictionary container in category.Values) + { + foreach (Check check in container.Values) + { + if (!check.CheckIt()) + m_log.WarnFormat( + "[CHECKS MANAGER]: Check {0}.{1}.{2} failed with message {3}", check.Category, check.Container, check.ShortName, check.LastFailureMessage); + } + } + } + } + } + + private static void OutputAllChecksToConsole(ICommandConsole con) + { + foreach (var category in RegisteredChecks.Values) + { + OutputCategoryChecksToConsole(con, category); + } + } + + private static void OutputCategoryChecksToConsole( + ICommandConsole con, SortedDictionary> category) + { + foreach (var container in category.Values) + { + OutputContainerChecksToConsole(con, container); + } + } + + private static void OutputContainerChecksToConsole(ICommandConsole con, SortedDictionary container) + { + foreach (Check check in container.Values) + { + con.Output(check.ToConsoleString()); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs new file mode 100644 index 0000000000..86a66207f6 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs @@ -0,0 +1,41 @@ +/* + * 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.Monitoring.Interfaces +{ + /// + /// Implemented by objects which allow statistical information to be pulled from them. + /// + public interface IPullStatsProvider + { + /// + /// Provide statistical information. Only temporary one long string. + /// + /// + string GetStats(); + } +} diff --git a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs new file mode 100644 index 0000000000..40df562a95 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs @@ -0,0 +1,58 @@ +/* + * 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.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Implemented by classes which collect up non-viewer statistical information + /// + public interface IStatsCollector + { + /// + /// Report back collected statistical information. + /// + /// + string Report(); + + /// + /// Report back collected statistical information in json + /// + /// + /// A + /// + string XReport(string uptime, string version); + + /// + /// Report back collected statistical information as an OSDMap of key/values + /// + /// + /// + OSDMap OReport(string uptime, string version); + } +} diff --git a/OpenSim/Framework/Monitoring/JobEngine.cs b/OpenSim/Framework/Monitoring/JobEngine.cs new file mode 100644 index 0000000000..6db9a67376 --- /dev/null +++ b/OpenSim/Framework/Monitoring/JobEngine.cs @@ -0,0 +1,341 @@ +/* + * 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.Concurrent; +using System.Reflection; +using System.Threading; +using log4net; +using OpenSim.Framework; + +namespace OpenSim.Framework.Monitoring +{ + public class JobEngine + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public int LogLevel { get; set; } + + public string Name { get; private set; } + + public string LoggingName { get; private set; } + + /// + /// Is this engine running? + /// + public bool IsRunning { get; private set; } + + /// + /// The current job that the engine is running. + /// + /// + /// Will be null if no job is currently running. + /// + public Job CurrentJob { get; private set; } + + /// + /// Number of jobs waiting to be processed. + /// + public int JobsWaiting { get { return m_jobQueue.Count; } } + + /// + /// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping. + /// + public int RequestProcessTimeoutOnStop { get; set; } + + /// + /// Controls whether we need to warn in the log about exceeding the max queue size. + /// + /// + /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in + /// order to avoid spamming the log with lots of warnings. + /// + private bool m_warnOverMaxQueue = true; + + private BlockingCollection m_jobQueue; + + private CancellationTokenSource m_cancelSource; + + /// + /// Used to signal that we are ready to complete stop. + /// + private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false); + + public JobEngine(string name, string loggingName) + { + Name = name; + LoggingName = loggingName; + + RequestProcessTimeoutOnStop = 5000; + } + + public void Start() + { + lock (this) + { + if (IsRunning) + return; + + IsRunning = true; + + m_finishedProcessingAfterStop.Reset(); + + m_jobQueue = new BlockingCollection(new ConcurrentQueue(), 5000); + m_cancelSource = new CancellationTokenSource(); + + WorkManager.StartThread( + ProcessRequests, + Name, + ThreadPriority.Normal, + false, + true, + null, + int.MaxValue); + } + } + + public void Stop() + { + lock (this) + { + try + { + if (!IsRunning) + return; + + IsRunning = false; + + int requestsLeft = m_jobQueue.Count; + + if (requestsLeft <= 0) + { + m_cancelSource.Cancel(); + } + else + { + m_log.InfoFormat("[{0}]: Waiting to write {1} events after stop.", LoggingName, requestsLeft); + + while (requestsLeft > 0) + { + if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop)) + { + // After timeout no events have been written + if (requestsLeft == m_jobQueue.Count) + { + m_log.WarnFormat( + "[{0}]: No requests processed after {1} ms wait. Discarding remaining {2} requests", + LoggingName, RequestProcessTimeoutOnStop, requestsLeft); + + break; + } + } + + requestsLeft = m_jobQueue.Count; + } + } + } + finally + { + m_cancelSource.Dispose(); + } + } + } + + /// + /// Make a job. + /// + /// + /// We provide this method to replace the constructor so that we can later pool job objects if necessary to + /// reduce memory churn. Normally one would directly call QueueJob() with parameters anyway. + /// + /// + /// Name. + /// Action. + /// Common identifier. + public static Job MakeJob(string name, Action action, string commonId = null) + { + return Job.MakeJob(name, action, commonId); + } + + /// + /// Remove the next job queued for processing. + /// + /// + /// Returns null if there is no next job. + /// Will not remove a job currently being performed. + /// + public Job RemoveNextJob() + { + Job nextJob; + m_jobQueue.TryTake(out nextJob); + + return nextJob; + } + + /// + /// Queue the job for processing. + /// + /// true, if job was queued, false otherwise. + /// Name of job. This appears on the console and in logging. + /// Action to perform. + /// + /// Common identifier for a set of jobs. This is allows a set of jobs to be removed + /// if required (e.g. all jobs for a given agent. Optional. + /// + public bool QueueJob(string name, Action action, string commonId = null) + { + return QueueJob(MakeJob(name, action, commonId)); + } + + /// + /// Queue the job for processing. + /// + /// true, if job was queued, false otherwise. + /// The job + /// + public bool QueueJob(Job job) + { + if (m_jobQueue.Count < m_jobQueue.BoundedCapacity) + { + m_jobQueue.Add(job); + + if (!m_warnOverMaxQueue) + m_warnOverMaxQueue = true; + + return true; + } + else + { + if (m_warnOverMaxQueue) + { + m_log.WarnFormat( + "[{0}]: Job queue at maximum capacity, not recording job from {1} in {2}", + LoggingName, job.Name, Name); + + m_warnOverMaxQueue = false; + } + + return false; + } + } + + private void ProcessRequests() + { + try + { + while (IsRunning || m_jobQueue.Count > 0) + { + try + { + CurrentJob = m_jobQueue.Take(m_cancelSource.Token); + } + catch (ObjectDisposedException e) + { + // If we see this whilst not running then it may be due to a race where this thread checks + // IsRunning after the stopping thread sets it to false and disposes of the cancellation source. + if (IsRunning) + throw e; + else + break; + } + + if (LogLevel >= 1) + m_log.DebugFormat("[{0}]: Processing job {1}", LoggingName, CurrentJob.Name); + + try + { + CurrentJob.Action(); + } + catch (Exception e) + { + m_log.Error( + string.Format( + "[{0}]: Job {1} failed, continuing. Exception ", LoggingName, CurrentJob.Name), e); + } + + if (LogLevel >= 1) + m_log.DebugFormat("[{0}]: Processed job {1}", LoggingName, CurrentJob.Name); + + CurrentJob = null; + } + } + catch (OperationCanceledException) + { + } + + m_finishedProcessingAfterStop.Set(); + } + + public class Job + { + /// + /// Name of the job. + /// + /// + /// This appears on console and debug output. + /// + public string Name { get; private set; } + + /// + /// Common ID for this job. + /// + /// + /// This allows all jobs with a certain common ID (e.g. a client UUID) to be removed en-masse if required. + /// Can be null if this is not required. + /// + public string CommonId { get; private set; } + + /// + /// Action to perform when this job is processed. + /// + public Action Action { get; private set; } + + private Job(string name, string commonId, Action action) + { + Name = name; + CommonId = commonId; + Action = action; + } + + /// + /// Make a job. It needs to be separately queued. + /// + /// + /// We provide this method to replace the constructor so that we can pool job objects if necessary to + /// to reduce memory churn. Normally one would directly call JobEngine.QueueJob() with parameters anyway. + /// + /// + /// Name. + /// Action. + /// Common identifier. + public static Job MakeJob(string name, Action action, string commonId = null) + { + return new Job(name, commonId, action); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs new file mode 100644 index 0000000000..c47462225b --- /dev/null +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -0,0 +1,137 @@ +/* + * 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 log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Experimental watchdog for memory usage. + /// + public static class MemoryWatchdog + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Is this watchdog active? + /// + public static bool Enabled + { + get { return m_enabled; } + set + { +// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); + + if (value && !m_enabled) + UpdateLastRecord(GC.GetTotalMemory(false), Util.EnvironmentTickCount()); + + m_enabled = value; + } + } + private static bool m_enabled; + + /// + /// Average heap allocation rate in bytes per millisecond. + /// + public static double AverageHeapAllocationRate + { + get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } + } + + /// + /// Last heap allocation in bytes + /// + public static double LastHeapAllocationRate + { + get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } + } + + /// + /// Maximum number of statistical samples. + /// + /// + /// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from + /// the main Watchdog. + /// + private static int m_maxSamples = 24; + + /// + /// Time when the watchdog was last updated. + /// + private static int m_lastUpdateTick; + + /// + /// Memory used at time of last watchdog update. + /// + private static long m_lastUpdateMemory; + + /// + /// Memory churn rate per millisecond. + /// +// private static double m_churnRatePerMillisecond; + + /// + /// Historical samples for calculating moving average. + /// + private static Queue m_samples = new Queue(m_maxSamples); + + public static void Update() + { + int now = Util.EnvironmentTickCount(); + long memoryNow = GC.GetTotalMemory(false); + long memoryDiff = memoryNow - m_lastUpdateMemory; + + if (memoryDiff >= 0) + { + if (m_samples.Count >= m_maxSamples) + m_samples.Dequeue(); + + double elapsed = Util.EnvironmentTickCountSubtract(now, m_lastUpdateTick); + + // This should never happen since it's not useful for updates to occur with no time elapsed, but + // protect ourselves from a divide-by-zero just in case. + if (elapsed == 0) + return; + + m_samples.Enqueue(memoryDiff / (double)elapsed); + } + + UpdateLastRecord(memoryNow, now); + } + + private static void UpdateLastRecord(long memoryNow, int timeNow) + { + m_lastUpdateMemory = memoryNow; + m_lastUpdateTick = timeNow; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b08e4f7fdd --- /dev/null +++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.Monitoring")] +[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("74506fe3-2f9d-44c1-94c9-a30f79d9e0cb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs new file mode 100644 index 0000000000..77315bb980 --- /dev/null +++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs @@ -0,0 +1,346 @@ +/* + * 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.Diagnostics; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Framework.Monitoring +{ + public class ServerStatsCollector + { + private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly string LogHeader = "[SERVER STATS]"; + + public bool Enabled = false; + private static Dictionary RegisteredStats = new Dictionary(); + + public readonly string CategoryServer = "server"; + + public readonly string ContainerThreadpool = "threadpool"; + public readonly string ContainerProcessor = "processor"; + public readonly string ContainerMemory = "memory"; + public readonly string ContainerNetwork = "network"; + public readonly string ContainerProcess = "process"; + + public string NetworkInterfaceTypes = "Ethernet"; + + readonly int performanceCounterSampleInterval = 500; +// int lastperformanceCounterSampleTime = 0; + + private class PerfCounterControl + { + public PerformanceCounter perfCounter; + public int lastFetch; + public string name; + public PerfCounterControl(PerformanceCounter pPc) + : this(pPc, String.Empty) + { + } + public PerfCounterControl(PerformanceCounter pPc, string pName) + { + perfCounter = pPc; + lastFetch = 0; + name = pName; + } + } + + PerfCounterControl processorPercentPerfCounter = null; + + // IRegionModuleBase.Initialize + public void Initialise(IConfigSource source) + { + if (source == null) + return; + + IConfig cfg = source.Configs["Monitoring"]; + + if (cfg != null) + Enabled = cfg.GetBoolean("ServerStatsEnabled", true); + + if (Enabled) + { + NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet"); + } + } + + public void Start() + { + if (RegisteredStats.Count == 0) + RegisterServerStats(); + } + + public void Close() + { + if (RegisteredStats.Count > 0) + { + foreach (Stat stat in RegisteredStats.Values) + { + StatsManager.DeregisterStat(stat); + stat.Dispose(); + } + RegisteredStats.Clear(); + } + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act) + { + MakeStat(pName, pDesc, pUnit, pContainer, act, MeasuresOfInterest.None); + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act, MeasuresOfInterest moi) + { + string desc = pDesc; + if (desc == null) + desc = pName; + Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, moi, act, StatVerbosity.Debug); + StatsManager.RegisterStat(stat); + RegisteredStats.Add(pName, stat); + } + + public void RegisterServerStats() + { +// lastperformanceCounterSampleTime = Util.EnvironmentTickCount(); + PerformanceCounter tempPC; + Stat tempStat; + string tempName; + + try + { + tempName = "CPUPercent"; + tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + processorPercentPerfCounter = new PerfCounterControl(tempPC); + // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy. + tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor, + StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter); }, + StatVerbosity.Info); + StatsManager.RegisterStat(tempStat); + RegisteredStats.Add(tempName, tempStat); + + MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds, 3); }); + + MakeStat("UserProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().UserProcessorTime.TotalSeconds, 3); }); + + MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds, 3); }); + + MakeStat("Threads", null, "threads", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e); + } + + MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = workerThreads; + }); + + MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = iocpThreads; + }); + + if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool && Util.GetSmartThreadPoolInfo() != null) + { + MakeStat("STPMaxThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxThreads); + MakeStat("STPMinThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MinThreads); + MakeStat("STPConcurrency", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxConcurrentWorkItems); + MakeStat("STPActiveThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().ActiveThreads); + MakeStat("STPInUseThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().InUseThreads); + MakeStat("STPWorkItemsWaiting", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().WaitingCallbacks); + } + + MakeStat( + "HTTPRequestsMade", + "Number of outbound HTTP requests made", + "requests", + ContainerNetwork, + s => s.Value = WebUtil.RequestNumber, + MeasuresOfInterest.AverageChangeOverTime); + + try + { + List okInterfaceTypes = new List(NetworkInterfaceTypes.Split(',')); + + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface nic in nics) + { + if (nic.OperationalStatus != OperationalStatus.Up) + continue; + + string nicInterfaceType = nic.NetworkInterfaceType.ToString(); + if (!okInterfaceTypes.Contains(nicInterfaceType)) + { + m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.", + LogHeader, nic.Name, nicInterfaceType); + m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}", + LogHeader, NetworkInterfaceTypes); + continue; + } + + if (nic.Supports(NetworkInterfaceComponent.IPv4)) + { + IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); + if (nicStats != null) + { + MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); + MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); + MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); + } + } + // TODO: add IPv6 (it may actually happen someday) + } + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); + } + + MakeStat("ProcessMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d, 3); }); + MakeStat("HeapMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(GC.GetTotalMemory(false) / 1024d / 1024d, 3); }); + MakeStat("LastHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + } + + // Notes on performance counters: + // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx + // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c + // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters + private delegate double PerfCounterNextValue(); + + private void GetNextValue(Stat stat, PerfCounterControl perfControl) + { + if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval) + { + if (perfControl != null && perfControl.perfCounter != null) + { + try + { + stat.Value = Math.Round(perfControl.perfCounter.NextValue(), 3); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e); + } + + perfControl.lastFetch = Util.EnvironmentTickCount(); + } + } + } + + // Lookup the nic that goes with this stat and set the value by using a fetch action. + // Not sure about closure with delegates inside delegates. + private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); + private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) + { + // Get the one nic that has the name of this stat + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces().Where( + (network) => network.Name == stat.Description); + try + { + foreach (NetworkInterface nic in nics) + { + IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); + if (intrStats != null) + { + double newVal = Math.Round(getter(intrStats) / factor, 3); + stat.Value = newVal; + } + break; + } + } + catch + { + // There are times interfaces go away so we just won't update the stat for this + m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description); + } + } + } + + public class ServerStatsAggregator : Stat + { + public ServerStatsAggregator( + string shortName, + string name, + string description, + string unitName, + string category, + string container + ) + : base( + shortName, + name, + description, + unitName, + category, + container, + StatType.Push, + MeasuresOfInterest.None, + null, + StatVerbosity.Info) + { + } + public override string ToConsoleString() + { + StringBuilder sb = new StringBuilder(); + + return sb.ToString(); + } + + public override OSDMap ToOSDMap() + { + OSDMap ret = new OSDMap(); + + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs new file mode 100755 index 0000000000..e4df7ee7f0 --- /dev/null +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -0,0 +1,536 @@ +/* + * 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.Diagnostics; +using System.Linq; +using System.Text; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework.Monitoring.Interfaces; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Collects sim statistics which aren't already being collected for the linden viewer's statistics pane + /// + public class SimExtraStatsCollector : BaseStatsCollector + { +// private long assetsInCache; +// private long texturesInCache; +// private long assetCacheMemoryUsage; +// private long textureCacheMemoryUsage; +// private TimeSpan assetRequestTimeAfterCacheMiss; +// private long blockedMissingTextureRequests; + +// private long assetServiceRequestFailures; +// private long inventoryServiceRetrievalFailures; + + private volatile float timeDilation; + private volatile float simFps; + private volatile float physicsFps; + private volatile float agentUpdates; + private volatile float rootAgents; + private volatile float childAgents; + private volatile float totalPrims; + private volatile float activePrims; + private volatile float totalFrameTime; + private volatile float netFrameTime; + private volatile float physicsFrameTime; + private volatile float otherFrameTime; + private volatile float imageFrameTime; + private volatile float inPacketsPerSecond; + private volatile float outPacketsPerSecond; + private volatile float unackedBytes; + private volatile float agentFrameTime; + private volatile float pendingDownloads; + private volatile float pendingUploads; + private volatile float activeScripts; + private volatile float scriptLinesPerSecond; + private volatile float m_frameDilation; + private volatile float m_usersLoggingIn; + private volatile float m_totalGeoPrims; + private volatile float m_totalMeshes; + private volatile float m_inUseThreads; + +// /// +// /// These statistics are being collected by push rather than pull. Pull would be simpler, but I had the +// /// notion of providing some flow statistics (which pull wouldn't give us). Though admittedly these +// /// haven't yet been implemented... +// /// +// public long AssetsInCache { get { return assetsInCache; } } +// +// /// +// /// Currently unused +// /// +// public long TexturesInCache { get { return texturesInCache; } } +// +// /// +// /// Currently misleading since we can't currently subtract removed asset memory usage without a performance hit +// /// +// public long AssetCacheMemoryUsage { get { return assetCacheMemoryUsage; } } +// +// /// +// /// Currently unused +// /// +// public long TextureCacheMemoryUsage { get { return textureCacheMemoryUsage; } } + + public float TimeDilation { get { return timeDilation; } } + public float SimFps { get { return simFps; } } + public float PhysicsFps { get { return physicsFps; } } + public float AgentUpdates { get { return agentUpdates; } } + public float RootAgents { get { return rootAgents; } } + public float ChildAgents { get { return childAgents; } } + public float TotalPrims { get { return totalPrims; } } + public float ActivePrims { get { return activePrims; } } + public float TotalFrameTime { get { return totalFrameTime; } } + public float NetFrameTime { get { return netFrameTime; } } + public float PhysicsFrameTime { get { return physicsFrameTime; } } + public float OtherFrameTime { get { return otherFrameTime; } } + public float ImageFrameTime { get { return imageFrameTime; } } + public float InPacketsPerSecond { get { return inPacketsPerSecond; } } + public float OutPacketsPerSecond { get { return outPacketsPerSecond; } } + public float UnackedBytes { get { return unackedBytes; } } + public float AgentFrameTime { get { return agentFrameTime; } } + public float PendingDownloads { get { return pendingDownloads; } } + public float PendingUploads { get { return pendingUploads; } } + public float ActiveScripts { get { return activeScripts; } } + public float ScriptLinesPerSecond { get { return scriptLinesPerSecond; } } + +// /// +// /// This is the time it took for the last asset request made in response to a cache miss. +// /// +// public TimeSpan AssetRequestTimeAfterCacheMiss { get { return assetRequestTimeAfterCacheMiss; } } +// +// /// +// /// Number of persistent requests for missing textures we have started blocking from clients. To some extent +// /// this is just a temporary statistic to keep this problem in view - the root cause of this lies either +// /// in a mishandling of the reply protocol, related to avatar appearance or may even originate in graphics +// /// driver bugs on clients (though this seems less likely). +// /// +// public long BlockedMissingTextureRequests { get { return blockedMissingTextureRequests; } } +// +// /// +// /// Record the number of times that an asset request has failed. Failures are effectively exceptions, such as +// /// request timeouts. If an asset service replies that a particular asset cannot be found, this is not counted +// /// as a failure +// /// +// public long AssetServiceRequestFailures { get { return assetServiceRequestFailures; } } + + /// + /// Number of known failures to retrieve avatar inventory from the inventory service. This does not + /// cover situations where the inventory service accepts the request but never returns any data, since + /// we do not yet timeout this situation. + /// + /// Commented out because we do not cache inventory at this point +// public long InventoryServiceRetrievalFailures { get { return inventoryServiceRetrievalFailures; } } + + /// + /// Retrieve the total frame time (in ms) of the last frame + /// + //public float TotalFrameTime { get { return totalFrameTime; } } + + /// + /// Retrieve the physics update component (in ms) of the last frame + /// + //public float PhysicsFrameTime { get { return physicsFrameTime; } } + + /// + /// Retain a dictionary of all packet queues stats reporters + /// + private IDictionary packetQueueStatsCollectors + = new Dictionary(); + +// public void AddAsset(AssetBase asset) +// { +// assetsInCache++; +// //assetCacheMemoryUsage += asset.Data.Length; +// } +// +// public void RemoveAsset(UUID uuid) +// { +// assetsInCache--; +// } +// +// public void AddTexture(AssetBase image) +// { +// if (image.Data != null) +// { +// texturesInCache++; +// +// // This could have been a pull stat, though there was originally a nebulous idea to measure flow rates +// textureCacheMemoryUsage += image.Data.Length; +// } +// } +// +// /// +// /// Signal that the asset cache has been cleared. +// /// +// public void ClearAssetCacheStatistics() +// { +// assetsInCache = 0; +// assetCacheMemoryUsage = 0; +// texturesInCache = 0; +// textureCacheMemoryUsage = 0; +// } +// +// public void AddAssetRequestTimeAfterCacheMiss(TimeSpan ts) +// { +// assetRequestTimeAfterCacheMiss = ts; +// } +// +// public void AddBlockedMissingTextureRequest() +// { +// blockedMissingTextureRequests++; +// } +// +// public void AddAssetServiceRequestFailure() +// { +// assetServiceRequestFailures++; +// } + +// public void AddInventoryServiceRetrievalFailure() +// { +// inventoryServiceRetrievalFailures++; +// } + + /// + /// Register as a packet queue stats provider + /// + /// An agent UUID + /// + public void RegisterPacketQueueStatsProvider(UUID uuid, IPullStatsProvider provider) + { + lock (packetQueueStatsCollectors) + { + // FIXME: If the region service is providing more than one region, then the child and root agent + // queues are wrongly replacing each other here. + packetQueueStatsCollectors[uuid] = new PacketQueueStatsCollector(provider); + } + } + + /// + /// Deregister a packet queue stats provider + /// + /// An agent UUID + public void DeregisterPacketQueueStatsProvider(UUID uuid) + { + lock (packetQueueStatsCollectors) + { + packetQueueStatsCollectors.Remove(uuid); + } + } + + /// + /// This is the method on which the classic sim stats reporter (which collects stats for + /// client purposes) sends information to listeners. + /// + /// + public void ReceiveClassicSimStatsPacket(SimStats stats) + { + // FIXME: SimStats shouldn't allow an arbitrary stat packing order (which is inherited from the original + // SimStatsPacket that was being used). + + // For an unknown reason the original designers decided not to + // include the spare MS statistic inside of this class, this is + // located inside the StatsBlock at location 21, thus it is skipped + timeDilation = stats.StatsBlock[0].StatValue; + simFps = stats.StatsBlock[1].StatValue; + physicsFps = stats.StatsBlock[2].StatValue; + agentUpdates = stats.StatsBlock[3].StatValue; + rootAgents = stats.StatsBlock[4].StatValue; + childAgents = stats.StatsBlock[5].StatValue; + totalPrims = stats.StatsBlock[6].StatValue; + activePrims = stats.StatsBlock[7].StatValue; + totalFrameTime = stats.StatsBlock[8].StatValue; + netFrameTime = stats.StatsBlock[9].StatValue; + physicsFrameTime = stats.StatsBlock[10].StatValue; + otherFrameTime = stats.StatsBlock[11].StatValue; + imageFrameTime = stats.StatsBlock[12].StatValue; + inPacketsPerSecond = stats.StatsBlock[13].StatValue; + outPacketsPerSecond = stats.StatsBlock[14].StatValue; + unackedBytes = stats.StatsBlock[15].StatValue; + agentFrameTime = stats.StatsBlock[16].StatValue; + pendingDownloads = stats.StatsBlock[17].StatValue; + pendingUploads = stats.StatsBlock[18].StatValue; + activeScripts = stats.StatsBlock[19].StatValue; + scriptLinesPerSecond = stats.StatsBlock[20].StatValue; + m_frameDilation = stats.StatsBlock[22].StatValue; + m_usersLoggingIn = stats.StatsBlock[23].StatValue; + m_totalGeoPrims = stats.StatsBlock[24].StatValue; + m_totalMeshes = stats.StatsBlock[25].StatValue; + m_inUseThreads = stats.StatsBlock[26].StatValue; + } + + /// + /// Report back collected statistical information. + /// + /// + public override string Report() + { + StringBuilder sb = new StringBuilder(Environment.NewLine); +// sb.Append("ASSET STATISTICS"); +// sb.Append(Environment.NewLine); + + /* + sb.Append( + string.Format( +@"Asset cache contains {0,6} non-texture assets using {1,10} K +Texture cache contains {2,6} texture assets using {3,10} K +Latest asset request time after cache miss: {4}s +Blocked client requests for missing textures: {5} +Asset service request failures: {6}"+ Environment.NewLine, + AssetsInCache, Math.Round(AssetCacheMemoryUsage / 1024.0), + TexturesInCache, Math.Round(TextureCacheMemoryUsage / 1024.0), + assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0, + BlockedMissingTextureRequests, + AssetServiceRequestFailures)); + */ + + /* + sb.Append( + string.Format( +@"Asset cache contains {0,6} assets +Latest asset request time after cache miss: {1}s +Blocked client requests for missing textures: {2} +Asset service request failures: {3}" + Environment.NewLine, + AssetsInCache, + assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0, + BlockedMissingTextureRequests, + AssetServiceRequestFailures)); + */ + + sb.Append(Environment.NewLine); + sb.Append("CONNECTION STATISTICS"); + sb.Append(Environment.NewLine); + + List stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives"); + + sb.AppendFormat( + "Client logouts due to no data receive timeout: {0}\n\n", + stats != null ? stats.Sum(s => s.Value).ToString() : "unknown"); + +// sb.Append(Environment.NewLine); +// sb.Append("INVENTORY STATISTICS"); +// sb.Append(Environment.NewLine); +// sb.Append( +// string.Format( +// "Initial inventory caching failures: {0}" + Environment.NewLine, +// InventoryServiceRetrievalFailures)); + + sb.Append(Environment.NewLine); + sb.Append("SAMPLE FRAME STATISTICS"); + sb.Append(Environment.NewLine); + sb.Append("Dilatn SimFPS PhyFPS AgntUp RootAg ChldAg Prims AtvPrm AtvScr ScrLPS"); + sb.Append(Environment.NewLine); + sb.Append( + string.Format( + "{0,6:0.00} {1,6:0} {2,6:0.0} {3,6:0.0} {4,6:0} {5,6:0} {6,6:0} {7,6:0} {8,6:0} {9,6:0}", + timeDilation, simFps, physicsFps, agentUpdates, rootAgents, + childAgents, totalPrims, activePrims, activeScripts, scriptLinesPerSecond)); + + sb.Append(Environment.NewLine); + sb.Append(Environment.NewLine); + // There is no script frame time currently because we don't yet collect it + sb.Append("PktsIn PktOut PendDl PendUl UnackB TotlFt NetFt PhysFt OthrFt AgntFt ImgsFt"); + sb.Append(Environment.NewLine); + sb.Append( + string.Format( + "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}\n\n", + inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, + netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); + + /* 20130319 RA: For the moment, disable the dump of 'scene' catagory as they are mostly output by + * the two formatted printouts above. + SortedDictionary> sceneStats; + if (StatsManager.TryGetStats("scene", out sceneStats)) + { + foreach (KeyValuePair> kvp in sceneStats) + { + foreach (Stat stat in kvp.Value.Values) + { + if (stat.Verbosity == StatVerbosity.Info) + { + sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName); + } + } + } + } + */ + + /* + sb.Append(Environment.NewLine); + sb.Append("PACKET QUEUE STATISTICS"); + sb.Append(Environment.NewLine); + sb.Append("Agent UUID "); + sb.Append( + string.Format( + " {0,7} {1,7} {2,7} {3,7} {4,7} {5,7} {6,7} {7,7} {8,7} {9,7}", + "Send", "In", "Out", "Resend", "Land", "Wind", "Cloud", "Task", "Texture", "Asset")); + sb.Append(Environment.NewLine); + + foreach (UUID key in packetQueueStatsCollectors.Keys) + { + sb.Append(string.Format("{0}: ", key)); + sb.Append(packetQueueStatsCollectors[key].Report()); + sb.Append(Environment.NewLine); + } + */ + + sb.Append(base.Report()); + + return sb.ToString(); + } + + /// + /// Report back collected statistical information as json serialization. + /// + /// + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + /// + /// Report back collected statistical information as an OSDMap + /// + /// + public override OSDMap OReport(string uptime, string version) + { + // Get the amount of physical memory, allocated with the instance of this program, in kilobytes; + // the working set is the set of memory pages currently visible to this program in physical RAM + // memory and includes both shared (e.g. system libraries) and private data + double memUsage = Process.GetCurrentProcess().WorkingSet64 / 1024.0; + + // Get the number of threads from the system that are currently + // running + int numberThreadsRunning = 0; + foreach (ProcessThread currentThread in + Process.GetCurrentProcess().Threads) + { + // A known issue with the current process .Threads property is + // that it can return null threads, thus don't count those as + // running threads and prevent the program function from failing + if (currentThread != null && + currentThread.ThreadState == ThreadState.Running) + { + numberThreadsRunning++; + } + } + + OSDMap args = new OSDMap(30); +// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache)); +// args["TimeAfterCacheMiss"] = OSD.FromString (String.Format ("{0:0.##}", +// assetRequestTimeAfterCacheMiss.Milliseconds / 1000.0)); +// args["BlockedMissingTextureRequests"] = OSD.FromString (String.Format ("{0:0.##}", +// BlockedMissingTextureRequests)); +// args["AssetServiceRequestFailures"] = OSD.FromString (String.Format ("{0:0.##}", +// AssetServiceRequestFailures)); +// args["abnormalClientThreadTerminations"] = OSD.FromString (String.Format ("{0:0.##}", +// abnormalClientThreadTerminations)); +// args["InventoryServiceRetrievalFailures"] = OSD.FromString (String.Format ("{0:0.##}", +// InventoryServiceRetrievalFailures)); + args["Dilatn"] = OSD.FromString (String.Format ("{0:0.##}", timeDilation)); + args["SimFPS"] = OSD.FromString (String.Format ("{0:0.##}", simFps)); + args["PhyFPS"] = OSD.FromString (String.Format ("{0:0.##}", physicsFps)); + args["AgntUp"] = OSD.FromString (String.Format ("{0:0.##}", agentUpdates)); + args["RootAg"] = OSD.FromString (String.Format ("{0:0.##}", rootAgents)); + args["ChldAg"] = OSD.FromString (String.Format ("{0:0.##}", childAgents)); + args["Prims"] = OSD.FromString (String.Format ("{0:0.##}", totalPrims)); + args["AtvPrm"] = OSD.FromString (String.Format ("{0:0.##}", activePrims)); + args["AtvScr"] = OSD.FromString (String.Format ("{0:0.##}", activeScripts)); + args["ScrLPS"] = OSD.FromString (String.Format ("{0:0.##}", scriptLinesPerSecond)); + args["PktsIn"] = OSD.FromString (String.Format ("{0:0.##}", inPacketsPerSecond)); + args["PktOut"] = OSD.FromString (String.Format ("{0:0.##}", outPacketsPerSecond)); + args["PendDl"] = OSD.FromString (String.Format ("{0:0.##}", pendingDownloads)); + args["PendUl"] = OSD.FromString (String.Format ("{0:0.##}", pendingUploads)); + args["UnackB"] = OSD.FromString (String.Format ("{0:0.##}", unackedBytes)); + args["TotlFt"] = OSD.FromString (String.Format ("{0:0.##}", totalFrameTime)); + args["NetFt"] = OSD.FromString (String.Format ("{0:0.##}", netFrameTime)); + args["PhysFt"] = OSD.FromString (String.Format ("{0:0.##}", physicsFrameTime)); + args["OthrFt"] = OSD.FromString (String.Format ("{0:0.##}", otherFrameTime)); + args["AgntFt"] = OSD.FromString (String.Format ("{0:0.##}", agentFrameTime)); + args["ImgsFt"] = OSD.FromString (String.Format ("{0:0.##}", imageFrameTime)); + args["Memory"] = OSD.FromString (base.XReport (uptime, version)); + args["Uptime"] = OSD.FromString (uptime); + args["Version"] = OSD.FromString (version); + + args["FrameDilatn"] = OSD.FromString(String.Format("{0:0.##}", m_frameDilation)); + args["Logging in Users"] = OSD.FromString(String.Format("{0:0.##}", + m_usersLoggingIn)); + args["GeoPrims"] = OSD.FromString(String.Format("{0:0.##}", + m_totalGeoPrims)); + args["Mesh Objects"] = OSD.FromString(String.Format("{0:0.##}", + m_totalMeshes)); + args["XEngine Thread Count"] = OSD.FromString(String.Format("{0:0.##}", + m_inUseThreads)); + args["Util Thread Count"] = OSD.FromString(String.Format("{0:0.##}", + Util.GetSmartThreadPoolInfo().InUseThreads)); + args["System Thread Count"] = OSD.FromString(String.Format( + "{0:0.##}", numberThreadsRunning)); + args["ProcMem"] = OSD.FromString(String.Format("{0:#,###,###.##}", + memUsage)); + + return args; + } + } + + + /// + /// Pull packet queue stats from packet queues and report + /// + public class PacketQueueStatsCollector : IStatsCollector + { + private IPullStatsProvider m_statsProvider; + + public PacketQueueStatsCollector(IPullStatsProvider provider) + { + m_statsProvider = provider; + } + + /// + /// Report back collected statistical information. + /// + /// + public string Report() + { + return m_statsProvider.GetStats(); + } + + public string XReport(string uptime, string version) + { + return ""; + } + + public OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs new file mode 100755 index 0000000000..318cf1cb9c --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs @@ -0,0 +1,119 @@ +/* + * 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.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// A statistic that wraps a counter. +// Built this way mostly so histograms and history can be created. +public class CounterStat : Stat +{ + private SortedDictionary m_histograms; + private object counterLock = new object(); + + public CounterStat( + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatVerbosity verbosity) + : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity) + { + m_histograms = new SortedDictionary(); + } + + // Histograms are presumably added at intialization time and the list does not change thereafter. + // Thus no locking of the histogram list. + public void AddHistogram(string histoName, EventHistogram histo) + { + m_histograms.Add(histoName, histo); + } + + public delegate void ProcessHistogram(string name, EventHistogram histo); + public void ForEachHistogram(ProcessHistogram process) + { + foreach (KeyValuePair kvp in m_histograms) + { + process(kvp.Key, kvp.Value); + } + } + + public void Event() + { + this.Event(1); + } + + // Count the underlying counter. + public void Event(int cnt) + { + lock (counterLock) + { + base.Value += cnt; + + foreach (EventHistogram histo in m_histograms.Values) + { + histo.Event(cnt); + } + } + } + + // CounterStat is a basic stat plus histograms + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "CounterStat"; + + // If there are any histograms, add a new field that is an array of histograms as OSDMaps + if (m_histograms.Count > 0) + { + lock (counterLock) + { + if (m_histograms.Count > 0) + { + OSDArray histos = new OSDArray(); + foreach (EventHistogram histo in m_histograms.Values) + { + histos.Add(histo.GetHistogramAsOSDMap()); + } + map.Add("Histograms", histos); + } + } + } + return map; + } +} +} diff --git a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs new file mode 100755 index 0000000000..f51f32247e --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs @@ -0,0 +1,173 @@ +/* + * 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.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// Create a time histogram of events. The histogram is built in a wrap-around +// array of equally distributed buckets. +// For instance, a minute long histogram of second sized buckets would be: +// new EventHistogram(60, 1000) +public class EventHistogram +{ + private int m_timeBase; + private int m_numBuckets; + private int m_bucketMilliseconds; + private int m_lastBucket; + private int m_totalHistogramMilliseconds; + private long[] m_histogram; + private object histoLock = new object(); + + public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) + { + m_numBuckets = numberOfBuckets; + m_bucketMilliseconds = millisecondsPerBucket; + m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; + + m_histogram = new long[m_numBuckets]; + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + + public void Event() + { + this.Event(1); + } + + // Record an event at time 'now' in the histogram. + public void Event(int cnt) + { + lock (histoLock) + { + // The time as displaced from the base of the histogram + int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); + + // If more than the total time of the histogram, we just start over + if (bucketTime > m_totalHistogramMilliseconds) + { + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + else + { + // To which bucket should we add this event? + int bucket = bucketTime / m_bucketMilliseconds; + + // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. + while (bucket != m_lastBucket) + { + // Zero from just after the last bucket to the new bucket or the end + for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) + { + m_histogram[jj] = 0; + } + m_lastBucket = bucket; + // If the new bucket is off the end, wrap around to the beginning + if (bucket > m_numBuckets) + { + bucket -= m_numBuckets; + m_lastBucket = 0; + m_histogram[m_lastBucket] = 0; + m_timeBase += m_totalHistogramMilliseconds; + } + } + } + m_histogram[m_lastBucket] += cnt; + } + } + + // Get a copy of the current histogram + public long[] GetHistogram() + { + long[] ret = new long[m_numBuckets]; + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = m_histogram[indx]; + } + } + return ret; + } + + public OSDMap GetHistogramAsOSDMap() + { + OSDMap ret = new OSDMap(); + + ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); + ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); + ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); + + // Compute a number for the first bucket in the histogram. + // This will allow readers to know how this histogram relates to any previously read histogram. + int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; + ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); + + ret.Add("Values", GetHistogramAsOSDArray()); + + return ret; + } + // Get a copy of the current histogram + public OSDArray GetHistogramAsOSDArray() + { + OSDArray ret = new OSDArray(m_numBuckets); + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = OSD.FromLong(m_histogram[indx]); + } + } + return ret; + } + + // Zero out the histogram + public void Zero() + { + lock (histoLock) + { + for (int ii = 0; ii < m_numBuckets; ii++) + m_histogram[ii] = 0; + } + } +} + +} diff --git a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs new file mode 100644 index 0000000000..55ddf0681c --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs @@ -0,0 +1,104 @@ +/* + * 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.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + public class PercentageStat : Stat + { + public long Antecedent { get; set; } + public long Consequent { get; set; } + + public override double Value + { + get + { + // Asking for an update here means that the updater cannot access this value without infinite recursion. + // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being + // called by the pull action and just return the value. + if (StatType == StatType.Pull) + PullAction(this); + + long c = Consequent; + + // Avoid any chance of a multi-threaded divide-by-zero + if (c == 0) + return 0; + + return (double)Antecedent / c * 100; + } + + set + { + throw new InvalidOperationException("Cannot set value on a PercentageStat"); + } + } + + public PercentageStat( + string shortName, + string name, + string description, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) + : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {} + + public override string ToConsoleString() + { + StringBuilder sb = new StringBuilder(); + + sb.AppendFormat( + "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})", + Category, Container, ShortName, Value, UnitName, Antecedent, Consequent); + + AppendMeasuresOfInterest(sb); + + return sb.ToString(); + } + + // PercentageStat is a basic stat plus percent calc + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "PercentageStat"; + + map.Add("Antecedent", OSD.FromLong(Antecedent)); + map.Add("Consequent", OSD.FromLong(Consequent)); + + return map; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs new file mode 100644 index 0000000000..a7cb2a6ae9 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -0,0 +1,326 @@ +/* + * 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 log4net; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Holds individual statistic details + /// + public class Stat : IDisposable + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + public StatType StatType { get; private set; } + + public MeasuresOfInterest MeasuresOfInterest { get; private set; } + + /// + /// Action used to update this stat when the value is requested if it's a pull type. + /// + public Action PullAction { get; private set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + public virtual string UnitName { get; private set; } + + public virtual double Value + { + get + { + // Asking for an update here means that the updater cannot access this value without infinite recursion. + // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being + // called by the pull action and just return the value. + if (StatType == StatType.Pull) + PullAction(this); + + return m_value; + } + + set + { + m_value = value; + } + } + + private double m_value; + + /// + /// Historical samples for calculating measures of interest average. + /// + /// + /// Will be null if no measures of interest require samples. + /// + private Queue m_samples; + + /// + /// Maximum number of statistical samples. + /// + /// + /// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from + /// the main Watchdog. + /// + private static int m_maxSamples = 24; + + public Stat( + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) + : this( + shortName, + name, + description, + unitName, + category, + container, + type, + MeasuresOfInterest.None, + pullAction, + verbosity) + { + } + + /// + /// Constructor + /// + /// Short name for the stat. Must not contain spaces. e.g. "LongFrames" + /// Human readable name for the stat. e.g. "Long frames" + /// Description of stat + /// + /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value. + /// e.g. " frames" + /// + /// Category under which this stat should appear, e.g. "scene". Do not capitalize. + /// Entity to which this stat relates. e.g. scene name if this is a per scene stat. + /// Push or pull + /// Pull stats need an action to update the stat on request. Push stats should set null here. + /// Measures of interest + /// Verbosity of stat. Controls whether it will appear in short stat display or only full display. + public Stat( + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatType type, + MeasuresOfInterest moi, + Action pullAction, + StatVerbosity verbosity) + { + if (StatsManager.SubCommands.Contains(category)) + throw new Exception( + string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); + + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + shortName = shortName.Replace(c, '#'); +// throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c)); + } + + ShortName = shortName; + Name = name; + Description = description; + UnitName = unitName; + Category = category; + Container = container; + StatType = type; + + if (StatType == StatType.Push && pullAction != null) + throw new Exception("A push stat cannot have a pull action"); + else + PullAction = pullAction; + + MeasuresOfInterest = moi; + + if ((moi & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime) + m_samples = new Queue(m_maxSamples); + + Verbosity = verbosity; + } + + // IDisposable.Dispose() + public virtual void Dispose() + { + return; + } + + /// + /// Record a value in the sample set. + /// + /// + /// Do not call this if MeasuresOfInterest.None + /// + public void RecordValue() + { + double newValue = Value; + + lock (m_samples) + { + if (m_samples.Count >= m_maxSamples) + m_samples.Dequeue(); + +// m_log.DebugFormat("[STAT]: Recording value {0} for {1}", newValue, Name); + + m_samples.Enqueue(newValue); + } + } + + public virtual string ToConsoleString() + { + StringBuilder sb = new StringBuilder(); + sb.AppendFormat( + "{0}.{1}.{2} : {3}{4}", + Category, + Container, + ShortName, + Value, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); + + AppendMeasuresOfInterest(sb); + + return sb.ToString(); + } + + public virtual OSDMap ToOSDMap() + { + OSDMap ret = new OSDMap(); + ret.Add("StatType", "Stat"); // used by overloading classes to denote type of stat + + ret.Add("Category", OSD.FromString(Category)); + ret.Add("Container", OSD.FromString(Container)); + ret.Add("ShortName", OSD.FromString(ShortName)); + ret.Add("Name", OSD.FromString(Name)); + ret.Add("Description", OSD.FromString(Description)); + ret.Add("UnitName", OSD.FromString(UnitName)); + ret.Add("Value", OSD.FromReal(Value)); + + double lastChangeOverTime, averageChangeOverTime; + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + ret.Add("LastChangeOverTime", OSD.FromReal(lastChangeOverTime)); + ret.Add("AverageChangeOverTime", OSD.FromReal(averageChangeOverTime)); + } + + return ret; + } + + // Compute the averages over time and return same. + // Return 'true' if averages were actually computed. 'false' if no average info. + public bool ComputeMeasuresOfInterest(out double lastChangeOverTime, out double averageChangeOverTime) + { + bool ret = false; + lastChangeOverTime = 0; + averageChangeOverTime = 0; + + if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime) + { + double totalChange = 0; + double? penultimateSample = null; + double? lastSample = null; + + lock (m_samples) + { + // m_log.DebugFormat( + // "[STAT]: Samples for {0} are {1}", + // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); + + foreach (double s in m_samples) + { + if (lastSample != null) + totalChange += s - (double)lastSample; + + penultimateSample = lastSample; + lastSample = s; + } + } + + if (lastSample != null && penultimateSample != null) + { + lastChangeOverTime + = ((double)lastSample - (double)penultimateSample) / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + } + + int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; + + averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + ret = true; + } + + return ret; + } + + protected void AppendMeasuresOfInterest(StringBuilder sb) + { + double lastChangeOverTime = 0; + double averageChangeOverTime = 0; + + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + sb.AppendFormat( + ", {0:0.##}{1}/s, {2:0.##}{3}/s", + lastChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName), + averageChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/StatsLogger.cs b/OpenSim/Framework/Monitoring/StatsLogger.cs new file mode 100644 index 0000000000..15a37aa104 --- /dev/null +++ b/OpenSim/Framework/Monitoring/StatsLogger.cs @@ -0,0 +1,151 @@ +/* + * 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.Text; +using System.Timers; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Provides a means to continuously log stats for debugging purposes. + /// + public static class StatsLogger + { + private static readonly ILog m_statsLog = LogManager.GetLogger("special.StatsLogger"); + + private static Timer m_loggingTimer; + private static int m_statsLogIntervalMs = 5000; + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "stats record", + "stats record start|stop", + "Control whether stats are being regularly recorded to a separate file.", + "For debug purposes. Experimental.", + HandleStatsRecordCommand); + + console.Commands.AddCommand( + "General", + false, + "stats save", + "stats save ", + "Save stats snapshot to a file. If the file already exists, then the report is appended.", + "For debug purposes. Experimental.", + HandleStatsSaveCommand); + } + + public static void HandleStatsRecordCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length != 3) + { + con.Output("Usage: stats record start|stop"); + return; + } + + if (cmd[2] == "start") + { + Start(); + con.OutputFormat("Now recording all stats to file every {0}ms", m_statsLogIntervalMs); + } + else if (cmd[2] == "stop") + { + Stop(); + con.Output("Stopped recording stats to file."); + } + } + + public static void HandleStatsSaveCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length != 3) + { + con.Output("Usage: stats save "); + return; + } + + string path = cmd[2]; + + using (StreamWriter sw = new StreamWriter(path, true)) + { + foreach (string line in GetReport()) + sw.WriteLine(line); + } + + MainConsole.Instance.OutputFormat("Stats saved to file {0}", path); + } + + public static void Start() + { + if (m_loggingTimer != null) + Stop(); + + m_loggingTimer = new Timer(m_statsLogIntervalMs); + m_loggingTimer.AutoReset = false; + m_loggingTimer.Elapsed += Log; + m_loggingTimer.Start(); + } + + public static void Stop() + { + if (m_loggingTimer != null) + { + m_loggingTimer.Stop(); + } + } + + private static void Log(object sender, ElapsedEventArgs e) + { + foreach (string line in GetReport()) + m_statsLog.Info(line); + + m_loggingTimer.Start(); + } + + private static List GetReport() + { + List lines = new List(); + + lines.Add(string.Format("*** STATS REPORT AT {0} ***", DateTime.Now)); + + foreach (string report in StatsManager.GetAllStatsReports()) + lines.Add(report); + + return lines; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs new file mode 100644 index 0000000000..3136ee82e9 --- /dev/null +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -0,0 +1,557 @@ +/* + * 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.Linq; +using System.Text; + +using OpenSim.Framework; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Static class used to register/deregister/fetch statistics + /// + public static class StatsManager + { + // Subcommand used to list other stats. + public const string AllSubCommand = "all"; + + // Subcommand used to list other stats. + public const string ListSubCommand = "list"; + + // All subcommands + public static HashSet SubCommands = new HashSet { AllSubCommand, ListSubCommand }; + + /// + /// Registered stats categorized by category/container/shortname + /// + /// + /// Do not add or remove directly from this dictionary. + /// + public static SortedDictionary>> RegisteredStats + = new SortedDictionary>>(); + +// private static AssetStatsCollector assetStats; +// private static UserStatsCollector userStats; +// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); + +// public static AssetStatsCollector AssetStats { get { return assetStats; } } +// public static UserStatsCollector UserStats { get { return userStats; } } + public static SimExtraStatsCollector SimExtraStats { get; set; } + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "stats show", + "stats show [list|all|([.])+", + "Show statistical information for this server", + "If no final argument is specified then legacy statistics information is currently shown.\n" + + "'list' argument will show statistic categories.\n" + + "'all' will show all statistics.\n" + + "A name will show statistics from that category.\n" + + "A . name will show statistics from that category in that container.\n" + + "More than one name can be given separated by spaces.\n" + + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", + HandleShowStatsCommand); + + console.Commands.AddCommand( + "General", + false, + "show stats", + "show stats [list|all|([.])+", + "Alias for 'stats show' command", + HandleShowStatsCommand); + + StatsLogger.RegisterConsoleCommands(console); + } + + public static void HandleShowStatsCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length > 2) + { + foreach (string name in cmd.Skip(2)) + { + string[] components = name.Split('.'); + + string categoryName = components[0]; + string containerName = components.Length > 1 ? components[1] : null; + string statName = components.Length > 2 ? components[2] : null; + + if (categoryName == AllSubCommand) + { + OutputAllStatsToConsole(con); + } + else if (categoryName == ListSubCommand) + { + con.Output("Statistic categories available are:"); + foreach (string category in RegisteredStats.Keys) + con.OutputFormat(" {0}", category); + } + else + { + SortedDictionary> category; + if (!RegisteredStats.TryGetValue(categoryName, out category)) + { + con.OutputFormat("No such category as {0}", categoryName); + } + else + { + if (String.IsNullOrEmpty(containerName)) + { + OutputCategoryStatsToConsole(con, category); + } + else + { + SortedDictionary container; + if (category.TryGetValue(containerName, out container)) + { + if (String.IsNullOrEmpty(statName)) + { + OutputContainerStatsToConsole(con, container); + } + else + { + Stat stat; + if (container.TryGetValue(statName, out stat)) + { + OutputStatToConsole(con, stat); + } + else + { + con.OutputFormat( + "No such stat {0} in {1}.{2}", statName, categoryName, containerName); + } + } + } + else + { + con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); + } + } + } + } + } + } + else + { + // Legacy + if (SimExtraStats != null) + con.Output(SimExtraStats.Report()); + else + OutputAllStatsToConsole(con); + } + } + + public static List GetAllStatsReports() + { + List reports = new List(); + + foreach (var category in RegisteredStats.Values) + reports.AddRange(GetCategoryStatsReports(category)); + + return reports; + } + + private static void OutputAllStatsToConsole(ICommandConsole con) + { + foreach (string report in GetAllStatsReports()) + con.Output(report); + } + + private static List GetCategoryStatsReports( + SortedDictionary> category) + { + List reports = new List(); + + foreach (var container in category.Values) + reports.AddRange(GetContainerStatsReports(container)); + + return reports; + } + + private static void OutputCategoryStatsToConsole( + ICommandConsole con, SortedDictionary> category) + { + foreach (string report in GetCategoryStatsReports(category)) + con.Output(report); + } + + private static List GetContainerStatsReports(SortedDictionary container) + { + List reports = new List(); + + foreach (Stat stat in container.Values) + reports.Add(stat.ToConsoleString()); + + return reports; + } + + private static void OutputContainerStatsToConsole( + ICommandConsole con, SortedDictionary container) + { + foreach (string report in GetContainerStatsReports(container)) + con.Output(report); + } + + private static void OutputStatToConsole(ICommandConsole con, Stat stat) + { + con.Output(stat.ToConsoleString()); + } + + // Creates an OSDMap of the format: + // { categoryName: { + // containerName: { + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // ... + // }, + // containerName: { + // ... + // }, + // ... + // }, + // categoryName: { + // ... + // }, + // ... + // } + // The passed in parameters will filter the categories, containers and stats returned. If any of the + // parameters are either EmptyOrNull or the AllSubCommand value, all of that type will be returned. + // Case matters. + public static OSDMap GetStatsAsOSDMap(string pCategoryName, string pContainerName, string pStatName) + { + OSDMap map = new OSDMap(); + + foreach (string catName in RegisteredStats.Keys) + { + // Do this category if null spec, "all" subcommand or category name matches passed parameter. + // Skip category if none of the above. + if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName)) + continue; + + OSDMap contMap = new OSDMap(); + foreach (string contName in RegisteredStats[catName].Keys) + { + if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName)) + continue; + + OSDMap statMap = new OSDMap(); + + SortedDictionary theStats = RegisteredStats[catName][contName]; + foreach (string statName in theStats.Keys) + { + if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName)) + continue; + + statMap.Add(statName, theStats[statName].ToOSDMap()); + } + + contMap.Add(contName, statMap); + } + map.Add(catName, contMap); + } + + return map; + } + + public static Hashtable HandleStatsRequest(Hashtable request) + { + Hashtable responsedata = new Hashtable(); +// string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/json"; + + string pCategoryName = StatsManager.AllSubCommand; + string pContainerName = StatsManager.AllSubCommand; + string pStatName = StatsManager.AllSubCommand; + + if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString(); + if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString(); + if (request.ContainsKey("stat")) pStatName = request["stat"].ToString(); + + string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); + + // If requestor wants it as a callback function, build response as a function rather than just the JSON string. + if (request.ContainsKey("callback")) + { + strOut = request["callback"].ToString() + "(" + strOut + ");"; + } + + // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", + // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + responsedata["access_control_allow_origin"] = "*"; + + return responsedata; + } + +// /// +// /// Start collecting statistics related to assets. +// /// Should only be called once. +// /// +// public static AssetStatsCollector StartCollectingAssetStats() +// { +// assetStats = new AssetStatsCollector(); +// +// return assetStats; +// } +// +// /// +// /// Start collecting statistics related to users. +// /// Should only be called once. +// /// +// public static UserStatsCollector StartCollectingUserStats() +// { +// userStats = new UserStatsCollector(); +// +// return userStats; +// } + + /// + /// Register a statistic. + /// + /// + /// + public static bool RegisterStat(Stat stat) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredStats) + { + // Stat name is not unique across category/container/shortname key. + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + if (TryGetStatParents(stat, out category, out container)) + return false; + + // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. + // This means that we don't need to lock or copy them on iteration, which will be a much more + // common operation after startup. + if (container != null) + newContainer = new SortedDictionary(container); + else + newContainer = new SortedDictionary(); + + if (category != null) + newCategory = new SortedDictionary>(category); + else + newCategory = new SortedDictionary>(); + + newContainer[stat.ShortName] = stat; + newCategory[stat.Container] = newContainer; + RegisteredStats[stat.Category] = newCategory; + } + + return true; + } + + /// + /// Deregister a statistic + /// > + /// + /// + public static bool DeregisterStat(Stat stat) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredStats) + { + if (!TryGetStatParents(stat, out category, out container)) + return false; + + newContainer = new SortedDictionary(container); + newContainer.Remove(stat.ShortName); + + newCategory = new SortedDictionary>(category); + newCategory.Remove(stat.Container); + + newCategory[stat.Container] = newContainer; + RegisteredStats[stat.Category] = newCategory; + + return true; + } + } + + public static bool TryGetStat(string category, string container, string statShortName, out Stat stat) + { + stat = null; + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!TryGetStatsForCategory(category, out categoryStats)) + return false; + + SortedDictionary containerStats; + + if (!categoryStats.TryGetValue(container, out containerStats)) + return false; + + return containerStats.TryGetValue(statShortName, out stat); + } + } + + public static bool TryGetStatsForCategory( + string category, out SortedDictionary> stats) + { + lock (RegisteredStats) + return RegisteredStats.TryGetValue(category, out stats); + } + + /// + /// Get the same stat for each container in a given category. + /// + /// + /// The stats if there were any to fetch. Otherwise null. + /// + /// + /// + public static List GetStatsFromEachContainer(string category, string statShortName) + { + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!RegisteredStats.TryGetValue(category, out categoryStats)) + return null; + + List stats = null; + + foreach (SortedDictionary containerStats in categoryStats.Values) + { + if (containerStats.ContainsKey(statShortName)) + { + if (stats == null) + stats = new List(); + + stats.Add(containerStats[statShortName]); + } + } + + return stats; + } + } + + public static bool TryGetStatParents( + Stat stat, + out SortedDictionary> category, + out SortedDictionary container) + { + category = null; + container = null; + + lock (RegisteredStats) + { + if (RegisteredStats.TryGetValue(stat.Category, out category)) + { + if (category.TryGetValue(stat.Container, out container)) + { + if (container.ContainsKey(stat.ShortName)) + return true; + } + } + } + + return false; + } + + public static void RecordStats() + { + lock (RegisteredStats) + { + foreach (SortedDictionary> category in RegisteredStats.Values) + { + foreach (SortedDictionary container in category.Values) + { + foreach (Stat stat in container.Values) + { + if (stat.MeasuresOfInterest != MeasuresOfInterest.None) + stat.RecordValue(); + } + } + } + } + } + } + + /// + /// Stat type. + /// + /// + /// A push stat is one which is continually updated and so it's value can simply by read. + /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated. + /// + public enum StatType + { + Push, + Pull + } + + /// + /// Measures of interest for this stat. + /// + [Flags] + public enum MeasuresOfInterest + { + None, + AverageChangeOverTime + } + + /// + /// Verbosity of stat. + /// + /// + /// Info will always be displayed. + /// + public enum StatVerbosity + { + Debug, + Info + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/UserStatsCollector.cs b/OpenSim/Framework/Monitoring/UserStatsCollector.cs new file mode 100644 index 0000000000..81e0fa4e21 --- /dev/null +++ b/OpenSim/Framework/Monitoring/UserStatsCollector.cs @@ -0,0 +1,110 @@ +/* + * 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.Timers; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Collects user service statistics + /// + public class UserStatsCollector : BaseStatsCollector + { + private Timer ageStatsTimer = new Timer(24 * 60 * 60 * 1000); + + private int successfulLoginsToday; + public int SuccessfulLoginsToday { get { return successfulLoginsToday; } } + + private int successfulLoginsYesterday; + public int SuccessfulLoginsYesterday { get { return successfulLoginsYesterday; } } + + private int successfulLogins; + public int SuccessfulLogins { get { return successfulLogins; } } + + private int logouts; + public int Logouts { get { return logouts; } } + + public UserStatsCollector() + { + ageStatsTimer.Elapsed += new ElapsedEventHandler(OnAgeing); + ageStatsTimer.Enabled = true; + } + + private void OnAgeing(object source, ElapsedEventArgs e) + { + successfulLoginsYesterday = successfulLoginsToday; + + // There is a possibility that an asset request could occur between the execution of these + // two statements. But we're better off without the synchronization overhead. + successfulLoginsToday = 0; + } + + /// + /// Record a successful login + /// + public void AddSuccessfulLogin() + { + successfulLogins++; + successfulLoginsToday++; + } + + public void AddLogout() + { + logouts++; + } + + /// + /// Report back collected statistical information. + /// + /// + override public string Report() + { + return string.Format( +@"Successful logins total : {0}, today : {1}, yesterday : {2} + Logouts total : {3}", + SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts); + } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("SuccessfulLogins", OSD.FromInteger(SuccessfulLogins)); + ret.Add("SuccessfulLoginsToday", OSD.FromInteger(SuccessfulLoginsToday)); + ret.Add("SuccessfulLoginsYesterday", OSD.FromInteger(SuccessfulLoginsYesterday)); + ret.Add("Logouts", OSD.FromInteger(Logouts)); + + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs new file mode 100644 index 0000000000..a644fa5cea --- /dev/null +++ b/OpenSim/Framework/Monitoring/Watchdog.cs @@ -0,0 +1,380 @@ +/* + * 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.Threading; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Manages launching threads and keeping watch over them for timeouts + /// + public static class Watchdog + { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + /// Timer interval in milliseconds for the watchdog timer + public const double WATCHDOG_INTERVAL_MS = 2500.0d; + + /// Default timeout in milliseconds before a thread is considered dead + public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000; + + [System.Diagnostics.DebuggerDisplay("{Thread.Name}")] + public class ThreadWatchdogInfo + { + public Thread Thread { get; private set; } + + /// + /// Approximate tick when this thread was started. + /// + /// + /// Not terribly good since this quickly wraps around. + /// + public int FirstTick { get; private set; } + + /// + /// Last time this heartbeat update was invoked + /// + public int LastTick { get; set; } + + /// + /// Number of milliseconds before we notify that the thread is having a problem. + /// + public int Timeout { get; set; } + + /// + /// Is this thread considered timed out? + /// + public bool IsTimedOut { get; set; } + + /// + /// Will this thread trigger the alarm function if it has timed out? + /// + public bool AlarmIfTimeout { get; set; } + + /// + /// Method execute if alarm goes off. If null then no alarm method is fired. + /// + public Func AlarmMethod { get; set; } + + /// + /// Stat structure associated with this thread. + /// + public Stat Stat { get; set; } + + public ThreadWatchdogInfo(Thread thread, int timeout, string name) + { + Thread = thread; + Timeout = timeout; + FirstTick = Environment.TickCount & Int32.MaxValue; + LastTick = FirstTick; + + Stat + = new Stat( + name, + string.Format("Last update of thread {0}", name), + "", + "ms", + "server", + "thread", + StatType.Pull, + MeasuresOfInterest.None, + stat => stat.Value = Environment.TickCount & Int32.MaxValue - LastTick, + StatVerbosity.Debug); + + StatsManager.RegisterStat(Stat); + } + + public ThreadWatchdogInfo(ThreadWatchdogInfo previousTwi) + { + Thread = previousTwi.Thread; + FirstTick = previousTwi.FirstTick; + LastTick = previousTwi.LastTick; + Timeout = previousTwi.Timeout; + IsTimedOut = previousTwi.IsTimedOut; + AlarmIfTimeout = previousTwi.AlarmIfTimeout; + AlarmMethod = previousTwi.AlarmMethod; + } + + public void Cleanup() + { + StatsManager.DeregisterStat(Stat); + } + } + + /// + /// This event is called whenever a tracked thread is + /// stopped or has not called UpdateThread() in time< + /// /summary> + public static event Action OnWatchdogTimeout; + + /// + /// Is this watchdog active? + /// + public static bool Enabled + { + get { return m_enabled; } + set + { + // m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); + + if (value == m_enabled) + return; + + m_enabled = value; + + if (m_enabled) + { + // Set now so we don't get alerted on the first run + LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue; + } + + m_watchdogTimer.Enabled = m_enabled; + } + } + + private static bool m_enabled; + private static Dictionary m_threads; + private static System.Timers.Timer m_watchdogTimer; + + /// + /// Last time the watchdog thread ran. + /// + /// + /// Should run every WATCHDOG_INTERVAL_MS + /// + public static int LastWatchdogThreadTick { get; private set; } + + static Watchdog() + { + m_threads = new Dictionary(); + m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS); + m_watchdogTimer.AutoReset = false; + m_watchdogTimer.Elapsed += WatchdogTimerElapsed; + } + + /// + /// Add a thread to the watchdog tracker. + /// + /// Information about the thread. + /// Name of the thread. + /// If true then creation of thread is logged. + public static void AddThread(ThreadWatchdogInfo info, string name, bool log = true) + { + if (log) + m_log.DebugFormat( + "[WATCHDOG]: Started tracking thread {0}, ID {1}", name, info.Thread.ManagedThreadId); + + lock (m_threads) + m_threads.Add(info.Thread.ManagedThreadId, info); + } + + /// + /// Marks the current thread as alive + /// + public static void UpdateThread() + { + UpdateThread(Thread.CurrentThread.ManagedThreadId); + } + + /// + /// Stops watchdog tracking on the current thread + /// + /// If true then normal events in thread removal are not logged. + /// + /// True if the thread was removed from the list of tracked + /// threads, otherwise false + /// + public static bool RemoveThread(bool log = true) + { + return RemoveThread(Thread.CurrentThread.ManagedThreadId, log); + } + + private static bool RemoveThread(int threadID, bool log = true) + { + lock (m_threads) + { + ThreadWatchdogInfo twi; + if (m_threads.TryGetValue(threadID, out twi)) + { + if (log) + m_log.DebugFormat( + "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); + + twi.Cleanup(); + m_threads.Remove(threadID); + + return true; + } + else + { + m_log.WarnFormat( + "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID); + + return false; + } + } + } + + public static bool AbortThread(int threadID) + { + lock (m_threads) + { + if (m_threads.ContainsKey(threadID)) + { + ThreadWatchdogInfo twi = m_threads[threadID]; + twi.Thread.Abort(); + RemoveThread(threadID); + + return true; + } + else + { + return false; + } + } + } + + private static void UpdateThread(int threadID) + { + ThreadWatchdogInfo threadInfo; + + // Although TryGetValue is not a thread safe operation, we use a try/catch here instead + // of a lock for speed. Adding/removing threads is a very rare operation compared to + // UpdateThread(), and a single UpdateThread() failure here and there won't break + // anything + try + { + if (m_threads.TryGetValue(threadID, out threadInfo)) + { + threadInfo.LastTick = Environment.TickCount & Int32.MaxValue; + threadInfo.IsTimedOut = false; + } + else + { + m_log.WarnFormat("[WATCHDOG]: Asked to update thread {0} which is not being monitored", threadID); + } + } + catch { } + } + + /// + /// Get currently watched threads for diagnostic purposes + /// + /// + public static ThreadWatchdogInfo[] GetThreadsInfo() + { + lock (m_threads) + return m_threads.Values.ToArray(); + } + + /// + /// Return the current thread's watchdog info. + /// + /// The watchdog info. null if the thread isn't being monitored. + public static ThreadWatchdogInfo GetCurrentThreadInfo() + { + lock (m_threads) + { + if (m_threads.ContainsKey(Thread.CurrentThread.ManagedThreadId)) + return m_threads[Thread.CurrentThread.ManagedThreadId]; + } + + return null; + } + + /// + /// Check watched threads. Fire alarm if appropriate. + /// + /// + /// + private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e) + { + int now = Environment.TickCount & Int32.MaxValue; + int msElapsed = now - LastWatchdogThreadTick; + + if (msElapsed > WATCHDOG_INTERVAL_MS * 2) + m_log.WarnFormat( + "[WATCHDOG]: {0} ms since Watchdog last ran. Interval should be approximately {1} ms", + msElapsed, WATCHDOG_INTERVAL_MS); + + LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue; + + Action callback = OnWatchdogTimeout; + + if (callback != null) + { + List callbackInfos = null; + + lock (m_threads) + { + foreach (ThreadWatchdogInfo threadInfo in m_threads.Values) + { + if (threadInfo.Thread.ThreadState == ThreadState.Stopped) + { + RemoveThread(threadInfo.Thread.ManagedThreadId); + + if (callbackInfos == null) + callbackInfos = new List(); + + callbackInfos.Add(threadInfo); + } + else if (!threadInfo.IsTimedOut && now - threadInfo.LastTick >= threadInfo.Timeout) + { + threadInfo.IsTimedOut = true; + + if (threadInfo.AlarmIfTimeout) + { + if (callbackInfos == null) + callbackInfos = new List(); + + // Send a copy of the watchdog info to prevent race conditions where the watchdog + // thread updates the monitoring info after an alarm has been sent out. + callbackInfos.Add(new ThreadWatchdogInfo(threadInfo)); + } + } + } + } + + if (callbackInfos != null) + foreach (ThreadWatchdogInfo callbackInfo in callbackInfos) + callback(callbackInfo); + } + + if (MemoryWatchdog.Enabled) + MemoryWatchdog.Update(); + + ChecksManager.CheckChecks(); + StatsManager.RecordStats(); + + m_watchdogTimer.Start(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/WorkManager.cs b/OpenSim/Framework/Monitoring/WorkManager.cs new file mode 100644 index 0000000000..d1a74ce70a --- /dev/null +++ b/OpenSim/Framework/Monitoring/WorkManager.cs @@ -0,0 +1,290 @@ +/* + * 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.Threading; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Manages various work items in the simulator. + /// + /// + /// Currently, here work can be started + /// * As a long-running and monitored thread. + /// * In a thread that will never timeout but where the job is expected to eventually complete. + /// * In a threadpool thread that will timeout if it takes a very long time to complete (> 10 mins). + /// * As a job which will be run in a single-threaded job engine. Such jobs must not incorporate delays (sleeps, + /// network waits, etc.). + /// + /// This is an evolving approach to better manage the work that OpenSimulator is asked to do from a very diverse + /// range of sources (client actions, incoming network, outgoing network calls, etc.). + /// + /// Util.FireAndForget is still available to insert jobs in the threadpool, though this is equivalent to + /// WorkManager.RunInThreadPool(). + /// + public static class WorkManager + { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static JobEngine JobEngine { get; private set; } + + static WorkManager() + { + JobEngine = new JobEngine("Non-blocking non-critical job engine", "JOB ENGINE"); + + StatsManager.RegisterStat( + new Stat( + "JobsWaiting", + "Number of jobs waiting for processing.", + "", + "", + "server", + "jobengine", + StatType.Pull, + MeasuresOfInterest.None, + stat => stat.Value = JobEngine.JobsWaiting, + StatVerbosity.Debug)); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug jobengine", + "debug jobengine ", + "Start, stop, get status or set logging level of the job engine.", + "If stopped then all outstanding jobs are processed immediately.", + HandleControlCommand); + } + + /// + /// Start a new long-lived thread. + /// + /// The method that will be executed in a new thread + /// A name to give to the new thread + /// Priority to run the thread at + /// True to run this thread as a background thread, otherwise false + /// Trigger an alarm function is we have timed out + /// If true then creation of thread is logged. + /// The newly created Thread object + public static Thread StartThread( + ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, bool log = true) + { + return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS, log); + } + + /// + /// Start a new thread that is tracked by the watchdog + /// + /// The method that will be executed in a new thread + /// A name to give to the new thread + /// Priority to run the thread at + /// True to run this thread as a background + /// thread, otherwise false + /// Trigger an alarm function is we have timed out + /// + /// Alarm method to call if alarmIfTimeout is true and there is a timeout. + /// Normally, this will just return some useful debugging information. + /// + /// Number of milliseconds to wait until we issue a warning about timeout. + /// If true then creation of thread is logged. + /// The newly created Thread object + public static Thread StartThread( + ThreadStart start, string name, ThreadPriority priority, bool isBackground, + bool alarmIfTimeout, Func alarmMethod, int timeout, bool log = true) + { + Thread thread = new Thread(start); + thread.Priority = priority; + thread.IsBackground = isBackground; + + Watchdog.ThreadWatchdogInfo twi + = new Watchdog.ThreadWatchdogInfo(thread, timeout, name) + { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod }; + + Watchdog.AddThread(twi, name, log:log); + + thread.Start(); + thread.Name = name; + + return thread; + } + + /// + /// Run the callback in a new thread immediately. If the thread exits with an exception log it but do + /// not propogate it. + /// + /// Code for the thread to execute. + /// Object to pass to the thread. + /// Name of the thread + public static void RunInThread(WaitCallback callback, object obj, string name, bool log = false) + { + if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) + { + Culture.SetCurrentCulture(); + callback(obj); + return; + } + + ThreadStart ts = new ThreadStart(delegate() + { + try + { + Culture.SetCurrentCulture(); + callback(obj); + Watchdog.RemoveThread(log:false); + } + catch (Exception e) + { + m_log.Error(string.Format("[WATCHDOG]: Exception in thread {0}.", name), e); + } + }); + + StartThread(ts, name, ThreadPriority.Normal, true, false, log:log); + } + + /// + /// Run the callback via a threadpool thread. + /// + /// + /// Such jobs may run after some delay but must always complete. + /// + /// + /// + /// The name of the job. This is used in monitoring and debugging. + public static void RunInThreadPool(System.Threading.WaitCallback callback, object obj, string name) + { + Util.FireAndForget(callback, obj, name); + } + + /// + /// Run a job. + /// + /// + /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job + /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is + /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to + /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small + /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more + /// sophisticated implementation could perform jobs concurrently when the server is under low load. + /// + /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any + /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine + /// beyond a single thread will require considerable thought. + /// + /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot + /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues + /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where + /// the job engine could be improved and so CPU utilization improved by better management of concurrency within + /// OpenSimulator. + /// + /// General classification for the job (e.g. "RezAttachments"). + /// Callback for job. + /// Object to pass to callback when run + /// Specific name of job (e.g. "RezAttachments for Joe Bloggs" + /// If set to true then the job may be run in ths calling thread. + /// If the true then the job must never timeout. + /// If set to true then extra logging is performed. + public static void RunJob( + string jobType, WaitCallback callback, object obj, string name, + bool canRunInThisThread = false, bool mustNotTimeout = false, + bool log = false) + { + if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) + { + Culture.SetCurrentCulture(); + callback(obj); + return; + } + + if (JobEngine.IsRunning) + JobEngine.QueueJob(name, () => callback(obj)); + else if (canRunInThisThread) + callback(obj); + else if (mustNotTimeout) + RunInThread(callback, obj, name, log); + else + Util.FireAndForget(callback, obj, name); + } + + private static void HandleControlCommand(string module, string[] args) + { + // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) + // return; + + if (args.Length < 3) + { + MainConsole.Instance.Output("Usage: debug jobengine "); + return; + } + + string subCommand = args[2]; + + if (subCommand == "stop") + { + JobEngine.Stop(); + MainConsole.Instance.OutputFormat("Stopped job engine."); + } + else if (subCommand == "start") + { + JobEngine.Start(); + MainConsole.Instance.OutputFormat("Started job engine."); + } + else if (subCommand == "status") + { + MainConsole.Instance.OutputFormat("Job engine running: {0}", JobEngine.IsRunning); + + JobEngine.Job job = JobEngine.CurrentJob; + MainConsole.Instance.OutputFormat("Current job {0}", job != null ? job.Name : "none"); + + MainConsole.Instance.OutputFormat( + "Jobs waiting: {0}", JobEngine.IsRunning ? JobEngine.JobsWaiting.ToString() : "n/a"); + MainConsole.Instance.OutputFormat("Log Level: {0}", JobEngine.LogLevel); + } + else if (subCommand == "log") + { + if (args.Length < 4) + { + MainConsole.Instance.Output("Usage: debug jobengine log "); + return; + } + + // int logLevel; + int logLevel = int.Parse(args[3]); + // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel)) + // { + JobEngine.LogLevel = logLevel; + MainConsole.Instance.OutputFormat("Set debug log level to {0}", JobEngine.LogLevel); + // } + } + else + { + MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/MultipartForm.cs b/OpenSim/Framework/MultipartForm.cs new file mode 100644 index 0000000000..7a13e8b21c --- /dev/null +++ b/OpenSim/Framework/MultipartForm.cs @@ -0,0 +1,144 @@ +/* + * 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.Net; +using System.IO; +using System.Text; + +namespace OpenSim.Framework +{ + public static class MultipartForm + { + #region Helper Classes + + public abstract class Element + { + public string Name; + } + + public class File : Element + { + public string Filename; + public string ContentType; + public byte[] Data; + + public File(string name, string filename, string contentType, byte[] data) + { + Name = name; + Filename = filename; + ContentType = contentType; + Data = data; + } + } + + public class Parameter : Element + { + public string Value; + + public Parameter(string name, string value) + { + Name = name; + Value = value; + } + } + + #endregion Helper Classes + + public static HttpWebResponse Post(HttpWebRequest request, List postParameters) + { + string boundary = Boundary(); + + // Set up the request properties + request.Method = "POST"; + request.ContentType = "multipart/form-data; boundary=" + boundary; + + #region Stream Writing + + using (MemoryStream formDataStream = new MemoryStream()) + { + foreach (var param in postParameters) + { + if (param is File) + { + File file = (File)param; + + // Add just the first part of this param, since we will write the file data directly to the Stream + string header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n", + boundary, + file.Name, + !String.IsNullOrEmpty(file.Filename) ? file.Filename : "tempfile", + file.ContentType); + + formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length); + formDataStream.Write(file.Data, 0, file.Data.Length); + } + else + { + Parameter parameter = (Parameter)param; + + string postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n", + boundary, + parameter.Name, + parameter.Value); + formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, postData.Length); + } + } + + // Add the end of the request + byte[] footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n"); + formDataStream.Write(footer, 0, footer.Length); + + request.ContentLength = formDataStream.Length; + + // Copy the temporary stream to the network stream + formDataStream.Seek(0, SeekOrigin.Begin); + using (Stream requestStream = request.GetRequestStream()) + formDataStream.CopyStream(requestStream, (int)formDataStream.Length); + } + + #endregion Stream Writing + + return request.GetResponse() as HttpWebResponse; + } + + private static string Boundary() + { + Random rnd = new Random(); + string formDataBoundary = String.Empty; + + while (formDataBoundary.Length < 15) + formDataBoundary = formDataBoundary + rnd.Next(); + + formDataBoundary = formDataBoundary.Substring(0, 15); + formDataBoundary = "-----------------------------" + formDataBoundary; + + return formDataBoundary; + } + } +} diff --git a/OpenSim/Framework/NetworkServersInfo.cs b/OpenSim/Framework/NetworkServersInfo.cs new file mode 100644 index 0000000000..4b7d4c7535 --- /dev/null +++ b/OpenSim/Framework/NetworkServersInfo.cs @@ -0,0 +1,75 @@ +/* + * 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 Nini.Config; + +namespace OpenSim.Framework +{ + public class NetworkServersInfo + { + public uint HttpListenerPort = ConfigSettings.DefaultRegionHttpPort; + public bool secureInventoryServer = false; + public bool isSandbox; + public bool HttpUsesSSL = false; + public string HttpSSLCN = ""; + public uint httpSSLPort = 9001; + + // "Out of band" managemnt https + public bool ssl_listener = false; + public uint https_port = 0; + public string cert_path = String.Empty; + public string cert_pass = String.Empty; + + public NetworkServersInfo() + { + } + + public NetworkServersInfo(uint defaultHomeLocX, uint defaultHomeLocY) + { + } + + public void loadFromConfiguration(IConfigSource config) + { + HttpListenerPort = + (uint) config.Configs["Network"].GetInt("http_listener_port", (int) ConfigSettings.DefaultRegionHttpPort); + httpSSLPort = + (uint)config.Configs["Network"].GetInt("http_listener_sslport", ((int)ConfigSettings.DefaultRegionHttpPort+1)); + HttpUsesSSL = config.Configs["Network"].GetBoolean("http_listener_ssl", false); + HttpSSLCN = config.Configs["Network"].GetString("http_listener_cn", "localhost"); + + // "Out of band management https" + ssl_listener = config.Configs["Network"].GetBoolean("https_listener",false); + if( ssl_listener) + { + cert_path = config.Configs["Network"].GetString("cert_path",String.Empty); + cert_pass = config.Configs["Network"].GetString("cert_pass",String.Empty); + https_port = (uint)config.Configs["Network"].GetInt("https_port", 0); + } + } + } +} diff --git a/OpenSim/Framework/NetworkUtil.cs b/OpenSim/Framework/NetworkUtil.cs new file mode 100644 index 0000000000..2e94b0d5b4 --- /dev/null +++ b/OpenSim/Framework/NetworkUtil.cs @@ -0,0 +1,250 @@ +/* + * 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.Net.Sockets; +using System.Net; +using System.Net.NetworkInformation; +using System.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework +{ + /// + /// Handles NAT translation in a 'manner of speaking' + /// Allows you to return multiple different external + /// hostnames depending on the requestors network + /// + /// This enables standard port forwarding techniques + /// to work correctly with OpenSim. + /// + public static class NetworkUtil + { + // Logger + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static bool m_disabled = true; + + public static bool Enabled + { + set { m_disabled = value; } + get { return m_disabled; } + } + + // IPv4Address, Subnet + static readonly Dictionary m_subnets = new Dictionary(); + + public static IPAddress GetIPFor(IPAddress user, IPAddress simulator) + { + if (m_disabled) + return simulator; + + // Check if we're accessing localhost. + foreach (IPAddress host in Dns.GetHostAddresses(Dns.GetHostName())) + { + if (host.Equals(user) && host.AddressFamily == AddressFamily.InterNetwork) + { + m_log.Info("[NetworkUtil] Localhost user detected, sending them '" + host + "' instead of '" + simulator + "'"); + return host; + } + } + + // Check for same LAN segment + foreach (KeyValuePair subnet in m_subnets) + { + byte[] subnetBytes = subnet.Value.GetAddressBytes(); + byte[] localBytes = subnet.Key.GetAddressBytes(); + byte[] destBytes = user.GetAddressBytes(); + + if (subnetBytes.Length != destBytes.Length || subnetBytes.Length != localBytes.Length) + return null; + + bool valid = true; + + for (int i = 0; i < subnetBytes.Length; i++) + { + if ((localBytes[i] & subnetBytes[i]) != (destBytes[i] & subnetBytes[i])) + { + valid = false; + break; + } + } + + if (subnet.Key.AddressFamily != AddressFamily.InterNetwork) + valid = false; + + if (valid) + { + m_log.Info("[NetworkUtil] Local LAN user detected, sending them '" + subnet.Key + "' instead of '" + simulator + "'"); + return subnet.Key; + } + } + + // Otherwise, return outside address + return simulator; + } + + private static IPAddress GetExternalIPFor(IPAddress destination, string defaultHostname) + { + // Adds IPv6 Support (Not that any of the major protocols supports it...) + if (destination.AddressFamily == AddressFamily.InterNetworkV6) + { + foreach (IPAddress host in Dns.GetHostAddresses(defaultHostname)) + { + if (host.AddressFamily == AddressFamily.InterNetworkV6) + { + m_log.Info("[NetworkUtil] Localhost user detected, sending them '" + host + "' instead of '" + defaultHostname + "'"); + return host; + } + } + } + + if (destination.AddressFamily != AddressFamily.InterNetwork) + return null; + + // Check if we're accessing localhost. + foreach (KeyValuePair pair in m_subnets) + { + IPAddress host = pair.Value; + if (host.Equals(destination) && host.AddressFamily == AddressFamily.InterNetwork) + { + m_log.Info("[NATROUTING] Localhost user detected, sending them '" + host + "' instead of '" + defaultHostname + "'"); + return destination; + } + } + + // Check for same LAN segment + foreach (KeyValuePair subnet in m_subnets) + { + byte[] subnetBytes = subnet.Value.GetAddressBytes(); + byte[] localBytes = subnet.Key.GetAddressBytes(); + byte[] destBytes = destination.GetAddressBytes(); + + if (subnetBytes.Length != destBytes.Length || subnetBytes.Length != localBytes.Length) + return null; + + bool valid = true; + + for (int i=0;i + /// ChatFromViewer Arguments + /// + public class OSChatMessage : EventArgs, IEventArgs + { + protected int m_channel; + protected string m_from; + protected string m_message; + protected Vector3 m_position; + + protected IScene m_scene; + protected IClientAPI m_sender; + protected object m_senderObject; + protected ChatTypeEnum m_type; + protected UUID m_fromID; + protected UUID m_toID; + + public OSChatMessage() + { + m_position = new Vector3(); + m_toID = UUID.Zero; + } + + /// + /// The message sent by the user + /// + public string Message + { + get { return m_message; } + set { m_message = value; } + } + + /// + /// The type of message, eg say, shout, broadcast. + /// + public ChatTypeEnum Type + { + get { return m_type; } + set { m_type = value; } + } + + /// + /// Which channel was this message sent on? Different channels may have different listeners. Public chat is on channel zero. + /// + public int Channel + { + get { return m_channel; } + set { m_channel = value; } + } + + /// + /// The position of the sender at the time of the message broadcast. + /// + public Vector3 Position + { + get { return m_position; } + set { m_position = value; } + } + + /// + /// The name of the sender (needed for scripts) + /// + public string From + { + get { return m_from; } + set { m_from = value; } + } + + /// + /// The name of the sender (needed for scripts) + /// + public string To + { + get { return m_from; } + set { m_from = value; } + } + + #region IEventArgs Members + + /// TODO: Sender and SenderObject should just be Sender and of + /// type IChatSender + + /// + /// The client responsible for sending the message, or null. + /// + public IClientAPI Sender + { + get { return m_sender; } + set { m_sender = value; } + } + + /// + /// The object responsible for sending the message, or null. + /// + public object SenderObject + { + get { return m_senderObject; } + set { m_senderObject = value; } + } + + public UUID SenderUUID + { + get { return m_fromID; } + set { m_fromID = value; } + } + + /// + /// The single recipient or all if not set. + /// + public UUID TargetUUID + { + get { return m_toID; } + set { m_toID = value; } + } + + /// + /// + /// + public IScene Scene + { + get { return m_scene; } + set { m_scene = value; } + } + + public override string ToString() + { + return m_message; + } + + #endregion + } +} diff --git a/OpenSim/Framework/ParcelMediaCommandEnum.cs b/OpenSim/Framework/ParcelMediaCommandEnum.cs new file mode 100644 index 0000000000..93c41ecd4d --- /dev/null +++ b/OpenSim/Framework/ParcelMediaCommandEnum.cs @@ -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.Framework +{ + public enum ParcelMediaCommandEnum + { + Stop = 0, + Pause = 1, + Play = 2, + Loop = 3, + Texture = 4, + Url = 5, + Time = 6, + Agent = 7, + Unload = 8, + AutoAlign = 9, + Type = 10, + Size = 11, + Desc = 12 + } +} diff --git a/OpenSim/Framework/PermissionsUtil.cs b/OpenSim/Framework/PermissionsUtil.cs new file mode 100644 index 0000000000..d785a781f5 --- /dev/null +++ b/OpenSim/Framework/PermissionsUtil.cs @@ -0,0 +1,87 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public static class PermissionsUtil + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Logs permissions flags. Useful when debugging permission problems. + /// + /// + public static void LogPermissions(String name, String message, uint basePerm, uint curPerm, uint nextPerm) + { + m_log.DebugFormat("Permissions of \"{0}\" at \"{1}\": Base {2} ({3:X4}), Current {4} ({5:X4}), NextOwner {6} ({7:X4})", + name, message, + PermissionsToString(basePerm), basePerm, PermissionsToString(curPerm), curPerm, PermissionsToString(nextPerm), nextPerm); + } + + /// + /// Converts a permissions bit-mask to a string (e.g., "MCT"). + /// + private static string PermissionsToString(uint perms) + { + string str = ""; + if ((perms & (int)PermissionMask.Modify) != 0) + str += "M"; + if ((perms & (int)PermissionMask.Copy) != 0) + str += "C"; + if ((perms & (int)PermissionMask.Transfer) != 0) + str += "T"; + if (str == "") + str = "."; + return str; + } + + /// + /// Applies an object's folded permissions to its regular permissions. + /// + /// The folded permissions. Only the lowest 7 bits are examined. + /// The permissions variable to modify. + public static void ApplyFoldedPermissions(uint foldedPerms, ref uint mainPerms) + { + if ((foldedPerms & 7) == 0) + return; // assume that if the folded permissions are 0 then this means that they weren't actually recorded + + if ((foldedPerms & ((uint)PermissionMask.Copy >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Copy; + if ((foldedPerms & ((uint)PermissionMask.Transfer >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Transfer; + if ((foldedPerms & ((uint)PermissionMask.Modify >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Modify; + } + + } +} diff --git a/OpenSim/Framework/PluginLoader.cs b/OpenSim/Framework/PluginLoader.cs new file mode 100644 index 0000000000..d12aa611b8 --- /dev/null +++ b/OpenSim/Framework/PluginLoader.cs @@ -0,0 +1,445 @@ +/* + * 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 log4net; +using Mono.Addins; + +namespace OpenSim.Framework +{ + /// + /// Exception thrown if an incorrect number of plugins are loaded + /// + public class PluginConstraintViolatedException : Exception + { + public PluginConstraintViolatedException () : base() {} + public PluginConstraintViolatedException (string msg) : base(msg) {} + public PluginConstraintViolatedException (string msg, Exception e) : base(msg, e) {} + } + + /// + /// Classes wishing to impose constraints on plugin loading must implement + /// this class and pass it to PluginLoader AddConstraint() + /// + public interface IPluginConstraint + { + string Message { get; } + bool Apply(string extpoint); + } + + /// + /// Classes wishing to select specific plugins from a range of possible options + /// must implement this class and pass it to PluginLoader Load() + /// + public interface IPluginFilter + { + bool Apply(PluginExtensionNode plugin); + } + + /// + /// Generic Plugin Loader + /// + public class PluginLoader : IDisposable where T : IPlugin + { + private const int max_loadable_plugins = 10000; + + private List loaded = new List(); + private List extpoints = new List(); + private PluginInitialiserBase initialiser; + + private Dictionary constraints + = new Dictionary(); + + private Dictionary filters + = new Dictionary(); + + private static readonly ILog log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PluginInitialiserBase Initialiser + { + set { initialiser = value; } + get { return initialiser; } + } + + public List Plugins + { + get { return loaded; } + } + + public T Plugin + { + get { return (loaded.Count == 1)? loaded [0] : default (T); } + } + + public PluginLoader() + { + Initialiser = new PluginInitialiserBase(); + initialise_plugin_dir_("."); + } + + public PluginLoader(PluginInitialiserBase init) + { + Initialiser = init; + initialise_plugin_dir_("."); + } + + public PluginLoader(PluginInitialiserBase init, string dir) + { + Initialiser = init; + initialise_plugin_dir_(dir); + } + + public void Add(string extpoint) + { + if (extpoints.Contains(extpoint)) + return; + + extpoints.Add(extpoint); + } + + public void Add(string extpoint, IPluginConstraint cons) + { + Add(extpoint); + AddConstraint(extpoint, cons); + } + + public void Add(string extpoint, IPluginFilter filter) + { + Add(extpoint); + AddFilter(extpoint, filter); + } + + public void AddConstraint(string extpoint, IPluginConstraint cons) + { + constraints.Add(extpoint, cons); + } + + public void AddFilter(string extpoint, IPluginFilter filter) + { + filters.Add(extpoint, filter); + } + + public void Load(string extpoint) + { + Add(extpoint); + Load(); + } + + public void Load() + { + foreach (string ext in extpoints) + { + log.Info("[PLUGINS]: Loading extension point " + ext); + + if (constraints.ContainsKey(ext)) + { + IPluginConstraint cons = constraints[ext]; + if (cons.Apply(ext)) + log.Error("[PLUGINS]: " + ext + " failed constraint: " + cons.Message); + } + + IPluginFilter filter = null; + + if (filters.ContainsKey(ext)) + filter = filters[ext]; + + List loadedPlugins = new List(); + foreach (PluginExtensionNode node in AddinManager.GetExtensionNodes(ext)) + { + log.Info("[PLUGINS]: Trying plugin " + node.Path); + + if ((filter != null) && (filter.Apply(node) == false)) + continue; + + T plugin = (T)node.CreateInstance(); + loadedPlugins.Add(plugin); + } + + // We do Initialise() in a second loop after CreateInstance + // So that modules who need init before others can do it + // Example: Script Engine Component System needs to load its components before RegionLoader starts + foreach (T plugin in loadedPlugins) + { + Initialiser.Initialise(plugin); + Plugins.Add(plugin); + } + } + } + + /// + /// Unregisters Mono.Addins event handlers, allowing temporary Mono.Addins + /// data to be garbage collected. Since the plugins created by this loader + /// are meant to outlive the loader itself, they must be disposed separately + /// + public void Dispose() + { + AddinManager.AddinLoadError -= on_addinloaderror_; + AddinManager.AddinLoaded -= on_addinloaded_; + } + + private void initialise_plugin_dir_(string dir) + { + if (AddinManager.IsInitialized == true) + return; + + log.Info("[PLUGINS]: Initializing addin manager"); + + AddinManager.AddinLoadError += on_addinloaderror_; + AddinManager.AddinLoaded += on_addinloaded_; + + //clear_registry_(dir); + + //suppress_console_output_(true); + AddinManager.Initialize(dir); + AddinManager.Registry.Update(null); + //suppress_console_output_(false); + } + + private void on_addinloaded_(object sender, AddinEventArgs args) + { + log.Info ("[PLUGINS]: Plugin Loaded: " + args.AddinId); + } + + private void on_addinloaderror_(object sender, AddinErrorEventArgs args) + { + if (args.Exception == null) + log.Error ("[PLUGINS]: Plugin Error: " + + args.Message); + else + log.Error ("[PLUGINS]: Plugin Error: " + + args.Exception.Message + "\n" + + args.Exception.StackTrace); + } + + private void clear_registry_(string dir) + { + // The Mono addin manager (in Mono.Addins.dll version 0.2.0.0) + // occasionally seems to corrupt its addin cache + // Hence, as a temporary solution we'll remove it before each startup + + try + { + if (Directory.Exists(dir + "/addin-db-000")) + Directory.Delete(dir + "/addin-db-000", true); + + if (Directory.Exists(dir + "/addin-db-001")) + Directory.Delete(dir + "/addin-db-001", true); + } + catch (IOException) + { + // If multiple services are started simultaneously, they may + // each test whether the directory exists at the same time, and + // attempt to delete the directory at the same time. However, + // one of the services will likely succeed first, causing the + // second service to throw an IOException. We catch it here and + // continue on our merry way. + // Mike 2008.08.01, patch from Zaki + } + } + + private static TextWriter prev_console_; + public void suppress_console_output_(bool save) + { + if (save) + { + prev_console_ = System.Console.Out; + System.Console.SetOut(new StreamWriter(Stream.Null)); + } + else + { + if (prev_console_ != null) + System.Console.SetOut(prev_console_); + } + } + } + + public class PluginExtensionNode : ExtensionNode + { + [NodeAttribute] + string id = ""; + + [NodeAttribute] + string provider = ""; + + [NodeAttribute] + string type = ""; + + Type typeobj; + + public string ID { get { return id; } } + public string Provider { get { return provider; } } + public string TypeName { get { return type; } } + + public Type TypeObject + { + get + { + if (typeobj != null) + return typeobj; + + if (type.Length == 0) + throw new InvalidOperationException("Type name not specified."); + + return typeobj = Addin.GetType(type, true); + } + } + + public object CreateInstance() + { + return Activator.CreateInstance(TypeObject); + } + } + + /// + /// Constraint that bounds the number of plugins to be loaded. + /// + public class PluginCountConstraint : IPluginConstraint + { + private int min; + private int max; + + public PluginCountConstraint(int exact) + { + min = exact; + max = exact; + } + + public PluginCountConstraint(int minimum, int maximum) + { + min = minimum; + max = maximum; + } + + public string Message + { + get + { + return "The number of plugins is constrained to the interval [" + + min + ", " + max + "]"; + } + } + + public bool Apply (string extpoint) + { + int count = AddinManager.GetExtensionNodes(extpoint).Count; + + if ((count < min) || (count > max)) + throw new PluginConstraintViolatedException(Message); + + return true; + } + } + + /// + /// Filters out which plugin to load based on the plugin name or names given. Plugin names are contained in + /// their addin.xml + /// + public class PluginProviderFilter : IPluginFilter + { + private string[] m_filters; + + /// + /// Constructor. + /// + /// + /// Plugin name or names on which to filter. Multiple names should be separated by commas. + /// + public PluginProviderFilter(string p) + { + m_filters = p.Split(','); + + for (int i = 0; i < m_filters.Length; i++) + { + m_filters[i] = m_filters[i].Trim(); + } + } + + /// + /// Apply this filter to the given plugin. + /// + /// + /// true if the plugin's name matched one of the filters, false otherwise. + public bool Apply (PluginExtensionNode plugin) + { + for (int i = 0; i < m_filters.Length; i++) + { + if (m_filters[i] == plugin.Provider) + { + return true; + } + } + + return false; + } + } + + /// + /// Filters plugins according to their ID. Plugin IDs are contained in their addin.xml + /// + public class PluginIdFilter : IPluginFilter + { + private string[] m_filters; + + /// + /// Constructor. + /// + /// + /// Plugin ID or IDs on which to filter. Multiple names should be separated by commas. + /// + public PluginIdFilter(string p) + { + m_filters = p.Split(','); + + for (int i = 0; i < m_filters.Length; i++) + { + m_filters[i] = m_filters[i].Trim(); + } + } + + /// + /// Apply this filter to . + /// + /// PluginExtensionNode instance to check whether it passes the filter. + /// true if the plugin's ID matches one of the filters, false otherwise. + public bool Apply (PluginExtensionNode plugin) + { + for (int i = 0; i < m_filters.Length; i++) + { + if (m_filters[i] == plugin.ID) + { + return true; + } + } + + return false; + } + } +} diff --git a/OpenSim/Framework/PluginManager.cs b/OpenSim/Framework/PluginManager.cs new file mode 100644 index 0000000000..011709602b --- /dev/null +++ b/OpenSim/Framework/PluginManager.cs @@ -0,0 +1,563 @@ + +/* + * 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.Text; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Mono.Addins; +using Mono.Addins.Setup; +using Mono.Addins.Description; +using OpenSim.Framework; + + +namespace OpenSim.Framework +{ + /// + /// Manager for registries and plugins + /// + public class PluginManager : SetupService + { + public AddinRegistry PluginRegistry; + + public PluginManager(AddinRegistry registry): base (registry) + { + PluginRegistry = registry; + + } + + /// + /// Installs the plugin. + /// + /// + /// The plugin. + /// + /// + /// Arguments. + /// + public bool InstallPlugin(int ndx, out Dictionary result) + { + Dictionary res = new Dictionary(); + + PackageCollection pack = new PackageCollection(); + PackageCollection toUninstall; + DependencyCollection unresolved; + + IProgressStatus ps = new ConsoleProgressStatus(false); + + AddinRepositoryEntry[] available = GetSortedAvailbleAddins(); + + if (ndx > (available.Length - 1)) + { + MainConsole.Instance.Output("Selection out of range"); + result = res; + return false; + } + + AddinRepositoryEntry aentry = available[ndx]; + + Package p = Package.FromRepository(aentry); + pack.Add(p); + + ResolveDependencies(ps, pack, out toUninstall, out unresolved); + + // Attempt to install the plugin disabled + if (Install(ps, pack) == true) + { + MainConsole.Instance.Output("Ignore the following error..."); + PluginRegistry.Update(ps); + Addin addin = PluginRegistry.GetAddin(aentry.Addin.Id); + PluginRegistry.DisableAddin(addin.Id); + addin.Enabled = false; + + MainConsole.Instance.Output("Installation Success"); + ListInstalledAddins(out res); + result = res; + return true; + } + else + { + MainConsole.Instance.Output("Installation Failed"); + result = res; + return false; + } + } + + // Remove plugin + /// + /// Uns the install. + /// + /// + /// Arguments. + /// + public void UnInstall(int ndx) + { + Addin[] addins = GetSortedAddinList("RobustPlugin"); + + if (ndx > (addins.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + Addin addin = addins[ndx]; + MainConsole.Instance.OutputFormat("Uninstalling plugin {0}", addin.Id); + AddinManager.Registry.DisableAddin(addin.Id); + addin.Enabled = false; + IProgressStatus ps = new ConsoleProgressStatus(false); + Uninstall(ps, addin.Id); + MainConsole.Instance.Output("Uninstall Success - restart to complete operation"); + return; + } + + /// + /// Checks the installed. + /// + /// + /// The installed. + /// + public string CheckInstalled() + { + return "CheckInstall"; + } + + /// + /// Lists the installed addins. + /// + /// + /// Result. + /// + public void ListInstalledAddins(out Dictionary result) + { + Dictionary res = new Dictionary(); + + Addin[] addins = GetSortedAddinList("RobustPlugin"); + if(addins.Count() < 1) + { + MainConsole.Instance.Output("Error!"); + } + int count = 0; + foreach (Addin addin in addins) + { + Dictionary r = new Dictionary(); + r["enabled"] = addin.Enabled == true ? true : false; + r["name"] = addin.LocalId; + r["version"] = addin.Version; + + res.Add(count.ToString(), r); + + count++; + } + result = res; + return; + } + + // List compatible plugins in registered repositories + /// + /// Lists the available. + /// + /// + /// Result. + /// + public void ListAvailable(out Dictionary result) + { + Dictionary res = new Dictionary(); + + AddinRepositoryEntry[] addins = GetSortedAvailbleAddins(); + + int count = 0; + foreach (AddinRepositoryEntry addin in addins) + { + Dictionary r = new Dictionary(); + r["name"] = addin.Addin.Name; + r["version"] = addin.Addin.Version; + r["repository"] = addin.RepositoryName; + + res.Add(count.ToString(), r); + count++; + } + result = res; + return; + } + + // List available updates ** 1 + /// + /// Lists the updates. + /// + public void ListUpdates() + { + IProgressStatus ps = new ConsoleProgressStatus(true); + Console.WriteLine ("Looking for updates..."); + Repositories.UpdateAllRepositories (ps); + Console.WriteLine ("Available add-in updates:"); + + AddinRepositoryEntry[] entries = Repositories.GetAvailableUpdates(); + + foreach (AddinRepositoryEntry entry in entries) + { + Console.WriteLine(String.Format("{0}",entry.Addin.Id)); + } + } + + // Sync to repositories + /// + /// Update this instance. + /// + public string Update() + { + IProgressStatus ps = new ConsoleProgressStatus(true); + Repositories.UpdateAllRepositories(ps); + return "Update"; + } + + // Register a repository + /// + /// Register a repository with our server. + /// + /// + /// result of the action + /// + /// + /// The URL of the repository we want to add + /// + public bool AddRepository(string repo) + { + Repositories.RegisterRepository(null, repo, true); + PluginRegistry.Rebuild(null); + + return true; + } + + /// + /// Gets the repository. + /// + public void GetRepository() + { + Repositories.UpdateAllRepositories(new ConsoleProgressStatus(false)); + } + + // Remove a repository from the list + /// + /// Removes the repository. + /// + /// + /// Arguments. + /// + public void RemoveRepository(string[] args) + { + AddinRepository[] reps = Repositories.GetRepositories(); + Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title)); + if (reps.Length == 0) + { + MainConsole.Instance.Output("No repositories have been registered."); + return; + } + + int n = Convert.ToInt16(args[2]); + if (n > (reps.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + AddinRepository rep = reps[n]; + Repositories.RemoveRepository(rep.Url); + return; + } + + // Enable repository + /// + /// Enables the repository. + /// + /// + /// Arguments. + /// + public void EnableRepository(string[] args) + { + AddinRepository[] reps = Repositories.GetRepositories(); + Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title)); + if (reps.Length == 0) + { + MainConsole.Instance.Output("No repositories have been registered."); + return; + } + + int n = Convert.ToInt16(args[2]); + if (n > (reps.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + AddinRepository rep = reps[n]; + Repositories.SetRepositoryEnabled(rep.Url, true); + return; + } + + // Disable a repository + /// + /// Disables the repository. + /// + /// + /// Arguments. + /// + public void DisableRepository(string[] args) + { + AddinRepository[] reps = Repositories.GetRepositories(); + Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title)); + if (reps.Length == 0) + { + MainConsole.Instance.Output("No repositories have been registered."); + return; + } + + int n = Convert.ToInt16(args[2]); + if (n > (reps.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + AddinRepository rep = reps[n]; + Repositories.SetRepositoryEnabled(rep.Url, false); + return; + } + + // List registered repositories + /// + /// Lists the repositories. + /// + /// + /// Result. + /// + public void ListRepositories(out Dictionary result) + { + Dictionary res = new Dictionary(); + result = res; + + AddinRepository[] reps = GetSortedAddinRepo(); + if (reps.Length == 0) + { + MainConsole.Instance.Output("No repositories have been registered."); + return; + } + + int count = 0; + foreach (AddinRepository rep in reps) + { + Dictionary r = new Dictionary(); + r["enabled"] = rep.Enabled == true ? true : false; + r["name"] = rep.Name; + r["url"] = rep.Url; + + res.Add(count.ToString(), r); + count++; + } + return; + } + + /// + /// Updates the registry. + /// + public void UpdateRegistry() + { + PluginRegistry.Update(); + } + + // Show plugin info + /// + /// Addins the info. + /// + /// + /// The info. + /// + /// + /// Arguments. + /// + public bool AddinInfo(int ndx, out Dictionary result) + { + Dictionary res = new Dictionary(); + result = res; + + Addin[] addins = GetSortedAddinList("RobustPlugin"); + + if (ndx > (addins.Length - 1)) + { + MainConsole.Instance.Output("Selection out of range"); + return false; + } + // author category description + Addin addin = addins[ndx]; + + res["author"] = addin.Description.Author; + res["category"] = addin.Description.Category; + res["description"] = addin.Description.Description; + res["name"] = addin.Name; + res["url"] = addin.Description.Url; + res["file_name"] = addin.Description.FileName; + + result = res; + return true; + } + + // Disable a plugin + /// + /// Disables the plugin. + /// + /// + /// Arguments. + /// + public void DisablePlugin(string[] args) + { + Addin[] addins = GetSortedAddinList("RobustPlugin"); + + int n = Convert.ToInt16(args[2]); + if (n > (addins.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + Addin addin = addins[n]; + AddinManager.Registry.DisableAddin(addin.Id); + addin.Enabled = false; + return; + } + + // Enable plugin + /// + /// Enables the plugin. + /// + /// + /// Arguments. + /// + public void EnablePlugin(string[] args) + { + Addin[] addins = GetSortedAddinList("RobustPlugin"); + + int n = Convert.ToInt16(args[2]); + if (n > (addins.Length -1)) + { + MainConsole.Instance.Output("Selection out of range"); + return; + } + + Addin addin = addins[n]; + + addin.Enabled = true; + AddinManager.Registry.EnableAddin(addin.Id); + // AddinManager.Registry.Update(); + if(PluginRegistry.IsAddinEnabled(addin.Id)) + { + ConsoleProgressStatus ps = new ConsoleProgressStatus(false); + if (!AddinManager.AddinEngine.IsAddinLoaded(addin.Id)) + { + MainConsole.Instance.Output("Ignore the following error..."); + AddinManager.Registry.Rebuild(ps); + AddinManager.AddinEngine.LoadAddin(ps, addin.Id); + } + } + else + { + MainConsole.Instance.OutputFormat("Not Enabled in this domain {0}", addin.Name); + } + return; + } + + + + #region Util + private void Testing() + { + Addin[] list = Registry.GetAddins(); + + var addins = list.Where( a => a.Description.Category == "RobustPlugin"); + + foreach (Addin addin in addins) + { + MainConsole.Instance.OutputFormat("Addin {0}", addin.Name); + } + } + + // These will let us deal with numbered lists instead + // of needing to type in the full ids + private AddinRepositoryEntry[] GetSortedAvailbleAddins() + { + ArrayList list = new ArrayList(); + list.AddRange(Repositories.GetAvailableAddins()); + + AddinRepositoryEntry[] addins = list.ToArray(typeof(AddinRepositoryEntry)) as AddinRepositoryEntry[]; + + Array.Sort(addins,(r1,r2) => r1.Addin.Id.CompareTo(r2.Addin.Id)); + + return addins; + } + + private AddinRepository[] GetSortedAddinRepo() + { + ArrayList list = new ArrayList(); + list.AddRange(Repositories.GetRepositories()); + + AddinRepository[] repos = list.ToArray(typeof(AddinRepository)) as AddinRepository[]; + Array.Sort (repos,(r1,r2) => r1.Name.CompareTo(r2.Name)); + + return repos; + } + + private Addin[] GetSortedAddinList(string category) + { + + ArrayList xlist = new ArrayList(); + ArrayList list = new ArrayList(); + try + { + list.AddRange(PluginRegistry.GetAddins()); + } + catch (Exception) + { + Addin[] x = xlist.ToArray(typeof(Addin)) as Addin[]; + return x; + } + + foreach (Addin addin in list) + { + if (addin.Description.Category == category) + xlist.Add(addin); + } + + Addin[] addins = xlist.ToArray(typeof(Addin)) as Addin[]; + Array.Sort(addins,(r1,r2) => r1.Id.CompareTo(r2.Id)); + + return addins; + } + #endregion Util + } +} diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs new file mode 100644 index 0000000000..5484f5c2d5 --- /dev/null +++ b/OpenSim/Framework/Pool.cs @@ -0,0 +1,91 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Naive pool implementation. + /// + /// + /// Currently assumes that objects are in a useable state when returned. + /// + public class Pool + { + /// + /// Number of objects in the pool. + /// + public int Count + { + get + { + lock (m_pool) + return m_pool.Count; + } + } + + private Stack m_pool; + + /// + /// Maximum pool size. Beyond this, any returned objects are not pooled. + /// + private int m_maxPoolSize; + + private Func m_createFunction; + + public Pool(Func createFunction, int maxSize) + { + m_maxPoolSize = maxSize; + m_createFunction = createFunction; + m_pool = new Stack(m_maxPoolSize); + } + + public T GetObject() + { + lock (m_pool) + { + if (m_pool.Count > 0) + return m_pool.Pop(); + else + return m_createFunction(); + } + } + + public void ReturnObject(T obj) + { + lock (m_pool) + { + if (m_pool.Count >= m_maxPoolSize) + return; + else + m_pool.Push(obj); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/PresenceType.cs b/OpenSim/Framework/PresenceType.cs new file mode 100644 index 0000000000..8c4c6e686b --- /dev/null +++ b/OpenSim/Framework/PresenceType.cs @@ -0,0 +1,39 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Indicate the type of ScenePresence. + /// + public enum PresenceType + { + User, + Npc + } +} \ No newline at end of file diff --git a/OpenSim/Framework/PrimeNumberHelper.cs b/OpenSim/Framework/PrimeNumberHelper.cs new file mode 100644 index 0000000000..477c274c3b --- /dev/null +++ b/OpenSim/Framework/PrimeNumberHelper.cs @@ -0,0 +1,115 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Utility class that is used to find small prime numbers and test is number prime number. + /// + public static class PrimeNumberHelper + { + /// + /// Precalculated prime numbers. + /// + private static readonly int[] Primes = new int[] + { + 3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, + 293, 353, 431, 521, 631, 761, 919, 1103, 1327, 1597, 1931, 2333, + 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, + 17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, + 90523, 108631, 130363, 156437, 187751, 225307, 270371, 324449, + 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263, + 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, + 5999471, 7199369 + }; + + /// + /// Get prime number that is equal or larger than . + /// + /// + /// Minimal returned prime number. + /// + /// + /// Primer number that is equal or larger than . If is too large, return -1. + /// + public static int GetPrime(int min) + { + if (min <= 2) + return 2; + + if (Primes[ Primes.Length - 1 ] < min) + { + for (int i = min | 1 ; i < 0x7FFFFFFF ; i += 2) + { + if (IsPrime(i)) + return i; + } + + return -1; + } + + for (int i = Primes.Length - 2 ; i >= 0 ; i--) + { + if (min == Primes[ i ]) + return min; + + if (min > Primes[ i ]) + return Primes[ i + 1 ]; + } + + return 2; + } + + /// + /// Just basic Sieve of Eratosthenes prime number test. + /// + /// + /// Number that is tested. + /// + /// + /// true, if is prime number; otherwise false. + /// + public static bool IsPrime(int candinate) + { + if ((candinate & 1) == 0) + + // Even number - only prime if 2 + return candinate == 2; + + int upperBound = (int) Math.Sqrt(candinate); + for (int i = 3 ; i < upperBound ; i += 2) + { + if (candinate % i == 0) + return false; + } + + return true; + } + } +} diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs new file mode 100644 index 0000000000..c8a53763af --- /dev/null +++ b/OpenSim/Framework/PrimitiveBaseShape.cs @@ -0,0 +1,1531 @@ +/* + * 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.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Reflection; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + public enum ProfileShape : byte + { + Circle = 0, + Square = 1, + IsometricTriangle = 2, + EquilateralTriangle = 3, + RightTriangle = 4, + HalfCircle = 5 + } + + public enum HollowShape : byte + { + Same = 0, + Circle = 16, + Square = 32, + Triangle = 48 + } + + public enum PCodeEnum : byte + { + Primitive = 9, + Avatar = 47, + Grass = 95, + NewTree = 111, + ParticleSystem = 143, + Tree = 255 + } + + public enum Extrusion : byte + { + Straight = 16, + Curve1 = 32, + Curve2 = 48, + Flexible = 128 + } + + [Serializable] + public class PrimitiveBaseShape + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly byte[] DEFAULT_TEXTURE = new Primitive.TextureEntry(new UUID("89556747-24cb-43ed-920b-47caed15465f")).GetBytes(); + + private byte[] m_textureEntry; + + private ushort _pathBegin; + private byte _pathCurve; + private ushort _pathEnd; + private sbyte _pathRadiusOffset; + private byte _pathRevolutions; + private byte _pathScaleX; + private byte _pathScaleY; + private byte _pathShearX; + private byte _pathShearY; + private sbyte _pathSkew; + private sbyte _pathTaperX; + private sbyte _pathTaperY; + private sbyte _pathTwist; + private sbyte _pathTwistBegin; + private byte _pCode; + private ushort _profileBegin; + private ushort _profileEnd; + private ushort _profileHollow; + private Vector3 _scale; + private byte _state; + private byte _lastattach; + private ProfileShape _profileShape; + private HollowShape _hollowShape; + + // Sculpted + [XmlIgnore] private UUID _sculptTexture; + [XmlIgnore] private byte _sculptType; + [XmlIgnore] private byte[] _sculptData = Utils.EmptyBytes; + + // Flexi + [XmlIgnore] private int _flexiSoftness; + [XmlIgnore] private float _flexiTension; + [XmlIgnore] private float _flexiDrag; + [XmlIgnore] private float _flexiGravity; + [XmlIgnore] private float _flexiWind; + [XmlIgnore] private float _flexiForceX; + [XmlIgnore] private float _flexiForceY; + [XmlIgnore] private float _flexiForceZ; + + //Bright n sparkly + [XmlIgnore] private float _lightColorR; + [XmlIgnore] private float _lightColorG; + [XmlIgnore] private float _lightColorB; + [XmlIgnore] private float _lightColorA = 1.0f; + [XmlIgnore] private float _lightRadius; + [XmlIgnore] private float _lightCutoff; + [XmlIgnore] private float _lightFalloff; + [XmlIgnore] private float _lightIntensity = 1.0f; + [XmlIgnore] private bool _flexiEntry; + [XmlIgnore] private bool _lightEntry; + [XmlIgnore] private bool _sculptEntry; + + // Light Projection Filter + [XmlIgnore] private bool _projectionEntry; + [XmlIgnore] private UUID _projectionTextureID; + [XmlIgnore] private float _projectionFOV; + [XmlIgnore] private float _projectionFocus; + [XmlIgnore] private float _projectionAmb; + + public byte ProfileCurve + { + get { return (byte)((byte)HollowShape | (byte)ProfileShape); } + + set + { + // Handle hollow shape component + byte hollowShapeByte = (byte)(value & 0xf0); + + if (!Enum.IsDefined(typeof(HollowShape), hollowShapeByte)) + { + m_log.WarnFormat( + "[SHAPE]: Attempt to set a ProfileCurve with a hollow shape value of {0}, which isn't a valid enum. Replacing with default shape.", + hollowShapeByte); + + this._hollowShape = HollowShape.Same; + } + else + { + this._hollowShape = (HollowShape)hollowShapeByte; + } + + // Handle profile shape component + byte profileShapeByte = (byte)(value & 0xf); + + if (!Enum.IsDefined(typeof(ProfileShape), profileShapeByte)) + { + m_log.WarnFormat( + "[SHAPE]: Attempt to set a ProfileCurve with a profile shape value of {0}, which isn't a valid enum. Replacing with square.", + profileShapeByte); + + this._profileShape = ProfileShape.Square; + } + else + { + this._profileShape = (ProfileShape)profileShapeByte; + } + } + } + + /// + /// Entries to store media textures on each face + /// + /// Do not change this value directly - always do it through an IMoapModule. + /// Lock before manipulating. + public MediaList Media { get; set; } + + public PrimitiveBaseShape() + { + PCode = (byte)PCodeEnum.Primitive; + m_textureEntry = DEFAULT_TEXTURE; + } + + /// + /// Construct a PrimitiveBaseShape object from a OpenMetaverse.Primitive object + /// + /// + public PrimitiveBaseShape(Primitive prim) + { +// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID); + + PCode = (byte)prim.PrimData.PCode; + + State = prim.PrimData.State; + LastAttachPoint = prim.PrimData.State; + PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin); + PathEnd = Primitive.PackEndCut(prim.PrimData.PathEnd); + PathScaleX = Primitive.PackPathScale(prim.PrimData.PathScaleX); + PathScaleY = Primitive.PackPathScale(prim.PrimData.PathScaleY); + PathShearX = (byte)Primitive.PackPathShear(prim.PrimData.PathShearX); + PathShearY = (byte)Primitive.PackPathShear(prim.PrimData.PathShearY); + PathSkew = Primitive.PackPathTwist(prim.PrimData.PathSkew); + ProfileBegin = Primitive.PackBeginCut(prim.PrimData.ProfileBegin); + ProfileEnd = Primitive.PackEndCut(prim.PrimData.ProfileEnd); + Scale = prim.Scale; + PathCurve = (byte)prim.PrimData.PathCurve; + ProfileCurve = (byte)prim.PrimData.ProfileCurve; + ProfileHollow = Primitive.PackProfileHollow(prim.PrimData.ProfileHollow); + PathRadiusOffset = Primitive.PackPathTwist(prim.PrimData.PathRadiusOffset); + PathRevolutions = Primitive.PackPathRevolutions(prim.PrimData.PathRevolutions); + PathTaperX = Primitive.PackPathTaper(prim.PrimData.PathTaperX); + PathTaperY = Primitive.PackPathTaper(prim.PrimData.PathTaperY); + PathTwist = Primitive.PackPathTwist(prim.PrimData.PathTwist); + PathTwistBegin = Primitive.PackPathTwist(prim.PrimData.PathTwistBegin); + + m_textureEntry = prim.Textures.GetBytes(); + + if (prim.Sculpt != null) + { + SculptEntry = (prim.Sculpt.Type != OpenMetaverse.SculptType.None); + SculptData = prim.Sculpt.GetBytes(); + SculptTexture = prim.Sculpt.SculptTexture; + SculptType = (byte)prim.Sculpt.Type; + } + else + { + SculptType = (byte)OpenMetaverse.SculptType.None; + } + } + + [XmlIgnore] + public Primitive.TextureEntry Textures + { + get + { +// m_log.DebugFormat("[SHAPE]: get m_textureEntry length {0}", m_textureEntry.Length); + try { return new Primitive.TextureEntry(m_textureEntry, 0, m_textureEntry.Length); } + catch { } + + m_log.Warn("[SHAPE]: Failed to decode texture, length=" + ((m_textureEntry != null) ? m_textureEntry.Length : 0)); + return new Primitive.TextureEntry(UUID.Zero); + } + + set { m_textureEntry = value.GetBytes(); } + } + + public byte[] TextureEntry + { + get { return m_textureEntry; } + + set + { + if (value == null) + m_textureEntry = new byte[1]; + else + m_textureEntry = value; + } + } + + public static PrimitiveBaseShape Default + { + get + { + PrimitiveBaseShape boxShape = CreateBox(); + + boxShape.SetScale(0.5f); + + return boxShape; + } + } + + public static PrimitiveBaseShape Create() + { + PrimitiveBaseShape shape = new PrimitiveBaseShape(); + return shape; + } + + public static PrimitiveBaseShape CreateBox() + { + PrimitiveBaseShape shape = Create(); + + shape._pathCurve = (byte) Extrusion.Straight; + shape._profileShape = ProfileShape.Square; + shape._pathScaleX = 100; + shape._pathScaleY = 100; + + return shape; + } + + public static PrimitiveBaseShape CreateSphere() + { + PrimitiveBaseShape shape = Create(); + + shape._pathCurve = (byte) Extrusion.Curve1; + shape._profileShape = ProfileShape.HalfCircle; + shape._pathScaleX = 100; + shape._pathScaleY = 100; + + return shape; + } + + public static PrimitiveBaseShape CreateCylinder() + { + PrimitiveBaseShape shape = Create(); + + shape._pathCurve = (byte) Extrusion.Curve1; + shape._profileShape = ProfileShape.Square; + + shape._pathScaleX = 100; + shape._pathScaleY = 100; + + return shape; + } + + public void SetScale(float side) + { + _scale = new Vector3(side, side, side); + } + + public void SetHeigth(float height) + { + _scale.Z = height; + } + + public void SetRadius(float radius) + { + _scale.X = _scale.Y = radius * 2f; + } + + // TODO: void returns need to change of course + public virtual void GetMesh() + { + } + + public PrimitiveBaseShape Copy() + { + return (PrimitiveBaseShape) MemberwiseClone(); + } + + public static PrimitiveBaseShape CreateCylinder(float radius, float heigth) + { + PrimitiveBaseShape shape = CreateCylinder(); + + shape.SetHeigth(heigth); + shape.SetRadius(radius); + + return shape; + } + + public void SetPathRange(Vector3 pathRange) + { + _pathBegin = Primitive.PackBeginCut(pathRange.X); + _pathEnd = Primitive.PackEndCut(pathRange.Y); + } + + public void SetPathRange(float begin, float end) + { + _pathBegin = Primitive.PackBeginCut(begin); + _pathEnd = Primitive.PackEndCut(end); + } + + public void SetSculptProperties(byte sculptType, UUID SculptTextureUUID) + { + _sculptType = sculptType; + _sculptTexture = SculptTextureUUID; + } + + public void SetProfileRange(Vector3 profileRange) + { + _profileBegin = Primitive.PackBeginCut(profileRange.X); + _profileEnd = Primitive.PackEndCut(profileRange.Y); + } + + public void SetProfileRange(float begin, float end) + { + _profileBegin = Primitive.PackBeginCut(begin); + _profileEnd = Primitive.PackEndCut(end); + } + + public byte[] ExtraParams + { + get + { + return ExtraParamsToBytes(); + } + set + { + ReadInExtraParamsBytes(value); + } + } + + public ushort PathBegin { + get { + return _pathBegin; + } + set { + _pathBegin = value; + } + } + + public byte PathCurve { + get { + return _pathCurve; + } + set { + _pathCurve = value; + } + } + + public ushort PathEnd { + get { + return _pathEnd; + } + set { + _pathEnd = value; + } + } + + public sbyte PathRadiusOffset { + get { + return _pathRadiusOffset; + } + set { + _pathRadiusOffset = value; + } + } + + public byte PathRevolutions { + get { + return _pathRevolutions; + } + set { + _pathRevolutions = value; + } + } + + public byte PathScaleX { + get { + return _pathScaleX; + } + set { + _pathScaleX = value; + } + } + + public byte PathScaleY { + get { + return _pathScaleY; + } + set { + _pathScaleY = value; + } + } + + public byte PathShearX { + get { + return _pathShearX; + } + set { + _pathShearX = value; + } + } + + public byte PathShearY { + get { + return _pathShearY; + } + set { + _pathShearY = value; + } + } + + public sbyte PathSkew { + get { + return _pathSkew; + } + set { + _pathSkew = value; + } + } + + public sbyte PathTaperX { + get { + return _pathTaperX; + } + set { + _pathTaperX = value; + } + } + + public sbyte PathTaperY { + get { + return _pathTaperY; + } + set { + _pathTaperY = value; + } + } + + public sbyte PathTwist { + get { + return _pathTwist; + } + set { + _pathTwist = value; + } + } + + public sbyte PathTwistBegin { + get { + return _pathTwistBegin; + } + set { + _pathTwistBegin = value; + } + } + + public byte PCode { + get { + return _pCode; + } + set { + _pCode = value; + } + } + + public ushort ProfileBegin { + get { + return _profileBegin; + } + set { + _profileBegin = value; + } + } + + public ushort ProfileEnd { + get { + return _profileEnd; + } + set { + _profileEnd = value; + } + } + + public ushort ProfileHollow { + get { + return _profileHollow; + } + set { + _profileHollow = value; + } + } + + public Vector3 Scale { + get { + return _scale; + } + set { + _scale = value; + } + } + + public byte State { + get { + return _state; + } + set { + _state = value; + } + } + + public byte LastAttachPoint { + get { + return _lastattach; + } + set { + _lastattach = value; + } + } + + public ProfileShape ProfileShape { + get { + return _profileShape; + } + set { + _profileShape = value; + } + } + + public HollowShape HollowShape { + get { + return _hollowShape; + } + set { + _hollowShape = value; + } + } + + public UUID SculptTexture { + get { + return _sculptTexture; + } + set { + _sculptTexture = value; + } + } + + public byte SculptType + { + get + { + return _sculptType; + } + set + { + _sculptType = value; + } + } + + // This is only used at runtime. For sculpties this holds the texture data, and for meshes + // the mesh data. + public byte[] SculptData + { + get + { + return _sculptData; + } + set + { +// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Setting SculptData to data with length {0}", value.Length); + _sculptData = value; + } + } + + public int FlexiSoftness + { + get + { + return _flexiSoftness; + } + set + { + _flexiSoftness = value; + } + } + + public float FlexiTension { + get { + return _flexiTension; + } + set { + _flexiTension = value; + } + } + + public float FlexiDrag { + get { + return _flexiDrag; + } + set { + _flexiDrag = value; + } + } + + public float FlexiGravity { + get { + return _flexiGravity; + } + set { + _flexiGravity = value; + } + } + + public float FlexiWind { + get { + return _flexiWind; + } + set { + _flexiWind = value; + } + } + + public float FlexiForceX { + get { + return _flexiForceX; + } + set { + _flexiForceX = value; + } + } + + public float FlexiForceY { + get { + return _flexiForceY; + } + set { + _flexiForceY = value; + } + } + + public float FlexiForceZ { + get { + return _flexiForceZ; + } + set { + _flexiForceZ = value; + } + } + + public float LightColorR { + get { + return _lightColorR; + } + set { + _lightColorR = value; + } + } + + public float LightColorG { + get { + return _lightColorG; + } + set { + _lightColorG = value; + } + } + + public float LightColorB { + get { + return _lightColorB; + } + set { + _lightColorB = value; + } + } + + public float LightColorA { + get { + return _lightColorA; + } + set { + _lightColorA = value; + } + } + + public float LightRadius { + get { + return _lightRadius; + } + set { + _lightRadius = value; + } + } + + public float LightCutoff { + get { + return _lightCutoff; + } + set { + _lightCutoff = value; + } + } + + public float LightFalloff { + get { + return _lightFalloff; + } + set { + _lightFalloff = value; + } + } + + public float LightIntensity { + get { + return _lightIntensity; + } + set { + _lightIntensity = value; + } + } + + public bool FlexiEntry { + get { + return _flexiEntry; + } + set { + _flexiEntry = value; + } + } + + public bool LightEntry { + get { + return _lightEntry; + } + set { + _lightEntry = value; + } + } + + public bool SculptEntry { + get { + return _sculptEntry; + } + set { + _sculptEntry = value; + } + } + + public bool ProjectionEntry { + get { + return _projectionEntry; + } + set { + _projectionEntry = value; + } + } + + public UUID ProjectionTextureUUID { + get { + return _projectionTextureID; + } + set { + _projectionTextureID = value; + } + } + + public float ProjectionFOV { + get { + return _projectionFOV; + } + set { + _projectionFOV = value; + } + } + + public float ProjectionFocus { + get { + return _projectionFocus; + } + set { + _projectionFocus = value; + } + } + + public float ProjectionAmbiance { + get { + return _projectionAmb; + } + set { + _projectionAmb = value; + } + } + + public ulong GetMeshKey(Vector3 size, float lod) + { + ulong hash = 5381; + + hash = djb2(hash, this.PathCurve); + hash = djb2(hash, (byte)((byte)this.HollowShape | (byte)this.ProfileShape)); + hash = djb2(hash, this.PathBegin); + hash = djb2(hash, this.PathEnd); + hash = djb2(hash, this.PathScaleX); + hash = djb2(hash, this.PathScaleY); + hash = djb2(hash, this.PathShearX); + hash = djb2(hash, this.PathShearY); + hash = djb2(hash, (byte)this.PathTwist); + hash = djb2(hash, (byte)this.PathTwistBegin); + hash = djb2(hash, (byte)this.PathRadiusOffset); + hash = djb2(hash, (byte)this.PathTaperX); + hash = djb2(hash, (byte)this.PathTaperY); + hash = djb2(hash, this.PathRevolutions); + hash = djb2(hash, (byte)this.PathSkew); + hash = djb2(hash, this.ProfileBegin); + hash = djb2(hash, this.ProfileEnd); + hash = djb2(hash, this.ProfileHollow); + + // TODO: Separate scale out from the primitive shape data (after + // scaling is supported at the physics engine level) + byte[] scaleBytes = size.GetBytes(); + for (int i = 0; i < scaleBytes.Length; i++) + hash = djb2(hash, scaleBytes[i]); + + // Include LOD in hash, accounting for endianness + byte[] lodBytes = new byte[4]; + Buffer.BlockCopy(BitConverter.GetBytes(lod), 0, lodBytes, 0, 4); + if (!BitConverter.IsLittleEndian) + { + Array.Reverse(lodBytes, 0, 4); + } + for (int i = 0; i < lodBytes.Length; i++) + hash = djb2(hash, lodBytes[i]); + + // include sculpt UUID + if (this.SculptEntry) + { + scaleBytes = this.SculptTexture.GetBytes(); + for (int i = 0; i < scaleBytes.Length; i++) + hash = djb2(hash, scaleBytes[i]); + } + + return hash; + } + + private ulong djb2(ulong hash, byte c) + { + return ((hash << 5) + hash) + (ulong)c; + } + + private ulong djb2(ulong hash, ushort c) + { + hash = ((hash << 5) + hash) + (ulong)((byte)c); + return ((hash << 5) + hash) + (ulong)(c >> 8); + } + + public byte[] ExtraParamsToBytes() + { +// m_log.DebugFormat("[EXTRAPARAMS]: Called ExtraParamsToBytes()"); + + ushort FlexiEP = 0x10; + ushort LightEP = 0x20; + ushort SculptEP = 0x30; + ushort ProjectionEP = 0x40; + + int i = 0; + uint TotalBytesLength = 1; // ExtraParamsNum + + uint ExtraParamsNum = 0; + if (_flexiEntry) + { + ExtraParamsNum++; + TotalBytesLength += 16;// data + TotalBytesLength += 2 + 4; // type + } + + if (_lightEntry) + { + ExtraParamsNum++; + TotalBytesLength += 16;// data + TotalBytesLength += 2 + 4; // type + } + + if (_sculptEntry) + { + ExtraParamsNum++; + TotalBytesLength += 17;// data + TotalBytesLength += 2 + 4; // type + } + + if (_projectionEntry) + { + ExtraParamsNum++; + TotalBytesLength += 28;// data + TotalBytesLength += 2 + 4;// type + } + + byte[] returnbytes = new byte[TotalBytesLength]; + + // uint paramlength = ExtraParamsNum; + + // Stick in the number of parameters + returnbytes[i++] = (byte)ExtraParamsNum; + + if (_flexiEntry) + { + byte[] FlexiData = GetFlexiBytes(); + + returnbytes[i++] = (byte)(FlexiEP % 256); + returnbytes[i++] = (byte)((FlexiEP >> 8) % 256); + + returnbytes[i++] = (byte)(FlexiData.Length % 256); + returnbytes[i++] = (byte)((FlexiData.Length >> 8) % 256); + returnbytes[i++] = (byte)((FlexiData.Length >> 16) % 256); + returnbytes[i++] = (byte)((FlexiData.Length >> 24) % 256); + Array.Copy(FlexiData, 0, returnbytes, i, FlexiData.Length); + i += FlexiData.Length; + } + + if (_lightEntry) + { + byte[] LightData = GetLightBytes(); + + returnbytes[i++] = (byte)(LightEP % 256); + returnbytes[i++] = (byte)((LightEP >> 8) % 256); + + returnbytes[i++] = (byte)(LightData.Length % 256); + returnbytes[i++] = (byte)((LightData.Length >> 8) % 256); + returnbytes[i++] = (byte)((LightData.Length >> 16) % 256); + returnbytes[i++] = (byte)((LightData.Length >> 24) % 256); + Array.Copy(LightData, 0, returnbytes, i, LightData.Length); + i += LightData.Length; + } + + if (_sculptEntry) + { + byte[] SculptData = GetSculptBytes(); + + returnbytes[i++] = (byte)(SculptEP % 256); + returnbytes[i++] = (byte)((SculptEP >> 8) % 256); + + returnbytes[i++] = (byte)(SculptData.Length % 256); + returnbytes[i++] = (byte)((SculptData.Length >> 8) % 256); + returnbytes[i++] = (byte)((SculptData.Length >> 16) % 256); + returnbytes[i++] = (byte)((SculptData.Length >> 24) % 256); + Array.Copy(SculptData, 0, returnbytes, i, SculptData.Length); + i += SculptData.Length; + } + + if (_projectionEntry) + { + byte[] ProjectionData = GetProjectionBytes(); + + returnbytes[i++] = (byte)(ProjectionEP % 256); + returnbytes[i++] = (byte)((ProjectionEP >> 8) % 256); + returnbytes[i++] = (byte)((ProjectionData.Length) % 256); + returnbytes[i++] = (byte)((ProjectionData.Length >> 16) % 256); + returnbytes[i++] = (byte)((ProjectionData.Length >> 20) % 256); + returnbytes[i++] = (byte)((ProjectionData.Length >> 24) % 256); + Array.Copy(ProjectionData, 0, returnbytes, i, ProjectionData.Length); + i += ProjectionData.Length; + } + + if (!_flexiEntry && !_lightEntry && !_sculptEntry && !_projectionEntry) + { + byte[] returnbyte = new byte[1]; + returnbyte[0] = 0; + return returnbyte; + } + + return returnbytes; + } + + public void ReadInUpdateExtraParam(ushort type, bool inUse, byte[] data) + { + const ushort FlexiEP = 0x10; + const ushort LightEP = 0x20; + const ushort SculptEP = 0x30; + const ushort ProjectionEP = 0x40; + + switch (type) + { + case FlexiEP: + if (!inUse) + { + _flexiEntry = false; + return; + } + ReadFlexiData(data, 0); + break; + + case LightEP: + if (!inUse) + { + _lightEntry = false; + return; + } + ReadLightData(data, 0); + break; + + case SculptEP: + if (!inUse) + { + _sculptEntry = false; + return; + } + ReadSculptData(data, 0); + break; + case ProjectionEP: + if (!inUse) + { + _projectionEntry = false; + return; + } + ReadProjectionData(data, 0); + break; + } + } + + public void ReadInExtraParamsBytes(byte[] data) + { + if (data == null || data.Length == 1) + return; + + const ushort FlexiEP = 0x10; + const ushort LightEP = 0x20; + const ushort SculptEP = 0x30; + const ushort ProjectionEP = 0x40; + + bool lGotFlexi = false; + bool lGotLight = false; + bool lGotSculpt = false; + bool lGotFilter = false; + + int i = 0; + byte extraParamCount = 0; + if (data.Length > 0) + { + extraParamCount = data[i++]; + } + + for (int k = 0; k < extraParamCount; k++) + { + ushort epType = Utils.BytesToUInt16(data, i); + + i += 2; + // uint paramLength = Helpers.BytesToUIntBig(data, i); + + i += 4; + switch (epType) + { + case FlexiEP: + ReadFlexiData(data, i); + i += 16; + lGotFlexi = true; + break; + + case LightEP: + ReadLightData(data, i); + i += 16; + lGotLight = true; + break; + + case SculptEP: + ReadSculptData(data, i); + i += 17; + lGotSculpt = true; + break; + case ProjectionEP: + ReadProjectionData(data, i); + i += 28; + lGotFilter = true; + break; + } + } + + if (!lGotFlexi) + _flexiEntry = false; + if (!lGotLight) + _lightEntry = false; + if (!lGotSculpt) + _sculptEntry = false; + if (!lGotFilter) + _projectionEntry = false; + } + + public void ReadSculptData(byte[] data, int pos) + { + UUID SculptUUID; + byte SculptTypel; + + if (data.Length-pos >= 17) + { + _sculptEntry = true; + byte[] SculptTextureUUID = new byte[16]; + SculptTypel = data[16 + pos]; + Array.Copy(data, pos, SculptTextureUUID,0, 16); + SculptUUID = new UUID(SculptTextureUUID, 0); + } + else + { + _sculptEntry = false; + SculptUUID = UUID.Zero; + SculptTypel = 0x00; + } + + if (_sculptEntry) + { + if (_sculptType != (byte)1 && _sculptType != (byte)2 && _sculptType != (byte)3 && _sculptType != (byte)4) + _sculptType = 4; + } + + _sculptTexture = SculptUUID; + _sculptType = SculptTypel; + //m_log.Info("[SCULPT]:" + SculptUUID.ToString()); + } + + public byte[] GetSculptBytes() + { + byte[] data = new byte[17]; + + _sculptTexture.GetBytes().CopyTo(data, 0); + data[16] = (byte)_sculptType; + + return data; + } + + public void ReadFlexiData(byte[] data, int pos) + { + if (data.Length-pos >= 16) + { + _flexiEntry = true; + _flexiSoftness = ((data[pos] & 0x80) >> 6) | ((data[pos + 1] & 0x80) >> 7); + + _flexiTension = (float)(data[pos++] & 0x7F) / 10.0f; + _flexiDrag = (float)(data[pos++] & 0x7F) / 10.0f; + _flexiGravity = (float)(data[pos++] / 10.0f) - 10.0f; + _flexiWind = (float)data[pos++] / 10.0f; + Vector3 lForce = new Vector3(data, pos); + _flexiForceX = lForce.X; + _flexiForceY = lForce.Y; + _flexiForceZ = lForce.Z; + } + else + { + _flexiEntry = false; + _flexiSoftness = 0; + + _flexiTension = 0.0f; + _flexiDrag = 0.0f; + _flexiGravity = 0.0f; + _flexiWind = 0.0f; + _flexiForceX = 0f; + _flexiForceY = 0f; + _flexiForceZ = 0f; + } + } + + public byte[] GetFlexiBytes() + { + byte[] data = new byte[16]; + int i = 0; + + // Softness is packed in the upper bits of tension and drag + data[i] = (byte)((_flexiSoftness & 2) << 6); + data[i + 1] = (byte)((_flexiSoftness & 1) << 7); + + data[i++] |= (byte)((byte)(_flexiTension * 10.01f) & 0x7F); + data[i++] |= (byte)((byte)(_flexiDrag * 10.01f) & 0x7F); + data[i++] = (byte)((_flexiGravity + 10.0f) * 10.01f); + data[i++] = (byte)(_flexiWind * 10.01f); + Vector3 lForce = new Vector3(_flexiForceX, _flexiForceY, _flexiForceZ); + lForce.GetBytes().CopyTo(data, i); + + return data; + } + + public void ReadLightData(byte[] data, int pos) + { + if (data.Length - pos >= 16) + { + _lightEntry = true; + Color4 lColor = new Color4(data, pos, false); + _lightIntensity = lColor.A; + _lightColorA = 1f; + _lightColorR = lColor.R; + _lightColorG = lColor.G; + _lightColorB = lColor.B; + + _lightRadius = Utils.BytesToFloat(data, pos + 4); + _lightCutoff = Utils.BytesToFloat(data, pos + 8); + _lightFalloff = Utils.BytesToFloat(data, pos + 12); + } + else + { + _lightEntry = false; + _lightColorA = 1f; + _lightColorR = 0f; + _lightColorG = 0f; + _lightColorB = 0f; + _lightRadius = 0f; + _lightCutoff = 0f; + _lightFalloff = 0f; + _lightIntensity = 0f; + } + } + + public byte[] GetLightBytes() + { + byte[] data = new byte[16]; + + // Alpha channel in color is intensity + Color4 tmpColor = new Color4(_lightColorR,_lightColorG,_lightColorB,_lightIntensity); + + tmpColor.GetBytes().CopyTo(data, 0); + Utils.FloatToBytes(_lightRadius).CopyTo(data, 4); + Utils.FloatToBytes(_lightCutoff).CopyTo(data, 8); + Utils.FloatToBytes(_lightFalloff).CopyTo(data, 12); + + return data; + } + + public void ReadProjectionData(byte[] data, int pos) + { + byte[] ProjectionTextureUUID = new byte[16]; + + if (data.Length - pos >= 28) + { + _projectionEntry = true; + Array.Copy(data, pos, ProjectionTextureUUID,0, 16); + _projectionTextureID = new UUID(ProjectionTextureUUID, 0); + + _projectionFOV = Utils.BytesToFloat(data, pos + 16); + _projectionFocus = Utils.BytesToFloat(data, pos + 20); + _projectionAmb = Utils.BytesToFloat(data, pos + 24); + } + else + { + _projectionEntry = false; + _projectionTextureID = UUID.Zero; + _projectionFOV = 0f; + _projectionFocus = 0f; + _projectionAmb = 0f; + } + } + + public byte[] GetProjectionBytes() + { + byte[] data = new byte[28]; + + _projectionTextureID.GetBytes().CopyTo(data, 0); + Utils.FloatToBytes(_projectionFOV).CopyTo(data, 16); + Utils.FloatToBytes(_projectionFocus).CopyTo(data, 20); + Utils.FloatToBytes(_projectionAmb).CopyTo(data, 24); + + return data; + } + + + /// + /// Creates a OpenMetaverse.Primitive and populates it with converted PrimitiveBaseShape values + /// + /// + public Primitive ToOmvPrimitive() + { + // position and rotation defaults here since they are not available in PrimitiveBaseShape + return ToOmvPrimitive(new Vector3(0.0f, 0.0f, 0.0f), + new Quaternion(0.0f, 0.0f, 0.0f, 1.0f)); + } + + + /// + /// Creates a OpenMetaverse.Primitive and populates it with converted PrimitiveBaseShape values + /// + /// + /// + /// + public Primitive ToOmvPrimitive(Vector3 position, Quaternion rotation) + { + OpenMetaverse.Primitive prim = new OpenMetaverse.Primitive(); + + prim.Scale = this.Scale; + prim.Position = position; + prim.Rotation = rotation; + + if (this.SculptEntry) + { + prim.Sculpt = new Primitive.SculptData(); + prim.Sculpt.Type = (OpenMetaverse.SculptType)this.SculptType; + prim.Sculpt.SculptTexture = this.SculptTexture; + } + + prim.PrimData.PathShearX = this.PathShearX < 128 ? (float)this.PathShearX * 0.01f : (float)(this.PathShearX - 256) * 0.01f; + prim.PrimData.PathShearY = this.PathShearY < 128 ? (float)this.PathShearY * 0.01f : (float)(this.PathShearY - 256) * 0.01f; + prim.PrimData.PathBegin = (float)this.PathBegin * 2.0e-5f; + prim.PrimData.PathEnd = 1.0f - (float)this.PathEnd * 2.0e-5f; + + prim.PrimData.PathScaleX = (200 - this.PathScaleX) * 0.01f; + prim.PrimData.PathScaleY = (200 - this.PathScaleY) * 0.01f; + + prim.PrimData.PathTaperX = this.PathTaperX * 0.01f; + prim.PrimData.PathTaperY = this.PathTaperY * 0.01f; + + prim.PrimData.PathTwistBegin = this.PathTwistBegin * 0.01f; + prim.PrimData.PathTwist = this.PathTwist * 0.01f; + + prim.PrimData.ProfileBegin = (float)this.ProfileBegin * 2.0e-5f; + prim.PrimData.ProfileEnd = 1.0f - (float)this.ProfileEnd * 2.0e-5f; + prim.PrimData.ProfileHollow = (float)this.ProfileHollow * 2.0e-5f; + + prim.PrimData.profileCurve = this.ProfileCurve; + prim.PrimData.ProfileHole = (HoleType)this.HollowShape; + + prim.PrimData.PathCurve = (PathCurve)this.PathCurve; + prim.PrimData.PathRadiusOffset = 0.01f * this.PathRadiusOffset; + prim.PrimData.PathRevolutions = 1.0f + 0.015f * this.PathRevolutions; + prim.PrimData.PathSkew = 0.01f * this.PathSkew; + + prim.PrimData.PCode = OpenMetaverse.PCode.Prim; + prim.PrimData.State = 0; + + if (this.FlexiEntry) + { + prim.Flexible = new Primitive.FlexibleData(); + prim.Flexible.Drag = this.FlexiDrag; + prim.Flexible.Force = new Vector3(this.FlexiForceX, this.FlexiForceY, this.FlexiForceZ); + prim.Flexible.Gravity = this.FlexiGravity; + prim.Flexible.Softness = this.FlexiSoftness; + prim.Flexible.Tension = this.FlexiTension; + prim.Flexible.Wind = this.FlexiWind; + } + + if (this.LightEntry) + { + prim.Light = new Primitive.LightData(); + prim.Light.Color = new Color4(this.LightColorR, this.LightColorG, this.LightColorB, this.LightColorA); + prim.Light.Cutoff = this.LightCutoff; + prim.Light.Falloff = this.LightFalloff; + prim.Light.Intensity = this.LightIntensity; + prim.Light.Radius = this.LightRadius; + } + + prim.Textures = this.Textures; + + prim.Properties = new Primitive.ObjectProperties(); + prim.Properties.Name = "Primitive"; + prim.Properties.Description = ""; + prim.Properties.CreatorID = UUID.Zero; + prim.Properties.GroupID = UUID.Zero; + prim.Properties.OwnerID = UUID.Zero; + prim.Properties.Permissions = new Permissions(); + prim.Properties.SalePrice = 10; + prim.Properties.SaleType = new SaleType(); + + return prim; + } + + /// + /// Encapsulates a list of media entries. + /// + /// This class is necessary because we want to replace auto-serialization of MediaEntry with something more + /// OSD like and less vulnerable to change. + public class MediaList : List, IXmlSerializable + { + public const string MEDIA_TEXTURE_TYPE = "sl"; + + public MediaList() : base() {} + public MediaList(IEnumerable collection) : base(collection) {} + public MediaList(int capacity) : base(capacity) {} + + public XmlSchema GetSchema() + { + return null; + } + + public string ToXml() + { + lock (this) + { + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter xtw = new XmlTextWriter(sw)) + { + xtw.WriteStartElement("OSMedia"); + xtw.WriteAttributeString("type", MEDIA_TEXTURE_TYPE); + xtw.WriteAttributeString("version", "0.1"); + + OSDArray meArray = new OSDArray(); + foreach (MediaEntry me in this) + { + OSD osd = (null == me ? new OSD() : me.GetOSD()); + meArray.Add(osd); + } + + xtw.WriteStartElement("OSData"); + xtw.WriteRaw(OSDParser.SerializeLLSDXmlString(meArray)); + xtw.WriteEndElement(); + + xtw.WriteEndElement(); + + xtw.Flush(); + return sw.ToString(); + } + } + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteRaw(ToXml()); + } + + public static MediaList FromXml(string rawXml) + { + MediaList ml = new MediaList(); + ml.ReadXml(rawXml); + return ml; + } + + public void ReadXml(string rawXml) + { + using (StringReader sr = new StringReader(rawXml)) + { + using (XmlTextReader xtr = new XmlTextReader(sr)) + { + xtr.MoveToContent(); + + string type = xtr.GetAttribute("type"); + //m_log.DebugFormat("[MOAP]: Loaded media texture entry with type {0}", type); + + if (type != MEDIA_TEXTURE_TYPE) + return; + + xtr.ReadStartElement("OSMedia"); + + OSDArray osdMeArray = (OSDArray)OSDParser.DeserializeLLSDXml(xtr.ReadInnerXml()); + foreach (OSD osdMe in osdMeArray) + { + MediaEntry me = (osdMe is OSDMap ? MediaEntry.FromOSD(osdMe) : new MediaEntry()); + Add(me); + } + + xtr.ReadEndElement(); + } + } + } + + public void ReadXml(XmlReader reader) + { + if (reader.IsEmptyElement) + return; + + ReadXml(reader.ReadInnerXml()); + } + } + } +} diff --git a/OpenSim/Framework/PriorityQueue.cs b/OpenSim/Framework/PriorityQueue.cs new file mode 100644 index 0000000000..e7a7f7f13c --- /dev/null +++ b/OpenSim/Framework/PriorityQueue.cs @@ -0,0 +1,323 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Framework.Client; +using log4net; + +namespace OpenSim.Framework +{ + public class PriorityQueue + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public delegate bool UpdatePriorityHandler(ref uint priority, ISceneEntity entity); + + /// + /// Total number of queues (priorities) available + /// + public const uint NumberOfQueues = 12; + + /// + /// Number of queuest (priorities) that are processed immediately + /// [] m_heaps = new MinHeap[NumberOfQueues]; + private Dictionary m_lookupTable; + + // internal state used to ensure the deqeues are spread across the priority + // queues "fairly". queuecounts is the amount to pull from each queue in + // each pass. weighted towards the higher priority queues + private uint m_nextQueue = 0; + private uint m_countFromQueue = 0; + private uint[] m_queueCounts = { 8, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 1 }; + + // next request is a counter of the number of updates queued, it provides + // a total ordering on the updates coming through the queue and is more + // lightweight (and more discriminating) than tick count + private UInt64 m_nextRequest = 0; + + /// + /// Lock for enqueue and dequeue operations on the priority queue + /// + private object m_syncRoot = new object(); + public object SyncRoot { + get { return this.m_syncRoot; } + } + +#region constructor + public PriorityQueue() : this(MinHeap.DEFAULT_CAPACITY) { } + + public PriorityQueue(int capacity) + { + m_lookupTable = new Dictionary(capacity); + + for (int i = 0; i < m_heaps.Length; ++i) + m_heaps[i] = new MinHeap(capacity); + + m_nextQueue = NumberOfImmediateQueues; + m_countFromQueue = m_queueCounts[m_nextQueue]; + } +#endregion Constructor + +#region PublicMethods + /// + /// Return the number of items in the queues + /// + public int Count + { + get + { + int count = 0; + for (int i = 0; i < m_heaps.Length; ++i) + count += m_heaps[i].Count; + + return count; + } + } + + /// + /// Enqueue an item into the specified priority queue + /// + public bool Enqueue(uint pqueue, IEntityUpdate value) + { + LookupItem lookup; + + uint localid = value.Entity.LocalId; + UInt64 entry = m_nextRequest++; + if (m_lookupTable.TryGetValue(localid, out lookup)) + { + entry = lookup.Heap[lookup.Handle].EntryOrder; + value.Update(lookup.Heap[lookup.Handle].Value); + lookup.Heap.Remove(lookup.Handle); + } + + pqueue = Util.Clamp(pqueue, 0, NumberOfQueues - 1); + lookup.Heap = m_heaps[pqueue]; + lookup.Heap.Add(new MinHeapItem(pqueue, entry, value), ref lookup.Handle); + m_lookupTable[localid] = lookup; + + return true; + } + + /// + /// Remove an item from one of the queues. Specifically, it removes the + /// oldest item from the next queue in order to provide fair access to + /// all of the queues + /// + public bool TryDequeue(out IEntityUpdate value, out Int32 timeinqueue) + { + // If there is anything in priority queue 0, return it first no + // matter what else. Breaks fairness. But very useful. + for (int iq = 0; iq < NumberOfImmediateQueues; iq++) + { + if (m_heaps[iq].Count > 0) + { + MinHeapItem item = m_heaps[iq].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); + value = item.Value; + + return true; + } + } + + // To get the fair queing, we cycle through each of the + // queues when finding an element to dequeue. + // We pull (NumberOfQueues - QueueIndex) items from each queue in order + // to give lower numbered queues a higher priority and higher percentage + // of the bandwidth. + + // Check for more items to be pulled from the current queue + if (m_heaps[m_nextQueue].Count > 0 && m_countFromQueue > 0) + { + m_countFromQueue--; + + MinHeapItem item = m_heaps[m_nextQueue].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); + value = item.Value; + + return true; + } + + // Find the next non-immediate queue with updates in it + for (int i = 0; i < NumberOfQueues; ++i) + { + m_nextQueue = (uint)((m_nextQueue + 1) % NumberOfQueues); + m_countFromQueue = m_queueCounts[m_nextQueue]; + + // if this is one of the immediate queues, just skip it + if (m_nextQueue < NumberOfImmediateQueues) + continue; + + if (m_heaps[m_nextQueue].Count > 0) + { + m_countFromQueue--; + + MinHeapItem item = m_heaps[m_nextQueue].RemoveMin(); + m_lookupTable.Remove(item.Value.Entity.LocalId); + timeinqueue = Util.EnvironmentTickCountSubtract(item.EntryTime); + value = item.Value; + + return true; + } + } + + timeinqueue = 0; + value = default(IEntityUpdate); + return false; + } + + /// + /// Reapply the prioritization function to each of the updates currently + /// stored in the priority queues. + /// (this.m_lookupTable.Values)) + { + if (lookup.Heap.TryGetValue(lookup.Handle, out item)) + { + uint pqueue = item.PriorityQueue; + uint localid = item.Value.Entity.LocalId; + + if (handler(ref pqueue, item.Value.Entity)) + { + // unless the priority queue has changed, there is no need to modify + // the entry + pqueue = Util.Clamp(pqueue, 0, NumberOfQueues - 1); + if (pqueue != item.PriorityQueue) + { + lookup.Heap.Remove(lookup.Handle); + + LookupItem litem = lookup; + litem.Heap = m_heaps[pqueue]; + litem.Heap.Add(new MinHeapItem(pqueue, item), ref litem.Handle); + m_lookupTable[localid] = litem; + } + } + else + { + // m_log.WarnFormat("[PQUEUE]: UpdatePriorityHandler returned false for {0}",item.Value.Entity.UUID); + lookup.Heap.Remove(lookup.Handle); + this.m_lookupTable.Remove(localid); + } + } + } + } + + /// + /// + public override string ToString() + { + string s = ""; + for (int i = 0; i < NumberOfQueues; i++) + s += String.Format("{0,7} ",m_heaps[i].Count); + return s; + } + +#endregion PublicMethods + +#region MinHeapItem + private struct MinHeapItem : IComparable + { + private IEntityUpdate value; + internal IEntityUpdate Value { + get { + return this.value; + } + } + + private uint pqueue; + internal uint PriorityQueue { + get { + return this.pqueue; + } + } + + private Int32 entrytime; + internal Int32 EntryTime { + get { + return this.entrytime; + } + } + + private UInt64 entryorder; + internal UInt64 EntryOrder + { + get { + return this.entryorder; + } + } + + internal MinHeapItem(uint pqueue, MinHeapItem other) + { + this.entrytime = other.entrytime; + this.entryorder = other.entryorder; + this.value = other.value; + this.pqueue = pqueue; + } + + internal MinHeapItem(uint pqueue, UInt64 entryorder, IEntityUpdate value) + { + this.entrytime = Util.EnvironmentTickCount(); + this.entryorder = entryorder; + this.value = value; + this.pqueue = pqueue; + } + + public override string ToString() + { + return String.Format("[{0},{1},{2}]",pqueue,entryorder,value.Entity.LocalId); + } + + public int CompareTo(MinHeapItem other) + { + // I'm assuming that the root part of an SOG is added to the update queue + // before the component parts + return Comparer.Default.Compare(this.EntryOrder, other.EntryOrder); + } + } +#endregion + +#region LookupItem + private struct LookupItem + { + internal MinHeap Heap; + internal IHandle Handle; + } +#endregion + } +} diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs new file mode 100644 index 0000000000..7c6569e30c --- /dev/null +++ b/OpenSim/Framework/RegionFlags.cs @@ -0,0 +1,54 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Region flags used internally by OpenSimulator to store installation specific information about regions. + /// + /// + /// Don't confuse with OpenMetaverse.RegionFlags which are client facing flags (i.e. they go over the wire). + /// Returned by IGridService.GetRegionFlags() + /// + [Flags] + public enum RegionFlags : int + { + DefaultRegion = 1, // Used for new Rez. Random if multiple defined + FallbackRegion = 2, // Regions we redirect to when the destination is down + RegionOnline = 4, // Set when a region comes online, unset when it unregisters and DeleteOnUnregister is false + NoDirectLogin = 8, // Region unavailable for direct logins (by name) + Persistent = 16, // Don't remove on unregister + LockedOut = 32, // Don't allow registration + NoMove = 64, // Don't allow moving this region + Reservation = 128, // This is an inactive reservation + Authenticate = 256, // Require authentication + Hyperlink = 512, // Record represents a HG link + DefaultHGRegion = 1024 // Record represents a default region for hypergrid teleports only. + } +} \ No newline at end of file diff --git a/OpenSim/Framework/RegionHandshakeArgs.cs b/OpenSim/Framework/RegionHandshakeArgs.cs new file mode 100644 index 0000000000..f5cb667eba --- /dev/null +++ b/OpenSim/Framework/RegionHandshakeArgs.cs @@ -0,0 +1,59 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class RegionHandshakeArgs : EventArgs + { + public bool isEstateManager; + public float billableFactor; + public float terrainHeightRange0; + public float terrainHeightRange1; + public float terrainHeightRange2; + public float terrainHeightRange3; + public float terrainStartHeight0; + public float terrainStartHeight1; + public float terrainStartHeight2; + public float terrainStartHeight3; + public byte simAccess; + public float waterHeight; + public uint regionFlags; + public string regionName; + public UUID SimOwner; + public UUID terrainBase0; + public UUID terrainBase1; + public UUID terrainBase2; + public UUID terrainBase3; + public UUID terrainDetail0; + public UUID terrainDetail1; + public UUID terrainDetail2; + public UUID terrainDetail3; + } +} diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs new file mode 100644 index 0000000000..79fbd962f9 --- /dev/null +++ b/OpenSim/Framework/RegionInfo.cs @@ -0,0 +1,1008 @@ +/* + * 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.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Xml; +using System.IO; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +//using OpenSim.Framework.Console; + +namespace OpenSim.Framework +{ + public class RegionLightShareData : ICloneable + { + public bool valid = false; + public UUID regionID = UUID.Zero; + public Vector3 waterColor = new Vector3(4.0f,38.0f,64.0f); + public float waterFogDensityExponent = 4.0f; + public float underwaterFogModifier = 0.25f; + public Vector3 reflectionWaveletScale = new Vector3(2.0f,2.0f,2.0f); + public float fresnelScale = 0.40f; + public float fresnelOffset = 0.50f; + public float refractScaleAbove = 0.03f; + public float refractScaleBelow = 0.20f; + public float blurMultiplier = 0.040f; + public Vector2 bigWaveDirection = new Vector2(1.05f,-0.42f); + public Vector2 littleWaveDirection = new Vector2(1.11f,-1.16f); + public UUID normalMapTexture = new UUID("822ded49-9a6c-f61c-cb89-6df54f42cdf4"); + public Vector4 horizon = new Vector4(0.25f, 0.25f, 0.32f, 0.32f); + public float hazeHorizon = 0.19f; + public Vector4 blueDensity = new Vector4(0.12f, 0.22f, 0.38f, 0.38f); + public float hazeDensity = 0.70f; + public float densityMultiplier = 0.18f; + public float distanceMultiplier = 0.8f; + public UInt16 maxAltitude = 1605; + public Vector4 sunMoonColor = new Vector4(0.24f, 0.26f, 0.30f, 0.30f); + public float sunMoonPosition = 0.317f; + public Vector4 ambient = new Vector4(0.35f,0.35f,0.35f,0.35f); + public float eastAngle = 0.0f; + public float sunGlowFocus = 0.10f; + public float sunGlowSize = 1.75f; + public float sceneGamma = 1.0f; + public float starBrightness = 0.0f; + public Vector4 cloudColor = new Vector4(0.41f, 0.41f, 0.41f, 0.41f); + public Vector3 cloudXYDensity = new Vector3(1.00f, 0.53f, 1.00f); + public float cloudCoverage = 0.27f; + public float cloudScale = 0.42f; + public Vector3 cloudDetailXYDensity = new Vector3(1.00f, 0.53f, 0.12f); + public float cloudScrollX = 0.20f; + public bool cloudScrollXLock = false; + public float cloudScrollY = 0.01f; + public bool cloudScrollYLock = false; + public bool drawClassicClouds = true; + + public delegate void SaveDelegate(RegionLightShareData wl); + public event SaveDelegate OnSave; + public void Save() + { + if (OnSave != null) + OnSave(this); + } + public object Clone() + { + return this.MemberwiseClone(); // call clone method + } + + } + + public class RegionInfo + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[REGION INFO]"; + + public string RegionFile = String.Empty; + public bool isSandbox = false; + public bool Persistent = true; + + private EstateSettings m_estateSettings; + private RegionSettings m_regionSettings; + // private IConfigSource m_configSource = null; + + public UUID originRegionID = UUID.Zero; + public string proxyUrl = ""; + public int ProxyOffset = 0; + public string regionSecret = UUID.Random().ToString(); + + public string osSecret; + + public UUID lastMapUUID = UUID.Zero; + public string lastMapRefresh = "0"; + + private float m_nonphysPrimMin = 0; + private int m_nonphysPrimMax = 0; + private float m_physPrimMin = 0; + private int m_physPrimMax = 0; + private bool m_clampPrimSize = false; + private int m_objectCapacity = 0; + private int m_maxPrimsPerUser = -1; + private int m_linksetCapacity = 0; + private string m_regionType = String.Empty; + private RegionLightShareData m_windlight = new RegionLightShareData(); + protected uint m_httpPort; + protected string m_serverURI; + protected string m_regionName = String.Empty; + protected bool Allow_Alternate_Ports; + public bool m_allow_alternate_ports; + protected string m_externalHostName; + protected IPEndPoint m_internalEndPoint; + protected uint m_remotingPort; + public UUID RegionID = UUID.Zero; + public string RemotingAddress; + public UUID ScopeID = UUID.Zero; + private UUID m_maptileStaticUUID = UUID.Zero; + + public uint WorldLocX = 0; + public uint WorldLocY = 0; + public uint WorldLocZ = 0; + + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// + public uint RegionSizeX = Constants.RegionSize; + + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// + public uint RegionSizeY = Constants.RegionSize; + + /// + /// Z dimension of the region. + /// + /// + /// XXX: Unknown if this accounts for regions with negative Z. + /// + public uint RegionSizeZ = Constants.RegionHeight; + + private Dictionary m_extraSettings = new Dictionary(); + + // Apparently, we're applying the same estatesettings regardless of whether it's local or remote. + + // MT: Yes. Estates can't span trust boundaries. Therefore, it can be + // assumed that all instances belonging to one estate are able to + // access the same database server. Since estate settings are lodaed + // from there, that should be sufficient for full remote administration + + // File based loading + // + public RegionInfo(string description, string filename, bool skipConsoleConfig, IConfigSource configSource) : this(description, filename, skipConsoleConfig, configSource, String.Empty) + { + } + + public RegionInfo(string description, string filename, bool skipConsoleConfig, IConfigSource configSource, string configName) + { + // m_configSource = configSource; + + if (filename.ToLower().EndsWith(".ini")) + { + if (!File.Exists(filename)) // New region config request + { + IniConfigSource newFile = new IniConfigSource(); + ReadNiniConfig(newFile, configName); + + newFile.Save(filename); + + RegionFile = filename; + + return; + } + + IniConfigSource source = new IniConfigSource(filename); + + bool saveFile = false; + if (source.Configs[configName] == null) + saveFile = true; + + ReadNiniConfig(source, configName); + + if (configName != String.Empty && saveFile) + source.Save(filename); + + RegionFile = filename; + + return; + } + + try + { + // This will throw if it's not legal Nini XML format + // + IConfigSource xmlsource = new XmlConfigSource(filename); + + ReadNiniConfig(xmlsource, configName); + + RegionFile = filename; + + return; + } + catch (Exception) + { + } + } + + // The web loader uses this + // + public RegionInfo(string description, XmlNode xmlNode, bool skipConsoleConfig, IConfigSource configSource) + { + XmlElement elem = (XmlElement)xmlNode; + string name = elem.GetAttribute("Name"); + string xmlstr = "" + xmlNode.OuterXml + ""; + XmlConfigSource source = new XmlConfigSource(XmlReader.Create(new StringReader(xmlstr))); + ReadNiniConfig(source, name); + + m_serverURI = string.Empty; + } + + public RegionInfo(uint legacyRegionLocX, uint legacyRegionLocY, IPEndPoint internalEndPoint, string externalUri) + { + RegionLocX = legacyRegionLocX; + RegionLocY = legacyRegionLocY; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; + m_internalEndPoint = internalEndPoint; + m_externalHostName = externalUri; + m_serverURI = string.Empty; + } + + public RegionInfo() + { + m_serverURI = string.Empty; + } + + public EstateSettings EstateSettings + { + get + { + if (m_estateSettings == null) + { + m_estateSettings = new EstateSettings(); + } + + return m_estateSettings; + } + + set { m_estateSettings = value; } + } + + public RegionSettings RegionSettings + { + get + { + if (m_regionSettings == null) + { + m_regionSettings = new RegionSettings(); + } + + return m_regionSettings; + } + + set { m_regionSettings = value; } + } + + public RegionLightShareData WindlightSettings + { + get + { + if (m_windlight == null) + { + m_windlight = new RegionLightShareData(); + } + + return m_windlight; + } + + set { m_windlight = value; } + } + + public float NonphysPrimMin + { + get { return m_nonphysPrimMin; } + } + + public int NonphysPrimMax + { + get { return m_nonphysPrimMax; } + } + + public float PhysPrimMin + { + get { return m_physPrimMin; } + } + + public int PhysPrimMax + { + get { return m_physPrimMax; } + } + + public bool ClampPrimSize + { + get { return m_clampPrimSize; } + } + + public int ObjectCapacity + { + get { return m_objectCapacity; } + } + + public int MaxPrimsPerUser + { + get { return m_maxPrimsPerUser; } + } + + public int LinksetCapacity + { + get { return m_linksetCapacity; } + } + + public int AgentCapacity { get; set; } + + public byte AccessLevel + { + get { return (byte)Util.ConvertMaturityToAccessLevel((uint)RegionSettings.Maturity); } + } + + public string RegionType + { + get { return m_regionType; } + } + + public UUID MaptileStaticUUID + { + get { return m_maptileStaticUUID; } + } + + public string MaptileStaticFile { get; private set; } + + /// + /// The port by which http communication occurs with the region (most noticeably, CAPS communication) + /// + public uint HttpPort + { + get { return m_httpPort; } + set { m_httpPort = value; } + } + + /// + /// A well-formed URI for the host region server (namely "http://" + ExternalHostName) + /// + + public string ServerURI + { + get { + if ( m_serverURI != string.Empty ) { + return m_serverURI; + } else { + return "http://" + m_externalHostName + ":" + m_httpPort + "/"; + } + } + set { + if ( value.EndsWith("/") ) { + m_serverURI = value; + } else { + m_serverURI = value + '/'; + } + } + } + + public string RegionName + { + get { return m_regionName; } + set { m_regionName = value; } + } + + public uint RemotingPort + { + get { return m_remotingPort; } + set { m_remotingPort = value; } + } + + /// + /// This accessor can throw all the exceptions that Dns.GetHostAddresses can throw. + /// + /// XXX Isn't this really doing too much to be a simple getter, rather than an explict method? + /// + public IPEndPoint ExternalEndPoint + { + get + { + // Old one defaults to IPv6 + //return new IPEndPoint(Dns.GetHostAddresses(m_externalHostName)[0], m_internalEndPoint.Port); + + IPAddress ia = null; + // If it is already an IP, don't resolve it - just return directly + if (IPAddress.TryParse(m_externalHostName, out ia)) + return new IPEndPoint(ia, m_internalEndPoint.Port); + + // Reset for next check + ia = null; + try + { + foreach (IPAddress Adr in Dns.GetHostAddresses(m_externalHostName)) + { + if (ia == null) + ia = Adr; + + if (Adr.AddressFamily == AddressFamily.InterNetwork) + { + ia = Adr; + break; + } + } + } + catch (SocketException e) + { + throw new Exception( + "Unable to resolve local hostname " + m_externalHostName + " innerException of type '" + + e + "' attached to this exception", e); + } + + return new IPEndPoint(ia, m_internalEndPoint.Port); + } + + set { m_externalHostName = value.ToString(); } + } + + public string ExternalHostName + { + get { return m_externalHostName; } + set { m_externalHostName = value; } + } + + public IPEndPoint InternalEndPoint + { + get { return m_internalEndPoint; } + set { m_internalEndPoint = value; } + } + + /// + /// The x co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. + /// + public uint RegionLocX + { + get { return WorldLocX / Constants.RegionSize; } + set { WorldLocX = value * Constants.RegionSize; } + } + + /// + /// The y co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. + /// + public uint RegionLocY + { + get { return WorldLocY / Constants.RegionSize; } + set { WorldLocY = value * Constants.RegionSize; } + } + + public void SetDefaultRegionSize() + { + WorldLocX = 0; + WorldLocY = 0; + WorldLocZ = 0; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; + RegionSizeZ = Constants.RegionHeight; + } + + // A unique region handle is created from the region's world coordinates. + // This cannot be changed because some code expects to receive the region handle and then + // compute the region coordinates from it. + public ulong RegionHandle + { + get { return Util.UIntsToLong(WorldLocX, WorldLocY); } + } + + public void SetEndPoint(string ipaddr, int port) + { + IPAddress tmpIP = IPAddress.Parse(ipaddr); + IPEndPoint tmpEPE = new IPEndPoint(tmpIP, port); + m_internalEndPoint = tmpEPE; + } + + public string GetSetting(string key) + { + string val; + string keylower = key.ToLower(); + if (m_extraSettings.TryGetValue(keylower, out val)) + return val; + m_log.DebugFormat("[RegionInfo] Could not locate value for parameter {0}", key); + return null; + } + + private void SetExtraSetting(string key, string value) + { + string keylower = key.ToLower(); + m_extraSettings[keylower] = value; + } + + private void ReadNiniConfig(IConfigSource source, string name) + { +// bool creatingNew = false; + + if (source.Configs.Count == 0) + { + MainConsole.Instance.Output("=====================================\n"); + MainConsole.Instance.Output("We are now going to ask a couple of questions about your region.\n"); + MainConsole.Instance.Output("You can press 'enter' without typing anything to use the default\n"); + MainConsole.Instance.Output("the default is displayed between [ ] brackets.\n"); + MainConsole.Instance.Output("=====================================\n"); + + if (name == String.Empty) + { + while (name.Trim() == string.Empty) + { + name = MainConsole.Instance.CmdPrompt("New region name", name); + if (name.Trim() == string.Empty) + { + MainConsole.Instance.Output("Cannot interactively create region with no name"); + } + } + } + + source.AddConfig(name); + +// creatingNew = true; + } + + if (name == String.Empty) + name = source.Configs[0].Name; + + if (source.Configs[name] == null) + { + source.AddConfig(name); + } + + RegionName = name; + IConfig config = source.Configs[name]; + + // Track all of the keys in this config and remove as they are processed + // The remaining keys will be added to generic key-value storage for + // whoever might need it + HashSet allKeys = new HashSet(); + foreach (string s in config.GetKeys()) + { + allKeys.Add(s); + } + + // RegionUUID + // + allKeys.Remove("RegionUUID"); + string regionUUID = config.GetString("RegionUUID", string.Empty); + if (!UUID.TryParse(regionUUID.Trim(), out RegionID)) + { + UUID newID = UUID.Random(); + while (RegionID == UUID.Zero) + { + regionUUID = MainConsole.Instance.CmdPrompt("RegionUUID", newID.ToString()); + if (!UUID.TryParse(regionUUID.Trim(), out RegionID)) + { + MainConsole.Instance.Output("RegionUUID must be a valid UUID"); + } + } + config.Set("RegionUUID", regionUUID); + } + + originRegionID = RegionID; // What IS this?! (Needed for RegionCombinerModule?) + + // Location + // + allKeys.Remove("Location"); + string location = config.GetString("Location", String.Empty); + if (location == String.Empty) + { + location = MainConsole.Instance.CmdPrompt("Region Location", "1000,1000"); + config.Set("Location", location); + } + + string[] locationElements = location.Split(new char[] {','}); + + RegionLocX = Convert.ToUInt32(locationElements[0]); + RegionLocY = Convert.ToUInt32(locationElements[1]); + + // Region size + // Default to legacy region size if not specified. + allKeys.Remove("SizeX"); + string configSizeX = config.GetString("SizeX", Constants.RegionSize.ToString()); + config.Set("SizeX", configSizeX); + RegionSizeX = Convert.ToUInt32(configSizeX); + allKeys.Remove("SizeY"); + string configSizeY = config.GetString("SizeY", Constants.RegionSize.ToString()); + config.Set("SizeY", configSizeX); + RegionSizeY = Convert.ToUInt32(configSizeY); + allKeys.Remove("SizeZ"); + string configSizeZ = config.GetString("SizeZ", Constants.RegionHeight.ToString()); + config.Set("SizeZ", configSizeX); + RegionSizeZ = Convert.ToUInt32(configSizeZ); + + DoRegionSizeSanityChecks(); + + // InternalAddress + // + IPAddress address; + allKeys.Remove("InternalAddress"); + if (config.Contains("InternalAddress")) + { + address = IPAddress.Parse(config.GetString("InternalAddress", String.Empty)); + } + else + { + address = IPAddress.Parse(MainConsole.Instance.CmdPrompt("Internal IP address", "0.0.0.0")); + config.Set("InternalAddress", address.ToString()); + } + + // InternalPort + // + int port; + allKeys.Remove("InternalPort"); + if (config.Contains("InternalPort")) + { + port = config.GetInt("InternalPort", 9000); + } + else + { + port = Convert.ToInt32(MainConsole.Instance.CmdPrompt("Internal port", "9000")); + config.Set("InternalPort", port); + } + m_internalEndPoint = new IPEndPoint(address, port); + + // AllowAlternatePorts + // + allKeys.Remove("AllowAlternatePorts"); + if (config.Contains("AllowAlternatePorts")) + { + m_allow_alternate_ports = config.GetBoolean("AllowAlternatePorts", true); + } + else + { + m_allow_alternate_ports = Convert.ToBoolean(MainConsole.Instance.CmdPrompt("Allow alternate ports", "False")); + + config.Set("AllowAlternatePorts", m_allow_alternate_ports.ToString()); + } + + // ExternalHostName + // + allKeys.Remove("ExternalHostName"); + string externalName; + if (config.Contains("ExternalHostName")) + { + externalName = config.GetString("ExternalHostName", "SYSTEMIP"); + } + else + { + externalName = MainConsole.Instance.CmdPrompt("External host name", "SYSTEMIP"); + config.Set("ExternalHostName", externalName); + } + if (externalName == "SYSTEMIP") + { + m_externalHostName = Util.GetLocalHost().ToString(); + m_log.InfoFormat( + "[REGIONINFO]: Resolving SYSTEMIP to {0} for external hostname of region {1}", + m_externalHostName, name); + } + else + { + m_externalHostName = externalName; + } + + // RegionType + m_regionType = config.GetString("RegionType", String.Empty); + allKeys.Remove("RegionType"); + + #region Prim and map stuff + + m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0); + allKeys.Remove("NonPhysicalPrimMin"); + + m_nonphysPrimMax = config.GetInt("NonPhysicalPrimMax", 0); + allKeys.Remove("NonPhysicalPrimMax"); + + m_physPrimMin = config.GetFloat("PhysicalPrimMin", 0); + allKeys.Remove("PhysicalPrimMin"); + + m_physPrimMax = config.GetInt("PhysicalPrimMax", 0); + allKeys.Remove("PhysicalPrimMax"); + + m_clampPrimSize = config.GetBoolean("ClampPrimSize", false); + allKeys.Remove("ClampPrimSize"); + + m_objectCapacity = config.GetInt("MaxPrims", 15000); + allKeys.Remove("MaxPrims"); + + m_maxPrimsPerUser = config.GetInt("MaxPrimsPerUser", -1); + allKeys.Remove("MaxPrimsPerUser"); + + m_linksetCapacity = config.GetInt("LinksetPrims", 0); + allKeys.Remove("LinksetPrims"); + + allKeys.Remove("MaptileStaticUUID"); + string mapTileStaticUUID = config.GetString("MaptileStaticUUID", UUID.Zero.ToString()); + if (UUID.TryParse(mapTileStaticUUID.Trim(), out m_maptileStaticUUID)) + { + config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString()); + } + + MaptileStaticFile = config.GetString("MaptileStaticFile", String.Empty); + allKeys.Remove("MaptileStaticFile"); + + #endregion + + AgentCapacity = config.GetInt("MaxAgents", 100); + allKeys.Remove("MaxAgents"); + + // Multi-tenancy + // + ScopeID = new UUID(config.GetString("ScopeID", UUID.Zero.ToString())); + allKeys.Remove("ScopeID"); + + foreach (String s in allKeys) + { + SetExtraSetting(s, config.GetString(s)); + } + } + + // Make sure user specified region sizes are sane. + // Must be multiples of legacy region size (256). + private void DoRegionSizeSanityChecks() + { + if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize) + { + // Doing non-legacy region sizes. + // Enforce region size to be multiples of the legacy region size (256) + uint partial = RegionSizeX % Constants.RegionSize; + if (partial != 0) + { + RegionSizeX -= partial; + if (RegionSizeX == 0) + RegionSizeX = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeX={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeX, RegionSizeX + partial); + } + partial = RegionSizeY % Constants.RegionSize; + if (partial != 0) + { + RegionSizeY -= partial; + if (RegionSizeY == 0) + RegionSizeY = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeY={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeY, RegionSizeY + partial); + } + + // Because of things in the viewer, regions MUST be square. + // Remove this check when viewers have been updated. + if (RegionSizeX != RegionSizeY) + { + uint minSize = Math.Min(RegionSizeX, RegionSizeY); + RegionSizeX = minSize; + RegionSizeY = minSize; + m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>", + LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + + // There is a practical limit to region size. + if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize) + { + RegionSizeX = Util.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize); + RegionSizeY = Util.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize); + m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>", + LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY); + } + + m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + } + + private void WriteNiniConfig(IConfigSource source) + { + IConfig config = source.Configs[RegionName]; + + if (config != null) + source.Configs.Remove(config); + + config = source.AddConfig(RegionName); + + config.Set("RegionUUID", RegionID.ToString()); + + string location = String.Format("{0},{1}", RegionLocX, RegionLocY); + config.Set("Location", location); + + if (RegionSizeX > 0) + config.Set("SizeX", RegionSizeX); + + if (RegionSizeY > 0) + config.Set("SizeY", RegionSizeY); + +// if (RegionSizeZ > 0) +// config.Set("SizeZ", RegionSizeZ); + + config.Set("InternalAddress", m_internalEndPoint.Address.ToString()); + config.Set("InternalPort", m_internalEndPoint.Port); + + config.Set("AllowAlternatePorts", m_allow_alternate_ports.ToString()); + + config.Set("ExternalHostName", m_externalHostName); + + if (m_nonphysPrimMin > 0) + config.Set("NonphysicalPrimMax", m_nonphysPrimMin); + + if (m_nonphysPrimMax > 0) + config.Set("NonphysicalPrimMax", m_nonphysPrimMax); + + if (m_physPrimMin > 0) + config.Set("PhysicalPrimMax", m_physPrimMin); + + if (m_physPrimMax > 0) + config.Set("PhysicalPrimMax", m_physPrimMax); + + config.Set("ClampPrimSize", m_clampPrimSize.ToString()); + + if (m_objectCapacity > 0) + config.Set("MaxPrims", m_objectCapacity); + + if (m_maxPrimsPerUser > -1) + config.Set("MaxPrimsPerUser", m_maxPrimsPerUser); + + if (m_linksetCapacity > 0) + config.Set("LinksetPrims", m_linksetCapacity); + + if (AgentCapacity > 0) + config.Set("MaxAgents", AgentCapacity); + + if (ScopeID != UUID.Zero) + config.Set("ScopeID", ScopeID.ToString()); + + if (RegionType != String.Empty) + config.Set("RegionType", RegionType); + + if (m_maptileStaticUUID != UUID.Zero) + config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString()); + + if (MaptileStaticFile != null && MaptileStaticFile != String.Empty) + config.Set("MaptileStaticFile", MaptileStaticFile); + } + + public void SaveRegionToFile(string description, string filename) + { + if (filename.ToLower().EndsWith(".ini")) + { + IniConfigSource source = new IniConfigSource(); + try + { + source = new IniConfigSource(filename); // Load if it exists + } + catch (Exception) + { + } + + WriteNiniConfig(source); + + source.Save(filename); + + return; + } + else + throw new Exception("Invalid file type for region persistence."); + } + + public void SaveLastMapUUID(UUID mapUUID) + { + lastMapUUID = mapUUID; + lastMapRefresh = Util.UnixTimeSinceEpoch().ToString(); + } + + public OSDMap PackRegionInfoData() + { + OSDMap args = new OSDMap(); + args["region_id"] = OSD.FromUUID(RegionID); + if ((RegionName != null) && !RegionName.Equals("")) + args["region_name"] = OSD.FromString(RegionName); + args["external_host_name"] = OSD.FromString(ExternalHostName); + args["http_port"] = OSD.FromString(HttpPort.ToString()); + args["server_uri"] = OSD.FromString(ServerURI); + + args["region_xloc"] = OSD.FromString(RegionLocX.ToString()); + args["region_yloc"] = OSD.FromString(RegionLocY.ToString()); + args["region_size_x"] = OSD.FromString(RegionSizeX.ToString()); + args["region_size_y"] = OSD.FromString(RegionSizeY.ToString()); + args["region_size_z"] = OSD.FromString(RegionSizeZ.ToString()); + + args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString()); + args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString()); + if ((RemotingAddress != null) && !RemotingAddress.Equals("")) + args["remoting_address"] = OSD.FromString(RemotingAddress); + args["remoting_port"] = OSD.FromString(RemotingPort.ToString()); + args["allow_alt_ports"] = OSD.FromBoolean(m_allow_alternate_ports); + if ((proxyUrl != null) && !proxyUrl.Equals("")) + args["proxy_url"] = OSD.FromString(proxyUrl); + if (RegionType != String.Empty) + args["region_type"] = OSD.FromString(RegionType); + + return args; + } + + public void UnpackRegionInfoData(OSDMap args) + { + if (args["region_id"] != null) + RegionID = args["region_id"].AsUUID(); + if (args["region_name"] != null) + RegionName = args["region_name"].AsString(); + if (args["external_host_name"] != null) + ExternalHostName = args["external_host_name"].AsString(); + if (args["http_port"] != null) + UInt32.TryParse(args["http_port"].AsString(), out m_httpPort); + if (args["server_uri"] != null) + ServerURI = args["server_uri"].AsString(); + if (args["region_xloc"] != null) + { + uint locx; + UInt32.TryParse(args["region_xloc"].AsString(), out locx); + RegionLocX = locx; + } + if (args["region_yloc"] != null) + { + uint locy; + UInt32.TryParse(args["region_yloc"].AsString(), out locy); + RegionLocY = locy; + } + if (args.ContainsKey("region_size_x")) + RegionSizeX = (uint)args["region_size_x"].AsInteger(); + if (args.ContainsKey("region_size_y")) + RegionSizeY = (uint)args["region_size_y"].AsInteger(); + if (args.ContainsKey("region_size_z")) + RegionSizeZ = (uint)args["region_size_z"].AsInteger(); + + IPAddress ip_addr = null; + if (args["internal_ep_address"] != null) + { + IPAddress.TryParse(args["internal_ep_address"].AsString(), out ip_addr); + } + int port = 0; + if (args["internal_ep_port"] != null) + { + Int32.TryParse(args["internal_ep_port"].AsString(), out port); + } + InternalEndPoint = new IPEndPoint(ip_addr, port); + if (args["remoting_address"] != null) + RemotingAddress = args["remoting_address"].AsString(); + if (args["remoting_port"] != null) + UInt32.TryParse(args["remoting_port"].AsString(), out m_remotingPort); + if (args["allow_alt_ports"] != null) + m_allow_alternate_ports = args["allow_alt_ports"].AsBoolean(); + if (args["proxy_url"] != null) + proxyUrl = args["proxy_url"].AsString(); + if (args["region_type"] != null) + m_regionType = args["region_type"].AsString(); + } + + public static RegionInfo Create(UUID regionID, string regionName, uint regX, uint regY, string externalHostName, uint httpPort, uint simPort, uint remotingPort, string serverURI) + { + RegionInfo regionInfo; + IPEndPoint neighbourInternalEndPoint = new IPEndPoint(Util.GetHostFromDNS(externalHostName), (int)simPort); + regionInfo = new RegionInfo(regX, regY, neighbourInternalEndPoint, externalHostName); + regionInfo.RemotingPort = remotingPort; + regionInfo.RemotingAddress = externalHostName; + regionInfo.HttpPort = httpPort; + regionInfo.RegionID = regionID; + regionInfo.RegionName = regionName; + regionInfo.ServerURI = serverURI; + return regionInfo; + } + } +} diff --git a/OpenSim/Framework/RegionInfoForEstateMenuArgs.cs b/OpenSim/Framework/RegionInfoForEstateMenuArgs.cs new file mode 100644 index 0000000000..f274da2e7a --- /dev/null +++ b/OpenSim/Framework/RegionInfoForEstateMenuArgs.cs @@ -0,0 +1,52 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class RegionInfoForEstateMenuArgs : EventArgs + { + public float billableFactor; + public uint estateID; + public byte maxAgents; + public float objectBonusFactor; + public uint parentEstateID; + public int pricePerMeter; + public int redirectGridX; + public int redirectGridY; + public uint regionFlags; + public byte simAccess; + public float sunHour; + public float terrainLowerLimit; + public float terrainRaiseLimit; + public bool useEstateSun; + public float waterHeight; + public string simName; + public string regionType; + } +} diff --git a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..3bcbe2fa71 --- /dev/null +++ b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.RegionLoader.Filesystem")] +[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("4ab5c74b-e886-40a1-b67d-a04df285e706")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs b/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs new file mode 100644 index 0000000000..8332c14478 --- /dev/null +++ b/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs @@ -0,0 +1,116 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; + +namespace OpenSim.Framework.RegionLoader.Filesystem +{ + public class RegionLoaderFileSystem : 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() + { + string regionConfigPath = Path.Combine(Util.configDir(), "Regions"); + bool allowRegionless = false; + + try + { + IConfig startupConfig = (IConfig)m_configSource.Configs["Startup"]; + regionConfigPath = startupConfig.GetString("regionload_regionsdir", regionConfigPath).Trim(); + allowRegionless = startupConfig.GetBoolean("allow_regionless", false); + } + catch (Exception) + { + // No INI setting recorded. + } + + if (!Directory.Exists(regionConfigPath)) + { + Directory.CreateDirectory(regionConfigPath); + } + + string[] configFiles = Directory.GetFiles(regionConfigPath, "*.xml"); + string[] iniFiles = Directory.GetFiles(regionConfigPath, "*.ini"); + + // Create an empty Regions.ini if there are no existing config files. + if (!allowRegionless && configFiles.Length == 0 && iniFiles.Length == 0) + { + new RegionInfo("DEFAULT REGION CONFIG", Path.Combine(regionConfigPath, "Regions.ini"), false, m_configSource); + iniFiles = Directory.GetFiles(regionConfigPath, "*.ini"); + } + + m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config files from {0}", regionConfigPath); + + List regionInfos = new List(); + + int i = 0; + foreach (string file in iniFiles) + { + m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config file {0}", file); + + IConfigSource source = new IniConfigSource(file); + + foreach (IConfig config in source.Configs) + { + RegionInfo regionInfo = new RegionInfo("REGION CONFIG #" + (i + 1), file, false, m_configSource, config.Name); + regionInfos.Add(regionInfo); + + m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loaded config for region {0}", regionInfo.RegionName); + + i++; + } + } + + foreach (string file in configFiles) + { + m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config file {0}", file); + + RegionInfo regionInfo = new RegionInfo("REGION CONFIG #" + (i + 1), file, false, m_configSource); + regionInfos.Add(regionInfo); + + m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loaded config for region {0}", regionInfo.RegionName); + + i++; + } + + return regionInfos.ToArray(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1b2519cb61 --- /dev/null +++ b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.RegionLoader.Web")] +[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("985afff8-e7ed-4056-acce-39abf7a43d33")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs b/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs new file mode 100644 index 0000000000..f60bb121c3 --- /dev/null +++ b/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs @@ -0,0 +1,147 @@ +/* + * 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; + +namespace OpenSim.Framework.RegionLoader.Web +{ + 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() + { + 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 + { + 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 + { + string xmlSource = String.Empty; + + using (HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse()) + { + m_log.Debug("[WEBLOADER]: Downloading region information..."); + + using (Stream s = webResponse.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(s)) + { + 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) + { + using (HttpWebResponse response = (HttpWebResponse)ex.Response) + { + if (response.StatusCode == HttpStatusCode.NotFound) + { + if (!allowRegionless) + throw ex; + } + else + { + throw ex; + } + } + } + + if (regionCount > 0 | allowRegionless) + { + return regionInfos; + } + else + { + m_log.Error("[WEBLOADER]: No region configs were available."); + return null; + } + } + } + } + } +} diff --git a/OpenSim/Framework/RegionSettings.cs b/OpenSim/Framework/RegionSettings.cs new file mode 100644 index 0000000000..a895c40cc1 --- /dev/null +++ b/OpenSim/Framework/RegionSettings.cs @@ -0,0 +1,523 @@ +/* + * 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 OpenMetaverse; +using System.Runtime.Serialization; + +namespace OpenSim.Framework +{ + public struct SpawnPoint + { + public float Yaw; + public float Pitch; + public float Distance; + + public void SetLocation(Vector3 pos, Quaternion rot, Vector3 point) + { + // The point is an absolute position, so we need the relative + // location to the spawn point + Vector3 offset = point - pos; + Distance = Vector3.Mag(offset); + + // Next we need to rotate this vector into the spawn point's + // coordinate system + rot.W = -rot.W; + offset = offset * rot; + + Vector3 dir = Vector3.Normalize(offset); + + // Get the bearing (yaw) + Yaw = (float)Math.Atan2(dir.Y, dir.X); + + // Get the elevation (pitch) + Pitch = (float)-Math.Atan2(dir.Z, Math.Sqrt(dir.X * dir.X + dir.Y * dir.Y)); + } + + public Vector3 GetLocation(Vector3 pos, Quaternion rot) + { + Quaternion y = Quaternion.CreateFromEulers(0, 0, Yaw); + Quaternion p = Quaternion.CreateFromEulers(0, Pitch, 0); + + Vector3 dir = new Vector3(1, 0, 0) * p * y; + Vector3 offset = dir * (float)Distance; + + offset *= rot; + + return pos + offset; + } + + /// + /// Returns a string representation of this SpawnPoint. + /// + /// + public override string ToString() + { + return string.Format("{0},{1},{2}", Yaw, Pitch, Distance); + } + + /// + /// Generate a SpawnPoint from a string + /// + /// + public static SpawnPoint Parse(string str) + { + string[] parts = str.Split(','); + if (parts.Length != 3) + throw new ArgumentException("Invalid string: " + str); + + SpawnPoint sp = new SpawnPoint(); + sp.Yaw = float.Parse(parts[0]); + sp.Pitch = float.Parse(parts[1]); + sp.Distance = float.Parse(parts[2]); + return sp; + } + } + + public class RegionSettings + { + public delegate void SaveDelegate(RegionSettings rs); + + public event SaveDelegate OnSave; + + /// + /// These appear to be terrain textures that are shipped with the client. + /// + public static readonly UUID DEFAULT_TERRAIN_TEXTURE_1 = new UUID("b8d3965a-ad78-bf43-699b-bff8eca6c975"); + public static readonly UUID DEFAULT_TERRAIN_TEXTURE_2 = new UUID("abb783e6-3e93-26c0-248a-247666855da3"); + public static readonly UUID DEFAULT_TERRAIN_TEXTURE_3 = new UUID("179cdabd-398a-9b6b-1391-4dc333ba321f"); + public static readonly UUID DEFAULT_TERRAIN_TEXTURE_4 = new UUID("beb169c7-11ea-fff2-efe5-0f24dc881df2"); + + public void Save() + { + if (OnSave != null) + OnSave(this); + } + + private UUID m_RegionUUID = UUID.Zero; + + public UUID RegionUUID + { + get { return m_RegionUUID; } + set { m_RegionUUID = value; } + } + + private bool m_BlockTerraform = false; + + public bool BlockTerraform + { + get { return m_BlockTerraform; } + set { m_BlockTerraform = value; } + } + + private bool m_BlockFly = false; + + public bool BlockFly + { + get { return m_BlockFly; } + set { m_BlockFly = value; } + } + + private bool m_AllowDamage = false; + + public bool AllowDamage + { + get { return m_AllowDamage; } + set { m_AllowDamage = value; } + } + + private bool m_RestrictPushing = false; + + public bool RestrictPushing + { + get { return m_RestrictPushing; } + set { m_RestrictPushing = value; } + } + + private bool m_AllowLandResell = true; + + public bool AllowLandResell + { + get { return m_AllowLandResell; } + set { m_AllowLandResell = value; } + } + + private bool m_AllowLandJoinDivide = true; + + public bool AllowLandJoinDivide + { + get { return m_AllowLandJoinDivide; } + set { m_AllowLandJoinDivide = value; } + } + + private bool m_BlockShowInSearch = false; + + public bool BlockShowInSearch + { + get { return m_BlockShowInSearch; } + set { m_BlockShowInSearch = value; } + } + + private int m_AgentLimit = 40; + + public int AgentLimit + { + get { return m_AgentLimit; } + set { m_AgentLimit = value; } + } + + private double m_ObjectBonus = 1.0; + + public double ObjectBonus + { + get { return m_ObjectBonus; } + set { m_ObjectBonus = value; } + } + + private int m_Maturity = 0; + + public int Maturity + { + get { return m_Maturity; } + set { m_Maturity = value; } + } + + private bool m_DisableScripts = false; + + public bool DisableScripts + { + get { return m_DisableScripts; } + set { m_DisableScripts = value; } + } + + private bool m_DisableCollisions = false; + + public bool DisableCollisions + { + get { return m_DisableCollisions; } + set { m_DisableCollisions = value; } + } + + private bool m_DisablePhysics = false; + + public bool DisablePhysics + { + get { return m_DisablePhysics; } + set { m_DisablePhysics = value; } + } + + private UUID m_TerrainTexture1 = UUID.Zero; + + public UUID TerrainTexture1 + { + get { return m_TerrainTexture1; } + set + { + if (value == UUID.Zero) + m_TerrainTexture1 = DEFAULT_TERRAIN_TEXTURE_1; + else + m_TerrainTexture1 = value; + } + } + + private UUID m_TerrainTexture2 = UUID.Zero; + + public UUID TerrainTexture2 + { + get { return m_TerrainTexture2; } + set + { + if (value == UUID.Zero) + m_TerrainTexture2 = DEFAULT_TERRAIN_TEXTURE_2; + else + m_TerrainTexture2 = value; + } + } + + private UUID m_TerrainTexture3 = UUID.Zero; + + public UUID TerrainTexture3 + { + get { return m_TerrainTexture3; } + set + { + if (value == UUID.Zero) + m_TerrainTexture3 = DEFAULT_TERRAIN_TEXTURE_3; + else + m_TerrainTexture3 = value; + } + } + + private UUID m_TerrainTexture4 = UUID.Zero; + + public UUID TerrainTexture4 + { + get { return m_TerrainTexture4; } + set + { + if (value == UUID.Zero) + m_TerrainTexture4 = DEFAULT_TERRAIN_TEXTURE_4; + else + m_TerrainTexture4 = value; + } + } + + private double m_Elevation1NW = 10; + + public double Elevation1NW + { + get { return m_Elevation1NW; } + set { m_Elevation1NW = value; } + } + + private double m_Elevation2NW = 60; + + public double Elevation2NW + { + get { return m_Elevation2NW; } + set { m_Elevation2NW = value; } + } + + private double m_Elevation1NE = 10; + + public double Elevation1NE + { + get { return m_Elevation1NE; } + set { m_Elevation1NE = value; } + } + + private double m_Elevation2NE = 60; + + public double Elevation2NE + { + get { return m_Elevation2NE; } + set { m_Elevation2NE = value; } + } + + private double m_Elevation1SE = 10; + + public double Elevation1SE + { + get { return m_Elevation1SE; } + set { m_Elevation1SE = value; } + } + + private double m_Elevation2SE = 60; + + public double Elevation2SE + { + get { return m_Elevation2SE; } + set { m_Elevation2SE = value; } + } + + private double m_Elevation1SW = 10; + + public double Elevation1SW + { + get { return m_Elevation1SW; } + set { m_Elevation1SW = value; } + } + + private double m_Elevation2SW = 60; + + public double Elevation2SW + { + get { return m_Elevation2SW; } + set { m_Elevation2SW = value; } + } + + private double m_WaterHeight = 20; + + public double WaterHeight + { + get { return m_WaterHeight; } + set { m_WaterHeight = value; } + } + + private double m_TerrainRaiseLimit = 100; + + public double TerrainRaiseLimit + { + get { return m_TerrainRaiseLimit; } + set { m_TerrainRaiseLimit = value; } + } + + private double m_TerrainLowerLimit = -100; + + public double TerrainLowerLimit + { + get { return m_TerrainLowerLimit; } + set { m_TerrainLowerLimit = value; } + } + + private bool m_UseEstateSun = true; + + public bool UseEstateSun + { + get { return m_UseEstateSun; } + set { m_UseEstateSun = value; } + } + + private bool m_Sandbox = false; + + public bool Sandbox + { + get { return m_Sandbox; } + set { m_Sandbox = value; } + } + + private Vector3 m_SunVector; + + public Vector3 SunVector + { + get { return m_SunVector; } + set { m_SunVector = value; } + } + + private UUID m_ParcelImageID; + + public UUID ParcelImageID + { + get { return m_ParcelImageID; } + set { m_ParcelImageID = value; } + } + + private UUID m_TerrainImageID; + + public UUID TerrainImageID + { + get { return m_TerrainImageID; } + set { m_TerrainImageID = value; } + } + + private bool m_FixedSun = false; + + public bool FixedSun + { + get { return m_FixedSun; } + set { m_FixedSun = value; } + } + + private double m_SunPosition = 0.0; + + public double SunPosition + { + get { return m_SunPosition; } + set { m_SunPosition = value; } + } + + private UUID m_Covenant = UUID.Zero; + + public UUID Covenant + { + get { return m_Covenant; } + set { m_Covenant = value; } + } + + private int m_CovenantChanged = 0; + + public int CovenantChangedDateTime + { + get { return m_CovenantChanged; } + set { m_CovenantChanged = value; } + } + + private int m_LoadedCreationDateTime; + public int LoadedCreationDateTime + { + get { return m_LoadedCreationDateTime; } + set { m_LoadedCreationDateTime = value; } + } + + public String LoadedCreationDate + { + get + { + TimeSpan ts = new TimeSpan(0, 0, LoadedCreationDateTime); + DateTime stamp = new DateTime(1970, 1, 1) + ts; + return stamp.ToLongDateString(); + } + } + + public String LoadedCreationTime + { + get + { + TimeSpan ts = new TimeSpan(0, 0, LoadedCreationDateTime); + DateTime stamp = new DateTime(1970, 1, 1) + ts; + return stamp.ToLongTimeString(); + } + } + + private String m_LoadedCreationID; + public String LoadedCreationID + { + get { return m_LoadedCreationID; } + set { m_LoadedCreationID = value; } + } + + /// + /// Connected Telehub object + /// + public UUID TelehubObject { get; set; } + + /// + /// Our connected Telehub's SpawnPoints + /// + public List l_SpawnPoints = new List(); + + // Add a SpawnPoint + // ** These are not region coordinates ** + // They are relative to the Telehub coordinates + // + public void AddSpawnPoint(SpawnPoint point) + { + l_SpawnPoints.Add(point); + } + + // Remove a SpawnPoint + public void RemoveSpawnPoint(int point_index) + { + l_SpawnPoints.RemoveAt(point_index); + } + + // Return the List of SpawnPoints + public List SpawnPoints() + { + return l_SpawnPoints; + + } + + // Clear the SpawnPoints List of all entries + public void ClearSpawnPoints() + { + l_SpawnPoints.Clear(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/RegistryCore.cs b/OpenSim/Framework/RegistryCore.cs new file mode 100644 index 0000000000..98d595a847 --- /dev/null +++ b/OpenSim/Framework/RegistryCore.cs @@ -0,0 +1,79 @@ +/* + * 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.Text; + +namespace OpenSim.Framework +{ + public class RegistryCore : IRegistryCore + { + protected Dictionary m_moduleInterfaces = new Dictionary(); + + /// + /// Register an Module interface. + /// + /// + /// + public void RegisterInterface(T iface) + { + lock (m_moduleInterfaces) + { + if (!m_moduleInterfaces.ContainsKey(typeof(T))) + { + m_moduleInterfaces.Add(typeof(T), iface); + } + } + } + + public bool TryGet(out T iface) + { + if (m_moduleInterfaces.ContainsKey(typeof(T))) + { + iface = (T)m_moduleInterfaces[typeof(T)]; + return true; + } + iface = default(T); + return false; + } + + public T Get() + { + return (T)m_moduleInterfaces[typeof(T)]; + } + + public void StackModuleInterface(M mod) + { + } + + public T[] RequestModuleInterfaces() + { + return new T[] { default(T) }; + } + } +} diff --git a/OpenSim/Framework/RequestAssetArgs.cs b/OpenSim/Framework/RequestAssetArgs.cs new file mode 100644 index 0000000000..5ebb20d1a7 --- /dev/null +++ b/OpenSim/Framework/RequestAssetArgs.cs @@ -0,0 +1,40 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + public class RequestAssetArgs : EventArgs + { + public int ChannelType; + public float Priority; + public int SourceType; + public UUID TransferID; + } +} diff --git a/OpenSim/Framework/SLUtil.cs b/OpenSim/Framework/SLUtil.cs new file mode 100644 index 0000000000..e66d5be192 --- /dev/null +++ b/OpenSim/Framework/SLUtil.cs @@ -0,0 +1,538 @@ +/* + * 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; +using System; +using System.Collections.Generic; + +namespace OpenSim.Framework +{ + public static class SLUtil + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Asset types used only in OpenSim. + /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here. + /// + public enum OpenSimAssetType : sbyte + { + Material = -2 + } + + + #region SL / file extension / content-type conversions + + /// + /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs + /// to the AssetType or OpenSimAssetType enums. + /// + public static object AssetTypeFromCode(sbyte assetType) + { + if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) + return (OpenMetaverse.AssetType)assetType; + else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType)) + return (OpenSimAssetType)assetType; + else + return OpenMetaverse.AssetType.Unknown; + } + + private class TypeMapping + { + private sbyte assetType; + private sbyte inventoryType; + private string contentType; + private string contentType2; + private string extension; + + public sbyte AssetTypeCode + { + get { return assetType; } + } + + public object AssetType + { + get { return AssetTypeFromCode(assetType); } + } + + public sbyte InventoryType + { + get { return inventoryType; } + } + + public string ContentType + { + get { return contentType; } + } + + public string ContentType2 + { + get { return contentType2; } + } + + public string Extension + { + get { return extension; } + } + + private TypeMapping(sbyte assetType, sbyte inventoryType, string contentType, string contentType2, string extension) + { + this.assetType = assetType; + this.inventoryType = inventoryType; + this.contentType = contentType; + this.contentType2 = contentType2; + this.extension = extension; + } + + public TypeMapping(AssetType assetType, sbyte inventoryType, string contentType, string contentType2, string extension) + : this((sbyte)assetType, inventoryType, contentType, contentType2, extension) + { + } + + public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, contentType2, extension) + { + } + + public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) + { + } + + public TypeMapping(AssetType assetType, FolderType inventoryType, string contentType, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) + { + } + + public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) + { + } + } + + /// + /// Maps between AssetType, InventoryType and Content-Type. + /// Where more than one possibility exists, the first one takes precedence. E.g.: + /// AssetType "AssetType.Texture" -> Content-Type "image-xj2c" + /// Content-Type "image/x-j2c" -> InventoryType "InventoryType.Texture" + /// + private static TypeMapping[] MAPPINGS = new TypeMapping[] { + new TypeMapping(AssetType.Unknown, InventoryType.Unknown, "application/octet-stream", "bin"), + new TypeMapping(AssetType.Texture, InventoryType.Texture, "image/x-j2c", "image/jp2", "j2c"), + new TypeMapping(AssetType.Texture, InventoryType.Snapshot, "image/x-j2c", "image/jp2", "j2c"), + new TypeMapping(AssetType.TextureTGA, InventoryType.Texture, "image/tga", "tga"), + new TypeMapping(AssetType.ImageTGA, InventoryType.Texture, "image/tga", "tga"), + new TypeMapping(AssetType.ImageJPEG, InventoryType.Texture, "image/jpeg", "jpg"), + new TypeMapping(AssetType.Sound, InventoryType.Sound, "audio/ogg", "application/ogg", "ogg"), + new TypeMapping(AssetType.SoundWAV, InventoryType.Sound, "audio/x-wav", "wav"), + new TypeMapping(AssetType.CallingCard, InventoryType.CallingCard, "application/vnd.ll.callingcard", "application/x-metaverse-callingcard", "callingcard"), + new TypeMapping(AssetType.Landmark, InventoryType.Landmark, "application/vnd.ll.landmark", "application/x-metaverse-landmark", "landmark"), + new TypeMapping(AssetType.Clothing, InventoryType.Wearable, "application/vnd.ll.clothing", "application/x-metaverse-clothing", "clothing"), + new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), + new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), + new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"), + new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"), + new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"), + new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"), + new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"), + new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"), + new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"), + new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"), + new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"), + new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"), + + // The next few items are about inventory folders + new TypeMapping(AssetType.Folder, FolderType.None, "application/vnd.ll.folder", "folder"), + new TypeMapping(AssetType.Folder, FolderType.Root, "application/vnd.ll.rootfolder", "rootfolder"), + new TypeMapping(AssetType.Folder, FolderType.Trash, "application/vnd.ll.trashfolder", "trashfolder"), + new TypeMapping(AssetType.Folder, FolderType.Snapshot, "application/vnd.ll.snapshotfolder", "snapshotfolder"), + new TypeMapping(AssetType.Folder, FolderType.LostAndFound, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"), + new TypeMapping(AssetType.Folder, FolderType.Favorites, "application/vnd.ll.favoritefolder", "favoritefolder"), + new TypeMapping(AssetType.Folder, FolderType.CurrentOutfit, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"), + new TypeMapping(AssetType.Folder, FolderType.Outfit, "application/vnd.ll.outfitfolder", "outfitfolder"), + new TypeMapping(AssetType.Folder, FolderType.MyOutfits, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"), + + // This next mappping is an asset to inventory item mapping. + // Note: LL stores folders as assets of type Folder = 8, and it has a corresponding InventoryType = 8 + // OpenSim doesn't store folders as assets, so this mapping should only be used when parsing things from the viewer to the server + new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"), + + // OpenSim specific + new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material") + }; + + private static Dictionary asset2Content; + private static Dictionary asset2Extension; + private static Dictionary inventory2Content; + private static Dictionary content2Asset; + private static Dictionary content2Inventory; + + static SLUtil() + { + asset2Content = new Dictionary(); + asset2Extension = new Dictionary(); + inventory2Content = new Dictionary(); + content2Asset = new Dictionary(); + content2Inventory = new Dictionary(); + + foreach (TypeMapping mapping in MAPPINGS) + { + sbyte assetType = mapping.AssetTypeCode; + if (!asset2Content.ContainsKey(assetType)) + asset2Content.Add(assetType, mapping.ContentType); + + if (!asset2Extension.ContainsKey(assetType)) + asset2Extension.Add(assetType, mapping.Extension); + + if (!inventory2Content.ContainsKey(mapping.InventoryType)) + inventory2Content.Add(mapping.InventoryType, mapping.ContentType); + + if (!content2Asset.ContainsKey(mapping.ContentType)) + content2Asset.Add(mapping.ContentType, assetType); + + if (!content2Inventory.ContainsKey(mapping.ContentType)) + content2Inventory.Add(mapping.ContentType, mapping.InventoryType); + + if (mapping.ContentType2 != null) + { + if (!content2Asset.ContainsKey(mapping.ContentType2)) + content2Asset.Add(mapping.ContentType2, assetType); + if (!content2Inventory.ContainsKey(mapping.ContentType2)) + content2Inventory.Add(mapping.ContentType2, mapping.InventoryType); + } + } + } + + public static string SLAssetTypeToContentType(int assetType) + { + string contentType; + if (!asset2Content.TryGetValue((sbyte)assetType, out contentType)) + contentType = asset2Content[(sbyte)AssetType.Unknown]; + return contentType; + } + + public static string SLInvTypeToContentType(int invType) + { + string contentType; + if (!inventory2Content.TryGetValue((sbyte)invType, out contentType)) + contentType = inventory2Content[(sbyte)InventoryType.Unknown]; + return contentType; + } + + public static sbyte ContentTypeToSLAssetType(string contentType) + { + sbyte assetType; + if (!content2Asset.TryGetValue(contentType, out assetType)) + assetType = (sbyte)AssetType.Unknown; + return (sbyte)assetType; + } + + public static sbyte ContentTypeToSLInvType(string contentType) + { + sbyte invType; + if (!content2Inventory.TryGetValue(contentType, out invType)) + invType = (sbyte)InventoryType.Unknown; + return (sbyte)invType; + } + + public static string SLAssetTypeToExtension(int assetType) + { + string extension; + if (!asset2Extension.TryGetValue((sbyte)assetType, out extension)) + extension = asset2Extension[(sbyte)AssetType.Unknown]; + return extension; + } + + #endregion SL / file extension / content-type conversions + + private class NotecardReader + { + private string rawInput; + private int lineNumber; + + public int LineNumber + { + get + { + return lineNumber; + } + } + + public NotecardReader(string _rawInput) + { + rawInput = (string)_rawInput.Clone(); + lineNumber = 0; + } + + public string getLine() + { + if(rawInput.Length == 0) + { + throw new NotANotecardFormatException(lineNumber + 1); + } + + int pos = rawInput.IndexOf('\n'); + if(pos < 0) + { + pos = rawInput.Length; + } + + /* cut line from rest */ + ++lineNumber; + string line = rawInput.Substring(0, pos); + if (pos + 1 >= rawInput.Length) + { + rawInput = string.Empty; + } + else + { + rawInput = rawInput.Substring(pos + 1); + } + /* clean up line from double spaces and tabs */ + line = line.Replace("\t", " "); + while(line.IndexOf(" ") >= 0) + { + line = line.Replace(" ", " "); + } + return line.Replace("\r", "").Trim(); + } + + public string getBlock(int length) + { + /* cut line from rest */ + if(length > rawInput.Length) + { + throw new NotANotecardFormatException(lineNumber); + } + string line = rawInput.Substring(0, length); + rawInput = rawInput.Substring(length); + return line; + } + } + + public class NotANotecardFormatException : Exception + { + public int lineNumber; + public NotANotecardFormatException(int _lineNumber) + : base() + { + lineNumber = _lineNumber; + } + } + + private static void skipSection(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string line; + while ((line = reader.getLine()) != "}") + { + if(line.IndexOf('{')>=0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static void skipInventoryItem(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data.Length == 0) + { + continue; + } + if(data[0] == "permissions") + { + skipSection(reader); + } + else if(data[0] == "sale_info") + { + skipSection(reader); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static void skipInventoryItems(NotecardReader reader) + { + if(reader.getLine() != "{") + { + throw new NotANotecardFormatException(reader.LineNumber); + } + + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data.Length == 0) + { + continue; + } + + if(data[0] == "inv_item") + { + skipInventoryItem(reader); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + + } + } + + private static void skipInventory(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data[0] == "count") + { + int count = Int32.Parse(data[1]); + for(int i = 0; i < count; ++i) + { + skipInventoryItems(reader); + } + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static string readNotecardText(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string notecardString = string.Empty; + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if (data.Length == 0) + { + continue; + } + + if (data[0] == "LLEmbeddedItems") + { + skipInventory(reader); + } + else if(data[0] == "Text" && data.Length == 3) + { + int length = Int32.Parse(data[2]); + notecardString = reader.getBlock(length); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + + } + return notecardString; + } + + private static string readNotecard(byte[] rawInput) + { + string rawIntermedInput = string.Empty; + + /* make up a Raw Encoding here */ + foreach(byte c in rawInput) + { + char d = (char)c; + rawIntermedInput += d; + } + + NotecardReader reader = new NotecardReader(rawIntermedInput); + string line; + try + { + line = reader.getLine(); + } + catch(Exception) + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + string[] versioninfo = line.Split(' '); + if(versioninfo.Length < 3) + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + else if(versioninfo[0] != "Linden" || versioninfo[1] != "text") + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + else + { + /* now we actually decode the Encoding, before we needed it in raw */ + string o = readNotecardText(reader); + byte[] a = new byte[o.Length]; + for(int i = 0; i < o.Length; ++i) + { + a[i] = (byte)o[i]; + } + return System.Text.Encoding.UTF8.GetString(a); + } + } + + /// + /// Parse a notecard in Linden format to a string of ordinary text. + /// + /// + /// + public static string ParseNotecardToString(byte[] rawInput) + { + return readNotecard(rawInput); + } + + /// + /// Parse a notecard in Linden format to a list of ordinary lines. + /// + /// + /// + public static string[] ParseNotecardToArray(byte[] rawInput) + { + return readNotecard(rawInput).Replace("\r", "").Split('\n'); + } + } +} diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs new file mode 100644 index 0000000000..ab3c285578 --- /dev/null +++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs @@ -0,0 +1,208 @@ +/* + * 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.Text; +using OpenMetaverse; +using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType; + +namespace OpenSim.Framework.Serialization +{ + /// + /// Constants for the archiving module + /// + public class ArchiveConstants + { + /// + /// The location of the archive control file + /// + public const string CONTROL_FILE_PATH = "archive.xml"; + + /// + /// Path for the assets held in an archive + /// + public const string ASSETS_PATH = "assets/"; + + /// + /// Path for the inventory data + /// + public const string INVENTORY_PATH = "inventory/"; + + /// + /// Path for regions in a multi-region archive + /// + public const string REGIONS_PATH = "regions/"; + + /// + /// Path for the prims file + /// + public const string OBJECTS_PATH = "objects/"; + + /// + /// Path for terrains. Technically these may be assets, but I think it's quite nice to split them out. + /// + public const string TERRAINS_PATH = "terrains/"; + + /// + /// Path for region settings. + /// + public const string SETTINGS_PATH = "settings/"; + + /// + /// Path for region settings. + /// + public const string LANDDATA_PATH = "landdata/"; + + /// + /// Path for user profiles + /// + public const string USERS_PATH = "userprofiles/"; + + /// + /// The character the separates the uuid from extension information in an archived asset filename + /// + public const string ASSET_EXTENSION_SEPARATOR = "_"; + + /// + /// Used to separate components in an inventory node name + /// + public const string INVENTORY_NODE_NAME_COMPONENT_SEPARATOR = "__"; + + /// + /// Template used for creating filenames in OpenSim Archives. + /// + public const string OAR_OBJECT_FILENAME_TEMPLATE = "{0}_{1:000}-{2:000}-{3:000}__{4}.xml"; + + /// + /// Extensions used for asset types in the archive + /// + public static readonly IDictionary ASSET_TYPE_TO_EXTENSION = new Dictionary(); + public static readonly IDictionary EXTENSION_TO_ASSET_TYPE = new Dictionary(); + + static ArchiveConstants() + { + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Animation] = ASSET_EXTENSION_SEPARATOR + "animation.bvh"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Bodypart] = ASSET_EXTENSION_SEPARATOR + "bodypart.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.CallingCard] = ASSET_EXTENSION_SEPARATOR + "callingcard.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Clothing] = ASSET_EXTENSION_SEPARATOR + "clothing.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Folder] = ASSET_EXTENSION_SEPARATOR + "folder.txt"; // Not sure if we'll ever see this + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Gesture] = ASSET_EXTENSION_SEPARATOR + "gesture.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Mesh] = ASSET_EXTENSION_SEPARATOR + "mesh.llmesh"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2"; + ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga"; + ASSET_TYPE_TO_EXTENSION[(sbyte)OpenSimAssetType.Material] = ASSET_EXTENSION_SEPARATOR + "material.xml"; // Not sure if we'll ever see this + + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "callingcard.txt"] = (sbyte)AssetType.CallingCard; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "clothing.txt"] = (sbyte)AssetType.Clothing; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "folder.txt"] = (sbyte)AssetType.Folder; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "gesture.txt"] = (sbyte)AssetType.Gesture; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = (sbyte)AssetType.ImageJPEG; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = (sbyte)AssetType.ImageTGA; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = (sbyte)AssetType.Landmark; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = (sbyte)AssetType.LSLBytecode; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = (sbyte)AssetType.LSLText; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "mesh.llmesh"] = (sbyte)AssetType.Mesh; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = (sbyte)AssetType.Notecard; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = (sbyte)AssetType.Object; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = (sbyte)AssetType.Simstate; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = (sbyte)AssetType.Sound; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = (sbyte)AssetType.SoundWAV; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "material.xml"] = (sbyte)OpenSimAssetType.Material; + } + + public static string CreateOarLandDataPath(LandData ld) + { + return string.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH, ld.GlobalID); + } + + /// + /// Create the filename used to store an object in an OpenSim Archive. + /// + /// + /// + /// + /// + public static string CreateOarObjectFilename(string objectName, UUID uuid, Vector3 pos) + { + return string.Format( + OAR_OBJECT_FILENAME_TEMPLATE, objectName, + Math.Round(pos.X), Math.Round(pos.Y), Math.Round(pos.Z), + uuid); + } + + /// + /// Create the path used to store an object in an OpenSim Archives. + /// + /// + /// + /// + /// + public static string CreateOarObjectPath(string objectName, UUID uuid, Vector3 pos) + { + return OBJECTS_PATH + CreateOarObjectFilename(objectName, uuid, pos); + } + + /// + /// Extract a plain path from an IAR path + /// + /// + /// + public static string ExtractPlainPathFromIarPath(string iarPath) + { + List plainDirs = new List(); + + string[] iarDirs = iarPath.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string iarDir in iarDirs) + { + if (!iarDir.Contains(ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR)) + plainDirs.Add(iarDir); + + int i = iarDir.LastIndexOf(ArchiveConstants.INVENTORY_NODE_NAME_COMPONENT_SEPARATOR); + + plainDirs.Add(iarDir.Remove(i)); + } + + return string.Join("/", plainDirs.ToArray()); + } + } +} diff --git a/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs new file mode 100644 index 0000000000..55640ac6a8 --- /dev/null +++ b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs @@ -0,0 +1,411 @@ +/* + * 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.Diagnostics; +using System.IO; +using System.Reflection; +using System.Xml; +using log4net; +using OpenMetaverse; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Framework.Serialization.External +{ + /// + /// Utilities for manipulating external representations of data structures in OpenSim + /// + public class ExternalRepresentationUtils + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Populate a node with data read from xml using a dictinoary of processors + /// + /// + /// + /// + /// true on successful, false if there were any processing failures + public static bool ExecuteReadProcessors( + NodeType nodeToFill, Dictionary> processors, XmlReader xtr) + { + return ExecuteReadProcessors( + nodeToFill, + processors, + xtr, + (o, nodeName, e) => { + m_log.Debug(string.Format("[ExternalRepresentationUtils]: Error while parsing element {0} ", + nodeName), e); + }); + } + + /// + /// Populate a node with data read from xml using a dictinoary of processors + /// + /// + /// + /// + /// + /// Action to take if there is a parsing problem. This will usually just be to log the exception + /// + /// true on successful, false if there were any processing failures + public static bool ExecuteReadProcessors( + NodeType nodeToFill, + Dictionary> processors, + XmlReader xtr, + Action parseExceptionAction) + { + bool errors = false; + int numErrors = 0; + + Stopwatch timer = new Stopwatch(); + timer.Start(); + + string nodeName = string.Empty; + while (xtr.NodeType != XmlNodeType.EndElement) + { + nodeName = xtr.Name; + + // m_log.DebugFormat("[ExternalRepresentationUtils]: Processing node: {0}", nodeName); + + Action p = null; + if (processors.TryGetValue(xtr.Name, out p)) + { + // m_log.DebugFormat("[ExternalRepresentationUtils]: Found processor for {0}", nodeName); + + try + { + p(nodeToFill, xtr); + } + catch (Exception e) + { + errors = true; + parseExceptionAction(nodeToFill, nodeName, e); + + if (xtr.EOF) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to unexpected end of XML"); + break; + } + + if (++numErrors == 10) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to too many parsing errors"); + break; + } + + if (xtr.NodeType == XmlNodeType.EndElement) + xtr.Read(); + } + } + else + { + // m_log.DebugFormat("[ExternalRepresentationUtils]: found unknown element \"{0}\"", nodeName); + xtr.ReadOuterXml(); // ignore + } + + if (timer.Elapsed.TotalSeconds >= 60) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to timeout"); + errors = true; + break; + } + } + + return errors; + } + + /// + /// Takes a XML representation of a SceneObjectPart and returns another XML representation + /// with creator data added to it. + /// + /// The SceneObjectPart represented in XML2 + /// The URL of the user agents service (home) for the creator + /// The service for retrieving user account information + /// The scope of the user account information (Grid ID) + /// The SceneObjectPart represented in XML2 + [Obsolete("This method is deprecated. Use RewriteSOP instead.")] + public static string RewriteSOP_Old(string xml, string homeURL, IUserAccountService userService, UUID scopeID) + { + if (xml == string.Empty || homeURL == string.Empty || userService == null) + return xml; + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + XmlNodeList sops = doc.GetElementsByTagName("SceneObjectPart"); + + foreach (XmlNode sop in sops) + { + UserAccount creator = null; + bool hasCreatorData = false; + XmlNodeList nodes = sop.ChildNodes; + foreach (XmlNode node in nodes) + { + if (node.Name == "CreatorID") + { + UUID uuid = UUID.Zero; + UUID.TryParse(node.InnerText, out uuid); + creator = userService.GetUserAccount(scopeID, uuid); + } + + if (node.Name == "CreatorData" && node.InnerText != null && node.InnerText != string.Empty) + hasCreatorData = true; + + //if (node.Name == "OwnerID") + //{ + // UserAccount owner = GetUser(node.InnerText); + // if (owner != null) + // node.InnerText = m_ProfileServiceURL + "/" + node.InnerText + "/" + owner.FirstName + " " + owner.LastName; + //} + } + if (!hasCreatorData && creator != null) + { + XmlElement creatorData = doc.CreateElement("CreatorData"); + creatorData.InnerText = CalcCreatorData(homeURL, creator.FirstName + " " + creator.LastName); + sop.AppendChild(creatorData); + } + } + + using (StringWriter wr = new StringWriter()) + { + doc.Save(wr); + return wr.ToString(); + } + } + + /// + /// Takes a XML representation of a SceneObjectPart and returns another XML representation + /// with creator data added to it. + /// + /// The SceneObjectPart represented in XML2 + /// An identifier for the component that's calling this function + /// The URL of the user agents service (home) for the creator + /// The service for retrieving user account information + /// The scope of the user account information (Grid ID) + /// The SceneObjectPart represented in XML2 + public static string RewriteSOP(string xmlData, string sceneName, string homeURL, IUserAccountService userService, UUID scopeID) + { + // Console.WriteLine("Input XML [{0}]", xmlData); + if (xmlData == string.Empty || homeURL == string.Empty || userService == null) + return xmlData; + + // Deal with bug + xmlData = ExternalRepresentationUtils.SanitizeXml(xmlData); + + using (StringWriter sw = new StringWriter()) + using (XmlTextWriter writer = new XmlTextWriter(sw)) + using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null)) + using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment })) + { + TransformXml(reader, writer, sceneName, homeURL, userService, scopeID); + + // Console.WriteLine("Output: [{0}]", sw.ToString()); + + return sw.ToString(); + } + } + + protected static void TransformXml(XmlReader reader, XmlWriter writer, string sceneName, string homeURI, IUserAccountService userAccountService, UUID scopeID) + { + // m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML"); + + int sopDepth = -1; + UserAccount creator = null; + bool hasCreatorData = false; + + while (reader.Read()) + { + // Console.WriteLine("Depth: {0}, name {1}", reader.Depth, reader.Name); + + switch (reader.NodeType) + { + case XmlNodeType.Attribute: + // Console.WriteLine("FOUND ATTRIBUTE {0}", reader.Name); + writer.WriteAttributeString(reader.Name, reader.Value); + break; + + case XmlNodeType.CDATA: + writer.WriteCData(reader.Value); + break; + + case XmlNodeType.Comment: + writer.WriteComment(reader.Value); + break; + + case XmlNodeType.DocumentType: + writer.WriteDocType(reader.Name, reader.Value, null, null); + break; + + case XmlNodeType.Element: + // m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name); + + writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); + + if (reader.HasAttributes) + { + while (reader.MoveToNextAttribute()) + writer.WriteAttributeString(reader.Name, reader.Value); + + reader.MoveToElement(); + } + + if (reader.LocalName == "SceneObjectPart") + { + if (sopDepth < 0) + { + sopDepth = reader.Depth; + // m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth); + } + } + else + { + if (sopDepth >= 0 && reader.Depth == sopDepth + 1) + { + if (reader.Name == "CreatorID") + { + reader.Read(); + if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID") + { + reader.Read(); + + if (reader.NodeType == XmlNodeType.Text) + { + UUID uuid = UUID.Zero; + UUID.TryParse(reader.Value, out uuid); + creator = userAccountService.GetUserAccount(scopeID, uuid); + writer.WriteElementString("UUID", reader.Value); + reader.Read(); + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + else if (reader.Name == "CreatorData") + { + reader.Read(); + if (reader.NodeType == XmlNodeType.Text) + { + hasCreatorData = true; + writer.WriteString(reader.Value); + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + } + } + + if (reader.IsEmptyElement) + { + // m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name); + writer.WriteEndElement(); + } + + break; + + case XmlNodeType.EndElement: + // m_log.DebugFormat("Depth {0} at EndElement", reader.Depth); + if (sopDepth == reader.Depth) + { + if (!hasCreatorData && creator != null) + writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", homeURI, creator.FirstName, creator.LastName)); + + // m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth"); + sopDepth = -1; + creator = null; + hasCreatorData = false; + } + writer.WriteEndElement(); + break; + + case XmlNodeType.EntityReference: + writer.WriteEntityRef(reader.Name); + break; + + case XmlNodeType.ProcessingInstruction: + writer.WriteProcessingInstruction(reader.Name, reader.Value); + break; + + case XmlNodeType.Text: + writer.WriteString(reader.Value); + break; + + case XmlNodeType.XmlDeclaration: + // For various reasons, not all serializations have xml declarations (or consistent ones) + // and as it's embedded inside a byte stream we don't need it anyway, so ignore. + break; + + default: + m_log.WarnFormat( + "[HG ASSET MAPPER]: Unrecognized node {0} in asset XML transform in {1}", + reader.NodeType, sceneName); + break; + } + } + } + + public static string CalcCreatorData(string homeURL, string name) + { + return homeURL + ";" + name; + } + + internal static string CalcCreatorData(string homeURL, UUID uuid, string name) + { + return homeURL + "/" + uuid + ";" + name; + } + + /// + /// Sanitation for bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + /// + /// + /// + public static string SanitizeXml(string xmlData) + { + string fixedData = xmlData; + if (fixedData != null) + // Loop, because it may contain multiple + while (fixedData.Contains("xmlns:xmlns:")) + fixedData = fixedData.Replace("xmlns:xmlns:", "xmlns:"); + return fixedData; + } + + } +} diff --git a/OpenSim/Framework/Serialization/External/LandDataSerializer.cs b/OpenSim/Framework/Serialization/External/LandDataSerializer.cs new file mode 100644 index 0000000000..e42d56f96b --- /dev/null +++ b/OpenSim/Framework/Serialization/External/LandDataSerializer.cs @@ -0,0 +1,266 @@ +/* + * 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.Text; +using System.Xml; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Framework.Serialization.External +{ + /// + /// Serialize and deserialize LandData as an external format. + /// + public class LandDataSerializer + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static Dictionary> m_ldProcessors + = new Dictionary>(); + + private static Dictionary> m_laeProcessors + = new Dictionary>(); + + static LandDataSerializer() + { + // LandData processors + m_ldProcessors.Add( + "Area", (ld, xtr) => ld.Area = Convert.ToInt32(xtr.ReadElementString("Area"))); + m_ldProcessors.Add( + "AuctionID", (ld, xtr) => ld.AuctionID = Convert.ToUInt32(xtr.ReadElementString("AuctionID"))); + m_ldProcessors.Add( + "AuthBuyerID", (ld, xtr) => ld.AuthBuyerID = UUID.Parse(xtr.ReadElementString("AuthBuyerID"))); + m_ldProcessors.Add( + "Category", (ld, xtr) => ld.Category = (ParcelCategory)Convert.ToSByte(xtr.ReadElementString("Category"))); + m_ldProcessors.Add( + "ClaimDate", (ld, xtr) => ld.ClaimDate = Convert.ToInt32(xtr.ReadElementString("ClaimDate"))); + m_ldProcessors.Add( + "ClaimPrice", (ld, xtr) => ld.ClaimPrice = Convert.ToInt32(xtr.ReadElementString("ClaimPrice"))); + m_ldProcessors.Add( + "GlobalID", (ld, xtr) => ld.GlobalID = UUID.Parse(xtr.ReadElementString("GlobalID"))); + m_ldProcessors.Add( + "GroupID", (ld, xtr) => ld.GroupID = UUID.Parse(xtr.ReadElementString("GroupID"))); + m_ldProcessors.Add( + "IsGroupOwned", (ld, xtr) => ld.IsGroupOwned = Convert.ToBoolean(xtr.ReadElementString("IsGroupOwned"))); + m_ldProcessors.Add( + "Bitmap", (ld, xtr) => ld.Bitmap = Convert.FromBase64String(xtr.ReadElementString("Bitmap"))); + m_ldProcessors.Add( + "Description", (ld, xtr) => ld.Description = xtr.ReadElementString("Description")); + m_ldProcessors.Add( + "Flags", (ld, xtr) => ld.Flags = Convert.ToUInt32(xtr.ReadElementString("Flags"))); + m_ldProcessors.Add( + "LandingType", (ld, xtr) => ld.LandingType = Convert.ToByte(xtr.ReadElementString("LandingType"))); + m_ldProcessors.Add( + "Name", (ld, xtr) => ld.Name = xtr.ReadElementString("Name")); + m_ldProcessors.Add( + "Status", (ld, xtr) => ld.Status = (ParcelStatus)Convert.ToSByte(xtr.ReadElementString("Status"))); + m_ldProcessors.Add( + "LocalID", (ld, xtr) => ld.LocalID = Convert.ToInt32(xtr.ReadElementString("LocalID"))); + m_ldProcessors.Add( + "MediaAutoScale", (ld, xtr) => ld.MediaAutoScale = Convert.ToByte(xtr.ReadElementString("MediaAutoScale"))); + m_ldProcessors.Add( + "MediaID", (ld, xtr) => ld.MediaID = UUID.Parse(xtr.ReadElementString("MediaID"))); + m_ldProcessors.Add( + "MediaURL", (ld, xtr) => ld.MediaURL = xtr.ReadElementString("MediaURL")); + m_ldProcessors.Add( + "MusicURL", (ld, xtr) => ld.MusicURL = xtr.ReadElementString("MusicURL")); + m_ldProcessors.Add( + "OwnerID", (ld, xtr) => ld.OwnerID = UUID.Parse(xtr.ReadElementString("OwnerID"))); + + m_ldProcessors.Add( + "ParcelAccessList", ProcessParcelAccessList); + + m_ldProcessors.Add( + "PassHours", (ld, xtr) => ld.PassHours = Convert.ToSingle(xtr.ReadElementString("PassHours"))); + m_ldProcessors.Add( + "PassPrice", (ld, xtr) => ld.PassPrice = Convert.ToInt32(xtr.ReadElementString("PassPrice"))); + m_ldProcessors.Add( + "SalePrice", (ld, xtr) => ld.SalePrice = Convert.ToInt32(xtr.ReadElementString("SalePrice"))); + m_ldProcessors.Add( + "SnapshotID", (ld, xtr) => ld.SnapshotID = UUID.Parse(xtr.ReadElementString("SnapshotID"))); + m_ldProcessors.Add( + "UserLocation", (ld, xtr) => ld.UserLocation = Vector3.Parse(xtr.ReadElementString("UserLocation"))); + m_ldProcessors.Add( + "UserLookAt", (ld, xtr) => ld.UserLookAt = Vector3.Parse(xtr.ReadElementString("UserLookAt"))); + + // No longer used here // + // m_ldProcessors.Add("Dwell", (landData, xtr) => return); + + m_ldProcessors.Add( + "OtherCleanTime", (ld, xtr) => ld.OtherCleanTime = Convert.ToInt32(xtr.ReadElementString("OtherCleanTime"))); + + // LandAccessEntryProcessors + m_laeProcessors.Add( + "AgentID", (lae, xtr) => lae.AgentID = UUID.Parse(xtr.ReadElementString("AgentID"))); + m_laeProcessors.Add( + "Time", (lae, xtr) => + { + // We really don't care about temp vs perm here and this + // would break on old oars. Assume all bans are perm + xtr.ReadElementString("Time"); + lae.Expires = 0; // Convert.ToUint( xtr.ReadElementString("Time")); + } + ); + m_laeProcessors.Add( + "AccessList", (lae, xtr) => lae.Flags = (AccessList)Convert.ToUInt32(xtr.ReadElementString("AccessList"))); + } + + public static void ProcessParcelAccessList(LandData ld, XmlReader xtr) + { + if (!xtr.IsEmptyElement) + { + while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) + { + LandAccessEntry lae = new LandAccessEntry(); + + xtr.ReadStartElement("ParcelAccessEntry"); + + ExternalRepresentationUtils.ExecuteReadProcessors(lae, m_laeProcessors, xtr); + + xtr.ReadEndElement(); + + ld.ParcelAccessList.Add(lae); + } + } + + xtr.Read(); + } + + /// + /// Reify/deserialize landData + /// + /// + /// + /// + public static LandData Deserialize(byte[] serializedLandData) + { + return Deserialize(Encoding.UTF8.GetString(serializedLandData, 0, serializedLandData.Length)); + } + + /// + /// Reify/deserialize landData + /// + /// + /// + /// + public static LandData Deserialize(string serializedLandData) + { + LandData landData = new LandData(); + + using (XmlTextReader reader = new XmlTextReader(new StringReader(serializedLandData))) + { + reader.ReadStartElement("LandData"); + + ExternalRepresentationUtils.ExecuteReadProcessors(landData, m_ldProcessors, reader); + + reader.ReadEndElement(); + } + + return landData; + } + + /// + /// Serialize land data + /// + /// + /// + /// Serialization options. + /// Can be null if there are no options. + /// "wipe-owners" will write UUID.Zero rather than the ownerID so that a later reload loads all parcels with the estate owner as the owner + /// + public static string Serialize(LandData landData, Dictionary options) + { + StringWriter sw = new StringWriter(); + XmlTextWriter xtw = new XmlTextWriter(sw); + xtw.Formatting = Formatting.Indented; + + xtw.WriteStartDocument(); + xtw.WriteStartElement("LandData"); + + xtw.WriteElementString("Area", Convert.ToString(landData.Area)); + xtw.WriteElementString("AuctionID", Convert.ToString(landData.AuctionID)); + xtw.WriteElementString("AuthBuyerID", landData.AuthBuyerID.ToString()); + xtw.WriteElementString("Category", Convert.ToString((sbyte)landData.Category)); + xtw.WriteElementString("ClaimDate", Convert.ToString(landData.ClaimDate)); + xtw.WriteElementString("ClaimPrice", Convert.ToString(landData.ClaimPrice)); + xtw.WriteElementString("GlobalID", landData.GlobalID.ToString()); + + UUID groupID = options.ContainsKey("wipe-owners") ? UUID.Zero : landData.GroupID; + xtw.WriteElementString("GroupID", groupID.ToString()); + + bool isGroupOwned = options.ContainsKey("wipe-owners") ? false : landData.IsGroupOwned; + xtw.WriteElementString("IsGroupOwned", Convert.ToString(isGroupOwned)); + + xtw.WriteElementString("Bitmap", Convert.ToBase64String(landData.Bitmap)); + xtw.WriteElementString("Description", landData.Description); + xtw.WriteElementString("Flags", Convert.ToString((uint)landData.Flags)); + xtw.WriteElementString("LandingType", Convert.ToString((byte)landData.LandingType)); + xtw.WriteElementString("Name", landData.Name); + xtw.WriteElementString("Status", Convert.ToString((sbyte)landData.Status)); + xtw.WriteElementString("LocalID", landData.LocalID.ToString()); + xtw.WriteElementString("MediaAutoScale", Convert.ToString(landData.MediaAutoScale)); + xtw.WriteElementString("MediaID", landData.MediaID.ToString()); + xtw.WriteElementString("MediaURL", landData.MediaURL); + xtw.WriteElementString("MusicURL", landData.MusicURL); + + UUID ownerID = options.ContainsKey("wipe-owners") ? UUID.Zero : landData.OwnerID; + xtw.WriteElementString("OwnerID", ownerID.ToString()); + + xtw.WriteStartElement("ParcelAccessList"); + foreach (LandAccessEntry pal in landData.ParcelAccessList) + { + xtw.WriteStartElement("ParcelAccessEntry"); + xtw.WriteElementString("AgentID", pal.AgentID.ToString()); + xtw.WriteElementString("Time", pal.Expires.ToString()); + xtw.WriteElementString("AccessList", Convert.ToString((uint)pal.Flags)); + xtw.WriteEndElement(); + } + xtw.WriteEndElement(); + + xtw.WriteElementString("PassHours", Convert.ToString(landData.PassHours)); + xtw.WriteElementString("PassPrice", Convert.ToString(landData.PassPrice)); + xtw.WriteElementString("SalePrice", Convert.ToString(landData.SalePrice)); + xtw.WriteElementString("SnapshotID", landData.SnapshotID.ToString()); + xtw.WriteElementString("UserLocation", landData.UserLocation.ToString()); + xtw.WriteElementString("UserLookAt", landData.UserLookAt.ToString()); + xtw.WriteElementString("Dwell", "0"); + xtw.WriteElementString("OtherCleanTime", Convert.ToString(landData.OtherCleanTime)); + + xtw.WriteEndElement(); + + xtw.Close(); + sw.Close(); + + return sw.ToString(); + } + } +} diff --git a/OpenSim/Framework/Serialization/External/OspResolver.cs b/OpenSim/Framework/Serialization/External/OspResolver.cs new file mode 100644 index 0000000000..fa7160f874 --- /dev/null +++ b/OpenSim/Framework/Serialization/External/OspResolver.cs @@ -0,0 +1,208 @@ +/* + * 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.Reflection; +using System.Text; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Framework.Serialization +{ + /// + /// Resolves OpenSim Profile Anchors (OSPA). An OSPA is a string used to provide information for + /// identifying user profiles or supplying a simple name if no profile is available. + /// + public class OspResolver + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const string OSPA_PREFIX = "ospa:"; + public const string OSPA_NAME_KEY = "n"; + public const string OSPA_NAME_VALUE_SEPARATOR = " "; + public const string OSPA_TUPLE_SEPARATOR = "|"; + public static readonly char[] OSPA_TUPLE_SEPARATOR_ARRAY = OSPA_TUPLE_SEPARATOR.ToCharArray(); + public const string OSPA_PAIR_SEPARATOR = "="; + + /// + /// Make an OSPA given a user UUID + /// + /// + /// + /// The OSPA. Null if a user with the given UUID could not be found. + public static string MakeOspa(UUID userId, IUserAccountService userService) + { + if (userService == null) + { + m_log.Warn("[OSP RESOLVER]: UserService is null"); + return userId.ToString(); + } + + UserAccount account = userService.GetUserAccount(UUID.Zero, userId); + if (account != null) + { + return MakeOspa(account.FirstName, account.LastName); + } +// else +// { +// m_log.WarnFormat("[OSP RESOLVER]: No user account for {0}", userId); +// System.Console.WriteLine("[OSP RESOLVER]: No user account for {0}", userId); +// } + + return null; + } + + /// + /// Make an OSPA given a user name + /// + /// + /// + public static string MakeOspa(string firstName, string lastName) + { + string ospa + = OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName; + +// m_log.DebugFormat("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName); +// System.Console.WriteLine("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName); + + return ospa; + } + + /// + /// Resolve an osp string into the most suitable internal OpenSim identifier. + /// + /// + /// In some cases this will be a UUID if a suitable profile exists on the system. In other cases, this may + /// just return the same identifier after creating a temporary profile. + /// + /// + /// + /// + /// A suitable UUID for use in Second Life client communication. If the string was not a valid ospa, then UUID.Zero + /// is returned. + /// + public static UUID ResolveOspa(string ospa, IUserAccountService userService) + { + if (!ospa.StartsWith(OSPA_PREFIX)) + { +// m_log.DebugFormat("[OSP RESOLVER]: ResolveOspa() got unrecognized format [{0}]", ospa); + return UUID.Zero; + } + +// m_log.DebugFormat("[OSP RESOLVER]: Resolving {0}", ospa); + + string ospaMeat = ospa.Substring(OSPA_PREFIX.Length); + string[] ospaTuples = ospaMeat.Split(OSPA_TUPLE_SEPARATOR_ARRAY); + + foreach (string tuple in ospaTuples) + { + int tupleSeparatorIndex = tuple.IndexOf(OSPA_PAIR_SEPARATOR); + + if (tupleSeparatorIndex < 0) + { + m_log.WarnFormat("[OSP RESOLVER]: Ignoring non-tuple component {0} in OSPA {1}", tuple, ospa); + continue; + } + + string key = tuple.Remove(tupleSeparatorIndex).Trim(); + string value = tuple.Substring(tupleSeparatorIndex + 1).Trim(); + + if (OSPA_NAME_KEY == key) + return ResolveOspaName(value, userService); + } + + return UUID.Zero; + } + + /// + /// Hash a profile name into a UUID + /// + /// + /// + public static UUID HashName(string name) + { + return new UUID(Utils.MD5(Encoding.Unicode.GetBytes(name)), 0); + } + + /// + /// Resolve an OSPI name by querying existing persistent user profiles. If there is no persistent user profile + /// then a temporary user profile is inserted in the cache. + /// + /// + /// + /// + /// An OpenSim internal identifier for the name given. Returns null if the name was not valid + /// + protected static UUID ResolveOspaName(string name, IUserAccountService userService) + { + if (userService == null) + return UUID.Zero; + + int nameSeparatorIndex = name.IndexOf(OSPA_NAME_VALUE_SEPARATOR); + + if (nameSeparatorIndex < 0) + { + m_log.WarnFormat("[OSP RESOLVER]: Ignoring unseparated name {0}", name); + return UUID.Zero; + } + + string firstName = name.Remove(nameSeparatorIndex).TrimEnd(); + string lastName = name.Substring(nameSeparatorIndex + 1).TrimStart(); + + UserAccount account = userService.GetUserAccount(UUID.Zero, firstName, lastName); + if (account != null) + { +// m_log.DebugFormat( +// "[OSP RESOLVER]: Found user account with uuid {0} for {1} {2}", +// account.PrincipalID, firstName, lastName); + + return account.PrincipalID; + } +// else +// { +// m_log.DebugFormat("[OSP RESOLVER]: No resolved OSPA user account for {0}", name); +// } + + // XXX: Disable temporary user profile creation for now as implementation is incomplete - justincc + /* + UserProfileData tempUserProfile = new UserProfileData(); + tempUserProfile.FirstName = firstName; + tempUserProfile.SurName = lastName; + tempUserProfile.ID = HashName(tempUserProfile.Name); + + m_log.DebugFormat( + "[OSP RESOLVER]: Adding temporary user profile for {0} {1}", tempUserProfile.Name, tempUserProfile.ID); + commsManager.UserService.AddTemporaryUserProfile(tempUserProfile); + + return tempUserProfile.ID; + */ + + return UUID.Zero; + } + } +} diff --git a/OpenSim/Framework/Serialization/External/RegionSettingsSerializer.cs b/OpenSim/Framework/Serialization/External/RegionSettingsSerializer.cs new file mode 100644 index 0000000000..19468c3b2a --- /dev/null +++ b/OpenSim/Framework/Serialization/External/RegionSettingsSerializer.cs @@ -0,0 +1,287 @@ +/* + * 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; +using OpenMetaverse; +using OpenSim.Framework; +using log4net; +using System.Reflection; + +namespace OpenSim.Framework.Serialization.External +{ + /// + /// Serialize and deserialize region settings as an external format. + /// + public class RegionSettingsSerializer + { + /// + /// Deserialize settings + /// + /// + /// + /// + public static RegionSettings Deserialize(byte[] serializedSettings) + { + return Deserialize(Encoding.ASCII.GetString(serializedSettings, 0, serializedSettings.Length)); + } + + /// + /// Deserialize settings + /// + /// + /// + /// + public static RegionSettings Deserialize(string serializedSettings) + { + RegionSettings settings = new RegionSettings(); + + StringReader sr = new StringReader(serializedSettings); + XmlTextReader xtr = new XmlTextReader(sr); + + xtr.ReadStartElement("RegionSettings"); + + xtr.ReadStartElement("General"); + + while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) + { + switch (xtr.Name) + { + case "AllowDamage": + settings.AllowDamage = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "AllowLandResell": + settings.AllowLandResell = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "AllowLandJoinDivide": + settings.AllowLandJoinDivide = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "BlockFly": + settings.BlockFly = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "BlockLandShowInSearch": + settings.BlockShowInSearch = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "BlockTerraform": + settings.BlockTerraform = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "DisableCollisions": + settings.DisableCollisions = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "DisablePhysics": + settings.DisablePhysics = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "DisableScripts": + settings.DisableScripts = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "MaturityRating": + settings.Maturity = int.Parse(xtr.ReadElementContentAsString()); + break; + case "RestrictPushing": + settings.RestrictPushing = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "AgentLimit": + settings.AgentLimit = int.Parse(xtr.ReadElementContentAsString()); + break; + case "ObjectBonus": + settings.ObjectBonus = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + } + } + + xtr.ReadEndElement(); + xtr.ReadStartElement("GroundTextures"); + + while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) + { + switch (xtr.Name) + { + case "Texture1": + settings.TerrainTexture1 = UUID.Parse(xtr.ReadElementContentAsString()); + break; + case "Texture2": + settings.TerrainTexture2 = UUID.Parse(xtr.ReadElementContentAsString()); + break; + case "Texture3": + settings.TerrainTexture3 = UUID.Parse(xtr.ReadElementContentAsString()); + break; + case "Texture4": + settings.TerrainTexture4 = UUID.Parse(xtr.ReadElementContentAsString()); + break; + case "ElevationLowSW": + settings.Elevation1SW = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationLowNW": + settings.Elevation1NW = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationLowSE": + settings.Elevation1SE = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationLowNE": + settings.Elevation1NE = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationHighSW": + settings.Elevation2SW = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationHighNW": + settings.Elevation2NW = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationHighSE": + settings.Elevation2SE = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "ElevationHighNE": + settings.Elevation2NE = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + } + } + + xtr.ReadEndElement(); + xtr.ReadStartElement("Terrain"); + + while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) + { + switch (xtr.Name) + { + case "WaterHeight": + settings.WaterHeight = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "TerrainRaiseLimit": + settings.TerrainRaiseLimit = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "TerrainLowerLimit": + settings.TerrainLowerLimit = double.Parse(xtr.ReadElementContentAsString(), Culture.NumberFormatInfo); + break; + case "UseEstateSun": + settings.UseEstateSun = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "FixedSun": + settings.FixedSun = bool.Parse(xtr.ReadElementContentAsString()); + break; + case "SunPosition": + settings.SunPosition = double.Parse(xtr.ReadElementContentAsString()); + break; + } + } + + xtr.ReadEndElement(); + + if (xtr.IsStartElement("Telehub")) + { + xtr.ReadStartElement("Telehub"); + + while (xtr.Read() && xtr.NodeType != XmlNodeType.EndElement) + { + switch (xtr.Name) + { + case "TelehubObject": + settings.TelehubObject = UUID.Parse(xtr.ReadElementContentAsString()); + break; + case "SpawnPoint": + string str = xtr.ReadElementContentAsString(); + SpawnPoint sp = SpawnPoint.Parse(str); + settings.AddSpawnPoint(sp); + break; + } + } + } + + xtr.Close(); + sr.Close(); + + return settings; + } + + public static string Serialize(RegionSettings settings) + { + StringWriter sw = new StringWriter(); + XmlTextWriter xtw = new XmlTextWriter(sw); + xtw.Formatting = Formatting.Indented; + xtw.WriteStartDocument(); + + xtw.WriteStartElement("RegionSettings"); + + xtw.WriteStartElement("General"); + xtw.WriteElementString("AllowDamage", settings.AllowDamage.ToString()); + xtw.WriteElementString("AllowLandResell", settings.AllowLandResell.ToString()); + xtw.WriteElementString("AllowLandJoinDivide", settings.AllowLandJoinDivide.ToString()); + xtw.WriteElementString("BlockFly", settings.BlockFly.ToString()); + xtw.WriteElementString("BlockLandShowInSearch", settings.BlockShowInSearch.ToString()); + xtw.WriteElementString("BlockTerraform", settings.BlockTerraform.ToString()); + xtw.WriteElementString("DisableCollisions", settings.DisableCollisions.ToString()); + xtw.WriteElementString("DisablePhysics", settings.DisablePhysics.ToString()); + xtw.WriteElementString("DisableScripts", settings.DisableScripts.ToString()); + xtw.WriteElementString("MaturityRating", settings.Maturity.ToString()); + xtw.WriteElementString("RestrictPushing", settings.RestrictPushing.ToString()); + xtw.WriteElementString("AgentLimit", settings.AgentLimit.ToString()); + xtw.WriteElementString("ObjectBonus", settings.ObjectBonus.ToString()); + xtw.WriteEndElement(); + + xtw.WriteStartElement("GroundTextures"); + xtw.WriteElementString("Texture1", settings.TerrainTexture1.ToString()); + xtw.WriteElementString("Texture2", settings.TerrainTexture2.ToString()); + xtw.WriteElementString("Texture3", settings.TerrainTexture3.ToString()); + xtw.WriteElementString("Texture4", settings.TerrainTexture4.ToString()); + xtw.WriteElementString("ElevationLowSW", settings.Elevation1SW.ToString()); + xtw.WriteElementString("ElevationLowNW", settings.Elevation1NW.ToString()); + xtw.WriteElementString("ElevationLowSE", settings.Elevation1SE.ToString()); + xtw.WriteElementString("ElevationLowNE", settings.Elevation1NE.ToString()); + xtw.WriteElementString("ElevationHighSW", settings.Elevation2SW.ToString()); + xtw.WriteElementString("ElevationHighNW", settings.Elevation2NW.ToString()); + xtw.WriteElementString("ElevationHighSE", settings.Elevation2SE.ToString()); + xtw.WriteElementString("ElevationHighNE", settings.Elevation2NE.ToString()); + xtw.WriteEndElement(); + + xtw.WriteStartElement("Terrain"); + xtw.WriteElementString("WaterHeight", settings.WaterHeight.ToString()); + xtw.WriteElementString("TerrainRaiseLimit", settings.TerrainRaiseLimit.ToString()); + xtw.WriteElementString("TerrainLowerLimit", settings.TerrainLowerLimit.ToString()); + xtw.WriteElementString("UseEstateSun", settings.UseEstateSun.ToString()); + xtw.WriteElementString("FixedSun", settings.FixedSun.ToString()); + xtw.WriteElementString("SunPosition", settings.SunPosition.ToString()); + // Note: 'SunVector' isn't saved because this value is owned by the Sun Module, which + // calculates it automatically according to the date and other factors. + xtw.WriteEndElement(); + + xtw.WriteStartElement("Telehub"); + if (settings.TelehubObject != UUID.Zero) + { + xtw.WriteElementString("TelehubObject", settings.TelehubObject.ToString()); + foreach (SpawnPoint sp in settings.SpawnPoints()) + xtw.WriteElementString("SpawnPoint", sp.ToString()); + } + xtw.WriteEndElement(); + + xtw.WriteEndElement(); + + xtw.Close(); + sw.Close(); + + return sw.ToString(); + } + } +} diff --git a/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs new file mode 100644 index 0000000000..994cede575 --- /dev/null +++ b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs @@ -0,0 +1,304 @@ +/* + * 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.Text; +using System.Xml; + +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Framework.Serialization.External +{ + /// + /// Serialize and deserialize user inventory items as an external format. + /// + public class UserInventoryItemSerializer + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static Dictionary> m_InventoryItemXmlProcessors + = new Dictionary>(); + + #region InventoryItemBase Processor initialization + static UserInventoryItemSerializer() + { + m_InventoryItemXmlProcessors.Add("Name", ProcessName); + m_InventoryItemXmlProcessors.Add("ID", ProcessID); + m_InventoryItemXmlProcessors.Add("InvType", ProcessInvType); + m_InventoryItemXmlProcessors.Add("CreatorUUID", ProcessCreatorUUID); + m_InventoryItemXmlProcessors.Add("CreatorID", ProcessCreatorID); + m_InventoryItemXmlProcessors.Add("CreatorData", ProcessCreatorData); + m_InventoryItemXmlProcessors.Add("CreationDate", ProcessCreationDate); + m_InventoryItemXmlProcessors.Add("Owner", ProcessOwner); + m_InventoryItemXmlProcessors.Add("Description", ProcessDescription); + m_InventoryItemXmlProcessors.Add("AssetType", ProcessAssetType); + m_InventoryItemXmlProcessors.Add("AssetID", ProcessAssetID); + m_InventoryItemXmlProcessors.Add("SaleType", ProcessSaleType); + m_InventoryItemXmlProcessors.Add("SalePrice", ProcessSalePrice); + m_InventoryItemXmlProcessors.Add("BasePermissions", ProcessBasePermissions); + m_InventoryItemXmlProcessors.Add("CurrentPermissions", ProcessCurrentPermissions); + m_InventoryItemXmlProcessors.Add("EveryOnePermissions", ProcessEveryOnePermissions); + m_InventoryItemXmlProcessors.Add("NextPermissions", ProcessNextPermissions); + m_InventoryItemXmlProcessors.Add("Flags", ProcessFlags); + m_InventoryItemXmlProcessors.Add("GroupID", ProcessGroupID); + m_InventoryItemXmlProcessors.Add("GroupOwned", ProcessGroupOwned); + } + #endregion + + #region InventoryItemBase Processors + private static void ProcessName(InventoryItemBase item, XmlReader reader) + { + item.Name = reader.ReadElementContentAsString("Name", String.Empty); + } + + private static void ProcessID(InventoryItemBase item, XmlReader reader) + { + item.ID = Util.ReadUUID(reader, "ID"); + } + + private static void ProcessInvType(InventoryItemBase item, XmlReader reader) + { + item.InvType = reader.ReadElementContentAsInt("InvType", String.Empty); + } + + private static void ProcessCreatorUUID(InventoryItemBase item, XmlReader reader) + { + item.CreatorId = reader.ReadElementContentAsString("CreatorUUID", String.Empty); + } + + private static void ProcessCreatorID(InventoryItemBase item, XmlReader reader) + { + // when it exists, this overrides the previous + item.CreatorId = reader.ReadElementContentAsString("CreatorID", String.Empty); + } + + private static void ProcessCreationDate(InventoryItemBase item, XmlReader reader) + { + item.CreationDate = reader.ReadElementContentAsInt("CreationDate", String.Empty); + } + + private static void ProcessOwner(InventoryItemBase item, XmlReader reader) + { + item.Owner = Util.ReadUUID(reader, "Owner"); + } + + private static void ProcessDescription(InventoryItemBase item, XmlReader reader) + { + item.Description = reader.ReadElementContentAsString("Description", String.Empty); + } + + private static void ProcessAssetType(InventoryItemBase item, XmlReader reader) + { + item.AssetType = reader.ReadElementContentAsInt("AssetType", String.Empty); + } + + private static void ProcessAssetID(InventoryItemBase item, XmlReader reader) + { + item.AssetID = Util.ReadUUID(reader, "AssetID"); + } + + private static void ProcessSaleType(InventoryItemBase item, XmlReader reader) + { + item.SaleType = (byte)reader.ReadElementContentAsInt("SaleType", String.Empty); + } + + private static void ProcessSalePrice(InventoryItemBase item, XmlReader reader) + { + item.SalePrice = reader.ReadElementContentAsInt("SalePrice", String.Empty); + } + + private static void ProcessBasePermissions(InventoryItemBase item, XmlReader reader) + { + item.BasePermissions = (uint)reader.ReadElementContentAsInt("BasePermissions", String.Empty); + } + + private static void ProcessCurrentPermissions(InventoryItemBase item, XmlReader reader) + { + item.CurrentPermissions = (uint)reader.ReadElementContentAsInt("CurrentPermissions", String.Empty); + } + + private static void ProcessEveryOnePermissions(InventoryItemBase item, XmlReader reader) + { + item.EveryOnePermissions = (uint)reader.ReadElementContentAsInt("EveryOnePermissions", String.Empty); + } + + private static void ProcessNextPermissions(InventoryItemBase item, XmlReader reader) + { + item.NextPermissions = (uint)reader.ReadElementContentAsInt("NextPermissions", String.Empty); + } + + private static void ProcessFlags(InventoryItemBase item, XmlReader reader) + { + item.Flags = (uint)reader.ReadElementContentAsInt("Flags", String.Empty); + } + + private static void ProcessGroupID(InventoryItemBase item, XmlReader reader) + { + item.GroupID = Util.ReadUUID(reader, "GroupID"); + } + + private static void ProcessGroupOwned(InventoryItemBase item, XmlReader reader) + { + item.GroupOwned = Util.ReadBoolean(reader); + } + + private static void ProcessCreatorData(InventoryItemBase item, XmlReader reader) + { + item.CreatorData = reader.ReadElementContentAsString("CreatorData", String.Empty); + } + + #endregion + + /// + /// Deserialize item + /// + /// + /// + /// + public static InventoryItemBase Deserialize(byte[] serialization) + { + return Deserialize(Encoding.ASCII.GetString(serialization, 0, serialization.Length)); + } + + /// + /// Deserialize settings + /// + /// + /// + /// + public static InventoryItemBase Deserialize(string serialization) + { + InventoryItemBase item = new InventoryItemBase(); + + using (XmlTextReader reader = new XmlTextReader(new StringReader(serialization))) + { + reader.ReadStartElement("InventoryItem"); + + ExternalRepresentationUtils.ExecuteReadProcessors( + item, m_InventoryItemXmlProcessors, reader); + + reader.ReadEndElement(); // InventoryItem + } + + //m_log.DebugFormat("[XXX]: parsed InventoryItemBase {0} - {1}", obj.Name, obj.UUID); + return item; + } + + public static string Serialize(InventoryItemBase inventoryItem, Dictionary options, IUserAccountService userAccountService) + { + StringWriter sw = new StringWriter(); + XmlTextWriter writer = new XmlTextWriter(sw); + writer.Formatting = Formatting.Indented; + writer.WriteStartDocument(); + + writer.WriteStartElement("InventoryItem"); + + writer.WriteStartElement("Name"); + writer.WriteString(inventoryItem.Name); + writer.WriteEndElement(); + writer.WriteStartElement("ID"); + writer.WriteString(inventoryItem.ID.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("InvType"); + writer.WriteString(inventoryItem.InvType.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("CreatorUUID"); + writer.WriteString(OspResolver.MakeOspa(inventoryItem.CreatorIdAsUuid, userAccountService)); + writer.WriteEndElement(); + writer.WriteStartElement("CreationDate"); + writer.WriteString(inventoryItem.CreationDate.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("Owner"); + writer.WriteString(inventoryItem.Owner.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("Description"); + writer.WriteString(inventoryItem.Description); + writer.WriteEndElement(); + writer.WriteStartElement("AssetType"); + writer.WriteString(inventoryItem.AssetType.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("AssetID"); + writer.WriteString(inventoryItem.AssetID.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("SaleType"); + writer.WriteString(inventoryItem.SaleType.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("SalePrice"); + writer.WriteString(inventoryItem.SalePrice.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("BasePermissions"); + writer.WriteString(inventoryItem.BasePermissions.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("CurrentPermissions"); + writer.WriteString(inventoryItem.CurrentPermissions.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("EveryOnePermissions"); + writer.WriteString(inventoryItem.EveryOnePermissions.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("NextPermissions"); + writer.WriteString(inventoryItem.NextPermissions.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("Flags"); + writer.WriteString(inventoryItem.Flags.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("GroupID"); + writer.WriteString(inventoryItem.GroupID.ToString()); + writer.WriteEndElement(); + writer.WriteStartElement("GroupOwned"); + writer.WriteString(inventoryItem.GroupOwned.ToString()); + writer.WriteEndElement(); + if (options.ContainsKey("creators") && !string.IsNullOrEmpty(inventoryItem.CreatorData)) + writer.WriteElementString("CreatorData", inventoryItem.CreatorData); + else if (options.ContainsKey("home")) + { + if (userAccountService != null) + { + UserAccount account = userAccountService.GetUserAccount(UUID.Zero, inventoryItem.CreatorIdAsUuid); + if (account != null) + { + string creatorData = ExternalRepresentationUtils.CalcCreatorData((string)options["home"], inventoryItem.CreatorIdAsUuid, account.FirstName + " " + account.LastName); + writer.WriteElementString("CreatorData", creatorData); + } + writer.WriteElementString("CreatorID", inventoryItem.CreatorId); + } + } + + writer.WriteEndElement(); + + writer.Close(); + sw.Close(); + + return sw.ToString(); + } + } +} diff --git a/OpenSim/Framework/Serialization/External/UserProfileSerializer.cs b/OpenSim/Framework/Serialization/External/UserProfileSerializer.cs new file mode 100644 index 0000000000..c685a157e1 --- /dev/null +++ b/OpenSim/Framework/Serialization/External/UserProfileSerializer.cs @@ -0,0 +1,73 @@ +/* + * 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.Xml; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Framework.Serialization.External +{ + /// + /// Serialize and deserialize user profiles as an external format. + /// + /// + /// Currently UNUSED. + /// + public class UserProfileSerializer + { + public const int MAJOR_VERSION = 0; + public const int MINOR_VERSION = 1; + + public static string Serialize(UUID userID, string firstName, string lastName) + { + StringWriter sw = new StringWriter(); + XmlTextWriter xtw = new XmlTextWriter(sw); + xtw.Formatting = Formatting.Indented; + xtw.WriteStartDocument(); + + xtw.WriteStartElement("user_profile"); + xtw.WriteAttributeString("major_version", MAJOR_VERSION.ToString()); + xtw.WriteAttributeString("minor_version", MINOR_VERSION.ToString()); + + xtw.WriteElementString("name", firstName + " " + lastName); + xtw.WriteElementString("id", userID.ToString()); + xtw.WriteElementString("about", ""); + + // Not sure if we're storing this yet, need to take a look +// xtw.WriteElementString("Url", profile.Url); + // or, indeed, interests + + xtw.WriteEndElement(); + + xtw.Close(); + sw.Close(); + + return sw.ToString(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0cce7223ee --- /dev/null +++ b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.Serialization")] +[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("919db41e-4ac0-4f24-9992-81d62c0ee183")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/Serialization/TarArchiveReader.cs b/OpenSim/Framework/Serialization/TarArchiveReader.cs new file mode 100644 index 0000000000..339a37ad11 --- /dev/null +++ b/OpenSim/Framework/Serialization/TarArchiveReader.cs @@ -0,0 +1,226 @@ +/* + * 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.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework.Serialization +{ + /// + /// Temporary code to do the bare minimum required to read a tar archive for our purposes + /// + public class TarArchiveReader + { + //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public enum TarEntryType + { + TYPE_UNKNOWN = 0, + TYPE_NORMAL_FILE = 1, + TYPE_HARD_LINK = 2, + TYPE_SYMBOLIC_LINK = 3, + TYPE_CHAR_SPECIAL = 4, + TYPE_BLOCK_SPECIAL = 5, + TYPE_DIRECTORY = 6, + TYPE_FIFO = 7, + TYPE_CONTIGUOUS_FILE = 8, + } + + /// + /// Binary reader for the underlying stream + /// + protected BinaryReader m_br; + + /// + /// Used to trim off null chars + /// + protected static char[] m_nullCharArray = new char[] { '\0' }; + /// + /// Used to trim off space chars + /// + protected static char[] m_spaceCharArray = new char[] { ' ' }; + + /// + /// Generate a tar reader which reads from the given stream. + /// + /// + public TarArchiveReader(Stream s) + { + m_br = new BinaryReader(s); + } + + /// + /// Read the next entry in the tar file. + /// + /// + /// the data for the entry. Returns null if there are no more entries + public byte[] ReadEntry(out string filePath, out TarEntryType entryType) + { + filePath = String.Empty; + entryType = TarEntryType.TYPE_UNKNOWN; + TarHeader header = ReadHeader(); + + if (null == header) + return null; + + entryType = header.EntryType; + filePath = header.FilePath; + return ReadData(header.FileSize); + } + + /// + /// Read the next 512 byte chunk of data as a tar header. + /// + /// A tar header struct. null if we have reached the end of the archive. + protected TarHeader ReadHeader() + { + byte[] header = m_br.ReadBytes(512); + + // If there are no more bytes in the stream, return null header + if (header.Length == 0) + return null; + + // If we've reached the end of the archive we'll be in null block territory, which means + // the next byte will be 0 + if (header[0] == 0) + return null; + + TarHeader tarHeader = new TarHeader(); + + // If we're looking at a GNU tar long link then extract the long name and pull up the next header + if (header[156] == (byte)'L') + { + int longNameLength = ConvertOctalBytesToDecimal(header, 124, 11); + tarHeader.FilePath = Encoding.ASCII.GetString(ReadData(longNameLength)); + //m_log.DebugFormat("[TAR ARCHIVE READER]: Got long file name {0}", tarHeader.FilePath); + header = m_br.ReadBytes(512); + } + else + { + tarHeader.FilePath = Encoding.ASCII.GetString(header, 0, 100); + tarHeader.FilePath = tarHeader.FilePath.Trim(m_nullCharArray); + //m_log.DebugFormat("[TAR ARCHIVE READER]: Got short file name {0}", tarHeader.FilePath); + } + + tarHeader.FileSize = ConvertOctalBytesToDecimal(header, 124, 11); + + switch (header[156]) + { + case 0: + tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; + break; + case (byte)'0': + tarHeader.EntryType = TarEntryType.TYPE_NORMAL_FILE; + break; + case (byte)'1': + tarHeader.EntryType = TarEntryType.TYPE_HARD_LINK; + break; + case (byte)'2': + tarHeader.EntryType = TarEntryType.TYPE_SYMBOLIC_LINK; + break; + case (byte)'3': + tarHeader.EntryType = TarEntryType.TYPE_CHAR_SPECIAL; + break; + case (byte)'4': + tarHeader.EntryType = TarEntryType.TYPE_BLOCK_SPECIAL; + break; + case (byte)'5': + tarHeader.EntryType = TarEntryType.TYPE_DIRECTORY; + break; + case (byte)'6': + tarHeader.EntryType = TarEntryType.TYPE_FIFO; + break; + case (byte)'7': + tarHeader.EntryType = TarEntryType.TYPE_CONTIGUOUS_FILE; + break; + } + + return tarHeader; + } + + /// + /// Read data following a header + /// + /// + /// + protected byte[] ReadData(int fileSize) + { + byte[] data = m_br.ReadBytes(fileSize); + + //m_log.DebugFormat("[TAR ARCHIVE READER]: fileSize {0}", fileSize); + + // Read the rest of the empty padding in the 512 byte block + if (fileSize % 512 != 0) + { + int paddingLeft = 512 - (fileSize % 512); + + //m_log.DebugFormat("[TAR ARCHIVE READER]: Reading {0} padding bytes", paddingLeft); + + m_br.ReadBytes(paddingLeft); + } + + return data; + } + + public void Close() + { + m_br.Close(); + } + + /// + /// Convert octal bytes to a decimal representation + /// + /// + /// + public static int ConvertOctalBytesToDecimal(byte[] bytes, int startIndex, int count) + { + // Trim leading white space: ancient tars do that instead + // of leading 0s :-( don't ask. really. + string oString = Encoding.ASCII.GetString(bytes, startIndex, count).TrimStart(m_spaceCharArray); + + int d = 0; + + foreach (char c in oString) + { + d <<= 3; + d |= c - '0'; + } + + return d; + } + } + + public class TarHeader + { + public string FilePath; + public int FileSize; + public TarArchiveReader.TarEntryType EntryType; + } +} diff --git a/OpenSim/Framework/Serialization/TarArchiveWriter.cs b/OpenSim/Framework/Serialization/TarArchiveWriter.cs new file mode 100644 index 0000000000..2a3bc4805d --- /dev/null +++ b/OpenSim/Framework/Serialization/TarArchiveWriter.cs @@ -0,0 +1,229 @@ +/* + * 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.Text; +using log4net; + +namespace OpenSim.Framework.Serialization +{ + /// + /// Temporary code to produce a tar archive in tar v7 format + /// + public class TarArchiveWriter + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Binary writer for the underlying stream + /// + protected BinaryWriter m_bw; + + public TarArchiveWriter(Stream s) + { + m_bw = new BinaryWriter(s); + } + + /// + /// Write a directory entry to the tar archive. We can only handle one path level right now! + /// + /// + public void WriteDir(string dirName) + { + // Directories are signalled by a final / + if (!dirName.EndsWith("/")) + dirName += "/"; + + WriteFile(dirName, new byte[0]); + } + + /// + /// Write a file to the tar archive + /// + /// + /// + public void WriteFile(string filePath, string data) + { + WriteFile(filePath, Util.UTF8NoBomEncoding.GetBytes(data)); + } + + /// + /// Write a file to the tar archive + /// + /// + /// + public void WriteFile(string filePath, byte[] data) + { + if (filePath.Length > 100) + WriteEntry("././@LongLink", Encoding.ASCII.GetBytes(filePath), 'L'); + + char fileType; + + if (filePath.EndsWith("/")) + { + fileType = '5'; + } + else + { + fileType = '0'; + } + + WriteEntry(filePath, data, fileType); + } + + /// + /// Finish writing the raw tar archive data to a stream. The stream will be closed on completion. + /// + /// Stream to which to write the data + /// + public void Close() + { + //m_log.Debug("[TAR ARCHIVE WRITER]: Writing final consecutive 0 blocks"); + + // Write two consecutive 0 blocks to end the archive + byte[] finalZeroPadding = new byte[1024]; + + lock (m_bw) + { + m_bw.Write(finalZeroPadding); + + m_bw.Flush(); + m_bw.Close(); + } + } + + public static byte[] ConvertDecimalToPaddedOctalBytes(int d, int padding) + { + string oString = ""; + + while (d > 0) + { + oString = Convert.ToString((byte)'0' + d & 7) + oString; + d >>= 3; + } + + while (oString.Length < padding) + { + oString = "0" + oString; + } + + byte[] oBytes = Encoding.ASCII.GetBytes(oString); + + return oBytes; + } + + /// + /// Write a particular entry + /// + /// + /// + /// + protected void WriteEntry(string filePath, byte[] data, char fileType) + { +// m_log.DebugFormat( +// "[TAR ARCHIVE WRITER]: Data for {0} is {1} bytes", filePath, (null == data ? "null" : data.Length.ToString())); + + byte[] header = new byte[512]; + + // file path field (100) + byte[] nameBytes = Encoding.ASCII.GetBytes(filePath); + int nameSize = (nameBytes.Length >= 100) ? 100 : nameBytes.Length; + Array.Copy(nameBytes, header, nameSize); + + // file mode (8) + byte[] modeBytes = Encoding.ASCII.GetBytes("0000777"); + Array.Copy(modeBytes, 0, header, 100, 7); + + // owner user id (8) + byte[] ownerIdBytes = Encoding.ASCII.GetBytes("0000764"); + Array.Copy(ownerIdBytes, 0, header, 108, 7); + + // group user id (8) + byte[] groupIdBytes = Encoding.ASCII.GetBytes("0000764"); + Array.Copy(groupIdBytes, 0, header, 116, 7); + + // file size in bytes (12) + int fileSize = data.Length; + //m_log.DebugFormat("[TAR ARCHIVE WRITER]: File size of {0} is {1}", filePath, fileSize); + + byte[] fileSizeBytes = ConvertDecimalToPaddedOctalBytes(fileSize, 11); + + Array.Copy(fileSizeBytes, 0, header, 124, 11); + + // last modification time (12) + byte[] lastModTimeBytes = Encoding.ASCII.GetBytes("11017037332"); + Array.Copy(lastModTimeBytes, 0, header, 136, 11); + + // entry type indicator (1) + header[156] = Encoding.ASCII.GetBytes(new char[] { fileType })[0]; + + Array.Copy(Encoding.ASCII.GetBytes("0000000"), 0, header, 329, 7); + Array.Copy(Encoding.ASCII.GetBytes("0000000"), 0, header, 337, 7); + + // check sum for header block (8) [calculated last] + Array.Copy(Encoding.ASCII.GetBytes(" "), 0, header, 148, 8); + + int checksum = 0; + foreach (byte b in header) + { + checksum += b; + } + + //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Decimal header checksum is {0}", checksum); + + byte[] checkSumBytes = ConvertDecimalToPaddedOctalBytes(checksum, 6); + + Array.Copy(checkSumBytes, 0, header, 148, 6); + + header[154] = 0; + + lock (m_bw) + { + // Write out header + m_bw.Write(header); + + // Write out data + // An IOException occurs if we try to write out an empty array in Mono 2.6 + if (data.Length > 0) + m_bw.Write(data); + + if (data.Length % 512 != 0) + { + int paddingRequired = 512 - (data.Length % 512); + + //m_log.DebugFormat("[TAR ARCHIVE WRITER]: Padding data with {0} bytes", paddingRequired); + + byte[] padding = new byte[paddingRequired]; + m_bw.Write(padding); + } + } + } + } +} diff --git a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs new file mode 100644 index 0000000000..e81cb78cfd --- /dev/null +++ b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs @@ -0,0 +1,166 @@ +/* + * 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 OpenMetaverse.StructuredData; +using NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Framework.Serialization.External; +using OpenSim.Tests.Common; + +namespace OpenSim.Framework.Serialization.Tests +{ + [TestFixture] + public class LandDataSerializerTest : OpenSimTestCase + { + private LandData land; + private LandData landWithParcelAccessList; + +// private static string preSerialized = "\n\n 128\n 0\n 00000000-0000-0000-0000-000000000000\n 10\n 0\n 0\n 54ff9641-dd40-4a2c-b1f1-47dd3af24e50\n d740204e-bbbf-44aa-949d-02c7d739f6a5\n False\n AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n land data to test LandDataSerializer\n 536870944\n 2\n LandDataSerializerTest Land\n 0\n 0\n 1\n d4452578-2f25-4b97-a81b-819af559cfd7\n http://videos.opensimulator.org/bumblebee.mp4\n \n 1b8eedf9-6d15-448b-8015-24286f1756bf\n \n 0\n 0\n 0\n 00000000-0000-0000-0000-000000000000\n <0, 0, 0>\n <0, 0, 0>\n 0\n 0\n"; + private static string preSerializedWithParcelAccessList + = "\n\n 128\n 0\n 00000000-0000-0000-0000-000000000000\n 10\n 0\n 0\n 54ff9641-dd40-4a2c-b1f1-47dd3af24e50\n d740204e-bbbf-44aa-949d-02c7d739f6a5\n False\n AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n land data to test LandDataSerializer\n 536870944\n 2\n LandDataSerializerTest Land\n 0\n 0\n 1\n d4452578-2f25-4b97-a81b-819af559cfd7\n http://videos.opensimulator.org/bumblebee.mp4\n \n 1b8eedf9-6d15-448b-8015-24286f1756bf\n \n \n 62d65d45-c91a-4f77-862c-46557d978b6c\n \n 2\n \n \n ec2a8d18-2378-4fe0-8b68-2a31b57c481e\n \n 1\n \n \n 0\n 0\n 0\n 00000000-0000-0000-0000-000000000000\n <0, 0, 0>\n <0, 0, 0>\n 0\n 0\n"; + + [SetUp] + public void setup() + { + // setup LandData object + this.land = new LandData(); + this.land.AABBMax = new Vector3(1, 2, 3); + this.land.AABBMin = new Vector3(129, 130, 131); + this.land.Area = 128; + this.land.AuctionID = 4; + this.land.AuthBuyerID = new UUID("7176df0c-6c50-45db-8a37-5e78be56a0cd"); + this.land.Category = ParcelCategory.Residential; + this.land.ClaimDate = 1; + this.land.ClaimPrice = 2; + this.land.GlobalID = new UUID("54ff9641-dd40-4a2c-b1f1-47dd3af24e50"); + this.land.GroupID = new UUID("d740204e-bbbf-44aa-949d-02c7d739f6a5"); + this.land.Description = "land data to test LandDataSerializer"; + this.land.Flags = (uint)(ParcelFlags.AllowDamage | ParcelFlags.AllowVoiceChat); + this.land.LandingType = (byte)LandingType.Direct; + this.land.Name = "LandDataSerializerTest Land"; + this.land.Status = ParcelStatus.Leased; + this.land.LocalID = 1; + this.land.MediaAutoScale = (byte)0x01; + this.land.MediaID = new UUID("d4452578-2f25-4b97-a81b-819af559cfd7"); + this.land.MediaURL = "http://videos.opensimulator.org/bumblebee.mp4"; + this.land.OwnerID = new UUID("1b8eedf9-6d15-448b-8015-24286f1756bf"); + + this.landWithParcelAccessList = this.land.Copy(); + this.landWithParcelAccessList.ParcelAccessList.Clear(); + + LandAccessEntry pae0 = new LandAccessEntry(); + pae0.AgentID = new UUID("62d65d45-c91a-4f77-862c-46557d978b6c"); + pae0.Flags = AccessList.Ban; + pae0.Expires = 0; + this.landWithParcelAccessList.ParcelAccessList.Add(pae0); + + LandAccessEntry pae1 = new LandAccessEntry(); + pae1.AgentID = new UUID("ec2a8d18-2378-4fe0-8b68-2a31b57c481e"); + pae1.Flags = AccessList.Access; + pae1.Expires = 0; + this.landWithParcelAccessList.ParcelAccessList.Add(pae1); + } + + /// + /// Test the LandDataSerializer.Serialize() method + /// +// [Test] +// public void LandDataSerializerSerializeTest() +// { +// TestHelpers.InMethod(); +// +// string serialized = LandDataSerializer.Serialize(this.land).Replace("\r\n", "\n"); +// Assert.That(serialized.Length > 0, "Serialize(LandData) returned empty string"); +// +// // adding a simple boolean variable because resharper nUnit integration doesn't like this +// // XML data in the Assert.That statement. Not sure why. +// bool result = (serialized == preSerialized); +// Assert.That(result, "result of Serialize LandData does not match expected result"); +// +// string serializedWithParcelAccessList = LandDataSerializer.Serialize(this.landWithParcelAccessList).Replace("\r\n", "\n"); +// Assert.That(serializedWithParcelAccessList.Length > 0, +// "Serialize(LandData) returned empty string for LandData object with ParcelAccessList"); +// result = (serializedWithParcelAccessList == preSerializedWithParcelAccessList); +// Assert.That(result, +// "result of Serialize(LandData) does not match expected result (pre-serialized with parcel access list"); +// } + + /// + /// Test the LandDataSerializer.Deserialize() method + /// + [Test] + public void TestLandDataDeserializeNoAccessLists() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + Dictionary options = new Dictionary(); + LandData ld = LandDataSerializer.Deserialize(LandDataSerializer.Serialize(this.land, options)); + Assert.That(ld, Is.Not.Null, "Deserialize(string) returned null"); +// Assert.That(ld.AABBMax, Is.EqualTo(land.AABBMax)); +// Assert.That(ld.AABBMin, Is.EqualTo(land.AABBMin)); + Assert.That(ld.Area, Is.EqualTo(land.Area)); + Assert.That(ld.AuctionID, Is.EqualTo(land.AuctionID)); + Assert.That(ld.AuthBuyerID, Is.EqualTo(land.AuthBuyerID)); + Assert.That(ld.Category, Is.EqualTo(land.Category)); + Assert.That(ld.ClaimDate, Is.EqualTo(land.ClaimDate)); + Assert.That(ld.ClaimPrice, Is.EqualTo(land.ClaimPrice)); + Assert.That(ld.GlobalID, Is.EqualTo(land.GlobalID), "Reified LandData.GlobalID != original LandData.GlobalID"); + Assert.That(ld.GroupID, Is.EqualTo(land.GroupID)); + Assert.That(ld.Description, Is.EqualTo(land.Description)); + Assert.That(ld.Flags, Is.EqualTo(land.Flags)); + Assert.That(ld.LandingType, Is.EqualTo(land.LandingType)); + Assert.That(ld.Name, Is.EqualTo(land.Name), "Reified LandData.Name != original LandData.Name"); + Assert.That(ld.Status, Is.EqualTo(land.Status)); + Assert.That(ld.LocalID, Is.EqualTo(land.LocalID)); + Assert.That(ld.MediaAutoScale, Is.EqualTo(land.MediaAutoScale)); + Assert.That(ld.MediaID, Is.EqualTo(land.MediaID)); + Assert.That(ld.MediaURL, Is.EqualTo(land.MediaURL)); + Assert.That(ld.OwnerID, Is.EqualTo(land.OwnerID)); + } + + [Test] + public void TestLandDataDeserializeWithAccessLists() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + LandData ld = LandDataSerializer.Deserialize(LandDataSerializerTest.preSerializedWithParcelAccessList); + Assert.That(ld != null, + "Deserialize(string) returned null (pre-serialized with parcel access list)"); + Assert.That(ld.GlobalID == this.landWithParcelAccessList.GlobalID, + "Reified LandData.GlobalID != original LandData.GlobalID (pre-serialized with parcel access list)"); + Assert.That(ld.Name == this.landWithParcelAccessList.Name, + "Reified LandData.Name != original LandData.Name (pre-serialized with parcel access list)"); + Assert.That(ld.ParcelAccessList.Count, Is.EqualTo(2)); + Assert.That(ld.ParcelAccessList[0].AgentID, Is.EqualTo(UUID.Parse("62d65d45-c91a-4f77-862c-46557d978b6c"))); + } + } +} diff --git a/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs new file mode 100644 index 0000000000..142726bbe7 --- /dev/null +++ b/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs @@ -0,0 +1,142 @@ +/* + * 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 OpenMetaverse.StructuredData; +using NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Framework.Serialization.External; +using OpenSim.Tests.Common; + +namespace OpenSim.Framework.Serialization.Tests +{ + [TestFixture] + public class RegionSettingsSerializerTests : OpenSimTestCase + { + private string m_serializedRs = @" + + + True + True + True + True + True + True + True + True + True + 1 + True + 40 + 1.4 + + + 00000000-0000-0000-0000-000000000020 + 00000000-0000-0000-0000-000000000040 + 00000000-0000-0000-0000-000000000060 + 00000000-0000-0000-0000-000000000080 + 1.9 + 15.9 + 49 + 45.3 + 2.1 + 4.5 + 9.2 + 19.2 + + + 23 + 17.9 + 0.4 + True + true + 12 + + + 00000000-0000-0000-0000-111111111111 + 1,-2,0.33 + +"; + + private RegionSettings m_rs; + + [SetUp] + public void Setup() + { + m_rs = new RegionSettings(); + m_rs.AgentLimit = 17; + m_rs.AllowDamage = true; + m_rs.AllowLandJoinDivide = true; + m_rs.AllowLandResell = true; + m_rs.BlockFly = true; + m_rs.BlockShowInSearch = true; + m_rs.BlockTerraform = true; + m_rs.DisableCollisions = true; + m_rs.DisablePhysics = true; + m_rs.DisableScripts = true; + m_rs.Elevation1NW = 15.9; + m_rs.Elevation1NE = 45.3; + m_rs.Elevation1SE = 49; + m_rs.Elevation1SW = 1.9; + m_rs.Elevation2NW = 4.5; + m_rs.Elevation2NE = 19.2; + m_rs.Elevation2SE = 9.2; + m_rs.Elevation2SW = 2.1; + m_rs.FixedSun = true; + m_rs.SunPosition = 12.0; + m_rs.ObjectBonus = 1.4; + m_rs.RestrictPushing = true; + m_rs.TerrainLowerLimit = 0.4; + m_rs.TerrainRaiseLimit = 17.9; + m_rs.TerrainTexture1 = UUID.Parse("00000000-0000-0000-0000-000000000020"); + m_rs.TerrainTexture2 = UUID.Parse("00000000-0000-0000-0000-000000000040"); + m_rs.TerrainTexture3 = UUID.Parse("00000000-0000-0000-0000-000000000060"); + m_rs.TerrainTexture4 = UUID.Parse("00000000-0000-0000-0000-000000000080"); + m_rs.UseEstateSun = true; + m_rs.WaterHeight = 23; + m_rs.TelehubObject = UUID.Parse("00000000-0000-0000-0000-111111111111"); + m_rs.AddSpawnPoint(SpawnPoint.Parse("1,-2,0.33")); + } + + [Test] + public void TestRegionSettingsDeserialize() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + RegionSettings deserRs = RegionSettingsSerializer.Deserialize(m_serializedRs); + Assert.That(deserRs, Is.Not.Null); + Assert.That(deserRs.TerrainTexture2, Is.EqualTo(m_rs.TerrainTexture2)); + Assert.That(deserRs.DisablePhysics, Is.EqualTo(m_rs.DisablePhysics)); + Assert.That(deserRs.TerrainLowerLimit, Is.EqualTo(m_rs.TerrainLowerLimit)); + Assert.That(deserRs.TelehubObject, Is.EqualTo(m_rs.TelehubObject)); + Assert.That(deserRs.SpawnPoints()[0].ToString(), Is.EqualTo(m_rs.SpawnPoints()[0].ToString())); + } + } +} diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs new file mode 100644 index 0000000000..828a8523d0 --- /dev/null +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -0,0 +1,178 @@ +/* + * 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.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Timers; +using log4net; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using Timer=System.Timers.Timer; +using Nini.Config; + +namespace OpenSim.Framework.Servers +{ + /// + /// Common base for the main OpenSimServers (user, grid, inventory, region, etc) + /// + public abstract class BaseOpenSimServer : ServerBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Used by tests to suppress Environment.Exit(0) so that post-run operations are possible. + /// + public bool SuppressExit { get; set; } + + /// + /// This will control a periodic log printout of the current 'show stats' (if they are active) for this + /// server. + /// + private int m_periodDiagnosticTimerMS = 60 * 60 * 1000; + private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); + + /// + /// Random uuid for private data + /// + protected string m_osSecret = String.Empty; + + protected BaseHttpServer m_httpServer; + public BaseHttpServer HttpServer + { + get { return m_httpServer; } + } + + public BaseOpenSimServer() : base() + { + // Random uuid for private data + m_osSecret = UUID.Random().ToString(); + + } + + /// + /// Must be overriden by child classes for their own server specific startup behaviour. + /// + protected virtual void StartupSpecific() + { + StatsManager.SimExtraStats = new SimExtraStatsCollector(); + RegisterCommonCommands(); + RegisterCommonComponents(Config); + + IConfig startupConfig = Config.Configs["Startup"]; + int logShowStatsSeconds = startupConfig.GetInt("LogShowStatsSeconds", m_periodDiagnosticTimerMS / 1000); + m_periodDiagnosticTimerMS = logShowStatsSeconds * 1000; + m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); + if (m_periodDiagnosticTimerMS != 0) + { + m_periodicDiagnosticsTimer.Interval = m_periodDiagnosticTimerMS; + m_periodicDiagnosticsTimer.Enabled = true; + } + } + + protected override void ShutdownSpecific() + { + m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); + + RemovePIDFile(); + + base.ShutdownSpecific(); + + if (!SuppressExit) + Environment.Exit(0); + } + + /// + /// Provides a list of help topics that are available. Overriding classes should append their topics to the + /// information returned when the base method is called. + /// + /// + /// + /// A list of strings that represent different help topics on which more information is available + /// + protected virtual List GetHelpTopics() { return new List(); } + + /// + /// Print statistics to the logfile, if they are active + /// + protected void LogDiagnostics(object source, ElapsedEventArgs e) + { + StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); + sb.Append(GetUptimeReport()); + sb.Append(StatsManager.SimExtraStats.Report()); + sb.Append(Environment.NewLine); + sb.Append(GetThreadsReport()); + + m_log.Debug(sb); + } + + /// + /// Performs initialisation of the scene, such as loading configuration from disk. + /// + public virtual void Startup() + { + StartupSpecific(); + + TimeSpan timeTaken = DateTime.Now - m_startuptime; + + MainConsole.Instance.OutputFormat( + "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.", + timeTaken.Minutes, timeTaken.Seconds); + } + + public string osSecret + { + // Secret uuid for the simulator + get { return m_osSecret; } + } + + public string StatReport(IOSHttpRequest httpRequest) + { + // If we catch a request for "callback", wrap the response in the value for jsonp + if (httpRequest.Query.ContainsKey("callback")) + { + return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");"; + } + else + { + return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs new file mode 100644 index 0000000000..9f8f4a8674 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs @@ -0,0 +1,41 @@ +/* + * 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.Servers.HttpServer +{ + public abstract class BaseHTTPHandler : BaseRequestHandler, IGenericHTTPHandler + { + public abstract Hashtable Handle(string path, Hashtable Request); + + protected BaseHTTPHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + + protected BaseHTTPHandler(string httpMethod, string path, string name, string description) + : base(httpMethod, path, name, description) {} + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs new file mode 100644 index 0000000000..f252bd5554 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -0,0 +1,2147 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography.X509Certificates; +using System.Reflection; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Xml; +using HttpServer; +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse.StructuredData; +using CoolHTTPListener = HttpServer.HttpListener; +using HttpListener=System.Net.HttpListener; +using LogPrio=HttpServer.LogPrio; +using OpenSim.Framework.Monitoring; +using System.IO.Compression; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class BaseHttpServer : IHttpServer + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); + private static Encoding UTF8NoBOM = new System.Text.UTF8Encoding(false); + + /// + /// This is a pending websocket request before it got an sucessful upgrade response. + /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to + /// start the connection and optionally provide an origin authentication method. + /// + /// + /// + public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler); + + /// + /// Gets or sets the debug level. + /// + /// + /// See MainServer.DebugLevel. + /// + public int DebugLevel { get; set; } + + /// + /// Request number for diagnostic purposes. + /// + /// + /// This is an internal number. In some debug situations an external number may also be supplied in the + /// opensim-request-id header but we are not currently logging this. + /// + public int RequestNumber { get; private set; } + + /// + /// Statistic for holding number of requests processed. + /// + private Stat m_requestsProcessedStat; + + private volatile int NotSocketErrors = 0; + public volatile bool HTTPDRunning = false; + + // protected HttpListener m_httpListener; + protected CoolHTTPListener m_httpListener2; + protected Dictionary m_rpcHandlers = new Dictionary(); + protected Dictionary jsonRpcHandlers = new Dictionary(); + protected Dictionary m_rpcHandlersKeepAlive = new Dictionary(); + protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ + protected Dictionary m_llsdHandlers = new Dictionary(); + protected Dictionary m_streamHandlers = new Dictionary(); + protected Dictionary m_HTTPHandlers = new Dictionary(); +// protected Dictionary m_agentHandlers = new Dictionary(); + protected Dictionary m_pollHandlers = + new Dictionary(); + + protected Dictionary m_WebSocketHandlers = + new Dictionary(); + + protected uint m_port; + protected uint m_sslport; + protected bool m_ssl; + private X509Certificate2 m_cert; + protected bool m_firstcaps = true; + protected string m_SSLCommonName = ""; + + protected IPAddress m_listenIPAddress = IPAddress.Any; + + public PollServiceRequestManager PollServiceRequestManager { get; private set; } + + public uint SSLPort + { + get { return m_sslport; } + } + + public string SSLCommonName + { + get { return m_SSLCommonName; } + } + + public uint Port + { + get { return m_port; } + } + + public bool UseSSL + { + get { return m_ssl; } + } + + public IPAddress ListenIPAddress + { + get { return m_listenIPAddress; } + set { m_listenIPAddress = value; } + } + + public BaseHttpServer(uint port) + { + m_port = port; + } + + public BaseHttpServer(uint port, bool ssl) : this (port) + { + m_ssl = ssl; + } + + public BaseHttpServer(uint port, bool ssl, uint sslport, string CN) : this (port, ssl) + { + if (m_ssl) + { + m_sslport = sslport; + } + } + + public BaseHttpServer(uint port, bool ssl, string CPath, string CPass) : this (port, ssl) + { + if (m_ssl) + { + m_cert = new X509Certificate2(CPath, CPass); + } + } + + /// + /// Add a stream handler to the http server. If the handler already exists, then nothing happens. + /// + /// + public void AddStreamHandler(IRequestHandler handler) + { + string httpMethod = handler.HttpMethod; + string path = handler.Path; + string handlerKey = GetHandlerKey(httpMethod, path); + + lock (m_streamHandlers) + { + if (!m_streamHandlers.ContainsKey(handlerKey)) + { + // m_log.DebugFormat("[BASE HTTP SERVER]: Adding handler key {0}", handlerKey); + m_streamHandlers.Add(handlerKey, handler); + } + } + } + + public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler) + { + lock (m_WebSocketHandlers) + { + if (!m_WebSocketHandlers.ContainsKey(servicepath)) + m_WebSocketHandlers.Add(servicepath, handler); + } + } + + public void RemoveWebSocketHandler(string servicepath) + { + lock (m_WebSocketHandlers) + if (m_WebSocketHandlers.ContainsKey(servicepath)) + m_WebSocketHandlers.Remove(servicepath); + } + + public List GetStreamHandlerKeys() + { + lock (m_streamHandlers) + return new List(m_streamHandlers.Keys); + } + + private static string GetHandlerKey(string httpMethod, string path) + { + return httpMethod + ":" + path; + } + + public bool AddXmlRPCHandler(string method, XmlRpcMethod handler) + { + return AddXmlRPCHandler(method, handler, true); + } + + public bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive) + { + lock (m_rpcHandlers) + { + m_rpcHandlers[method] = handler; + m_rpcHandlersKeepAlive[method] = keepAlive; // default + } + + return true; + } + + public XmlRpcMethod GetXmlRPCHandler(string method) + { + lock (m_rpcHandlers) + { + if (m_rpcHandlers.ContainsKey(method)) + { + return m_rpcHandlers[method]; + } + else + { + return null; + } + } + } + + public List GetXmlRpcHandlerKeys() + { + lock (m_rpcHandlers) + return new List(m_rpcHandlers.Keys); + } + + // JsonRPC + public bool AddJsonRPCHandler(string method, JsonRPCMethod handler) + { + lock(jsonRpcHandlers) + { + jsonRpcHandlers.Add(method, handler); + } + return true; + } + + public JsonRPCMethod GetJsonRPCHandler(string method) + { + lock (jsonRpcHandlers) + { + if (jsonRpcHandlers.ContainsKey(method)) + { + return jsonRpcHandlers[method]; + } + else + { + return null; + } + } + } + + public List GetJsonRpcHandlerKeys() + { + lock (jsonRpcHandlers) + return new List(jsonRpcHandlers.Keys); + } + + public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) + { + //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); + + lock (m_HTTPHandlers) + { + if (!m_HTTPHandlers.ContainsKey(methodName)) + { + m_HTTPHandlers.Add(methodName, handler); + return true; + } + } + + //must already have a handler for that path so return false + return false; + } + + public List GetHTTPHandlerKeys() + { + lock (m_HTTPHandlers) + return new List(m_HTTPHandlers.Keys); + } + + public bool AddPollServiceHTTPHandler(string methodName, PollServiceEventArgs args) + { + lock (m_pollHandlers) + { + if (!m_pollHandlers.ContainsKey(methodName)) + { + m_pollHandlers.Add(methodName, args); + return true; + } + } + + return false; + } + + public List GetPollServiceHandlerKeys() + { + lock (m_pollHandlers) + return new List(m_pollHandlers.Keys); + } + +// // Note that the agent string is provided simply to differentiate +// // the handlers - it is NOT required to be an actual agent header +// // value. +// public bool AddAgentHandler(string agent, IHttpAgentHandler handler) +// { +// lock (m_agentHandlers) +// { +// if (!m_agentHandlers.ContainsKey(agent)) +// { +// m_agentHandlers.Add(agent, handler); +// return true; +// } +// } +// +// //must already have a handler for that path so return false +// return false; +// } +// +// public List GetAgentHandlerKeys() +// { +// lock (m_agentHandlers) +// return new List(m_agentHandlers.Keys); +// } + + public bool AddLLSDHandler(string path, LLSDMethod handler) + { + lock (m_llsdHandlers) + { + if (!m_llsdHandlers.ContainsKey(path)) + { + m_llsdHandlers.Add(path, handler); + return true; + } + } + return false; + } + + public List GetLLSDHandlerKeys() + { + lock (m_llsdHandlers) + return new List(m_llsdHandlers.Keys); + } + + public bool SetDefaultLLSDHandler(DefaultLLSDMethod handler) + { + m_defaultLlsdHandler = handler; + return true; + } + + public void OnRequest(object source, RequestEventArgs args) + { + RequestNumber++; + + try + { + IHttpClientContext context = (IHttpClientContext)source; + IHttpRequest request = args.Request; + + PollServiceEventArgs psEvArgs; + + if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) + { + psEvArgs.RequestsReceived++; + + PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request); + + if (psEvArgs.Request != null) + { + OSHttpRequest req = new OSHttpRequest(context, request); + + Stream requestStream = req.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + + string[] querystringkeys = req.QueryString.AllKeys; + string[] rHeaders = req.Headers.AllKeys; + + keysvals.Add("body", requestBody); + keysvals.Add("uri", req.RawUrl); + keysvals.Add("content-type", req.ContentType); + keysvals.Add("http-method", req.HttpMethod); + + foreach (string queryname in querystringkeys) + { + keysvals.Add(queryname, req.QueryString[queryname]); + } + + foreach (string headername in rHeaders) + { + headervals[headername] = req.Headers[headername]; + } + + keysvals.Add("headers", headervals); + keysvals.Add("querystringkeys", querystringkeys); + + psEvArgs.Request(psreq.RequestID, keysvals); + } + + PollServiceRequestManager.Enqueue(psreq); + } + else + { + OnHandleRequestIOThread(context, request); + } + } + catch (Exception e) + { + m_log.Error(String.Format("[BASE HTTP SERVER]: OnRequest() failed: {0} ", e.Message), e); + } + } + + private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) + { + OSHttpRequest req = new OSHttpRequest(context, request); + WebSocketRequestDelegate dWebSocketRequestDelegate = null; + lock (m_WebSocketHandlers) + { + if (m_WebSocketHandlers.ContainsKey(req.RawUrl)) + dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl]; + } + if (dWebSocketRequestDelegate != null) + { + dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192)); + return; + } + + OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); + resp.ReuseContext = true; + HandleRequest(req, resp); + + // !!!HACK ALERT!!! + // There seems to be a bug in the underlying http code that makes subsequent requests + // come up with trash in Accept headers. Until that gets fixed, we're cleaning them up here. + if (request.AcceptTypes != null) + for (int i = 0; i < request.AcceptTypes.Length; i++) + request.AcceptTypes[i] = string.Empty; + } + + // public void ConvertIHttpClientContextToOSHttp(object stateinfo) + // { + // HttpServerContextObj objstate = (HttpServerContextObj)stateinfo; + + // OSHttpRequest request = objstate.oreq; + // OSHttpResponse resp = objstate.oresp; + + // HandleRequest(request,resp); + // } + + /// + /// This methods is the start of incoming HTTP request handling. + /// + /// + /// + public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response) + { + if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread + { + try + { + byte[] buffer500 = SendHTML500(response); + response.OutputStream.Write(buffer500, 0, buffer500.Length); + response.Send(); + } + catch + { + } + + return; + } + + string requestMethod = request.HttpMethod; + string uriString = request.RawUrl; + + int requestStartTick = Environment.TickCount; + + // Will be adjusted later on. + int requestEndTick = requestStartTick; + + IRequestHandler requestHandler = null; + + try + { + // OpenSim.Framework.WebUtil.OSHeaderRequestID +// if (request.Headers["opensim-request-id"] != null) +// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]); + //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); + + Culture.SetCurrentCulture(); + +// // This is the REST agent interface. We require an agent to properly identify +// // itself. If the REST handler recognizes the prefix it will attempt to +// // satisfy the request. If it is not recognizable, and no damage has occurred +// // the request can be passed through to the other handlers. This is a low +// // probability event; if a request is matched it is normally expected to be +// // handled +// IHttpAgentHandler agentHandler; +// +// if (TryGetAgentHandler(request, response, out agentHandler)) +// { +// if (HandleAgentRequest(agentHandler, request, response)) +// { +// requestEndTick = Environment.TickCount; +// return; +// } +// } + + //response.KeepAlive = true; + response.SendChunked = false; + + string path = request.RawUrl; + string handlerKey = GetHandlerKey(request.HttpMethod, path); + byte[] buffer = null; + + if (TryGetStreamHandler(handlerKey, out requestHandler)) + { + if (DebugLevel >= 3) + LogIncomingToStreamHandler(request, requestHandler); + + response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. + + if (requestHandler is IStreamedRequestHandler) + { + IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; + + buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response); + } + else if (requestHandler is IGenericHTTPHandler) + { + //m_log.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler"); + IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler; + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + + reader.Close(); + //requestStream.Close(); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + //string host = String.Empty; + + string[] querystringkeys = request.QueryString.AllKeys; + string[] rHeaders = request.Headers.AllKeys; + + foreach (string queryname in querystringkeys) + { + keysvals.Add(queryname, request.QueryString[queryname]); + } + + foreach (string headername in rHeaders) + { + //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]); + headervals[headername] = request.Headers[headername]; + } + + // if (headervals.Contains("Host")) + // { + // host = (string)headervals["Host"]; + // } + + keysvals.Add("requestbody", requestBody); + keysvals.Add("headers",headervals); + if (keysvals.Contains("method")) + { + //m_log.Warn("[HTTP]: Contains Method"); + //string method = (string)keysvals["method"]; + //m_log.Warn("[HTTP]: " + requestBody); + + } + + buffer = DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response); + } + else + { + IStreamHandler streamHandler = (IStreamHandler)requestHandler; + + using (MemoryStream memoryStream = new MemoryStream()) + { + streamHandler.Handle(path, request.InputStream, memoryStream, request, response); + memoryStream.Flush(); + buffer = memoryStream.ToArray(); + } + } + } + else + { + switch (request.ContentType) + { + case null: + case "text/html": + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleHTTPRequest(request, response); + break; + + case "application/llsd+xml": + case "application/xml+llsd": + case "application/llsd+json": + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleLLSDRequests(request, response); + break; + + case "application/json-rpc": + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleJsonRpcRequests(request, response); + break; + + case "text/xml": + case "application/xml": + case "application/json": + + default: + //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); + // Point of note.. the DoWeHaveA methods check for an EXACT path + // if (request.RawUrl.Contains("/CAPS/EQG")) + // { + // int i = 1; + // } + //m_log.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler"); + if (DoWeHaveALLSDHandler(request.RawUrl)) + { + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleLLSDRequests(request, response); + } + // m_log.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl); + else if (DoWeHaveAHTTPHandler(request.RawUrl)) + { + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleHTTPRequest(request, response); + } + else + { + if (DebugLevel >= 3) + LogIncomingToXmlRpcHandler(request); + + // generic login request. + buffer = HandleXmlRpcRequests(request, response); + } + + break; + } + } + + request.InputStream.Close(); + + if (buffer != null) + { + if (WebUtil.DebugLevel >= 5) + { + string output = System.Text.Encoding.UTF8.GetString(buffer); + + if (WebUtil.DebugLevel >= 6) + { + // Always truncate binary blobs. We don't have a ContentType, so detect them using the request name. + if ((requestHandler != null && requestHandler.Name == "GetMesh")) + { + if (output.Length > WebUtil.MaxRequestDiagLength) + output = output.Substring(0, WebUtil.MaxRequestDiagLength) + "..."; + } + } + + WebUtil.LogResponseDetail(RequestNumber, output); + } + + if (!response.SendChunked && response.ContentLength64 <= 0) + response.ContentLength64 = buffer.LongLength; + + response.OutputStream.Write(buffer, 0, buffer.Length); + } + + // Do not include the time taken to actually send the response to the caller in the measurement + // time. This is to avoid logging when it's the client that is slow to process rather than the + // server + requestEndTick = Environment.TickCount; + + response.Send(); + + //response.OutputStream.Close(); + + //response.FreeContext(); + } + catch (SocketException e) + { + // At least on linux, it appears that if the client makes a request without requiring the response, + // an unconnected socket exception is thrown when we close the response output stream. There's no + // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore + // the exception instead. + // + // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go + // with the minimum first + m_log.Warn(String.Format("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux ", e.Message), e); + } + catch (IOException e) + { + m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e); + } + catch (Exception e) + { + m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e); + try + { + byte[] buffer500 = SendHTML500(response); + response.OutputStream.Write(buffer500, 0, buffer500.Length); + response.Send(); + } + catch + { + } + } + finally + { + // Every month or so this will wrap and give bad numbers, not really a problem + // since its just for reporting + int tickdiff = requestEndTick - requestStartTick; + if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture") + { + m_log.InfoFormat( + "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", + RequestNumber, + requestMethod, + uriString, + requestHandler != null ? requestHandler.Name : "", + requestHandler != null ? requestHandler.Description : "", + request.RemoteIPEndPoint, + tickdiff); + } + else if (DebugLevel >= 4) + { + m_log.DebugFormat( + "[LOGHTTP] HTTP IN {0} :{1} took {2}ms", + RequestNumber, + Port, + tickdiff); + } + } + } + + private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler) + { + m_log.DebugFormat( + "[LOGHTTP] HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}", + RequestNumber, + Port, + request.HttpMethod, + request.Url.PathAndQuery, + requestHandler.Name, + requestHandler.Description, + request.RemoteIPEndPoint); + + if (DebugLevel >= 5) + LogIncomingInDetail(request); + } + + private void LogIncomingToContentTypeHandler(OSHttpRequest request) + { + m_log.DebugFormat( + "[LOGHTTP] HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", + RequestNumber, + Port, + string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType, + request.HttpMethod, + request.Url.PathAndQuery, + request.RemoteIPEndPoint); + + if (DebugLevel >= 5) + LogIncomingInDetail(request); + } + + private void LogIncomingToXmlRpcHandler(OSHttpRequest request) + { + m_log.DebugFormat( + "[LOGHTTP] HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}", + RequestNumber, + Port, + request.HttpMethod, + request.Url.PathAndQuery, + request.RemoteIPEndPoint); + + if (DebugLevel >= 5) + LogIncomingInDetail(request); + } + + private void LogIncomingInDetail(OSHttpRequest request) + { + if (request.ContentType == "application/octet-stream") + return; // never log these; they're just binary data + + Stream inputStream = Util.Copy(request.InputStream); + Stream innerStream = null; + try + { + if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip")) + { + innerStream = inputStream; + inputStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress); + } + + using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8)) + { + string output; + + if (DebugLevel == 5) + { + char[] chars = new char[WebUtil.MaxRequestDiagLength + 1]; // +1 so we know to add "..." only if needed + int len = reader.Read(chars, 0, WebUtil.MaxRequestDiagLength + 1); + output = new string(chars, 0, Math.Min(len, WebUtil.MaxRequestDiagLength)); + if (len > WebUtil.MaxRequestDiagLength) + output += "..."; + } + else + { + output = reader.ReadToEnd(); + } + + m_log.DebugFormat("[LOGHTTP] {0}", Util.BinaryToASCII(output)); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + inputStream.Dispose(); + } + } + + private readonly string HANDLER_SEPARATORS = "/?&#-"; + + private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler) + { + string bestMatch = null; + + lock (m_streamHandlers) + { + foreach (string pattern in m_streamHandlers.Keys) + { + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + streamHandler = null; + return false; + } + else + { + streamHandler = m_streamHandlers[bestMatch]; + return true; + } + } + } + + private bool TryGetPollServiceHTTPHandler(string handlerKey, out PollServiceEventArgs oServiceEventArgs) + { + string bestMatch = null; + + lock (m_pollHandlers) + { + foreach (string pattern in m_pollHandlers.Keys) + { + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + oServiceEventArgs = null; + return false; + } + else + { + oServiceEventArgs = m_pollHandlers[bestMatch]; + return true; + } + } + } + + private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler) + { +// m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey); + + string bestMatch = null; + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) + { + if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) + { + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + HTTPHandler = null; + return false; + } + else + { + HTTPHandler = m_HTTPHandlers[bestMatch]; + return true; + } + } + } + +// private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler) +// { +// agentHandler = null; +// +// lock (m_agentHandlers) +// { +// foreach (IHttpAgentHandler handler in m_agentHandlers.Values) +// { +// if (handler.Match(request, response)) +// { +// agentHandler = handler; +// return true; +// } +// } +// } +// +// return false; +// } + + /// + /// Try all the registered xmlrpc handlers when an xmlrpc request is received. + /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method. + /// + /// + /// + private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) + { + String requestBody; + + Stream requestStream = request.InputStream; + Stream innerStream = null; + try + { + if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip")) + { + innerStream = requestStream; + requestStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress); + } + + using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8)) + { + requestBody = reader.ReadToEnd(); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + requestStream.Dispose(); + } + + //m_log.Debug(requestBody); + requestBody = requestBody.Replace("", ""); + + string responseString = String.Empty; + XmlRpcRequest xmlRprcRequest = null; + + try + { + xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody); + } + catch (XmlException e) + { + if (DebugLevel >= 1) + { + if (DebugLevel >= 2) + m_log.Warn( + string.Format( + "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}. XML was '{1}'. Sending blank response. Exception ", + request.RemoteIPEndPoint, requestBody), + e); + else + { + m_log.WarnFormat( + "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}, length {1}. Sending blank response.", + request.RemoteIPEndPoint, requestBody.Length); + } + } + } + + if (xmlRprcRequest != null) + { + string methodName = xmlRprcRequest.MethodName; + if (methodName != null) + { + xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1] + XmlRpcResponse xmlRpcResponse; + + XmlRpcMethod method; + bool methodWasFound; + bool keepAlive = false; + lock (m_rpcHandlers) + { + methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method); + if (methodWasFound) + keepAlive = m_rpcHandlersKeepAlive[methodName]; + } + + if (methodWasFound) + { + xmlRprcRequest.Params.Add(request.Url); // Param[2] + + string xff = "X-Forwarded-For"; + string xfflower = xff.ToLower(); + foreach (string s in request.Headers.AllKeys) + { + if (s != null && s.Equals(xfflower)) + { + xff = xfflower; + break; + } + } + xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3] + + try + { + xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint); + } + catch(Exception e) + { + string errorMessage + = String.Format( + "Requested method [{0}] from {1} threw exception: {2} {3}", + methodName, request.RemoteIPEndPoint.Address, e.Message, e.StackTrace); + + m_log.ErrorFormat("[BASE HTTP SERVER]: {0}", errorMessage); + + // if the registered XmlRpc method threw an exception, we pass a fault-code along + xmlRpcResponse = new XmlRpcResponse(); + + // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + xmlRpcResponse.SetFault(-32603, errorMessage); + } + + // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here + response.KeepAlive = keepAlive; + } + else + { + xmlRpcResponse = new XmlRpcResponse(); + + // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + xmlRpcResponse.SetFault( + XmlRpcErrorCodes.SERVER_ERROR_METHOD, + String.Format("Requested method [{0}] not found", methodName)); + } + + response.ContentType = "text/xml"; + using (MemoryStream outs = new MemoryStream()) + using (XmlTextWriter writer = new XmlTextWriter(outs, UTF8NoBOM)) + { + writer.Formatting = Formatting.None; + XmlRpcResponseSerializer.Singleton.Serialize(writer, xmlRpcResponse); + writer.Flush(); + outs.Flush(); + outs.Position = 0; + using (StreamReader sr = new StreamReader(outs)) + { + responseString = sr.ReadToEnd(); + } + } + } + else + { + //HandleLLSDRequests(request, response); + response.ContentType = "text/plain"; + response.StatusCode = 404; + response.StatusDescription = "Not Found"; + response.ProtocolVersion = "HTTP/1.0"; + responseString = "Not found"; + response.KeepAlive = false; + + m_log.ErrorFormat( + "[BASE HTTP SERVER]: Handler not found for http request {0} {1}", + request.HttpMethod, request.Url.PathAndQuery); + } + } + + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + return buffer; + } + + // JsonRpc (v2.0 only) + // Batch requests not yet supported + private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response) + { + Stream requestStream = request.InputStream; + JsonRpcResponse jsonRpcResponse = new JsonRpcResponse(); + OSDMap jsonRpcRequest = null; + + try + { + jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream); + } + catch (LitJson.JsonException e) + { + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + jsonRpcResponse.Error.Message = e.Message; + } + + requestStream.Close(); + + if (jsonRpcRequest != null) + { + if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0") + { + jsonRpcResponse.JsonRpc = "2.0"; + + // If we have no id, then it's a "notification" + if (jsonRpcRequest.ContainsKey("id")) + { + jsonRpcResponse.Id = jsonRpcRequest["id"].AsString(); + } + + string methodname = jsonRpcRequest["method"]; + JsonRPCMethod method; + + if (jsonRpcHandlers.ContainsKey(methodname)) + { + lock(jsonRpcHandlers) + { + jsonRpcHandlers.TryGetValue(methodname, out method); + } + bool res = false; + try + { + res = method(jsonRpcRequest, ref jsonRpcResponse); + if(!res) + { + // The handler sent back an unspecified error + if(jsonRpcResponse.Error.Code == 0) + { + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + } + } + } + catch (Exception e) + { + string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message); + m_log.Error(ErrorMessage); + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + jsonRpcResponse.Error.Message = ErrorMessage; + } + } + else // Error no hanlder defined for requested method + { + jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest; + jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname); + } + } + else // not json-rpc 2.0 could be v1 + { + jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest; + jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification"; + + if (jsonRpcRequest.ContainsKey("id")) + jsonRpcResponse.Id = jsonRpcRequest["id"].AsString(); + } + } + + response.KeepAlive = true; + string responseData = string.Empty; + + responseData = jsonRpcResponse.Serialize(); + + byte[] buffer = Encoding.UTF8.GetBytes(responseData); + return buffer; + } + + private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) + { + //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + reader.Close(); + requestStream.Close(); + + //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody); + response.KeepAlive = true; + + OSD llsdRequest = null; + OSD llsdResponse = null; + + bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest")); + + if (requestBody.Length == 0) + // Get Request + { + requestBody = "requestget"; + } + try + { + llsdRequest = OSDParser.Deserialize(requestBody); + } + catch (Exception ex) + { + m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message); + } + + if (llsdRequest != null)// && m_defaultLlsdHandler != null) + { + LLSDMethod llsdhandler = null; + + if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV) + { + // we found a registered llsd handler to service this request + llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString()); + } + else + { + // we didn't find a registered llsd handler to service this request + // check if we have a default llsd handler + + if (m_defaultLlsdHandler != null) + { + // LibOMV path + llsdResponse = m_defaultLlsdHandler(llsdRequest, request.RemoteIPEndPoint); + } + else + { + // Oops, no handler for this.. give em the failed message + llsdResponse = GenerateNoLLSDHandlerResponse(); + } + } + } + else + { + llsdResponse = GenerateNoLLSDHandlerResponse(); + } + + byte[] buffer = new byte[0]; + + if (llsdResponse.ToString() == "shutdown404!") + { + response.ContentType = "text/plain"; + response.StatusCode = 404; + response.StatusDescription = "Not Found"; + response.ProtocolVersion = "HTTP/1.0"; + buffer = Encoding.UTF8.GetBytes("Not found"); + } + else + { + // Select an appropriate response format + buffer = BuildLLSDResponse(request, response, llsdResponse); + } + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + response.KeepAlive = true; + + return buffer; + } + + private byte[] BuildLLSDResponse(OSHttpRequest request, OSHttpResponse response, OSD llsdResponse) + { + if (request.AcceptTypes != null && request.AcceptTypes.Length > 0) + { + foreach (string strAccept in request.AcceptTypes) + { + switch (strAccept) + { + case "application/llsd+xml": + case "application/xml": + case "text/xml": + response.ContentType = strAccept; + return OSDParser.SerializeLLSDXmlBytes(llsdResponse); + case "application/llsd+json": + case "application/json": + response.ContentType = strAccept; + return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse)); + } + } + } + + if (!String.IsNullOrEmpty(request.ContentType)) + { + switch (request.ContentType) + { + case "application/llsd+xml": + case "application/xml": + case "text/xml": + response.ContentType = request.ContentType; + return OSDParser.SerializeLLSDXmlBytes(llsdResponse); + case "application/llsd+json": + case "application/json": + response.ContentType = request.ContentType; + return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse)); + } + } + + // response.ContentType = "application/llsd+json"; + // return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse)); + response.ContentType = "application/llsd+xml"; + return OSDParser.SerializeLLSDXmlBytes(llsdResponse); + } + + /// + /// Checks if we have an Exact path in the LLSD handlers for the path provided + /// + /// URI of the request + /// true if we have one, false if not + private bool DoWeHaveALLSDHandler(string path) + { + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + string bestMatch = null; + + lock (m_llsdHandlers) + { + foreach (string pattern in m_llsdHandlers.Keys) + { + if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length) + bestMatch = pattern; + } + } + + // extra kicker to remove the default XMLRPC login case.. just in case.. + if (path != "/" && bestMatch == "/" && searchquery != "/") + return false; + + if (path == "/") + return false; + + if (String.IsNullOrEmpty(bestMatch)) + { + return false; + } + else + { + return true; + } + } + + /// + /// Checks if we have an Exact path in the HTTP handlers for the path provided + /// + /// URI of the request + /// true if we have one, false if not + private bool DoWeHaveAHTTPHandler(string path) + { + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + string bestMatch = null; + + //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery); + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length) + { + bestMatch = pattern; + } + } + + // extra kicker to remove the default XMLRPC login case.. just in case.. + if (path == "/") + return false; + + if (String.IsNullOrEmpty(bestMatch)) + { + return false; + } + else + { + return true; + } + } + } + + private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler) + { + llsdHandler = null; + // Pull out the first part of the path + // splitting the path by '/' means we'll get the following return.. + // {0}/{1}/{2} + // where {0} isn't something we really control 100% + + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i=1; i bestMatch.Length) + { + // You have to specifically register for '/' and to get it, you must specificaly request it + // + if (pattern == "/" && searchquery == "/" || pattern != "/") + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + llsdHandler = null; + return false; + } + else + { + llsdHandler = m_llsdHandlers[bestMatch]; + return true; + } + } + } + + private OSDMap GenerateNoLLSDHandlerResponse() + { + OSDMap map = new OSDMap(); + map["reason"] = OSD.FromString("LLSDRequest"); + map["message"] = OSD.FromString("No handler registered for LLSD Requests"); + map["login"] = OSD.FromString("false"); + return map; + } + + public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response) + { +// m_log.DebugFormat( +// "[BASE HTTP SERVER]: HandleHTTPRequest for request to {0}, method {1}", +// request.RawUrl, request.HttpMethod); + + switch (request.HttpMethod) + { + case "OPTIONS": + response.StatusCode = (int)OSHttpStatusCode.SuccessOk; + return null; + + default: + return HandleContentVerbs(request, response); + } + } + + private byte[] HandleContentVerbs(OSHttpRequest request, OSHttpResponse response) + { +// m_log.DebugFormat("[BASE HTTP SERVER]: HandleContentVerbs for request to {0}", request.RawUrl); + + // This is a test. There's a workable alternative.. as this way sucks. + // We'd like to put this into a text file parhaps that's easily editable. + // + // For this test to work, I used the following secondlife.exe parameters + // "C:\Program Files\SecondLifeWindLight\SecondLifeWindLight.exe" -settings settings_windlight.xml -channel "Second Life WindLight" -set SystemLanguage en-us -loginpage http://10.1.1.2:8002/?show_login_form=TRUE -loginuri http://10.1.1.2:8002 -user 10.1.1.2 + // + // Even after all that, there's still an error, but it's a start. + // + // I depend on show_login_form being in the secondlife.exe parameters to figure out + // to display the form, or process it. + // a better way would be nifty. + + byte[] buffer; + + Stream requestStream = request.InputStream; + + Encoding encoding = Encoding.UTF8; + StreamReader reader = new StreamReader(requestStream, encoding); + + string requestBody = reader.ReadToEnd(); + // avoid warning for now + reader.ReadToEnd(); + reader.Close(); + requestStream.Close(); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + + Hashtable requestVars = new Hashtable(); + + string host = String.Empty; + + string[] querystringkeys = request.QueryString.AllKeys; + string[] rHeaders = request.Headers.AllKeys; + + keysvals.Add("body", requestBody); + keysvals.Add("uri", request.RawUrl); + keysvals.Add("content-type", request.ContentType); + keysvals.Add("http-method", request.HttpMethod); + + foreach (string queryname in querystringkeys) + { +// m_log.DebugFormat( +// "[BASE HTTP SERVER]: Got query paremeter {0}={1}", queryname, request.QueryString[queryname]); + keysvals.Add(queryname, request.QueryString[queryname]); + requestVars.Add(queryname, keysvals[queryname]); + } + + foreach (string headername in rHeaders) + { +// m_log.Debug("[BASE HTTP SERVER]: " + headername + "=" + request.Headers[headername]); + headervals[headername] = request.Headers[headername]; + } + + if (headervals.Contains("Host")) + { + host = (string)headervals["Host"]; + } + + keysvals.Add("headers", headervals); + keysvals.Add("querystringkeys", querystringkeys); + keysvals.Add("requestvars", requestVars); +// keysvals.Add("form", request.Form); + + if (keysvals.Contains("method")) + { +// m_log.Debug("[BASE HTTP SERVER]: Contains Method"); + string method = (string) keysvals["method"]; +// m_log.Debug("[BASE HTTP SERVER]: " + requestBody); + GenericHTTPMethod requestprocessor; + bool foundHandler = TryGetHTTPHandler(method, out requestprocessor); + if (foundHandler) + { + Hashtable responsedata1 = requestprocessor(keysvals); + buffer = DoHTTPGruntWork(responsedata1,response); + + //SendHTML500(response); + } + else + { +// m_log.Warn("[BASE HTTP SERVER]: Handler Not Found"); + buffer = SendHTML404(response, host); + } + } + else + { + GenericHTTPMethod requestprocessor; + bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor); + if (foundHandler) + { + Hashtable responsedata2 = requestprocessor(keysvals); + buffer = DoHTTPGruntWork(responsedata2, response); + + //SendHTML500(response); + } + else + { +// m_log.Warn("[BASE HTTP SERVER]: Handler Not Found2"); + buffer = SendHTML404(response, host); + } + } + + return buffer; + } + + private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler) + { + httpHandler = null; + // Pull out the first part of the path + // splitting the path by '/' means we'll get the following return.. + // {0}/{1}/{2} + // where {0} isn't something we really control 100% + + string[] pathbase = path.Split('/'); + string searchquery = "/"; + + if (pathbase.Length < 1) + return false; + + for (int i = 1; i < pathbase.Length; i++) + { + searchquery += pathbase[i]; + if (pathbase.Length - 1 != i) + searchquery += "/"; + } + + // while the matching algorithm below doesn't require it, we're expecting a query in the form + // + // [] = optional + // /resource/UUID/action[/action] + // + // now try to get the closest match to the reigstered path + // at least for OGP, registered path would probably only consist of the /resource/ + + string bestMatch = null; + +// m_log.DebugFormat( +// "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery); + + lock (m_HTTPHandlers) + { + foreach (string pattern in m_HTTPHandlers.Keys) + { + if (searchquery.ToLower().StartsWith(pattern.ToLower())) + { + if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length) + { + // You have to specifically register for '/' and to get it, you must specifically request it + if (pattern == "/" && searchquery == "/" || pattern != "/") + bestMatch = pattern; + } + } + } + + if (String.IsNullOrEmpty(bestMatch)) + { + httpHandler = null; + return false; + } + else + { + if (bestMatch == "/" && searchquery != "/") + return false; + + httpHandler = m_HTTPHandlers[bestMatch]; + return true; + } + } + } + + internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response) + { + //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response"); + int responsecode = (int)responsedata["int_response_code"]; + string responseString = (string)responsedata["str_response_string"]; + string contentType = (string)responsedata["content_type"]; + + if (responsedata.ContainsKey("error_status_text")) + { + response.StatusDescription = (string)responsedata["error_status_text"]; + } + if (responsedata.ContainsKey("http_protocol_version")) + { + response.ProtocolVersion = (string)responsedata["http_protocol_version"]; + } + + if (responsedata.ContainsKey("keepalive")) + { + bool keepalive = (bool)responsedata["keepalive"]; + response.KeepAlive = keepalive; + + } + + if (responsedata.ContainsKey("reusecontext")) + response.ReuseContext = (bool) responsedata["reusecontext"]; + + // Cross-Origin Resource Sharing with simple requests + if (responsedata.ContainsKey("access_control_allow_origin")) + response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]); + + //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this + //and should check for NullReferenceExceptions + + if (string.IsNullOrEmpty(contentType)) + { + contentType = "text/html"; + } + + // The client ignores anything but 200 here for web login, so ensure that this is 200 for that + + response.StatusCode = responsecode; + + if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently) + { + response.RedirectLocation = (string)responsedata["str_redirect_location"]; + response.StatusCode = responsecode; + } + + response.AddHeader("Content-Type", contentType); + + byte[] buffer; + + if (!(contentType.Contains("image") + || contentType.Contains("x-shockwave-flash") + || contentType.Contains("application/x-oar") + || contentType.Contains("application/vnd.ll.mesh"))) + { + // Text + buffer = Encoding.UTF8.GetBytes(responseString); + } + else + { + // Binary! + buffer = Convert.FromBase64String(responseString); + } + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + return buffer; + } + + public byte[] SendHTML404(OSHttpResponse response, string host) + { + // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s + response.StatusCode = 404; + response.AddHeader("Content-type", "text/html"); + + string responseString = GetHTTP404(host); + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + return buffer; + } + + public byte[] SendHTML500(OSHttpResponse response) + { + // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s + response.StatusCode = (int)OSHttpStatusCode.SuccessOk; + response.AddHeader("Content-type", "text/html"); + + string responseString = GetHTTP500(); + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + + return buffer; + } + + public void Start() + { + Start(true); + } + + /// + /// Start the http server + /// + /// + /// If true then poll responses are performed asynchronsly. + /// Option exists to allow regression tests to perform processing synchronously. + /// + public void Start(bool performPollResponsesAsync) + { + m_log.InfoFormat( + "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port); + + try + { + //m_httpListener = new HttpListener(); + + NotSocketErrors = 0; + if (!m_ssl) + { + //m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); + //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/"); + m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port); + m_httpListener2.ExceptionThrown += httpServerException; + m_httpListener2.LogWriter = httpserverlog; + + // Uncomment this line in addition to those in HttpServerLogWriter + // if you want more detailed trace information from the HttpServer + //m_httpListener2.UseTraceLogs = true; + + //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor; + } + else + { + //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/"); + //m_httpListener.Prefixes.Add("http://+:" + m_port + "/"); + m_httpListener2 = CoolHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert); + m_httpListener2.ExceptionThrown += httpServerException; + m_httpListener2.LogWriter = httpserverlog; + } + + m_httpListener2.RequestReceived += OnRequest; + //m_httpListener.Start(); + m_httpListener2.Start(64); + + // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events + PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000); + PollServiceRequestManager.Start(); + + HTTPDRunning = true; + + //HttpListenerContext context; + //while (true) + //{ + // context = m_httpListener.GetContext(); + // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context); + // } + } + catch (Exception e) + { + m_log.Error("[BASE HTTP SERVER]: Error - " + e.Message); + m_log.Error("[BASE HTTP SERVER]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?"); + + // We want this exception to halt the entire server since in current configurations we aren't too + // useful without inbound HTTP. + throw e; + } + + m_requestsProcessedStat + = new Stat( + "HTTPRequestsServed", + "Number of inbound HTTP requests processed", + "", + "requests", + "httpserver", + Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = RequestNumber, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_requestsProcessedStat); + } + + public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) + { + switch (err) + { + case SocketError.NotSocket: + NotSocketErrors++; + + break; + } + } + + public void httpServerException(object source, Exception exception) + { + m_log.Error(String.Format("[BASE HTTP SERVER]: {0} had an exception: {1} ", source.ToString(), exception.Message), exception); + /* + if (HTTPDRunning)// && NotSocketErrors > 5) + { + Stop(); + Thread.Sleep(200); + StartHTTP(); + m_log.Warn("[HTTPSERVER]: Died. Trying to kick....."); + } + */ + } + + public void Stop() + { + HTTPDRunning = false; + + StatsManager.DeregisterStat(m_requestsProcessedStat); + + try + { + PollServiceRequestManager.Stop(); + + m_httpListener2.ExceptionThrown -= httpServerException; + //m_httpListener2.DisconnectHandler = null; + + m_httpListener2.LogWriter = null; + m_httpListener2.RequestReceived -= OnRequest; + m_httpListener2.Stop(); + } + catch (NullReferenceException) + { + m_log.Warn("[BASE HTTP SERVER]: Null Reference when stopping HttpServer."); + } + } + + public void RemoveStreamHandler(string httpMethod, string path) + { + string handlerKey = GetHandlerKey(httpMethod, path); + + //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey); + + lock (m_streamHandlers) + m_streamHandlers.Remove(handlerKey); + } + + public void RemoveHTTPHandler(string httpMethod, string path) + { + lock (m_HTTPHandlers) + { + if (httpMethod != null && httpMethod.Length == 0) + { + m_HTTPHandlers.Remove(path); + return; + } + + m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path)); + } + } + + public void RemovePollServiceHTTPHandler(string httpMethod, string path) + { + lock (m_pollHandlers) + m_pollHandlers.Remove(path); + } + +// public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler) +// { +// lock (m_agentHandlers) +// { +// IHttpAgentHandler foundHandler; +// +// if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler) +// { +// m_agentHandlers.Remove(agent); +// return true; +// } +// } +// +// return false; +// } + + public void RemoveXmlRPCHandler(string method) + { + lock (m_rpcHandlers) + m_rpcHandlers.Remove(method); + } + + public void RemoveJsonRPCHandler(string method) + { + lock(jsonRpcHandlers) + jsonRpcHandlers.Remove(method); + } + + public bool RemoveLLSDHandler(string path, LLSDMethod handler) + { + lock (m_llsdHandlers) + { + LLSDMethod foundHandler; + + if (m_llsdHandlers.TryGetValue(path, out foundHandler) && foundHandler == handler) + { + m_llsdHandlers.Remove(path); + return true; + } + } + + return false; + } + + public string GetHTTP404(string host) + { + string file = Path.Combine(".", "http_404.html"); + if (!File.Exists(file)) + return getDefaultHTTP404(host); + + StreamReader sr = File.OpenText(file); + string result = sr.ReadToEnd(); + sr.Close(); + return result; + } + + public string GetHTTP500() + { + string file = Path.Combine(".", "http_500.html"); + if (!File.Exists(file)) + return getDefaultHTTP500(); + + StreamReader sr = File.OpenText(file); + string result = sr.ReadToEnd(); + sr.Close(); + return result; + } + + // Fallback HTTP responses in case the HTTP error response files don't exist + private static string getDefaultHTTP404(string host) + { + return "404 Page not found

Ooops!

The page you requested has been obsconded with by knomes. Find hippos quick!

If you are trying to log-in, your link parameters should have: "-loginpage http://" + host + "/?method=login -loginuri http://" + host + "/" in your link

"; + } + + private static string getDefaultHTTP500() + { + return "500 Internal Server Error

Ooops!

The server you requested is overun by knomes! Find hippos quick!

"; + } + } + + public class HttpServerContextObj + { + public IHttpClientContext context = null; + public IHttpRequest req = null; + public OSHttpRequest oreq = null; + public OSHttpResponse oresp = null; + + public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs) + { + context = contxt; + req = reqs; + } + + public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp) + { + oreq = osreq; + oresp = osresp; + } + } + + /// + /// Relays HttpServer log messages to our own logging mechanism. + /// + /// To use this you must uncomment the switch section + /// + /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs + /// property in StartHttp() for the HttpListener + public class HttpServerLogWriter : ILogWriter + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public void Write(object source, LogPrio priority, string message) + { + /* + switch (priority) + { + case LogPrio.Trace: + m_log.DebugFormat("[{0}]: {1}", source, message); + break; + case LogPrio.Debug: + m_log.DebugFormat("[{0}]: {1}", source, message); + break; + case LogPrio.Error: + m_log.ErrorFormat("[{0}]: {1}", source, message); + break; + case LogPrio.Info: + m_log.InfoFormat("[{0}]: {1}", source, message); + break; + case LogPrio.Warning: + m_log.WarnFormat("[{0}]: {1}", source, message); + break; + case LogPrio.Fatal: + m_log.ErrorFormat("[{0}]: FATAL! - {1}", source, message); + break; + default: + break; + } + */ + + return; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs new file mode 100644 index 0000000000..72b3065172 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs @@ -0,0 +1,60 @@ +/* + * 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; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Base handler for writing to an output stream + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseOutputStreamHandler : BaseRequestHandler, IRequestHandler + { + protected BaseOutputStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + + protected BaseOutputStreamHandler(string httpMethod, string path, string name, string description) + : base(httpMethod, path, name, description) {} + + public virtual void Handle( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + ProcessRequest(path, request, response, httpRequest, httpResponse); + + RequestsHandled++; + } + + protected virtual void ProcessRequest( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs new file mode 100644 index 0000000000..d4a1ec3172 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs @@ -0,0 +1,117 @@ +/* + * 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 OpenSim.Framework.Monitoring; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public abstract class BaseRequestHandler + { + public int RequestsReceived { get; protected set; } + + public int RequestsHandled { get; protected set; } + + public virtual string ContentType + { + get { return "application/xml"; } + } + + private readonly string m_httpMethod; + + public virtual string HttpMethod + { + get { return m_httpMethod; } + } + + private readonly string m_path; + + public string Name { get; private set; } + + public string Description { get; private set; } + + protected BaseRequestHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + + protected BaseRequestHandler(string httpMethod, string path, string name, string description) + { + Name = name; + Description = description; + m_httpMethod = httpMethod; + m_path = path; + + // FIXME: A very temporary measure to stop the simulator stats being overwhelmed with user CAPS info. + // Needs to be fixed properly in stats display + if (!path.StartsWith("/CAPS/")) + { + StatsManager.RegisterStat( + new Stat( + "requests", + "requests", + "Number of requests received by this service endpoint", + "requests", + "service", + string.Format("{0}:{1}", httpMethod, path), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + s => s.Value = RequestsReceived, + StatVerbosity.Debug)); + } + } + + public virtual string Path + { + get { return m_path; } + } + + public string GetParam(string path) + { + if (CheckParam(path)) + { + return path.Substring(m_path.Length); + } + + return String.Empty; + } + + protected bool CheckParam(string path) + { + if (String.IsNullOrEmpty(path)) + { + return false; + } + + return path.StartsWith(Path); + } + + public string[] SplitParams(string path) + { + string param = GetParam(path); + + return param.Split(new char[] { '/', '?', '&' }, StringSplitOptions.RemoveEmptyEntries); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs new file mode 100644 index 0000000000..41aa19b67c --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs @@ -0,0 +1,85 @@ +/* + * 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.Net; +using OpenSim.Framework.ServiceAuth; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler + { + protected IServiceAuth m_Auth; + + protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) { } + + protected BaseStreamHandler(string httpMethod, string path, string name, string description) + : base(httpMethod, path, name, description) {} + + protected BaseStreamHandler(string httpMethod, string path, IServiceAuth auth) + : base(httpMethod, path, null, null) + { + m_Auth = auth; + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + if (m_Auth != null) + { + HttpStatusCode statusCode; + + if (!m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader, out statusCode)) + { + httpResponse.StatusCode = (int)statusCode; + httpResponse.ContentType = "text/plain"; + return new byte[0]; + } + } + + byte[] result = ProcessRequest(path, request, httpRequest, httpResponse); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs new file mode 100644 index 0000000000..1b8854573b --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs @@ -0,0 +1,107 @@ +/* + * 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 OpenSim.Framework; +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// BaseStreamHandlerBasicDOSProtector Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler + { + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {} + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options) + : base(httpMethod, path, name, description) + { + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result; + RequestsReceived++; + string clientstring = GetClientString(httpRequest); + string endpoint = GetRemoteAddr(httpRequest); + if (_dosProtector.Process(clientstring, endpoint)) + result = ProcessRequest(path, request, httpRequest, httpResponse); + else + result = ThrottledRequest(path, request, httpRequest, httpResponse); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } + + protected virtual byte[] ThrottledRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return new byte[0]; + } + + + private string GetRemoteAddr(IOSHttpRequest httpRequest) + { + string remoteaddr = string.Empty; + if (httpRequest.Headers["remote_addr"] != null) + remoteaddr = httpRequest.Headers["remote_addr"]; + + return remoteaddr; + } + + private string GetClientString(IOSHttpRequest httpRequest) + { + string clientstring = string.Empty; + + if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null) + clientstring = httpRequest.Headers["x-forwarded-for"]; + else + clientstring = GetRemoteAddr(httpRequest); + + return clientstring; + + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs new file mode 100644 index 0000000000..1b03f543d3 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs @@ -0,0 +1,76 @@ +/* + * 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; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate string BinaryMethod(byte[] data, string path, string param); + + public class BinaryStreamHandler : BaseStreamHandler + { + private BinaryMethod m_method; + + public BinaryStreamHandler(string httpMethod, string path, BinaryMethod binaryMethod) + : this(httpMethod, path, binaryMethod, null, null) {} + + public BinaryStreamHandler(string httpMethod, string path, BinaryMethod binaryMethod, string name, string description) + : base(httpMethod, path, name, description) + { + m_method = binaryMethod; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] data = ReadFully(request); + string param = GetParam(path); + string responseString = m_method(data, path, param); + + return Encoding.UTF8.GetBytes(responseString); + } + + private static byte[] ReadFully(Stream stream) + { + byte[] buffer = new byte[1024]; + using (MemoryStream ms = new MemoryStream(1024*256)) + { + while (true) + { + int read = stream.Read(buffer, 0, buffer.Length); + + if (read <= 0) + { + return ms.ToArray(); + } + + ms.Write(buffer, 0, read); + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs new file mode 100644 index 0000000000..cd4b8ffa29 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs @@ -0,0 +1,119 @@ +/* + * 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.Servers.HttpServer +{ + public class GenericHTTPDOSProtector + { + private readonly GenericHTTPMethod _normalMethod; + private readonly GenericHTTPMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + public Hashtable Process(Hashtable request) + { + Hashtable process = null; + string clientstring= GetClientString(request); + string endpoint = GetRemoteAddr(request); + if (_dosProtector.Process(clientstring, endpoint)) + process = _normalMethod(request); + else + process = _throttledMethod(request); + + if (_options.MaxConcurrentSessions>0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + return process; + } + + private string GetRemoteAddr(Hashtable request) + { + string remoteaddr = ""; + if (!request.ContainsKey("headers")) + return remoteaddr; + Hashtable requestinfo = (Hashtable)request["headers"]; + if (!requestinfo.ContainsKey("remote_addr")) + return remoteaddr; + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + remoteaddr = remote_addrobj.ToString(); + } + + } + return remoteaddr; + } + + private string GetClientString(Hashtable request) + { + string clientstring = ""; + if (!request.ContainsKey("headers")) + return clientstring; + + Hashtable requestinfo = (Hashtable)request["headers"]; + if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for")) + { + object str = requestinfo["x-forwarded-for"]; + if (str != null) + { + if (!string.IsNullOrEmpty(str.ToString())) + { + return str.ToString(); + } + } + } + if (!requestinfo.ContainsKey("remote_addr")) + return clientstring; + + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + clientstring = remote_addrobj.ToString(); + } + } + + return clientstring; + + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs new file mode 100644 index 0000000000..456f3a4e3a --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs @@ -0,0 +1,33 @@ +/* + * 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.Servers.HttpServer +{ + public delegate Hashtable GenericHTTPMethod(Hashtable request); +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs new file mode 100644 index 0000000000..7078895533 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs @@ -0,0 +1,35 @@ +/* + * 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.Servers.HttpServer +{ + public interface IHttpAgentHandler + { + bool Handle(OSHttpRequest req, OSHttpResponse resp); + bool Match(OSHttpRequest req, OSHttpResponse resp); + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs new file mode 100644 index 0000000000..d162bc12fb --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs @@ -0,0 +1,150 @@ +/* + * 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 Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Interface to OpenSimulator's built in HTTP server. Use this to register handlers (http, llsd, xmlrpc, etc.) + /// for given URLs. + /// + public interface IHttpServer + { + uint SSLPort { get; } + string SSLCommonName { get; } + + uint Port { get; } + bool UseSSL { get; } + +// // Note that the agent string is provided simply to differentiate +// // the handlers - it is NOT required to be an actual agent header +// // value. +// bool AddAgentHandler(string agent, IHttpAgentHandler handler); + + /// + /// Add a handler for an HTTP request. + /// + /// + /// This handler can actually be invoked either as + /// + /// http://:/?method= + /// + /// or + /// + /// http://: + /// + /// if the method name starts with a slash. For example, AddHTTPHandler("/object/", ...) on a standalone region + /// server will register a handler that can be invoked with either + /// + /// http://localhost:9000/?method=/object/ + /// + /// or + /// + /// http://localhost:9000/object/ + /// + /// In addition, the handler invoked by the HTTP server for any request is the one when best matches the request + /// URI. So if a handler for "/myapp/" is registered and a request for "/myapp/page" is received, then + /// the "/myapp/" handler is invoked if no "/myapp/page" handler exists. + /// + /// + /// + /// + /// true if the handler was successfully registered, false if a handler with the same name already existed. + /// + bool AddHTTPHandler(string methodName, GenericHTTPMethod handler); + + bool AddPollServiceHTTPHandler(string methodName, PollServiceEventArgs args); + + /// + /// Adds a LLSD handler, yay. + /// + /// /resource/ path + /// handle the LLSD response + /// + bool AddLLSDHandler(string path, LLSDMethod handler); + + /// + /// Add a stream handler to the http server. If the handler already exists, then nothing happens. + /// + /// + void AddStreamHandler(IRequestHandler handler); + + bool AddXmlRPCHandler(string method, XmlRpcMethod handler); + bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); + + bool AddJsonRPCHandler(string method, JsonRPCMethod handler); + + /// + /// Websocket HTTP server handlers. + /// + /// + /// + void AddWebSocketHandler(string servicepath, BaseHttpServer.WebSocketRequestDelegate handler); + + + void RemoveWebSocketHandler(string servicepath); + + /// + /// Gets the XML RPC handler for given method name + /// + /// Name of the method + /// Returns null if not found + XmlRpcMethod GetXmlRPCHandler(string method); + + bool SetDefaultLLSDHandler(DefaultLLSDMethod handler); + +// /// +// /// Remove the agent if it is registered. +// /// +// /// +// /// +// /// +// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler); + + /// + /// Remove an HTTP handler + /// + /// + /// + void RemoveHTTPHandler(string httpMethod, string path); + + void RemovePollServiceHTTPHandler(string httpMethod, string path); + + bool RemoveLLSDHandler(string path, LLSDMethod handler); + + void RemoveStreamHandler(string httpMethod, string path); + + void RemoveXmlRPCHandler(string method); + + void RemoveJsonRPCHandler(string method); + + string GetHTTP404(string host); + + string GetHTTP500(); + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs new file mode 100644 index 0000000000..caf0edd7c8 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs @@ -0,0 +1,59 @@ +/* + * 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.IO; +using System.Net; +using System.Text; +using System.Web; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public interface IOSHttpRequest + { + string[] AcceptTypes { get; } + Encoding ContentEncoding { get; } + long ContentLength { get; } + long ContentLength64 { get; } + string ContentType { get; } + HttpCookieCollection Cookies { get; } + bool HasEntityBody { get; } + NameValueCollection Headers { get; } + string HttpMethod { get; } + Stream InputStream { get; } + bool IsSecured { get; } + bool KeepAlive { get; } + NameValueCollection QueryString { get; } + Hashtable Query { get; } + string RawUrl { get; } + IPEndPoint RemoteIPEndPoint { get; } + Uri Url { get; } + string UserAgent { get; } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs new file mode 100644 index 0000000000..f61b090be4 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs @@ -0,0 +1,132 @@ +/* + * 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.IO; +using System.Net; +using System.Text; +using System.Web; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public interface IOSHttpResponse + { + /// + /// Content type property. + /// + /// + /// Setting this property will also set IsContentTypeSet to + /// true. + /// + string ContentType { get; set; } + + /// + /// Boolean property indicating whether the content type + /// property actively has been set. + /// + /// + /// IsContentTypeSet will go away together with .NET base. + /// + // public bool IsContentTypeSet + // { + // get { return _contentTypeSet; } + // } + // private bool _contentTypeSet; + + /// + /// Length of the body content; 0 if there is no body. + /// + long ContentLength { get; set; } + + /// + /// Alias for ContentLength. + /// + long ContentLength64 { get; set; } + + /// + /// Encoding of the body content. + /// + Encoding ContentEncoding { get; set; } + + bool KeepAlive { get; set; } + + /// + /// Get or set the keep alive timeout property (default is + /// 20). Setting this to 0 also disables KeepAlive. Setting + /// this to something else but 0 also enable KeepAlive. + /// + int KeepAliveTimeout { get; set; } + + /// + /// Return the output stream feeding the body. + /// + /// + /// On its way out... + /// + Stream OutputStream { get; } + + string ProtocolVersion { get; set; } + + /// + /// Return the output stream feeding the body. + /// + Stream Body { get; } + + /// + /// Set a redirct location. + /// + string RedirectLocation { set; } + + /// + /// Chunk transfers. + /// + bool SendChunked { get; set; } + + /// + /// HTTP status code. + /// + int StatusCode { get; set; } + + /// + /// HTTP status description. + /// + string StatusDescription { get; set; } + + bool ReuseContext { get; set; } + + /// + /// Add a header field and content to the response. + /// + /// string containing the header field + /// name + /// string containing the header field + /// value + void AddHeader(string key, string value); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs new file mode 100644 index 0000000000..b8541cbe4c --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs @@ -0,0 +1,91 @@ +/* + * 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; +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public interface IRequestHandler + { + /// + /// Name for this handler. + /// + /// + /// Used for diagnostics. The path doesn't always describe what the handler does. Can be null if none + /// specified. + /// + string Name { get; } + + /// + /// Description for this handler. + /// + /// + /// Used for diagnostics. The path doesn't always describe what the handler does. Can be null if none + /// specified. + /// + string Description { get; } + + // Return response content type + string ContentType { get; } + + // Return required http method + string HttpMethod { get; } + + // Return path + string Path { get; } + + /// + /// Number of requests received by this handler + /// + int RequestsReceived { get; } + + /// + /// Number of requests handled. + /// + /// + /// Should be equal to RequestsReceived unless requested are being handled slowly or there is deadlock. + /// + int RequestsHandled { get; } + } + + public interface IStreamedRequestHandler : IRequestHandler + { + // Handle request stream, return byte array + byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse); + } + + public interface IStreamHandler : IRequestHandler + { + void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse); + } + + public interface IGenericHTTPHandler : IRequestHandler + { + Hashtable Handle(string path, Hashtable request); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs new file mode 100644 index 0000000000..5bab50871f --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs @@ -0,0 +1,34 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate bool JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response); +} diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs new file mode 100644 index 0000000000..2fe1a7d8bc --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs @@ -0,0 +1,190 @@ +/* + * 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.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.IO; +using OpenMetaverse.StructuredData; +using OpenMetaverse; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Json rpc request manager. + /// + public class JsonRpcRequestManager + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public JsonRpcRequestManager() + { + } + + /// + /// Sends json-rpc request with a serializable type. + /// + /// + /// OSD Map. + /// + /// + /// Serializable type . + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// Id for our call. + /// + public bool JsonRpcRequest(ref object parameters, string method, string uri, string jsonId) + { + if (jsonId == null) + throw new ArgumentNullException("jsonId"); + if (uri == null) + throw new ArgumentNullException("uri"); + if (method == null) + throw new ArgumentNullException("method"); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + OSDMap request = new OSDMap(); + request.Add("jsonrpc", OSD.FromString("2.0")); + request.Add("id", OSD.FromString(jsonId)); + request.Add("method", OSD.FromString(method)); + request.Add("params", OSD.SerializeMembers(parameters)); + + OSDMap response; + try + { + response = WebUtil.PostToService(uri, request, 10000, true); + } + catch (Exception e) + { + m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e); + return false; + } + + if (!response.ContainsKey("_Result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + response = (OSDMap)response["_Result"]; + + OSD data; + + if (response.ContainsKey("error")) + { + data = response["error"]; + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}", + method, uri, OSDParser.SerializeJsonString(data)); + return false; + } + + if (!response.ContainsKey("result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + + data = response["result"]; + OSD.DeserializeMembers(ref parameters, (OSDMap)data); + + return true; + } + + /// + /// Sends json-rpc request with OSD parameter. + /// + /// + /// The rpc request. + /// + /// + /// data - incoming as parameters, outgoing as result/error + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// If set to true json identifier. + /// + public bool JsonRpcRequest(ref OSD data, string method, string uri, string jsonId) + { + if (string.IsNullOrEmpty(jsonId)) + jsonId = UUID.Random().ToString(); + + OSDMap request = new OSDMap(); + request.Add("jsonrpc", OSD.FromString("2.0")); + request.Add("id", OSD.FromString(jsonId)); + request.Add("method", OSD.FromString(method)); + request.Add("params", data); + + OSDMap response; + try + { + response = WebUtil.PostToService(uri, request, 10000, true); + } + catch (Exception e) + { + m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e); + return false; + } + + if (!response.ContainsKey("_Result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + response = (OSDMap)response["_Result"]; + + if (response.ContainsKey("error")) + { + data = response["error"]; + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}", + method, uri, OSDParser.SerializeJsonString(data)); + return false; + } + + data = response; + + return true; + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs new file mode 100644 index 0000000000..2c50587794 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs @@ -0,0 +1,150 @@ +/* + * 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.Net; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public sealed class ErrorCode + { + private ErrorCode() {} + + public const int ParseError = -32700; + public const int InvalidRequest = -32600; + public const int MethodNotFound = -32601; + public const int InvalidParams = -32602; + public const int InternalError = -32604; + + } + + public class JsonRpcError + { + internal OSDMap Error = new OSDMap(); + + public int Code + { + get + { + if (Error.ContainsKey("code")) + return Error["code"].AsInteger(); + else + return 0; + } + set + { + Error["code"] = OSD.FromInteger(value); + } + } + + public string Message + { + get + { + if (Error.ContainsKey("message")) + return Error["message"].AsString(); + else + return null; + } + set + { + Error["message"] = OSD.FromString(value); + } + } + + public OSD Data + { + get; set; + } + } + + public class JsonRpcResponse + { + public string JsonRpc + { + get + { + return Reply["jsonrpc"].AsString(); + } + set + { + Reply["jsonrpc"] = OSD.FromString(value); + } + } + + public string Id + { + get + { + return Reply["id"].AsString(); + } + set + { + Reply["id"] = OSD.FromString(value); + } + } + + public OSD Result + { + get; set; + } + + public JsonRpcError Error + { + get; set; + } + + public OSDMap Reply = new OSDMap(); + + public JsonRpcResponse() + { + Error = new JsonRpcError(); + } + + public string Serialize() + { + if (Result != null) + Reply["result"] = Result; + + if (Error.Code != 0) + { + Reply["error"] = (OSD)Error.Error; + } + + string result = string.Empty; + try + { + result = OSDParser.SerializeJsonString(Reply); + } + catch + { + + } + return result; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs new file mode 100644 index 0000000000..35655c6bd7 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs @@ -0,0 +1,35 @@ +/* + * 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 OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate OSD LLSDMethod(string path, OSD request, string endpoint); + public delegate OSD DefaultLLSDMethod(OSD request, IPEndPoint client); +} diff --git a/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs new file mode 100644 index 0000000000..563b88f748 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs @@ -0,0 +1,33 @@ +/* + * 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.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate OSD LLSDMethodString(OSD request, string thePath); +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs new file mode 100644 index 0000000000..2c2b47ded2 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs @@ -0,0 +1,183 @@ +/* + * 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.Text.RegularExpressions; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Any OSHttpHandler must return one of the following results: + /// + /// + /// result code + /// meaning + /// + /// + /// Pass + /// handler did not process the request + /// + /// + /// Done + /// handler did process the request, OSHttpServer + /// can clean up and close the request + /// + /// + ///
+ public enum OSHttpHandlerResult + { + Unprocessed, + Pass, + Done, + } + + /// + /// An OSHttpHandler that matches on the "content-type" header can + /// supply an OSHttpContentTypeChecker delegate which will be + /// invoked by the request matcher in OSHttpRequestPump. + /// + /// true if the handler is interested in the content; + /// false otherwise + public delegate bool OSHttpContentTypeChecker(OSHttpRequest req); + + public abstract class OSHttpHandler + { + /// + /// Regular expression used to match against method of + /// the incoming HTTP request. If you want to match any string + /// either use '.*' or null. To match on the empty string use + /// '^$'. + /// + public virtual Regex Method + { + get { return _method; } + } + protected Regex _method; + + /// + /// Regular expression used to match against path of the + /// incoming HTTP request. If you want to match any string + /// either use '.*' or null. To match on the empty string use + /// '^$'. + /// + public virtual Regex Path + { + get { return _path; } + } + protected Regex _path; + + /// + /// Dictionary of (query name, regular expression) tuples, + /// allowing us to match on URI query fields. + /// + public virtual Dictionary Query + { + get { return _query; } + } + protected Dictionary _query; + + /// + /// Dictionary of (header name, regular expression) tuples, + /// allowing us to match on HTTP header fields. + /// + public virtual Dictionary Headers + { + get { return _headers; } + } + protected Dictionary _headers; + + /// + /// Dictionary of (header name, regular expression) tuples, + /// allowing us to match on HTTP header fields. + /// + /// + /// This feature is currently not implemented as it requires + /// (trivial) changes to HttpServer.HttpListener that have not + /// been implemented. + /// + public virtual Regex IPEndPointWhitelist + { + get { return _ipEndPointRegex; } + } + protected Regex _ipEndPointRegex; + + + /// + /// Base class constructor. + /// + /// null or path regex + /// null or dictionary of header + /// regexs + /// null or content type + /// regex + /// null or IP address regex + public OSHttpHandler(Regex method, Regex path, Dictionary query, + Dictionary headers, Regex contentType, Regex whitelist) + { + _method = method; + _path = path; + _query = query; + _ipEndPointRegex = whitelist; + + if (null == _headers && null != contentType) + { + _headers = new Dictionary(); + _headers.Add("content-type", contentType); + } + } + + + /// + /// Process an incoming OSHttpRequest that matched our + /// requirements. + /// + /// + /// OSHttpHandlerResult.Pass if we are after all not + /// interested in the request; OSHttpHandlerResult.Done if we + /// did process the request. + /// + public abstract OSHttpHandlerResult Process(OSHttpRequest request); + + public override string ToString() + { + StringWriter sw = new StringWriter(); + sw.WriteLine("{0}", base.ToString()); + sw.WriteLine(" method regex {0}", null == Method ? "null" : Method.ToString()); + sw.WriteLine(" path regex {0}", null == Path ? "null": Path.ToString()); + foreach (string tag in Headers.Keys) + { + sw.WriteLine(" header {0} : {1}", tag, Headers[tag].ToString()); + } + sw.WriteLine(" IP whitelist {0}", null == IPEndPointWhitelist ? "null" : IPEndPointWhitelist.ToString()); + sw.WriteLine(); + sw.Close(); + return sw.ToString(); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs new file mode 100644 index 0000000000..95dafcdd36 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs @@ -0,0 +1,145 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using log4net; +using Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate XmlRpcResponse OSHttpHttpProcessor(XmlRpcRequest request); + + public class OSHttpHttpHandler: OSHttpHandler + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // contains handler for processing HTTP Request + private GenericHTTPMethod _handler; + + /// + /// Instantiate an HTTP handler. + /// + /// a GenericHTTPMethod + /// null or HTTP method regex + /// null or path regex + /// null or dictionary with query regexs + /// null or dictionary with header + /// regexs + /// null or IP address whitelist + public OSHttpHttpHandler(GenericHTTPMethod handler, Regex method, Regex path, + Dictionary query, + Dictionary headers, Regex whitelist) + : base(method, path, query, headers, new Regex(@"^text/html", RegexOptions.IgnoreCase | RegexOptions.Compiled), + whitelist) + { + _handler = handler; + } + + /// + /// Instantiate an HTTP handler. + /// + /// a GenericHTTPMethod + public OSHttpHttpHandler(GenericHTTPMethod handler) + : this(handler, new Regex(@"^GET$", RegexOptions.IgnoreCase | RegexOptions.Compiled), null, null, null, null) + { + } + + /// + /// Invoked by OSHttpRequestPump. + /// + public override OSHttpHandlerResult Process(OSHttpRequest request) + { + // call handler method + Hashtable responseData = _handler(request.Query); + + int responseCode = (int)responseData["int_response_code"]; + string responseString = (string)responseData["str_response_string"]; + string contentType = (string)responseData["content_type"]; + + //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this + //and should check for NullReferenceExceptions + + if (string.IsNullOrEmpty(contentType)) + { + contentType = "text/html"; + } + + OSHttpResponse response = new OSHttpResponse(request); + + // We're forgoing the usual error status codes here because the client + // ignores anything but 200 and 301 + + response.StatusCode = (int)OSHttpStatusCode.SuccessOk; + + if (responseCode == (int)OSHttpStatusCode.RedirectMovedPermanently) + { + response.RedirectLocation = (string)responseData["str_redirect_location"]; + response.StatusCode = responseCode; + } + + response.AddHeader("Content-type", contentType); + + byte[] buffer; + + if (!contentType.Contains("image")) + { + buffer = Encoding.UTF8.GetBytes(responseString); + } + else + { + buffer = Convert.FromBase64String(responseString); + } + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.Body.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + _log.ErrorFormat("[OSHttpHttpHandler]: Error: {0}", ex.Message); + } + finally + { + response.Send(); + } + + return OSHttpHandlerResult.Done; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs new file mode 100644 index 0000000000..05ec6dc8c4 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs @@ -0,0 +1,271 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using System.Web; +using HttpServer; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class OSHttpRequest : IOSHttpRequest + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected IHttpRequest _request = null; + protected IHttpClientContext _context = null; + + public string[] AcceptTypes + { + get { return _request.AcceptTypes; } + } + + public Encoding ContentEncoding + { + get { return _contentEncoding; } + } + private Encoding _contentEncoding; + + public long ContentLength + { + get { return _request.ContentLength; } + } + + public long ContentLength64 + { + get { return ContentLength; } + } + + public string ContentType + { + get { return _contentType; } + } + private string _contentType; + + public HttpCookieCollection Cookies + { + get + { + RequestCookies cookies = _request.Cookies; + HttpCookieCollection httpCookies = new HttpCookieCollection(); + foreach (RequestCookie cookie in cookies) + httpCookies.Add(new HttpCookie(cookie.Name, cookie.Value)); + return httpCookies; + } + } + + public bool HasEntityBody + { + get { return _request.ContentLength != 0; } + } + + public NameValueCollection Headers + { + get { return _request.Headers; } + } + + public string HttpMethod + { + get { return _request.Method; } + } + + public Stream InputStream + { + get { return _request.Body; } + } + + public bool IsSecured + { + get { return _context.IsSecured; } + } + + public bool KeepAlive + { + get { return ConnectionType.KeepAlive == _request.Connection; } + } + + public NameValueCollection QueryString + { + get { return _queryString; } + } + private NameValueCollection _queryString; + + public Hashtable Query + { + get { return _query; } + } + private Hashtable _query; + + /// + /// POST request values, if applicable + /// +// public Hashtable Form { get; private set; } + + public string RawUrl + { + get { return _request.Uri.AbsolutePath; } + } + + public IPEndPoint RemoteIPEndPoint + { + get { return _remoteIPEndPoint; } + } + private IPEndPoint _remoteIPEndPoint; + + public Uri Url + { + get { return _request.Uri; } + } + + public string UserAgent + { + get { return _userAgent; } + } + private string _userAgent; + + internal IHttpRequest IHttpRequest + { + get { return _request; } + } + + internal IHttpClientContext IHttpClientContext + { + get { return _context; } + } + + /// + /// Internal whiteboard for handlers to store temporary stuff + /// into. + /// + internal Dictionary Whiteboard + { + get { return _whiteboard; } + } + private Dictionary _whiteboard = new Dictionary(); + + public OSHttpRequest() {} + + public OSHttpRequest(IHttpClientContext context, IHttpRequest req) + { + _request = req; + _context = context; + + if (null != req.Headers["content-encoding"]) + { + try + { + _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); + } + catch (Exception) + { + // ignore + } + } + + if (null != req.Headers["content-type"]) + _contentType = _request.Headers["content-type"]; + if (null != req.Headers["user-agent"]) + _userAgent = req.Headers["user-agent"]; + + if (null != req.Headers["remote_addr"]) + { + try + { + IPAddress addr = IPAddress.Parse(req.Headers["remote_addr"]); + // sometimes req.Headers["remote_port"] returns a comma separated list, so use + // the first one in the list and log it + string[] strPorts = req.Headers["remote_port"].Split(new char[] { ',' }); + if (strPorts.Length > 1) + { + _log.ErrorFormat("[OSHttpRequest]: format exception on addr/port {0}:{1}, ignoring", + req.Headers["remote_addr"], req.Headers["remote_port"]); + } + int port = Int32.Parse(strPorts[0]); + _remoteIPEndPoint = new IPEndPoint(addr, port); + } + catch (FormatException) + { + _log.ErrorFormat("[OSHttpRequest]: format exception on addr/port {0}:{1}, ignoring", + req.Headers["remote_addr"], req.Headers["remote_port"]); + } + } + + _queryString = new NameValueCollection(); + _query = new Hashtable(); + try + { + foreach (HttpInputItem item in req.QueryString) + { + try + { + _queryString.Add(item.Name, item.Value); + _query[item.Name] = item.Value; + } + catch (InvalidCastException) + { + _log.DebugFormat("[OSHttpRequest]: error parsing {0} query item, skipping it", item.Name); + continue; + } + } + } + catch (Exception) + { + _log.ErrorFormat("[OSHttpRequest]: Error parsing querystring"); + } + +// Form = new Hashtable(); +// foreach (HttpInputItem item in req.Form) +// { +// _log.DebugFormat("[OSHttpRequest]: Got form item {0}={1}", item.Name, item.Value); +// Form.Add(item.Name, item.Value); +// } + } + + public override string ToString() + { + StringBuilder me = new StringBuilder(); + me.Append(String.Format("OSHttpRequest: {0} {1}\n", HttpMethod, RawUrl)); + foreach (string k in Headers.AllKeys) + { + me.Append(String.Format(" {0}: {1}\n", k, Headers[k])); + } + if (null != RemoteIPEndPoint) + { + me.Append(String.Format(" IP: {0}\n", RemoteIPEndPoint)); + } + + return me.ToString(); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs new file mode 100644 index 0000000000..bdea278810 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs @@ -0,0 +1,298 @@ +/* + * 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. + */ + +// #define DEBUGGING + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using log4net; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// An OSHttpRequestPump fetches incoming OSHttpRequest objects + /// from the OSHttpRequestQueue and feeds them to all subscribed + /// parties. Each OSHttpRequestPump encapsulates one thread to do + /// the work and there is a fixed number of pumps for each + /// OSHttpServer object. + /// + public class OSHttpRequestPump + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected OSHttpServer _server; + protected OSHttpRequestQueue _queue; + protected Thread _engine; + + private int _id; + + public string EngineID + { + get { return String.Format("{0} pump {1}", _server.EngineID, _id); } + } + + public OSHttpRequestPump(OSHttpServer server, OSHttpRequestQueue queue, int id) + { + _server = server; + _queue = queue; + _id = id; + + _engine = new Thread(new ThreadStart(Engine)); + _engine.IsBackground = true; + _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",EngineID); + + ThreadTracker.Add(_engine); + } + + public static OSHttpRequestPump[] Pumps(OSHttpServer server, OSHttpRequestQueue queue, int poolSize) + { + OSHttpRequestPump[] pumps = new OSHttpRequestPump[poolSize]; + for (int i = 0; i < pumps.Length; i++) + { + pumps[i] = new OSHttpRequestPump(server, queue, i); + } + + return pumps; + } + + public void Start() + { + _engine = new Thread(new ThreadStart(Engine)); + _engine.IsBackground = true; + _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",EngineID); + + ThreadTracker.Add(_engine); + } + + public void Engine() + { + OSHttpRequest req = null; + + while (true) + { + try + { + // dequeue an OSHttpRequest from OSHttpServer's + // request queue + req = _queue.Dequeue(); + + // get a copy of the list of registered handlers + List handlers = _server.OSHttpHandlers; + + // prune list and have it sorted from most + // specific to least specific + handlers = MatchHandlers(req, handlers); + + // process req: we try each handler in turn until + // we are either out of handlers or get back a + // Pass or Done + OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed; + foreach (OSHttpHandler h in handlers) + { + rc = h.Process(req); + + // Pass: handler did not process the request, + // try next handler + if (OSHttpHandlerResult.Pass == rc) continue; + + // Handled: handler has processed the request + if (OSHttpHandlerResult.Done == rc) break; + + // hmm, something went wrong + throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc)); + } + + if (OSHttpHandlerResult.Unprocessed == rc) + { + _log.InfoFormat("[{0}] OSHttpHandler: no handler registered for {1}", EngineID, req); + + // set up response header + OSHttpResponse resp = new OSHttpResponse(req); + resp.StatusCode = (int)OSHttpStatusCode.ClientErrorNotFound; + resp.StatusDescription = String.Format("no handler on call for {0}", req); + resp.ContentType = "text/html"; + + // add explanatory message + StreamWriter body = new StreamWriter(resp.Body); + body.WriteLine(""); + body.WriteLine("
Ooops...
"); + body.WriteLine(String.Format("

{0}

", resp.StatusDescription)); + body.WriteLine(""); + body.Flush(); + + // and ship it back + resp.Send(); + } + } + catch (Exception e) + { + _log.DebugFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.ToString()); + _log.ErrorFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.Message); + } + } + } + + protected List MatchHandlers(OSHttpRequest req, List handlers) + { + Dictionary scoredHandlers = new Dictionary(); + + _log.DebugFormat("[{0}] MatchHandlers for {1}", EngineID, req); + foreach (OSHttpHandler h in handlers) + { + // initial anchor + scoredHandlers[h] = 0; + + // first, check whether IPEndPointWhitelist applies + // and, if it does, whether client is on that white + // list. + if (null != h.IPEndPointWhitelist) + { + // TODO: following code requires code changes to + // HttpServer.HttpRequest to become functional + + IPEndPoint remote = req.RemoteIPEndPoint; + if (null != remote) + { + Match epm = h.IPEndPointWhitelist.Match(remote.ToString()); + if (!epm.Success) + { + scoredHandlers.Remove(h); + continue; + } + } + } + + if (null != h.Method) + { + Match m = h.Method.Match(req.HttpMethod); + if (!m.Success) + { + scoredHandlers.Remove(h); + continue; + } + scoredHandlers[h]++; + } + + // whitelist ok, now check path + if (null != h.Path) + { + Match m = h.Path.Match(req.RawUrl); + if (!m.Success) + { + scoredHandlers.Remove(h); + continue; + } + scoredHandlers[h] += m.ToString().Length; + } + + // whitelist & path ok, now check query string + if (null != h.Query) + { + int queriesMatch = MatchOnNameValueCollection(req.QueryString, h.Query); + if (0 == queriesMatch) + { + _log.DebugFormat("[{0}] request {1}", EngineID, req); + _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h); + + scoredHandlers.Remove(h); + continue; + } + scoredHandlers[h] += queriesMatch; + } + + // whitelist, path, query string ok, now check headers + if (null != h.Headers) + { + int headersMatch = MatchOnNameValueCollection(req.Headers, h.Headers); + if (0 == headersMatch) + { + _log.DebugFormat("[{0}] request {1}", EngineID, req); + _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h); + + scoredHandlers.Remove(h); + continue; + } + scoredHandlers[h] += headersMatch; + } + } + + List matchingHandlers = new List(scoredHandlers.Keys); + matchingHandlers.Sort(delegate(OSHttpHandler x, OSHttpHandler y) + { + return scoredHandlers[x] - scoredHandlers[y]; + }); + LogDumpHandlerList(matchingHandlers); + return matchingHandlers; + } + + protected int MatchOnNameValueCollection(NameValueCollection collection, Dictionary regexs) + { + int matched = 0; + + foreach (string tag in regexs.Keys) + { + // do we have a header "tag"? + if (null == collection[tag]) + { + return 0; + } + + // does the content of collection[tag] match + // the supplied regex? + Match cm = regexs[tag].Match(collection[tag]); + if (!cm.Success) + { + return 0; + } + + // ok: matches + matched++; + continue; + } + + return matched; + } + + [ConditionalAttribute("DEBUGGING")] + private void LogDumpHandlerList(List l) + { + _log.DebugFormat("[{0}] OSHttpHandlerList dump:", EngineID); + foreach (OSHttpHandler h in l) + _log.DebugFormat(" ", h.ToString()); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs new file mode 100644 index 0000000000..881c5d17e6 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs @@ -0,0 +1,68 @@ +/* + * 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.Threading; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// OSHttpRequestQueues are used to hand over incoming HTTP + /// requests to OSHttpRequestPump objects. + /// + public class OSHttpRequestQueue : Queue + { + private object _syncObject = new object(); + + new public void Enqueue(OSHttpRequest req) + { + lock (_syncObject) + { + base.Enqueue(req); + Monitor.Pulse(_syncObject); + } + } + + new public OSHttpRequest Dequeue() + { + OSHttpRequest req = null; + + lock (_syncObject) + { + while (null == req) + { + Monitor.Wait(_syncObject); + if (0 != this.Count) req = base.Dequeue(); + } + } + + return req; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs new file mode 100644 index 0000000000..89fb5d4562 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs @@ -0,0 +1,332 @@ +/* + * 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.Net; +using System.Text; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// OSHttpResponse is the OpenSim representation of an HTTP + /// response. + /// + public class OSHttpResponse : IOSHttpResponse + { + /// + /// Content type property. + /// + /// + /// Setting this property will also set IsContentTypeSet to + /// true. + /// + public virtual string ContentType + { + get + { + return _httpResponse.ContentType; + } + + set + { + _httpResponse.ContentType = value; + } + } + + /// + /// Boolean property indicating whether the content type + /// property actively has been set. + /// + /// + /// IsContentTypeSet will go away together with .NET base. + /// + // public bool IsContentTypeSet + // { + // get { return _contentTypeSet; } + // } + // private bool _contentTypeSet; + + + /// + /// Length of the body content; 0 if there is no body. + /// + public long ContentLength + { + get + { + return _httpResponse.ContentLength; + } + + set + { + _httpResponse.ContentLength = value; + } + } + + /// + /// Alias for ContentLength. + /// + public long ContentLength64 + { + get { return ContentLength; } + set { ContentLength = value; } + } + + /// + /// Encoding of the body content. + /// + public Encoding ContentEncoding + { + get + { + return _httpResponse.Encoding; + } + + set + { + _httpResponse.Encoding = value; + } + } + + public bool KeepAlive + { + get + { + return _httpResponse.Connection == ConnectionType.KeepAlive; + } + + set + { + if (value) + _httpResponse.Connection = ConnectionType.KeepAlive; + else + _httpResponse.Connection = ConnectionType.Close; + } + } + + /// + /// Get or set the keep alive timeout property (default is + /// 20). Setting this to 0 also disables KeepAlive. Setting + /// this to something else but 0 also enable KeepAlive. + /// + public int KeepAliveTimeout + { + get + { + return _httpResponse.KeepAlive; + } + + set + { + if (value == 0) + { + _httpResponse.Connection = ConnectionType.Close; + _httpResponse.KeepAlive = 0; + } + else + { + _httpResponse.Connection = ConnectionType.KeepAlive; + _httpResponse.KeepAlive = value; + } + } + } + + /// + /// Return the output stream feeding the body. + /// + /// + /// On its way out... + /// + public Stream OutputStream + { + get + { + return _httpResponse.Body; + } + } + + public string ProtocolVersion + { + get + { + return _httpResponse.ProtocolVersion; + } + + set + { + _httpResponse.ProtocolVersion = value; + } + } + + /// + /// Return the output stream feeding the body. + /// + public Stream Body + { + get + { + return _httpResponse.Body; + } + } + + /// + /// Set a redirct location. + /// + public string RedirectLocation + { + // get { return _redirectLocation; } + set + { + _httpResponse.Redirect(value); + } + } + + + /// + /// Chunk transfers. + /// + public bool SendChunked + { + get + { + return _httpResponse.Chunked; + } + + set + { + _httpResponse.Chunked = value; + } + } + + /// + /// HTTP status code. + /// + public virtual int StatusCode + { + get + { + return (int)_httpResponse.Status; + } + + set + { + _httpResponse.Status = (HttpStatusCode)value; + } + } + + + /// + /// HTTP status description. + /// + public string StatusDescription + { + get + { + return _httpResponse.Reason; + } + + set + { + _httpResponse.Reason = value; + } + } + + public bool ReuseContext + { + get + { + if (_httpClientContext != null) + { + return !_httpClientContext.EndWhenDone; + } + return true; + } + set + { + if (_httpClientContext != null) + { + _httpClientContext.EndWhenDone = !value; + } + } + } + + protected IHttpResponse _httpResponse; + private IHttpClientContext _httpClientContext; + + public OSHttpResponse() {} + + public OSHttpResponse(IHttpResponse resp) + { + _httpResponse = resp; + } + + /// + /// Instantiate an OSHttpResponse object from an OSHttpRequest + /// object. + /// Incoming OSHttpRequest to which we are + /// replying + public OSHttpResponse(OSHttpRequest req) + { + _httpResponse = new HttpResponse(req.IHttpClientContext, req.IHttpRequest); + _httpClientContext = req.IHttpClientContext; + } + public OSHttpResponse(HttpResponse resp, IHttpClientContext clientContext) + { + _httpResponse = resp; + _httpClientContext = clientContext; + } + + /// + /// Add a header field and content to the response. + /// + /// string containing the header field + /// name + /// string containing the header field + /// value + public void AddHeader(string key, string value) + { + _httpResponse.AddHeader(key, value); + } + + /// + /// Send the response back to the remote client + /// + public void Send() + { + _httpResponse.Body.Flush(); + _httpResponse.Send(); + } + + public void FreeContext() + { + if (_httpClientContext != null) + _httpClientContext.Close(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs new file mode 100644 index 0000000000..cd6284259a --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs @@ -0,0 +1,210 @@ +/* + * 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.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text.RegularExpressions; +using System.Threading; +using System.Security.Cryptography.X509Certificates; +using log4net; +using HttpServer; + +using HttpListener = HttpServer.HttpListener; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// OSHttpServer provides an HTTP server bound to a specific + /// port. When instantiated with just address and port it uses + /// normal HTTP, when instantiated with address, port, and X509 + /// certificate, it uses HTTPS. + /// + public class OSHttpServer + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private object _syncObject = new object(); + + // underlying HttpServer.HttpListener + protected HttpListener _listener; + // underlying core/engine thread + protected Thread _engine; + + // Queue containing (OS)HttpRequests + protected OSHttpRequestQueue _queue; + + // OSHttpRequestPumps "pumping" incoming OSHttpRequests + // upwards + protected OSHttpRequestPump[] _pumps; + + // thread identifier + protected string _engineId; + public string EngineID + { + get { return _engineId; } + } + + /// + /// True if this is an HTTPS connection; false otherwise. + /// + protected bool _isSecure; + public bool IsSecure + { + get { return _isSecure; } + } + + public int QueueSize + { + get { return _pumps.Length; } + } + + /// + /// List of registered OSHttpHandlers for this OSHttpServer instance. + /// + protected List _httpHandlers = new List(); + public List OSHttpHandlers + { + get + { + lock (_httpHandlers) + { + return new List(_httpHandlers); + } + } + } + + + /// + /// Instantiate an HTTP server. + /// + public OSHttpServer(IPAddress address, int port, int poolSize) + { + _engineId = String.Format("OSHttpServer (HTTP:{0})", port); + _isSecure = false; + _log.DebugFormat("[{0}] HTTP server instantiated", EngineID); + + _listener = new HttpListener(address, port); + _queue = new OSHttpRequestQueue(); + _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize); + } + + /// + /// Instantiate an HTTPS server. + /// + public OSHttpServer(IPAddress address, int port, X509Certificate certificate, int poolSize) + { + _engineId = String.Format("OSHttpServer [HTTPS:{0}/ps:{1}]", port, poolSize); + _isSecure = true; + _log.DebugFormat("[{0}] HTTPS server instantiated", EngineID); + + _listener = new HttpListener(address, port, certificate); + _queue = new OSHttpRequestQueue(); + _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize); + } + + /// + /// Turn an HttpRequest into an OSHttpRequestItem and place it + /// in the queue. The OSHttpRequestQueue object will pulse the + /// next available idle pump. + /// + protected void OnHttpRequest(HttpClientContext client, HttpRequest request) + { + // turn request into OSHttpRequest + OSHttpRequest req = new OSHttpRequest(client, request); + + // place OSHttpRequest into _httpRequestQueue, will + // trigger Pulse to idle waiting pumps + _queue.Enqueue(req); + } + + /// + /// Start the HTTP server engine. + /// + public void Start() + { + _engine = new Thread(new ThreadStart(Engine)); + _engine.IsBackground = true; + _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",_engineId); + + ThreadTracker.Add(_engine); + + // start the pumps... + for (int i = 0; i < _pumps.Length; i++) + _pumps[i].Start(); + } + + public void Stop() + { + lock (_syncObject) Monitor.Pulse(_syncObject); + } + + /// + /// Engine keeps the HTTP server running. + /// + private void Engine() + { + try { + _listener.RequestHandler += OnHttpRequest; + _listener.Start(QueueSize); + _log.InfoFormat("[{0}] HTTP server started", EngineID); + + lock (_syncObject) Monitor.Wait(_syncObject); + } + catch (Exception ex) + { + _log.DebugFormat("[{0}] HTTP server startup failed: {1}", EngineID, ex.ToString()); + } + + _log.InfoFormat("[{0}] HTTP server terminated", EngineID); + } + + + /// + /// Add an HTTP request handler. + /// + /// OSHttpHandler delegate + /// regex object for path matching + /// dictionary containing header names + /// and regular expressions to match against header values + public void AddHandler(OSHttpHandler handler) + { + lock (_httpHandlers) + { + if (_httpHandlers.Contains(handler)) + { + _log.DebugFormat("[OSHttpServer] attempt to add already existing handler ignored"); + return; + } + _httpHandlers.Add(handler); + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs new file mode 100644 index 0000000000..a736c8b753 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs @@ -0,0 +1,279 @@ +/* + * 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.Servers.HttpServer +{ + /// + /// HTTP status codes (almost) as defined by W3C in http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html and IETF in http://tools.ietf.org/html/rfc6585 + /// + public enum OSHttpStatusCode : int + { + #region 1xx Informational status codes providing a provisional response. + + /// + /// 100 Tells client that to keep on going sending its request + /// + InfoContinue = 100, + + /// + /// 101 Server understands request, proposes to switch to different application level protocol + /// + InfoSwitchingProtocols = 101, + + #endregion + + #region 2xx Success codes + + /// + /// 200 Request successful + /// + SuccessOk = 200, + + /// + /// 201 Request successful, new resource created + /// + SuccessOkCreated = 201, + + /// + /// 202 Request accepted, processing still on-going + /// + SuccessOkAccepted = 202, + + /// + /// 203 Request successful, meta information not authoritative + /// + SuccessOkNonAuthoritativeInformation = 203, + + /// + /// 204 Request successful, nothing to return in the body + /// + SuccessOkNoContent = 204, + + /// + /// 205 Request successful, reset displayed content + /// + SuccessOkResetContent = 205, + + /// + /// 206 Request successful, partial content returned + /// + SuccessOkPartialContent = 206, + + #endregion + + #region 3xx Redirect code: user agent needs to go somewhere else + + /// + /// 300 Redirect: different presentation forms available, take a pick + /// + RedirectMultipleChoices = 300, + + /// + /// 301 Redirect: requested resource has moved and now lives somewhere else + /// + RedirectMovedPermanently = 301, + + /// + /// 302 Redirect: Resource temporarily somewhere else, location might change + /// + RedirectFound = 302, + + /// + /// 303 Redirect: See other as result of a POST + /// + RedirectSeeOther = 303, + + /// + /// 304 Redirect: Resource still the same as before + /// + RedirectNotModified = 304, + + /// + /// 305 Redirect: Resource must be accessed via proxy provided in location field + /// + RedirectUseProxy = 305, + + /// + /// 307 Redirect: Resource temporarily somewhere else, location might change + /// + RedirectMovedTemporarily = 307, + + #endregion + + #region 4xx Client error: the client borked the request + + /// + /// 400 Client error: bad request, server does not grok what the client wants + /// + ClientErrorBadRequest = 400, + + /// + /// 401 Client error: the client is not authorized, response provides WWW-Authenticate header field with a challenge + /// + ClientErrorUnauthorized = 401, + + /// + /// 402 Client error: Payment required (reserved for future use) + /// + ClientErrorPaymentRequired = 402, + + /// + /// 403 Client error: Server understood request, will not deliver, do not try again. + ClientErrorForbidden = 403, + + /// + /// 404 Client error: Server cannot find anything matching the client request. + /// + ClientErrorNotFound = 404, + + /// + /// 405 Client error: The method specified by the client in the request is not allowed for the resource requested + /// + ClientErrorMethodNotAllowed = 405, + + /// + /// 406 Client error: Server cannot generate suitable response for the resource and content characteristics requested by the client + /// + ClientErrorNotAcceptable = 406, + + /// + /// 407 Client error: Similar to 401, Server requests that client authenticate itself with the proxy first + /// + ClientErrorProxyAuthRequired = 407, + + /// + /// 408 Client error: Server got impatient with client and decided to give up waiting for the client's request to arrive + /// + ClientErrorRequestTimeout = 408, + + /// + /// 409 Client error: Server could not fulfill the request for a resource as there is a conflict with the current state of the resource but thinks client can do something about this + /// + ClientErrorConflict = 409, + + /// + /// 410 Client error: The resource has moved somewhere else, but server has no clue where. + /// + ClientErrorGone = 410, + + /// + /// 411 Client error: The server is picky again and insists on having a content-length header field in the request + /// + ClientErrorLengthRequired = 411, + + /// + /// 412 Client error: one or more preconditions supplied in the client's request is false + /// + ClientErrorPreconditionFailed = 412, + + /// + /// 413 Client error: For fear of reflux, the server refuses to swallow that much data. + /// + ClientErrorRequestEntityToLarge = 413, + + /// + /// 414 Client error: The server considers the Request-URI to be indecently long and refuses to even look at it. + /// + ClientErrorRequestURITooLong = 414, + + /// + /// 415 Client error: The server has no clue about the media type requested by the client (contrary to popular belief it is not a warez server) + /// + ClientErrorUnsupportedMediaType = 415, + + /// + /// 416 Client error: The requested range cannot be delivered by the server. + /// + ClientErrorRequestRangeNotSatisfiable = 416, + + /// + /// 417 Client error: The expectations of the client as expressed in one or more Expect header fields cannot be met by the server, the server is awfully sorry about this. + /// + ClientErrorExpectationFailed = 417, + + /// + /// 428 Client error :The 428 status code indicates that the origin server requires the request to be conditional. + /// + ClientErrorPreconditionRequired = 428, + + /// + /// 429 Client error: The 429 status code indicates that the user has sent too many requests in a given amount of time ("rate limiting"). + /// + ClientErrorTooManyRequests = 429, + + /// + /// 431 Client error: The 431 status code indicates that the server is unwilling to process the request because its header fields are too large. The request MAY be resubmitted after reducing the size of the request header fields. + /// + ClientErrorRequestHeaderFieldsTooLarge = 431, + + /// + /// 499 Client error: Wildcard error. + /// + ClientErrorJoker = 499, + + #endregion + + #region 5xx Server errors (rare) + + /// + /// 500 Server error: something really strange and unexpected happened + /// + ServerErrorInternalError = 500, + + /// + /// 501 Server error: The server does not do the functionality required to carry out the client request. not at all. certainly not before breakfast. but also not after breakfast. + /// + ServerErrorNotImplemented = 501, + + /// + /// 502 Server error: While acting as a proxy or a gateway, the server got ditched by the upstream server and as a consequence regretfully cannot fulfill the client's request + /// + ServerErrorBadGateway = 502, + + /// + /// 503 Server error: Due to unforseen circumstances the server cannot currently deliver the service requested. Retry-After header might indicate when to try again. + /// + ServerErrorServiceUnavailable = 503, + + /// + /// 504 Server error: The server blames the upstream server for not being able to deliver the service requested and claims that the upstream server is too slow delivering the goods. + /// + ServerErrorGatewayTimeout = 504, + + /// + /// 505 Server error: The server does not support the HTTP version conveyed in the client's request. + /// + ServerErrorHttpVersionNotSupported = 505, + + /// + /// 511 Server error: The 511 status code indicates that the client needs to authenticate to gain network access. + /// + ServerErrorNetworkAuthenticationRequired = 511, + + #endregion + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs new file mode 100644 index 0000000000..ef573a4186 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs @@ -0,0 +1,180 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using log4net; +using Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate XmlRpcResponse OSHttpXmlRpcProcessor(XmlRpcRequest request); + + public class OSHttpXmlRpcHandler: OSHttpHandler + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// XmlRpcMethodMatch tries to reify (deserialize) an incoming + /// XmlRpc request (and posts it to the "whiteboard") and + /// checks whether the method name is one we are interested + /// in. + /// + /// true if the handler is interested in the content; + /// false otherwise + protected bool XmlRpcMethodMatch(OSHttpRequest req) + { + XmlRpcRequest xmlRpcRequest = null; + + // check whether req is already reified + // if not: reify (and post to whiteboard) + try + { + if (req.Whiteboard.ContainsKey("xmlrequest")) + { + xmlRpcRequest = req.Whiteboard["xmlrequest"] as XmlRpcRequest; + } + else + { + StreamReader body = new StreamReader(req.InputStream); + string requestBody = body.ReadToEnd(); + xmlRpcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody); + req.Whiteboard["xmlrequest"] = xmlRpcRequest; + } + } + catch (XmlException) + { + _log.ErrorFormat("[OSHttpXmlRpcHandler] failed to deserialize XmlRpcRequest from {0}", req.ToString()); + return false; + } + + // check against methodName + if ((null != xmlRpcRequest) + && !String.IsNullOrEmpty(xmlRpcRequest.MethodName) + && xmlRpcRequest.MethodName == _methodName) + { + _log.DebugFormat("[OSHttpXmlRpcHandler] located handler {0} for {1}", _methodName, req.ToString()); + return true; + } + + return false; + } + + // contains handler for processing XmlRpc Request + private XmlRpcMethod _handler; + + // contains XmlRpc method name + private string _methodName; + + + /// + /// Instantiate an XmlRpc handler. + /// + /// XmlRpcMethod + /// delegate + /// XmlRpc method name + /// XmlRpc path prefix (regular expression) + /// Dictionary with header names and + /// regular expressions to match content of headers + /// IP whitelist of remote end points + /// to accept (regular expression) + /// + /// Except for handler and methodName, all other parameters + /// can be null, in which case they are not taken into account + /// when the handler is being looked up. + /// + public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName, Regex path, + Dictionary headers, Regex whitelist) + : base(new Regex(@"^POST$", RegexOptions.IgnoreCase | RegexOptions.Compiled), path, null, headers, + new Regex(@"^(text|application)/xml", RegexOptions.IgnoreCase | RegexOptions.Compiled), + whitelist) + { + _handler = handler; + _methodName = methodName; + } + + + /// + /// Instantiate an XmlRpc handler. + /// + /// XmlRpcMethod + /// delegate + /// XmlRpc method name + public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName) + : this(handler, methodName, null, null, null) + { + } + + + /// + /// Invoked by OSHttpRequestPump. + /// + public override OSHttpHandlerResult Process(OSHttpRequest request) + { + XmlRpcResponse xmlRpcResponse; + string responseString; + + // check whether we are interested in this request + if (!XmlRpcMethodMatch(request)) return OSHttpHandlerResult.Pass; + + + OSHttpResponse resp = new OSHttpResponse(request); + try + { + // reified XmlRpcRequest must still be on the whiteboard + XmlRpcRequest xmlRpcRequest = request.Whiteboard["xmlrequest"] as XmlRpcRequest; + xmlRpcResponse = _handler(xmlRpcRequest); + responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); + + resp.ContentType = "text/xml"; + byte[] buffer = Encoding.UTF8.GetBytes(responseString); + + resp.SendChunked = false; + resp.ContentLength = buffer.Length; + resp.ContentEncoding = Encoding.UTF8; + + resp.Body.Write(buffer, 0, buffer.Length); + resp.Body.Flush(); + + resp.Send(); + + } + catch (Exception ex) + { + _log.WarnFormat("[OSHttpXmlRpcHandler]: Error: {0}", ex.Message); + return OSHttpHandlerResult.Pass; + } + return OSHttpHandlerResult.Done; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs new file mode 100644 index 0000000000..947710094d --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs @@ -0,0 +1,86 @@ +/* + * 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 OpenMetaverse; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate void RequestMethod(UUID requestID, Hashtable request); + public delegate bool HasEventsMethod(UUID requestID, UUID pId); + + public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId); + + public delegate Hashtable NoEventsMethod(UUID requestID, UUID pId); + + public class PollServiceEventArgs : EventArgs + { + public HasEventsMethod HasEvents; + public GetEventsMethod GetEvents; + public NoEventsMethod NoEvents; + public RequestMethod Request; + public UUID Id; + public int TimeOutms; + public EventType Type; + + public enum EventType : int + { + LongPoll = 0, + LslHttp = 1, + Inventory = 2 + } + + public string Url { get; set; } + + /// + /// Number of requests received for this poll service. + /// + public int RequestsReceived { get; set; } + + /// + /// Number of requests handled by this poll service. + /// + public int RequestsHandled { get; set; } + + public PollServiceEventArgs( + RequestMethod pRequest, + string pUrl, + HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents, + UUID pId, int pTimeOutms) + { + Request = pRequest; + Url = pUrl; + HasEvents = pHasEvents; + GetEvents = pGetEvents; + NoEvents = pNoEvents; + Id = pId; + TimeOutms = pTimeOutms; + Type = EventType.LongPoll; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs new file mode 100644 index 0000000000..caf0e98ef0 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs @@ -0,0 +1,97 @@ +/* + * 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.Reflection; +using System.Text; +using HttpServer; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class PollServiceHttpRequest + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public readonly PollServiceEventArgs PollServiceArgs; + public readonly IHttpClientContext HttpContext; + public readonly IHttpRequest Request; + public readonly int RequestTime; + public readonly UUID RequestID; + + public PollServiceHttpRequest( + PollServiceEventArgs pPollServiceArgs, IHttpClientContext pHttpContext, IHttpRequest pRequest) + { + PollServiceArgs = pPollServiceArgs; + HttpContext = pHttpContext; + Request = pRequest; + RequestTime = System.Environment.TickCount; + RequestID = UUID.Random(); + } + + internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata) + { + OSHttpResponse response + = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext); + + byte[] buffer = server.DoHTTPGruntWork(responsedata, response); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", ex); + } + finally + { + //response.OutputStream.Close(); + try + { + response.OutputStream.Flush(); + response.Send(); + + //if (!response.KeepAlive && response.ReuseContext) + // response.FreeContext(); + } + catch (Exception e) + { + m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", e); + } + + PollServiceArgs.RequestsHandled++; + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs new file mode 100644 index 0000000000..28bba704ed --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -0,0 +1,331 @@ +/* + * 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.Threading; +using System.Reflection; +using log4net; +using HttpServer; +using OpenSim.Framework; +using OpenSim.Framework.Monitoring; +using Amib.Threading; +using System.IO; +using System.Text; +using System.Collections.Generic; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class PollServiceRequestManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Is the poll service request manager running? + /// + /// + /// Can be running either synchronously or asynchronously + /// + public bool IsRunning { get; private set; } + + /// + /// Is the poll service performing responses asynchronously (with its own threads) or synchronously (via + /// external calls)? + /// + public bool PerformResponsesAsync { get; private set; } + + /// + /// Number of responses actually processed and sent to viewer (or aborted due to error). + /// + public int ResponsesProcessed { get; private set; } + + private readonly BaseHttpServer m_server; + + private BlockingQueue m_requests = new BlockingQueue(); + private static List m_longPollRequests = new List(); + + private uint m_WorkerThreadCount = 0; + private Thread[] m_workerThreads; + + private SmartThreadPool m_threadPool = new SmartThreadPool(20000, 12, 2); + +// private int m_timeout = 1000; // increase timeout 250; now use the event one + + public PollServiceRequestManager( + BaseHttpServer pSrv, bool performResponsesAsync, uint pWorkerThreadCount, int pTimeout) + { + m_server = pSrv; + PerformResponsesAsync = performResponsesAsync; + m_WorkerThreadCount = pWorkerThreadCount; + m_workerThreads = new Thread[m_WorkerThreadCount]; + + StatsManager.RegisterStat( + new Stat( + "QueuedPollResponses", + "Number of poll responses queued for processing.", + "", + "", + "httpserver", + m_server.Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = m_requests.Count(), + StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "ProcessedPollResponses", + "Number of poll responses processed.", + "", + "", + "httpserver", + m_server.Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = ResponsesProcessed, + StatVerbosity.Debug)); + } + + public void Start() + { + IsRunning = true; + + if (PerformResponsesAsync) + { + //startup worker threads + for (uint i = 0; i < m_WorkerThreadCount; i++) + { + m_workerThreads[i] + = WorkManager.StartThread( + PoolWorkerJob, + string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port), + ThreadPriority.Normal, + false, + false, + null, + int.MaxValue); + } + + WorkManager.StartThread( + this.CheckLongPollThreads, + string.Format("LongPollServiceWatcherThread:{0}", m_server.Port), + ThreadPriority.Normal, + false, + true, + null, + 1000 * 60 * 10); + } + } + + private void ReQueueEvent(PollServiceHttpRequest req) + { + if (IsRunning) + { + // delay the enqueueing for 100ms. There's no need to have the event + // actively on the queue + Timer t = new Timer(self => { + ((Timer)self).Dispose(); + m_requests.Enqueue(req); + }); + + t.Change(100, Timeout.Infinite); + + } + } + + public void Enqueue(PollServiceHttpRequest req) + { + if (IsRunning) + { + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) + { + lock (m_longPollRequests) + m_longPollRequests.Add(req); + } + else + m_requests.Enqueue(req); + } + } + + private void CheckLongPollThreads() + { + // The only purpose of this thread is to check the EQs for events. + // If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests. + // If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests. + // All other types of tasks (Inventory handlers, http-in, etc) don't have the long-poll nature, + // so if they aren't ready to be served by a worker thread (no events), they are placed + // directly back in the "ready-to-serve" queue by the worker thread. + while (IsRunning) + { + Thread.Sleep(500); + Watchdog.UpdateThread(); + +// List not_ready = new List(); + lock (m_longPollRequests) + { + if (m_longPollRequests.Count > 0 && IsRunning) + { + List ready = m_longPollRequests.FindAll(req => + (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ + (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout + ); + + ready.ForEach(req => + { + m_requests.Enqueue(req); + m_longPollRequests.Remove(req); + }); + + } + + } + } + } + + public void Stop() + { + IsRunning = false; +// m_timeout = -10000; // cause all to expire + Thread.Sleep(1000); // let the world move + + foreach (Thread t in m_workerThreads) + Watchdog.AbortThread(t.ManagedThreadId); + + PollServiceHttpRequest wreq; + + lock (m_longPollRequests) + { + if (m_longPollRequests.Count > 0 && IsRunning) + m_longPollRequests.ForEach(req => m_requests.Enqueue(req)); + } + + while (m_requests.Count() > 0) + { + try + { + wreq = m_requests.Dequeue(0); + ResponsesProcessed++; + wreq.DoHTTPGruntWork( + m_server, wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id)); + } + catch + { + } + } + + m_longPollRequests.Clear(); + m_requests.Clear(); + } + + // work threads + + private void PoolWorkerJob() + { + while (IsRunning) + { + Watchdog.UpdateThread(); + WaitPerformResponse(); + } + } + + public void WaitPerformResponse() + { + PollServiceHttpRequest req = m_requests.Dequeue(5000); +// m_log.DebugFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString())); + + if (req != null) + { + try + { + if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id)) + { + Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id); + + if (responsedata == null) + return; + + // This is the event queue. + // Even if we're not running we can still perform responses by explicit request. + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll + || !PerformResponsesAsync) + { + try + { + ResponsesProcessed++; + req.DoHTTPGruntWork(m_server, responsedata); + } + catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream + { + // Ignore it, no need to reply + m_log.Error(e); + } + } + else + { + m_threadPool.QueueWorkItem(x => + { + try + { + ResponsesProcessed++; + req.DoHTTPGruntWork(m_server, responsedata); + } + catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream + { + // Ignore it, no need to reply + m_log.Error(e); + } + catch (Exception e) + { + m_log.Error(e); + } + + return null; + }, null); + } + } + else + { + if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) + { + ResponsesProcessed++; + req.DoHTTPGruntWork( + m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id)); + } + else + { + ReQueueEvent(req); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("Exception in poll service thread: " + e.ToString()); + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..63335bd468 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.Servers.HttpServer")] +[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("c4ea5baa-81c4-4867-a645-1ec360c1f164")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs new file mode 100644 index 0000000000..bd55657815 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs @@ -0,0 +1,70 @@ +/* + * 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.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate TResponse RestDeserialiseMethod(TRequest request); + + public class RestDeserialiseHandler : BaseOutputStreamHandler, IStreamHandler + where TRequest : new() + { + private RestDeserialiseMethod m_method; + + public RestDeserialiseHandler(string httpMethod, string path, RestDeserialiseMethod method) + : this(httpMethod, path, method, null, null) {} + + public RestDeserialiseHandler( + string httpMethod, string path, RestDeserialiseMethod method, string name, string description) + : base(httpMethod, path, name, description) + { + m_method = method; + } + + protected override void ProcessRequest(string path, Stream request, Stream responseStream, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + TRequest deserial; + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + XmlSerializer deserializer = new XmlSerializer(typeof (TRequest)); + deserial = (TRequest) deserializer.Deserialize(xmlReader); + } + + TResponse response = m_method(deserial); + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof (TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs new file mode 100644 index 0000000000..7f898394fd --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs @@ -0,0 +1,62 @@ +/* + * 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.Servers.HttpServer +{ + public class RestHTTPHandler : BaseHTTPHandler + { + private GenericHTTPMethod m_dhttpMethod; + + public GenericHTTPMethod Method + { + get { return m_dhttpMethod; } + } + + public RestHTTPHandler(string httpMethod, string path, GenericHTTPMethod dhttpMethod) + : base(httpMethod, path) + { + m_dhttpMethod = dhttpMethod; + } + + public RestHTTPHandler( + string httpMethod, string path, GenericHTTPMethod dhttpMethod, string name, string description) + : base(httpMethod, path, name, description) + { + m_dhttpMethod = dhttpMethod; + } + + public override Hashtable Handle(string path, Hashtable request) + { + string param = GetParam(path); + request.Add("param", param); + request.Add("path", path); + return m_dhttpMethod(request); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestMethod.cs b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs new file mode 100644 index 0000000000..80bc7ef251 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs @@ -0,0 +1,32 @@ +/* + * 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.Servers.HttpServer +{ + public delegate string RestMethod(string request, string path, string param, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse); +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs new file mode 100644 index 0000000000..afe052fc0a --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs @@ -0,0 +1,86 @@ +/* + * 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.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Makes an asynchronous REST request which doesn't require us to do anything with the response. + /// + public class RestObjectPoster + { + public static void BeginPostObject(string requestUrl, TRequest obj) + { + BeginPostObject("POST", requestUrl, obj); + } + + public static void BeginPostObject(string verb, string requestUrl, TRequest obj) + { + Type type = typeof (TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } + + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private static void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest) result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs new file mode 100644 index 0000000000..a911b9fa75 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs @@ -0,0 +1,108 @@ +/* + * 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.Text; +using System.Xml; +using System.Xml.Serialization; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate void ReturnResponse(T reponse); + + /// + /// Makes an asynchronous REST request with a callback to invoke with the response. + /// + public class RestObjectPosterResponse + { +// private static readonly log4net.ILog m_log +// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public ReturnResponse ResponseCallback; + + public void BeginPostObject(string requestUrl, TRequest obj) + { + BeginPostObject("POST", requestUrl, obj); + } + + public void BeginPostObject(string verb, string requestUrl, TRequest obj) + { + Type type = typeof (TRequest); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + request.Timeout = 10000; + + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } + + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest) result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + TResponse deserial; + XmlSerializer deserializer = new XmlSerializer(typeof (TResponse)); + Stream stream = resp.GetResponseStream(); + + // This is currently a bad debug stanza since it gobbles us the response... +// StreamReader reader = new StreamReader(stream); +// m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd()); + + deserial = (TResponse) deserializer.Deserialize(stream); + + if (deserial != null && ResponseCallback != null) + { + ResponseCallback(deserial); + } + } + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs new file mode 100644 index 0000000000..ad69cd22cb --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs @@ -0,0 +1,295 @@ +/* + * 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.Text; +using System.Xml; +using System.Xml.Serialization; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class RestSessionObject + { + private string sid; + private string aid; + private TRequest request_body; + + public string SessionID + { + get { return sid; } + set { sid = value; } + } + + public string AvatarID + { + get { return aid; } + set { aid = value; } + } + + public TRequest Body + { + get { return request_body; } + set { request_body = value; } + } + } + + public class SynchronousRestSessionObjectPoster + { + public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid) + { + RestSessionObject sobj = new RestSessionObject(); + sobj.SessionID = sid; + sobj.AvatarID = aid; + sobj.Body = obj; + + Type type = typeof(RestSessionObject); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + request.Timeout = 20000; + + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } + + TResponse deserial = default(TResponse); + using (WebResponse resp = request.GetResponse()) + { + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + + using (Stream respStream = resp.GetResponseStream()) + deserial = (TResponse)deserializer.Deserialize(respStream); + } + + return deserial; + } + } + + public class RestSessionObjectPosterResponse + { + public ReturnResponse ResponseCallback; + + public void BeginPostObject(string requestUrl, TRequest obj, string sid, string aid) + { + BeginPostObject("POST", requestUrl, obj, sid, aid); + } + + public void BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid) + { + RestSessionObject sobj = new RestSessionObject(); + sobj.SessionID = sid; + sobj.AvatarID = aid; + sobj.Body = obj; + + Type type = typeof(RestSessionObject); + + WebRequest request = WebRequest.Create(requestUrl); + request.Method = verb; + request.ContentType = "text/xml"; + request.Timeout = 10000; + + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } + + // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); + request.BeginGetResponse(AsyncCallback, request); + } + + private void AsyncCallback(IAsyncResult result) + { + WebRequest request = (WebRequest)result.AsyncState; + using (WebResponse resp = request.EndGetResponse(result)) + { + TResponse deserial; + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + Stream stream = resp.GetResponseStream(); + + // This is currently a bad debug stanza since it gobbles us the response... + // StreamReader reader = new StreamReader(stream); + // m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd()); + + deserial = (TResponse)deserializer.Deserialize(stream); + if (stream != null) + stream.Close(); + + if (deserial != null && ResponseCallback != null) + { + ResponseCallback(deserial); + } + } + } + } + + public delegate bool CheckIdentityMethod(string sid, string aid); + + public class RestDeserialiseSecureHandler : BaseOutputStreamHandler, IStreamHandler + where TRequest : new() + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private RestDeserialiseMethod m_method; + private CheckIdentityMethod m_smethod; + + public RestDeserialiseSecureHandler( + string httpMethod, string path, + RestDeserialiseMethod method, CheckIdentityMethod smethod) + : base(httpMethod, path) + { + m_smethod = smethod; + m_method = method; + } + + protected override void ProcessRequest(string path, Stream request, Stream responseStream, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RestSessionObject deserial = default(RestSessionObject); + bool fail = false; + + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + try + { + XmlSerializer deserializer = new XmlSerializer(typeof(RestSessionObject)); + deserial = (RestSessionObject)deserializer.Deserialize(xmlReader); + } + catch (Exception e) + { + m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e); + fail = true; + } + } + + TResponse response = default(TResponse); + if (!fail && m_smethod(deserial.SessionID, deserial.AvatarID)) + { + response = m_method(deserial.Body); + } + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof(TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } + + public delegate bool CheckTrustedSourceMethod(IPEndPoint peer); + + public class RestDeserialiseTrustedHandler : BaseOutputStreamHandler, IStreamHandler + where TRequest : new() + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// The operation to perform once trust has been established. + /// + private RestDeserialiseMethod m_method; + + /// + /// The method used to check whether a request is trusted. + /// + private CheckTrustedSourceMethod m_tmethod; + + public RestDeserialiseTrustedHandler(string httpMethod, string path, RestDeserialiseMethod method, CheckTrustedSourceMethod tmethod) + : base(httpMethod, path) + { + m_tmethod = tmethod; + m_method = method; + } + + protected override void ProcessRequest(string path, Stream request, Stream responseStream, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + TRequest deserial = default(TRequest); + bool fail = false; + + using (XmlTextReader xmlReader = new XmlTextReader(request)) + { + try + { + XmlSerializer deserializer = new XmlSerializer(typeof(TRequest)); + deserial = (TRequest)deserializer.Deserialize(xmlReader); + } + catch (Exception e) + { + m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e); + fail = true; + } + } + + TResponse response = default(TResponse); + if (!fail && m_tmethod(httpRequest.RemoteIPEndPoint)) + { + response = m_method(deserial); + } + + using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream)) + { + XmlSerializer serializer = new XmlSerializer(typeof(TResponse)); + serializer.Serialize(xmlWriter, response); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs new file mode 100644 index 0000000000..0305dee118 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs @@ -0,0 +1,65 @@ +/* + * 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; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class RestStreamHandler : BaseStreamHandler + { + private RestMethod m_restMethod; + + public RestMethod Method + { + get { return m_restMethod; } + } + + public RestStreamHandler(string httpMethod, string path, RestMethod restMethod) + : this(httpMethod, path, restMethod, null, null) {} + + public RestStreamHandler(string httpMethod, string path, RestMethod restMethod, string name, string description) + : base(httpMethod, path, name, description) + { + m_restMethod = restMethod; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + Encoding encoding = Encoding.UTF8; + StreamReader streamReader = new StreamReader(request, encoding); + + string requestBody = streamReader.ReadToEnd(); + streamReader.Close(); + + string param = GetParam(path); + string responseString = m_restMethod(requestBody, path, param, httpRequest, httpResponse); + + return Encoding.UTF8.GetBytes(responseString); + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs new file mode 100644 index 0000000000..c2925e33f0 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs @@ -0,0 +1,1159 @@ +/* + * 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.Net; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + // Sealed class. If you're going to unseal it, implement IDisposable. + /// + /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service + /// + public sealed class WebSocketHttpServerHandler : BaseRequestHandler + { + + private class WebSocketState + { + public List ReceivedBytes; + public int ExpectedBytes; + public WebsocketFrameHeader Header; + public bool FrameComplete; + public WebSocketFrame ContinuationFrame; + } + + /// + /// Binary Data will trigger this event + /// + public event DataDelegate OnData; + + /// + /// Textual Data will trigger this event + /// + public event TextDelegate OnText; + + /// + /// A ping request form the other side will trigger this event. + /// This class responds to the ping automatically. You shouldn't send a pong. + /// it's informational. + /// + public event PingDelegate OnPing; + + /// + /// This is a response to a ping you sent. + /// + public event PongDelegate OnPong; + + /// + /// This is a regular HTTP Request... This may be removed in the future. + /// +// public event RegularHttpRequestDelegate OnRegularHttpRequest; + + /// + /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired + /// + public event UpgradeCompletedDelegate OnUpgradeCompleted; + + /// + /// If the upgrade failed, this will be fired + /// + public event UpgradeFailedDelegate OnUpgradeFailed; + + /// + /// When the websocket is closed, this will be fired. + /// + public event CloseDelegate OnClose; + + /// + /// Set this delegate to allow your module to validate the origin of the + /// Websocket request. Primary line of defense against cross site scripting + /// + public ValidateHandshake HandshakeValidateMethodOverride = null; + + private ManualResetEvent _receiveDone = new ManualResetEvent(false); + + private OSHttpRequest _request; + private HTTPNetworkContext _networkContext; + private IHttpClientContext _clientContext; + + private int _pingtime = 0; + private byte[] _buffer; + private int _bufferPosition; + private int _bufferLength; + private bool _closing; + private bool _upgraded; + private int _maxPayloadBytes = 41943040; + private int _initialMsgTimeout = 0; + private int _defaultReadTimeout = 10000; + + private const string HandshakeAcceptText = + "HTTP/1.1 101 Switching Protocols\r\n" + + "upgrade: websocket\r\n" + + "Connection: Upgrade\r\n" + + "sec-websocket-accept: {0}\r\n\r\n";// + + //"{1}"; + + private const string HandshakeDeclineText = + "HTTP/1.1 {0} {1}\r\n" + + "Connection: close\r\n\r\n"; + + /// + /// Mysterious constant defined in RFC6455 to append to the client provided security key + /// + private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + public WebSocketHttpServerHandler(OSHttpRequest preq, IHttpClientContext pContext, int bufferlen) + : base(preq.HttpMethod, preq.Url.OriginalString) + { + _request = preq; + _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing(); + _networkContext.Stream.ReadTimeout = _defaultReadTimeout; + _clientContext = pContext; + _bufferLength = bufferlen; + _buffer = new byte[_bufferLength]; + } + + // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices. + ~WebSocketHttpServerHandler() + { + Dispose(); + + } + + /// + /// Sets the length of the stream buffer + /// + /// Byte length. + public void SetChunksize(int pChunk) + { + if (!_upgraded) + { + _buffer = new byte[pChunk]; + } + else + { + throw new InvalidOperationException("You must set the chunksize before the connection is upgraded"); + } + } + + /// + /// This is the famous nagle. + /// + public bool NoDelay_TCP_Nagle + { + get + { + if (_networkContext != null && _networkContext.Socket != null) + { + return _networkContext.Socket.NoDelay; + } + else + { + throw new InvalidOperationException("The socket has been shutdown"); + } + } + set + { + if (_networkContext != null && _networkContext.Socket != null) + _networkContext.Socket.NoDelay = value; + else + { + throw new InvalidOperationException("The socket has been shutdown"); + } + } + } + + /// + /// This triggers the websocket to start the upgrade process... + /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead + /// of the more context appropriate HandshakeAndUpgrade() + /// + public void Start() + { + HandshakeAndUpgrade(); + } + + /// + /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade. + /// + public int MaxPayloadSize + { + get { return _maxPayloadBytes; } + set { _maxPayloadBytes = value; } + } + + /// + /// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading + /// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector. + /// + public int InitialMsgTimeout + { + get { return _initialMsgTimeout; } + set { _initialMsgTimeout = value; } + } + + /// + /// This triggers the websocket start the upgrade process + /// + public void HandshakeAndUpgrade() + { + string webOrigin = string.Empty; + string websocketKey = string.Empty; + string acceptKey = string.Empty; + string accepthost = string.Empty; + if (!string.IsNullOrEmpty(_request.Headers["origin"])) + webOrigin = _request.Headers["origin"]; + + if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"])) + websocketKey = _request.Headers["sec-websocket-key"]; + + if (!string.IsNullOrEmpty(_request.Headers["host"])) + accepthost = _request.Headers["host"]; + + if (string.IsNullOrEmpty(_request.Headers["upgrade"])) + { + FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no upgrade request submitted"); + } + + string connectionheader = _request.Headers["upgrade"]; + if (connectionheader.ToLower() != "websocket") + { + FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no connection upgrade request submitted"); + } + + // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail. + // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless + // Something asked for it... + if (HandshakeValidateMethodOverride != null) + { + if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost)) + { + acceptKey = GenerateAcceptKey(websocketKey); + string rawaccept = string.Format(HandshakeAcceptText, acceptKey); + SendUpgradeSuccess(rawaccept); + + + } + else + { + FailUpgrade(OSHttpStatusCode.ClientErrorForbidden, "Origin Validation Failed"); + } + } + else + { + acceptKey = GenerateAcceptKey(websocketKey); + string rawaccept = string.Format(HandshakeAcceptText, acceptKey); + SendUpgradeSuccess(rawaccept); + } + } + public IPEndPoint GetRemoteIPEndpoint() + { + return _request.RemoteIPEndPoint; + } + + /// + /// Generates a handshake response key string based on the client's + /// provided key to prove to the client that we're allowing the Websocket + /// upgrade of our own free will and we were not coerced into doing it. + /// + /// Client provided security key + /// + private static string GenerateAcceptKey(string key) + { + if (string.IsNullOrEmpty(key)) + return string.Empty; + + string acceptkey = key + WebsocketHandshakeAcceptHashConstant; + + SHA1 hashobj = SHA1.Create(); + string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey))); + hashobj.Clear(); + + return ret; + } + + /// + /// Informs the otherside that we accepted their upgrade request + /// + /// The HTTP 1.1 101 response that says Yay \o/ + private void SendUpgradeSuccess(string pHandshakeResponse) + { + // Create a new websocket state so we can keep track of data in between network reads. + WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true}; + + byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); + + + + + try + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Reset(); + } + // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. + _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); + + // Write the upgrade handshake success message + _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length); + _networkContext.Stream.Flush(); + _upgraded = true; + UpgradeCompletedDelegate d = OnUpgradeCompleted; + if (d != null) + d(this, new UpgradeCompletedEventArgs()); + if (_initialMsgTimeout > 0) + { + if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout))) + Close(string.Empty); + } + } + catch (IOException) + { + Close(string.Empty); + } + catch (ObjectDisposedException) + { + Close(string.Empty); + } + } + + /// + /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:( + /// + /// HTTP Status reflecting the reason why + /// Textual reason for the upgrade fail + private void FailUpgrade(OSHttpStatusCode pCode, string pMessage ) + { + string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty)); + byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse); + _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length); + _networkContext.Stream.Flush(); + _networkContext.Stream.Dispose(); + + UpgradeFailedDelegate d = OnUpgradeFailed; + if (d != null) + d(this,new UpgradeFailedEventArgs()); + } + + + /// + /// This is our ugly Async OnReceive event handler. + /// This chunks the input stream based on the length of the provided buffer and processes out + /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer. + /// + /// Our Async State from beginread + private void OnReceive(IAsyncResult ar) + { + WebSocketState _socketState = ar.AsyncState as WebSocketState; + try + { + int bytesRead = _networkContext.Stream.EndRead(ar); + if (bytesRead == 0) + { + // Do Disconnect + _networkContext.Stream.Dispose(); + _networkContext = null; + return; + } + _bufferPosition += bytesRead; + + if (_bufferPosition > _bufferLength) + { + // Message too big for chunksize.. not sure how this happened... + //Close(string.Empty); + } + + int offset = 0; + bool headerread = true; + int headerforwardposition = 0; + while (headerread && offset < bytesRead) + { + if (_socketState.FrameComplete) + { + WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader; + + headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader, + out headerforwardposition); + offset += headerforwardposition; + + if (headerread) + { + _socketState.FrameComplete = false; + if (pheader.PayloadLen > (ulong) _maxPayloadBytes) + { + Close("Invalid Payload size"); + + return; + } + if (pheader.PayloadLen > 0) + { + if ((int) pheader.PayloadLen > _bufferPosition - offset) + { + byte[] writebytes = new byte[_bufferPosition - offset]; + + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset); + _socketState.ExpectedBytes = (int) pheader.PayloadLen; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = pheader; // We need to add the header so that we can unmask it + offset += (int) _bufferPosition - offset; + } + else + { + byte[] writebytes = new byte[pheader.PayloadLen]; + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen); + WebSocketReader.Mask(pheader.Mask, writebytes); + pheader.IsMasked = false; + _socketState.FrameComplete = true; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = pheader; + offset += (int) pheader.PayloadLen; + } + } + else + { + pheader.Mask = 0; + _socketState.FrameComplete = true; + _socketState.Header = pheader; + } + + if (_socketState.FrameComplete) + { + ProcessFrame(_socketState); + _socketState.Header.SetDefault(); + _socketState.ReceivedBytes.Clear(); + _socketState.ExpectedBytes = 0; + + } + } + } + else + { + WebsocketFrameHeader frameHeader = _socketState.Header; + int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count; + + if (bytesleft > _bufferPosition) + { + byte[] writebytes = new byte[_bufferPosition]; + + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition); + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = frameHeader; // We need to add the header so that we can unmask it + offset += (int) _bufferPosition; + } + else + { + byte[] writebytes = new byte[_bufferPosition]; + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition); + _socketState.FrameComplete = true; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = frameHeader; + offset += (int) _bufferPosition; + } + if (_socketState.FrameComplete) + { + ProcessFrame(_socketState); + _socketState.Header.SetDefault(); + _socketState.ReceivedBytes.Clear(); + _socketState.ExpectedBytes = 0; + // do some processing + } + } + } + if (offset > 0) + { + // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning. + if (offset <_buffer.Length) + Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset); + _bufferPosition -= offset; + } + if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing) + { + _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive, + _socketState); + } + else + { + // We can't read the stream anymore... + } + } + catch (IOException) + { + Close(string.Empty); + } + catch (ObjectDisposedException) + { + Close(string.Empty); + } + } + + /// + /// Sends a string to the other side + /// + /// the string message that is to be sent + public void SendMessage(string message) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + byte[] messagedata = Encoding.UTF8.GetBytes(message); + WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; + textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; + textMessageFrame.Header.IsEnd = true; + SendSocket(textMessageFrame.ToBytes()); + + } + + public void SendData(byte[] data) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data}; + dataMessageFrame.Header.IsEnd = true; + dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; + SendSocket(dataMessageFrame.ToBytes()); + + } + + /// + /// Writes raw bytes to the websocket. Unframed data will cause disconnection + /// + /// + private void SendSocket(byte[] data) + { + if (!_closing) + { + try + { + + _networkContext.Stream.Write(data, 0, data.Length); + } + catch (IOException) + { + + } + } + } + + /// + /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames. + /// + public void SendPingCheck() + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; + pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; + pingFrame.Header.IsEnd = true; + _pingtime = Util.EnvironmentTickCount(); + SendSocket(pingFrame.ToBytes()); + } + + /// + /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so. + /// + /// + public void Close(string message) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + if (_networkContext == null) + return; + if (_networkContext.Stream != null) + { + if (_networkContext.Stream.CanWrite) + { + byte[] messagedata = Encoding.UTF8.GetBytes(message); + WebSocketFrame closeResponseFrame = new WebSocketFrame() + { + Header = WebsocketFrameHeader.HeaderDefault(), + WebSocketPayload = messagedata + }; + closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close; + closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length; + closeResponseFrame.Header.IsEnd = true; + SendSocket(closeResponseFrame.ToBytes()); + } + } + CloseDelegate closeD = OnClose; + if (closeD != null) + { + closeD(this, new CloseEventArgs()); + } + + _closing = true; + } + + /// + /// Processes a websocket frame and triggers consumer events + /// + /// We need to modify the websocket state here depending on the frame + private void ProcessFrame(WebSocketState psocketState) + { + if (psocketState.Header.IsMasked) + { + byte[] unmask = psocketState.ReceivedBytes.ToArray(); + WebSocketReader.Mask(psocketState.Header.Mask, unmask); + psocketState.ReceivedBytes = new List(unmask); + } + if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + switch (psocketState.Header.Opcode) + { + case WebSocketReader.OpCode.Ping: + PingDelegate pingD = OnPing; + if (pingD != null) + { + pingD(this, new PingEventArgs()); + } + + WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]}; + pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong; + pongFrame.Header.IsEnd = true; + SendSocket(pongFrame.ToBytes()); + break; + case WebSocketReader.OpCode.Pong: + + PongDelegate pongD = OnPong; + if (pongD != null) + { + pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)}); + } + break; + case WebSocketReader.OpCode.Binary: + if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. + { + psocketState.ContinuationFrame = new WebSocketFrame + { + Header = psocketState.Header, + WebSocketPayload = + psocketState.ReceivedBytes.ToArray() + }; + } + else + { + // Send Done Event! + DataDelegate dataD = OnData; + if (dataD != null) + { + dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()}); + } + } + break; + case WebSocketReader.OpCode.Text: + if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. + { + psocketState.ContinuationFrame = new WebSocketFrame + { + Header = psocketState.Header, + WebSocketPayload = + psocketState.ReceivedBytes.ToArray() + }; + } + else + { + TextDelegate textD = OnText; + if (textD != null) + { + textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) }); + } + + // Send Done Event! + } + break; + case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes + //Console.WriteLine("currhead " + psocketState.Header.IsEnd); + //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd); + byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length]; + byte[] newdata = psocketState.ReceivedBytes.ToArray(); + Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length); + Buffer.BlockCopy(newdata, 0, combineddata, + psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length); + psocketState.ContinuationFrame.WebSocketPayload = combineddata; + psocketState.Header.PayloadLen = (ulong)combineddata.Length; + if (psocketState.Header.IsEnd) + { + if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text) + { + // Send Done event + TextDelegate textD = OnText; + if (textD != null) + { + textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) }); + } + } + else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary) + { + // Send Done event + DataDelegate dataD = OnData; + if (dataD != null) + { + dataD(this, new WebsocketDataEventArgs() { Data = combineddata }); + } + } + else + { + // protocol violation + } + psocketState.ContinuationFrame = null; + } + break; + case WebSocketReader.OpCode.Close: + Close(string.Empty); + + break; + + } + psocketState.Header.SetDefault(); + psocketState.ReceivedBytes.Clear(); + psocketState.ExpectedBytes = 0; + } + public void Dispose() + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + if (_networkContext != null && _networkContext.Stream != null) + { + if (_networkContext.Stream.CanWrite) + _networkContext.Stream.Flush(); + _networkContext.Stream.Close(); + _networkContext.Stream.Dispose(); + _networkContext.Stream = null; + } + + if (_request != null && _request.InputStream != null) + { + _request.InputStream.Close(); + _request.InputStream.Dispose(); + _request = null; + } + + if (_clientContext != null) + { + _clientContext.Close(); + _clientContext = null; + } + } + } + + /// + /// Reads a byte stream and returns Websocket frames. + /// + public class WebSocketReader + { + /// + /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames + /// + private const byte EndBit = 0x80; + + /// + /// These are the Frame Opcodes + /// + public enum OpCode + { + // Data Opcodes + Continue = 0x0, + Text = 0x1, + Binary = 0x2, + + // Control flow Opcodes + Close = 0x8, + Ping = 0x9, + Pong = 0xA + } + + /// + /// Masks and Unmasks data using the frame mask. Mask is applied per octal + /// Note: Frames from clients MUST be masked + /// Note: Frames from servers MUST NOT be masked + /// + /// Int representing 32 bytes of mask data. Mask is applied per octal + /// + public static void Mask(int pMask, byte[] pBuffer) + { + byte[] maskKey = BitConverter.GetBytes(pMask); + int currentMaskIndex = 0; + for (int i = 0; i < pBuffer.Length; i++) + { + pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]); + if (currentMaskIndex == 3) + { + currentMaskIndex = 0; + } + else + { + currentMaskIndex++; + + } + + } + } + + /// + /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader, + /// and an int to move the buffer forward when it reads a header. False when it can't read a header + /// + /// Bytes read from the stream + /// Starting place in the stream to begin trying to read from + /// Lenth in the stream to try and read from. Provided for cases where the + /// buffer's length is larger then the data in it + /// Outputs the read WebSocket frame header + /// Informs the calling stream to move the buffer forward + /// True if it got a header, False if it didn't get a header + public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader, + out int moveBuffer) + { + oHeader = WebsocketFrameHeader.ZeroHeader; + int minumheadersize = 2; + if (length > pBuffer.Length - pOffset) + throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied"); + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + + byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3 + byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block + + oHeader = new WebsocketFrameHeader(); + oHeader.SetDefault(); + + if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit) + { + oHeader.IsEnd = true; + } + else + { + oHeader.IsEnd = false; + } + //Opcode + oHeader.Opcode = (WebSocketReader.OpCode)nibble2; + //Mask + oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7); + + // Payload length + oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F); + + int index = 2; // LargerPayload length starts at byte 3 + + switch (oHeader.PayloadLen) + { + case 126: + minumheadersize += 2; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + Array.Reverse(pBuffer, pOffset + index, 2); // two bytes + oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index); + index += 2; + break; + case 127: // we got more this is a bigger frame + // 8 bytes - uint64 - most significant bit 0 network byte order + minumheadersize += 8; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + Array.Reverse(pBuffer, pOffset + index, 8); + oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index); + index += 8; + break; + + } + //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation + if (oHeader.IsMasked) + { + minumheadersize += 4; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index); + index += 4; + } + moveBuffer = index; + return true; + + } + } + + /// + /// RFC6455 Websocket Frame + /// + public class WebSocketFrame + { + /* + * RFC6455 +nib 0 1 2 3 4 5 6 7 +byt 0 1 2 3 +dec 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) + + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | + + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + + * When reading these, the frames are possibly fragmented and interleaved with control frames + * the fragmented frames are not interleaved with data frames. Just control frames + */ + public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]}; + public WebsocketFrameHeader Header; + public byte[] WebSocketPayload; + + public byte[] ToBytes() + { + Header.PayloadLen = (ulong)WebSocketPayload.Length; + return Header.ToBytes(WebSocketPayload); + } + + } + + public struct WebsocketFrameHeader + { + //public byte CurrentMaskIndex; + /// + /// The last frame in a sequence of fragmented frames or the one and only frame for this message. + /// + public bool IsEnd; + + /// + /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked + /// + public bool IsMasked; + + /// + /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped + /// + public int Mask; + /* +byt 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +---------------+---------------+---------------+---------------+ + | Octal 1 | Octal 2 | Octal 3 | Octal 4 | + +---------------+---------------+---------------+---------------+ +*/ + + + public WebSocketReader.OpCode Opcode; + + public UInt64 PayloadLen; + //public UInt64 PayloadLeft; + // Payload is X + Y + //public UInt64 ExtensionDataLength; + //public UInt64 ApplicationDataLength; + public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault(); + + public void SetDefault() + { + + //CurrentMaskIndex = 0; + IsEnd = true; + IsMasked = true; + Mask = 0; + Opcode = WebSocketReader.OpCode.Close; + // PayloadLeft = 0; + PayloadLen = 0; + // ExtensionDataLength = 0; + // ApplicationDataLength = 0; + + } + + /// + /// Returns a byte array representing the Frame header + /// + /// This is the frame data payload. The header describes the size of the payload. + /// If payload is null, a Zero sized payload is assumed + /// Returns a byte array representing the frame header + public byte[] ToBytes(byte[] payload) + { + List result = new List(); + + // Squeeze in our opcode and our ending bit. + result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) )); + + // Again with the three different byte interpretations of size.. + + //bytesize + if (PayloadLen <= 125) + { + result.Add((byte) PayloadLen); + } //Uint16 + else if (PayloadLen <= ushort.MaxValue) + { + result.Add(126); + byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen)); + Array.Reverse(payloadLengthByte); + result.AddRange(payloadLengthByte); + } //UInt64 + else + { + result.Add(127); + byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen); + Array.Reverse(payloadLengthByte); + result.AddRange(payloadLengthByte); + } + + // Only add a payload if it's not null + if (payload != null) + { + result.AddRange(payload); + } + return result.ToArray(); + } + + /// + /// A Helper method to define the defaults + /// + /// + + public static WebsocketFrameHeader HeaderDefault() + { + return new WebsocketFrameHeader + { + //CurrentMaskIndex = 0, + IsEnd = false, + IsMasked = true, + Mask = 0, + Opcode = WebSocketReader.OpCode.Close, + //PayloadLeft = 0, + PayloadLen = 0, + // ExtensionDataLength = 0, + // ApplicationDataLength = 0 + }; + } + } + + public delegate void DataDelegate(object sender, WebsocketDataEventArgs data); + + public delegate void TextDelegate(object sender, WebsocketTextEventArgs text); + + public delegate void PingDelegate(object sender, PingEventArgs pingdata); + + public delegate void PongDelegate(object sender, PongEventArgs pongdata); + + public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request); + + public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata); + + public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata); + + public delegate void CloseDelegate(object sender, CloseEventArgs closedata); + + public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost); + + + public class WebsocketDataEventArgs : EventArgs + { + public byte[] Data; + } + + public class WebsocketTextEventArgs : EventArgs + { + public string Data; + } + + public class PingEventArgs : EventArgs + { + /// + /// The ping event can arbitrarily contain data + /// + public byte[] Data; + } + + public class PongEventArgs : EventArgs + { + /// + /// The pong event can arbitrarily contain data + /// + public byte[] Data; + + public int PingResponseMS; + + } + + public class RegularHttpRequestEvnetArgs : EventArgs + { + + } + + public class UpgradeCompletedEventArgs : EventArgs + { + + } + + public class UpgradeFailedEventArgs : EventArgs + { + + } + + public class CloseEventArgs : EventArgs + { + + } + + +} diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs new file mode 100644 index 0000000000..f2122080c0 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs @@ -0,0 +1,91 @@ +/* + * 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 Nwc.XmlRpc; +using OpenSim.Framework; + + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class XmlRpcBasicDOSProtector + { + private readonly XmlRpcMethod _normalMethod; + private readonly XmlRpcMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + + } + public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client) + { + + XmlRpcResponse resp = null; + string clientstring = GetClientString(request, client); + string endpoint = GetEndPoint(request, client); + if (_dosProtector.Process(clientstring, endpoint)) + resp = _normalMethod(request, client); + else + resp = _throttledMethod(request, client); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + return resp; + } + + private string GetClientString(XmlRpcRequest request, IPEndPoint client) + { + string clientstring; + if (_options.AllowXForwardedFor && request.Params.Count > 3) + { + object headerstr = request.Params[3]; + if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString())) + clientstring = request.Params[3].ToString(); + else + clientstring = client.Address.ToString(); + } + else + clientstring = client.Address.ToString(); + return clientstring; + } + + private string GetEndPoint(XmlRpcRequest request, IPEndPoint client) + { + return client.Address.ToString(); + } + + } + + +} diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs new file mode 100644 index 0000000000..27cf3f33d3 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs @@ -0,0 +1,34 @@ +/* + * 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 Nwc.XmlRpc; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate XmlRpcResponse XmlRpcMethod(XmlRpcRequest request, IPEndPoint client); +} diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs new file mode 100644 index 0000000000..57931d449d --- /dev/null +++ b/OpenSim/Framework/Servers/MainServer.cs @@ -0,0 +1,357 @@ +/* + * 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.Net; +using System.Text; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Framework.Servers +{ + public class MainServer + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static BaseHttpServer instance = null; + private static Dictionary m_Servers = new Dictionary(); + + /// + /// Control the printing of certain debug messages. + /// + /// + /// If DebugLevel >= 1 then short warnings are logged when receiving bad input data. + /// If DebugLevel >= 2 then long warnings are logged when receiving bad input data. + /// If DebugLevel >= 3 then short notices about all incoming non-poll HTTP requests are logged. + /// If DebugLevel >= 4 then the time taken to fulfill the request is logged. + /// If DebugLevel >= 5 then the start of the body of incoming non-poll HTTP requests will be logged. + /// If DebugLevel >= 6 then the entire body of incoming non-poll HTTP requests will be logged. + /// + public static int DebugLevel + { + get { return s_debugLevel; } + set + { + s_debugLevel = value; + + lock (m_Servers) + foreach (BaseHttpServer server in m_Servers.Values) + server.DebugLevel = s_debugLevel; + } + } + + private static int s_debugLevel; + + /// + /// Set the main HTTP server instance. + /// + /// + /// This will be used to register all handlers that listen to the default port. + /// + /// + /// Thrown if the HTTP server has not already been registered via AddHttpServer() + /// + public static BaseHttpServer Instance + { + get { return instance; } + + set + { + lock (m_Servers) + if (!m_Servers.ContainsValue(value)) + throw new Exception("HTTP server must already have been registered to be set as the main instance"); + + instance = value; + } + } + + /// + /// Get all the registered servers. + /// + /// + /// Returns a copy of the dictionary so this can be iterated through without locking. + /// + /// + public static Dictionary Servers + { + get { return new Dictionary(m_Servers); } + } + + public static void RegisterHttpConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "Comms", false, "show http-handlers", + "show http-handlers", + "Show all registered http handlers", HandleShowHttpHandlersCommand); + + console.Commands.AddCommand( + "Debug", false, "debug http", "debug http []", + "Turn on http request logging.", + "If in or all and\n" + + " level <= 0 then no extra logging is done.\n" + + " level >= 1 then short warnings are logged when receiving bad input data.\n" + + " level >= 2 then long warnings are logged when receiving bad input data.\n" + + " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n" + + " level >= 4 then the time taken to fulfill the request is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n" + + " no level is specified then the current level is returned.\n\n" + + "If out or all and\n" + + " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n" + + " level >= 4 then the time taken to fulfill the request is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n", + HandleDebugHttpCommand); + } + + /// + /// Turn on some debugging values for OpenSim. + /// + /// + private static void HandleDebugHttpCommand(string module, string[] cmdparams) + { + if (cmdparams.Length < 3) + { + MainConsole.Instance.Output("Usage: debug http 0..6"); + return; + } + + bool inReqs = false; + bool outReqs = false; + bool allReqs = false; + + string subCommand = cmdparams[2]; + + if (subCommand.ToLower() == "in") + { + inReqs = true; + } + else if (subCommand.ToLower() == "out") + { + outReqs = true; + } + else if (subCommand.ToLower() == "all") + { + allReqs = true; + } + else + { + MainConsole.Instance.Output("You must specify in, out or all"); + return; + } + + if (cmdparams.Length >= 4) + { + string rawNewDebug = cmdparams[3]; + int newDebug; + + if (!int.TryParse(rawNewDebug, out newDebug)) + { + MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawNewDebug); + return; + } + + if (newDebug < 0 || newDebug > 6) + { + MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0..6", newDebug); + return; + } + + if (allReqs || inReqs) + { + MainServer.DebugLevel = newDebug; + MainConsole.Instance.OutputFormat("IN debug level set to {0}", newDebug); + } + + if (allReqs || outReqs) + { + WebUtil.DebugLevel = newDebug; + MainConsole.Instance.OutputFormat("OUT debug level set to {0}", newDebug); + } + } + else + { + if (allReqs || inReqs) + MainConsole.Instance.OutputFormat("Current IN debug level is {0}", MainServer.DebugLevel); + + if (allReqs || outReqs) + MainConsole.Instance.OutputFormat("Current OUT debug level is {0}", WebUtil.DebugLevel); + } + } + + private static void HandleShowHttpHandlersCommand(string module, string[] args) + { + if (args.Length != 2) + { + MainConsole.Instance.Output("Usage: show http-handlers"); + return; + } + + StringBuilder handlers = new StringBuilder(); + + lock (m_Servers) + { + foreach (BaseHttpServer httpServer in m_Servers.Values) + { + handlers.AppendFormat( + "Registered HTTP Handlers for server at {0}:{1}\n", httpServer.ListenIPAddress, httpServer.Port); + + handlers.AppendFormat("* XMLRPC:\n"); + foreach (String s in httpServer.GetXmlRpcHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* HTTP:\n"); + foreach (String s in httpServer.GetHTTPHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* HTTP (poll):\n"); + foreach (String s in httpServer.GetPollServiceHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* JSONRPC:\n"); + foreach (String s in httpServer.GetJsonRpcHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + +// handlers.AppendFormat("* Agent:\n"); +// foreach (String s in httpServer.GetAgentHandlerKeys()) +// handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* LLSD:\n"); + foreach (String s in httpServer.GetLLSDHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* StreamHandlers ({0}):\n", httpServer.GetStreamHandlerKeys().Count); + foreach (String s in httpServer.GetStreamHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.Append("\n"); + } + } + + MainConsole.Instance.Output(handlers.ToString()); + } + + /// + /// Register an already started HTTP server to the collection of known servers. + /// + /// + public static void AddHttpServer(BaseHttpServer server) + { + lock (m_Servers) + { + if (m_Servers.ContainsKey(server.Port)) + throw new Exception(string.Format("HTTP server for port {0} already exists.", server.Port)); + + m_Servers.Add(server.Port, server); + } + } + + /// + /// Removes the http server listening on the given port. + /// + /// + /// It is the responsibility of the caller to do clean up. + /// + /// + /// + public static bool RemoveHttpServer(uint port) + { + lock (m_Servers) + { + if (instance != null && instance.Port == port) + instance = null; + + return m_Servers.Remove(port); + } + } + + /// + /// Does this collection of servers contain one with the given port? + /// + /// + /// Unlike GetHttpServer, this will not instantiate a server if one does not exist on that port. + /// + /// + /// true if a server with the given port is registered, false otherwise. + public static bool ContainsHttpServer(uint port) + { + lock (m_Servers) + return m_Servers.ContainsKey(port); + } + + /// + /// Get the default http server or an http server for a specific port. + /// + /// + /// If the requested HTTP server doesn't already exist then a new one is instantiated and started. + /// + /// + /// If 0 then the default HTTP server is returned. + public static IHttpServer GetHttpServer(uint port) + { + return GetHttpServer(port, null); + } + + /// + /// Get the default http server, an http server for a specific port + /// and/or an http server bound to a specific address + /// + /// + /// If the requested HTTP server doesn't already exist then a new one is instantiated and started. + /// + /// + /// If 0 then the default HTTP server is returned. + /// A specific IP address to bind to. If null then the default IP address is used. + public static IHttpServer GetHttpServer(uint port, IPAddress ipaddr) + { + if (port == 0) + return Instance; + + if (instance != null && port == Instance.Port) + return Instance; + + lock (m_Servers) + { + if (m_Servers.ContainsKey(port)) + return m_Servers[port]; + + m_Servers[port] = new BaseHttpServer(port); + + if (ipaddr != null) + m_Servers[port].ListenIPAddress = ipaddr; + + m_Servers[port].Start(); + + return m_Servers[port]; + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..792c62e2c7 --- /dev/null +++ b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Framework.Servers")] +[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("b48e8b3e-5c5c-4673-b31f-21e13b8e568b")] + +// 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")] diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs new file mode 100644 index 0000000000..e403ba09f5 --- /dev/null +++ b/OpenSim/Framework/Servers/ServerBase.cs @@ -0,0 +1,1047 @@ +/* + * 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using log4net; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using Nini.Config; +using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Framework.Servers +{ + public class ServerBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public IConfigSource Config { get; protected set; } + + /// + /// Console to be used for any command line output. Can be null, in which case there should be no output. + /// + protected ICommandConsole m_console; + + protected OpenSimAppender m_consoleAppender; + protected FileAppender m_logFileAppender; + + protected DateTime m_startuptime; + protected string m_startupDirectory = Environment.CurrentDirectory; + + protected string m_pidFile = String.Empty; + + protected ServerStatsCollector m_serverStatsCollector; + + /// + /// Server version information. Usually VersionInfo + information about git commit, operating system, etc. + /// + protected string m_version; + + public ServerBase() + { + m_startuptime = DateTime.Now; + m_version = VersionInfo.Version; + EnhanceVersionInformation(); + } + + protected void CreatePIDFile(string path) + { + if (File.Exists(path)) + m_log.ErrorFormat( + "[SERVER BASE]: Previous pid file {0} still exists on startup. Possibly previously unclean shutdown.", + path); + + try + { + string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); + + using (FileStream fs = File.Create(path)) + { + Byte[] buf = Encoding.ASCII.GetBytes(pidstring); + fs.Write(buf, 0, buf.Length); + } + + m_pidFile = path; + + m_log.InfoFormat("[SERVER BASE]: Created pid file {0}", m_pidFile); + } + catch (Exception e) + { + m_log.Warn(string.Format("[SERVER BASE]: Could not create PID file at {0} ", path), e); + } + } + + protected void RemovePIDFile() + { + if (m_pidFile != String.Empty) + { + try + { + File.Delete(m_pidFile); + } + catch (Exception e) + { + m_log.Error(string.Format("[SERVER BASE]: Error whilst removing {0} ", m_pidFile), e); + } + + m_pidFile = String.Empty; + } + } + + /// + /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details, + /// etc.). + /// + public void LogEnvironmentInformation() + { + // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net + // XmlConfigurator calls first accross servers. + m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory); + + m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version); + + // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and + // the clr version number doesn't match the project version number under Mono. + //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); + m_log.InfoFormat( + "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit", + Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32"); + } + + public void RegisterCommonAppenders(IConfig startupConfig) + { + ILoggerRepository repository = LogManager.GetRepository(); + IAppender[] appenders = repository.GetAppenders(); + + foreach (IAppender appender in appenders) + { + if (appender.Name == "Console") + { + m_consoleAppender = (OpenSimAppender)appender; + } + else if (appender.Name == "LogFileAppender") + { + m_logFileAppender = (FileAppender)appender; + } + } + + if (null == m_consoleAppender) + { + Notice("No appender named Console found (see the log4net config file for this executable)!"); + } + else + { + // FIXME: This should be done through an interface rather than casting. + m_consoleAppender.Console = (ConsoleBase)m_console; + + // If there is no threshold set then the threshold is effectively everything. + if (null == m_consoleAppender.Threshold) + m_consoleAppender.Threshold = Level.All; + + Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold)); + } + + if (m_logFileAppender != null && startupConfig != null) + { + string cfgFileName = startupConfig.GetString("LogFile", null); + if (cfgFileName != null) + { + m_logFileAppender.File = cfgFileName; + m_logFileAppender.ActivateOptions(); + } + + m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File); + } + } + + /// + /// Register common commands once m_console has been set if it is going to be set + /// + public void RegisterCommonCommands() + { + if (m_console == null) + return; + + m_console.Commands.AddCommand( + "General", false, "show info", "show info", "Show general information about the server", HandleShow); + + m_console.Commands.AddCommand( + "General", false, "show version", "show version", "Show server version", HandleShow); + + m_console.Commands.AddCommand( + "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow); + + m_console.Commands.AddCommand( + "General", false, "get log level", "get log level", "Get the current console logging level", + (mod, cmd) => ShowLogLevel()); + + m_console.Commands.AddCommand( + "General", false, "set log level", "set log level ", + "Set the console logging level for this session.", HandleSetLogLevel); + + m_console.Commands.AddCommand( + "General", false, "config set", + "config set
", + "Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig); + + m_console.Commands.AddCommand( + "General", false, "config get", + "config get [
] []", + "Synonym for config show", + HandleConfig); + + m_console.Commands.AddCommand( + "General", false, "config show", + "config show [
] []", + "Show config information", + "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine + + "If a section is given but not a field, then all fields in that section are printed.", + HandleConfig); + + m_console.Commands.AddCommand( + "General", false, "config save", + "config save ", + "Save current configuration to a file at the given path", HandleConfig); + + m_console.Commands.AddCommand( + "General", false, "command-script", + "command-script \n"); + o.Append("\n"); + } + + public static void InsertPeriodicUpdaters(ref StringBuilder o, string[] divID, int[] seconds, string[] reportfrag) + { + o.Append(""); + } + + public static void HtmlHeaders_O(ref StringBuilder o) + { + o.Append("\n"); + o.Append(""); + o.Append(""); + } + + public static void HtmlHeaders_C(ref StringBuilder o) + { + o.Append(""); + o.Append(""); + } + + public static void AddReportLinks(ref StringBuilder o, Dictionary reports, string pClass) + { + int repcount = 0; + foreach (string str in reports.Keys) + { + if (reports[str].ReportName.Length > 0) + { + if (repcount > 0) + { + o.Append("|  "); + } + A(ref o, reports[str].ReportName, str, pClass); + o.Append("  "); + repcount++; + } + } + } + + public static void A(ref StringBuilder o, string linktext, string linkhref, string pClass) + { + o.Append(" 0) + { + GenericClass(ref o, pClass); + } + o.Append(" href=\""); + o.Append(linkhref); + o.Append("\">"); + o.Append(linktext); + o.Append(""); + } + } +} diff --git a/OpenSim/Region/UserStatistics/IStatsReport.cs b/OpenSim/Region/UserStatistics/IStatsReport.cs new file mode 100644 index 0000000000..80c44874e9 --- /dev/null +++ b/OpenSim/Region/UserStatistics/IStatsReport.cs @@ -0,0 +1,39 @@ +/* + * 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.Region.UserStatistics +{ + public interface IStatsController + { + string ReportName { get; } + Hashtable ProcessModel(Hashtable pParams); + string RenderView(Hashtable pModelResult); + string RenderJson(Hashtable pModelResult); + } +} diff --git a/OpenSim/Region/UserStatistics/LogLinesAJAX.cs b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs new file mode 100644 index 0000000000..4d45b80b06 --- /dev/null +++ b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs @@ -0,0 +1,159 @@ +/* + * 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 System.Text; +using System.Text.RegularExpressions; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Region.UserStatistics +{ + public class LogLinesAJAX : IStatsController + { + private Regex normalizeEndLines = new Regex(@"\r\n", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.Multiline); + + private Regex webFormat = new Regex(@"[^\s]*\s([^,]*),[^\s]*\s([A-Z]*)[^\s-][^\[]*\[([^\]]*)\]([^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + private Regex TitleColor = new Regex(@"[^\s]*\s(?:[^,]*),[^\s]*\s(?:[A-Z]*)[^\s-][^\[]*\[([^\]]*)\](?:[^\n]*)", + RegexOptions.Singleline | RegexOptions.Compiled); + + + #region IStatsController Members + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable nh = new Hashtable(); + nh.Add("loglines", pParams["LogLines"]); + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + StringBuilder output = new StringBuilder(); + + HTMLUtil.HR(ref output, ""); + output.Append("

ActiveLog

\n"); + + string tmp = normalizeEndLines.Replace(pModelResult["loglines"].ToString(), "\n"); + + string[] result = Regex.Split(tmp, "\n"); + + string formatopen = ""; + string formatclose = ""; + + for (int i = 0; i < result.Length; i++) + { + if (result[i].Length >= 30) + { + string logtype = result[i].Substring(24, 6); + switch (logtype) + { + case "WARN ": + formatopen = ""; + formatclose = ""; + break; + + case "ERROR ": + formatopen = ""; + formatclose = ""; + break; + + default: + formatopen = ""; + formatclose = ""; + break; + + } + } + StringBuilder replaceStr = new StringBuilder(); + //string titlecolorresults = + + string formatresult = Regex.Replace(TitleColor.Replace(result[i], "$1"), "[^ABCDEFabcdef0-9]", ""); + if (formatresult.Length > 6) + { + formatresult = formatresult.Substring(0, 6); + + } + for (int j = formatresult.Length; j <= 5; j++) + formatresult += "0"; + replaceStr.Append("$1 - [$3] $4
"); + string repstr = replaceStr.ToString(); + + output.Append(formatopen); + output.Append(webFormat.Replace(result[i], repstr)); + output.Append(formatclose); + } + + + return output.ToString(); + } + + /// + /// Return the last log lines. Output in the format: + ///
+        /// {"logLines": [
+        /// "line1",
+        /// "line2",
+        /// ...
+        /// ]
+        /// }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) + { + OSDMap logInfo = new OpenMetaverse.StructuredData.OSDMap(); + + OSDArray logLines = new OpenMetaverse.StructuredData.OSDArray(); + string tmp = normalizeEndLines.Replace(pModelResult["loglines"].ToString(), "\n"); + string[] result = Regex.Split(tmp, "\n"); + for (int i = 0; i < result.Length; i++) + { + logLines.Add(new OSDString(result[i])); + } + logInfo.Add("logLines", logLines); + return logInfo.ToString(); + } + + #endregion + } +} diff --git a/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs b/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9fac53e12c --- /dev/null +++ b/OpenSim/Region/UserStatistics/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Region.UserStatistics")] +[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("42b28288-5fdd-478f-8903-8dccbbb2d5f9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Region/UserStatistics/Prototype_distributor.cs b/OpenSim/Region/UserStatistics/Prototype_distributor.cs new file mode 100644 index 0000000000..6f8b2aa634 --- /dev/null +++ b/OpenSim/Region/UserStatistics/Prototype_distributor.cs @@ -0,0 +1,80 @@ +/* + * 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.Collections; +using System.Collections.Generic; +using System.Text; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Prototype_distributor : IStatsController + { + private string jsFileName = "prototype.js"; + private string prototypejs = string.Empty; + + public Prototype_distributor() + { + jsFileName = "prototype.js"; + } + + public Prototype_distributor(string jsName) + { + jsFileName = jsName; + } + + public string ReportName + { + get { return ""; } + } + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable pResult = new Hashtable(); + pResult["js"] = jsFileName; + return pResult; + } + + public string RenderView(Hashtable pModelResult) + { + string fileName = (string)pModelResult["js"]; + using (StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/" + fileName, FileMode.Open))) + { + prototypejs = fs.ReadToEnd(); + fs.Close(); + } + return prototypejs; + } + + public string RenderJson(Hashtable pModelResult) + { + return "{}"; + } + + } +} diff --git a/OpenSim/Region/UserStatistics/Sessions_Report.cs b/OpenSim/Region/UserStatistics/Sessions_Report.cs new file mode 100644 index 0000000000..0e94912ee4 --- /dev/null +++ b/OpenSim/Region/UserStatistics/Sessions_Report.cs @@ -0,0 +1,288 @@ +/* + * 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.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Sessions_Report : IStatsController + { + #region IStatsController Members + + public string ReportName + { + get { return "Sessions"; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable modeldata = new Hashtable(); + modeldata.Add("Scenes", pParams["Scenes"]); + modeldata.Add("Reports", pParams["Reports"]); + SqliteConnection dbConn = (SqliteConnection)pParams["DatabaseConnection"]; + List lstSessions = new List(); + Hashtable requestvars = (Hashtable) pParams["RequestVars"]; + + + string puserUUID = string.Empty; + string clientVersionString = string.Empty; + int queryparams = 0; + + if (requestvars != null) + { + if (requestvars.ContainsKey("UserID")) + { + UUID testUUID = UUID.Zero; + if (UUID.TryParse(requestvars["UserID"].ToString(), out testUUID)) + { + puserUUID = requestvars["UserID"].ToString(); + + } + } + + if (requestvars.ContainsKey("VersionString")) + { + clientVersionString = requestvars["VersionString"].ToString(); + } + } + + lock (dbConn) + { + string sql = + "SELECT distinct a.name_f, a.name_l, a.Agent_ID, b.Session_ID, b.client_version, b.last_updated, b.start_time FROM stats_session_data a LEFT OUTER JOIN stats_session_data b ON a.Agent_ID = b.Agent_ID"; + + if (puserUUID.Length > 0) + { + if (queryparams == 0) + sql += " WHERE"; + else + sql += " AND"; + + sql += " b.agent_id=:agent_id"; + queryparams++; + } + + if (clientVersionString.Length > 0) + { + if (queryparams == 0) + sql += " WHERE"; + else + sql += " AND"; + + sql += " b.client_version=:client_version"; + queryparams++; + } + + sql += " ORDER BY a.name_f, a.name_l, b.last_updated;"; + + SqliteCommand cmd = new SqliteCommand(sql, dbConn); + + if (puserUUID.Length > 0) + cmd.Parameters.Add(new SqliteParameter(":agent_id", puserUUID)); + if (clientVersionString.Length > 0) + cmd.Parameters.Add(new SqliteParameter(":client_version", clientVersionString)); + + SqliteDataReader sdr = cmd.ExecuteReader(); + + if (sdr.HasRows) + { + UUID userUUID = UUID.Zero; + + SessionList activeSessionList = new SessionList(); + activeSessionList.user_id=UUID.Random(); + while (sdr.Read()) + { + UUID readUUID = UUID.Parse(sdr["agent_id"].ToString()); + if (readUUID != userUUID) + { + activeSessionList = new SessionList(); + activeSessionList.user_id = readUUID; + activeSessionList.firstname = sdr["name_f"].ToString(); + activeSessionList.lastname = sdr["name_l"].ToString(); + activeSessionList.sessions = new List(); + lstSessions.Add(activeSessionList); + } + + ShortSessionData ssd = new ShortSessionData(); + + ssd.last_update = Utils.UnixTimeToDateTime((uint)Convert.ToInt32(sdr["last_updated"])); + ssd.start_time = Utils.UnixTimeToDateTime((uint)Convert.ToInt32(sdr["start_time"])); + ssd.session_id = UUID.Parse(sdr["session_id"].ToString()); + ssd.client_version = sdr["client_version"].ToString(); + activeSessionList.sessions.Add(ssd); + + userUUID = activeSessionList.user_id; + } + } + sdr.Close(); + sdr.Dispose(); + + } + modeldata["SessionData"] = lstSessions; + return modeldata; + } + + public string RenderView(Hashtable pModelResult) + { + List lstSession = (List) pModelResult["SessionData"]; + Dictionary reports = (Dictionary)pModelResult["Reports"]; + + const string STYLESHEET = + @" + +"; + + StringBuilder output = new StringBuilder(); + HTMLUtil.HtmlHeaders_O(ref output); + output.Append(STYLESHEET); + HTMLUtil.HtmlHeaders_C(ref output); + + HTMLUtil.AddReportLinks(ref output, reports, ""); + + HTMLUtil.TABLE_O(ref output, "defaultr"); + HTMLUtil.TR_O(ref output, "defaultr"); + HTMLUtil.TD_O(ref output, "header"); + output.Append("FirstName"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("LastName"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SessionEnd"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("SessionLength"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "header"); + output.Append("Client"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + if (lstSession.Count == 0) + { + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "align_top", 1, 5); + output.Append("No results for that query"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + foreach (SessionList ssnlst in lstSession) + { + int cnt = 0; + foreach (ShortSessionData sesdata in ssnlst.sessions) + { + HTMLUtil.TR_O(ref output, ""); + if (cnt++ == 0) + { + HTMLUtil.TD_O(ref output, "align_top", ssnlst.sessions.Count, 1); + output.Append(ssnlst.firstname); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "align_top", ssnlst.sessions.Count, 1); + output.Append(ssnlst.lastname); + HTMLUtil.TD_C(ref output); + } + HTMLUtil.TD_O(ref output, "content"); + output.Append(sesdata.last_update.ToShortDateString()); + output.Append(" - "); + output.Append(sesdata.last_update.ToShortTimeString()); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + TimeSpan dtlength = sesdata.last_update.Subtract(sesdata.start_time); + if (dtlength.Days > 0) + { + output.Append(dtlength.Days); + output.Append(" Days "); + } + if (dtlength.Hours > 0) + { + output.Append(dtlength.Hours); + output.Append(" Hours "); + } + if (dtlength.Minutes > 0) + { + output.Append(dtlength.Minutes); + output.Append(" Minutes"); + } + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, "content"); + output.Append(sesdata.client_version); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + + } + HTMLUtil.TR_O(ref output, ""); + HTMLUtil.TD_O(ref output, "align_top", 1, 5); + HTMLUtil.HR(ref output, ""); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + } + HTMLUtil.TABLE_C(ref output); + output.Append("\n"); + return output.ToString(); + } + + public class SessionList + { + public string firstname; + public string lastname; + public UUID user_id; + public List sessions; + } + + public struct ShortSessionData + { + public UUID session_id; + public string client_version; + public DateTime last_update; + public DateTime start_time; + } + + public string RenderJson(Hashtable pModelResult) + { + return "{}"; + } + #endregion + } + +} diff --git a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs new file mode 100644 index 0000000000..06d9e91449 --- /dev/null +++ b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs @@ -0,0 +1,276 @@ +/* + * 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 System.Text; +using Mono.Data.SqliteClient; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Monitoring; + +namespace OpenSim.Region.UserStatistics +{ + public class SimStatsAJAX : IStatsController + { + #region IStatsController Members + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + List m_scene = (List)pParams["Scenes"]; + + Hashtable nh = new Hashtable(); + nh.Add("hdata", m_scene); + nh.Add("simstats", pParams["SimStats"]); + return nh; + } + + public string RenderView(Hashtable pModelResult) + { + StringBuilder output = new StringBuilder(); + List all_scenes = (List) pModelResult["hdata"]; + Dictionary sdatadic = (Dictionary)pModelResult["simstats"]; + + const string TableClass = "defaultr"; + const string TRClass = "defaultr"; + const string TDHeaderClass = "header"; + const string TDDataClass = "content"; + //const string TDDataClassRight = "contentright"; + const string TDDataClassCenter = "contentcenter"; + + foreach (USimStatsData sdata in sdatadic.Values) + { + + + foreach (Scene sn in all_scenes) + { + if (sn.RegionInfo.RegionID == sdata.RegionId) + { + output.Append("

"); + output.Append(sn.RegionInfo.RegionName); + output.Append("

"); + } + } + HTMLUtil.TABLE_O(ref output, TableClass); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Dilatn"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("SimFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PhysFPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AgntUp"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("RootAg"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ChldAg"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("Prims"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ATvPrm"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AtvScr"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("ScrLPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.TimeDilation); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.SimFps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PhysicsFps); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.AgentUpdates); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.RootAgents); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ChildAgents); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.TotalPrims); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ActivePrims); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ActiveScripts); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.ScriptLinesPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("FrmMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("AgtMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PhysMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("OthrMS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("OutPPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("InPPS"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("NoAckKB"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PndDWN"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDHeaderClass); + output.Append("PndUP"); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TR_O(ref output, TRClass); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.TotalFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClass); + output.Append(sdata.AgentFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PhysicsFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.OtherFrameTime); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.OutPacketsPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.InPacketsPerSecond); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.UnackedBytes); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PendingDownloads); + HTMLUtil.TD_C(ref output); + HTMLUtil.TD_O(ref output, TDDataClassCenter); + output.Append(sdata.PendingUploads); + HTMLUtil.TD_C(ref output); + HTMLUtil.TR_C(ref output); + HTMLUtil.TABLE_C(ref output); + + } + + return output.ToString(); + } + + /// + /// Return stat information for all regions in the sim. Returns data of the form: + ///
+        /// {"REGIONNAME": {
+        ///     "region": "REGIONNAME",
+        ///     "timeDilation": "101", 
+        ///     ...     // the rest of the stat info
+        ///     },
+        ///  ...    // entries for each region
+        ///  }
+        /// 
+ ///
+ /// + /// + public string RenderJson(Hashtable pModelResult) + { + List all_scenes = (List) pModelResult["hdata"]; + Dictionary sdatadic = (Dictionary)pModelResult["simstats"]; + + OSDMap allStatsInfo = new OpenMetaverse.StructuredData.OSDMap(); + foreach (USimStatsData sdata in sdatadic.Values) + { + OSDMap statsInfo = new OpenMetaverse.StructuredData.OSDMap(); + string regionName = "unknown"; + foreach (Scene sn in all_scenes) + { + if (sn.RegionInfo.RegionID == sdata.RegionId) + { + regionName = sn.RegionInfo.RegionName; + break; + } + } + statsInfo.Add("region", new OSDString(regionName)); + statsInfo.Add("timeDilation", new OSDString(sdata.TimeDilation.ToString())); + statsInfo.Add("simFPS", new OSDString(sdata.SimFps.ToString())); + statsInfo.Add("physicsFPS", new OSDString(sdata.PhysicsFps.ToString())); + statsInfo.Add("agentUpdates", new OSDString(sdata.AgentUpdates.ToString())); + statsInfo.Add("rootAgents", new OSDString(sdata.RootAgents.ToString())); + statsInfo.Add("childAgents", new OSDString(sdata.ChildAgents.ToString())); + statsInfo.Add("totalPrims", new OSDString(sdata.TotalPrims.ToString())); + statsInfo.Add("activePrims", new OSDString(sdata.ActivePrims.ToString())); + statsInfo.Add("activeScripts", new OSDString(sdata.ActiveScripts.ToString())); + statsInfo.Add("scriptLinesPerSec", new OSDString(sdata.ScriptLinesPerSecond.ToString())); + statsInfo.Add("totalFrameTime", new OSDString(sdata.TotalFrameTime.ToString())); + statsInfo.Add("agentFrameTime", new OSDString(sdata.AgentFrameTime.ToString())); + statsInfo.Add("physicsFrameTime", new OSDString(sdata.PhysicsFrameTime.ToString())); + statsInfo.Add("otherFrameTime", new OSDString(sdata.OtherFrameTime.ToString())); + statsInfo.Add("outPacketsPerSec", new OSDString(sdata.OutPacketsPerSecond.ToString())); + statsInfo.Add("inPacketsPerSec", new OSDString(sdata.InPacketsPerSecond.ToString())); + statsInfo.Add("unackedByptes", new OSDString(sdata.UnackedBytes.ToString())); + statsInfo.Add("pendingDownloads", new OSDString(sdata.PendingDownloads.ToString())); + statsInfo.Add("pendingUploads", new OSDString(sdata.PendingUploads.ToString())); + + allStatsInfo.Add(regionName, statsInfo); + } + return allStatsInfo.ToString(); + } + + #endregion + } +} diff --git a/OpenSim/Region/UserStatistics/Updater_distributor.cs b/OpenSim/Region/UserStatistics/Updater_distributor.cs new file mode 100644 index 0000000000..601e06b65c --- /dev/null +++ b/OpenSim/Region/UserStatistics/Updater_distributor.cs @@ -0,0 +1,70 @@ +/* + * 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.Collections; +using System.Collections.Generic; +using System.Text; +using OpenSim.Framework; + +namespace OpenSim.Region.UserStatistics +{ + public class Updater_distributor : IStatsController + { + private string updaterjs = string.Empty; + + public string ReportName + { + get { return ""; } + } + + public Hashtable ProcessModel(Hashtable pParams) + { + Hashtable pResult = new Hashtable(); + if (updaterjs.Length == 0) + { + StreamReader fs = new StreamReader(new FileStream(Util.dataDir() + "/data/updater.js", FileMode.Open)); + updaterjs = fs.ReadToEnd(); + fs.Close(); + fs.Dispose(); + } + pResult["js"] = updaterjs; + return pResult; + } + + public string RenderView(Hashtable pModelResult) + { + return pModelResult["js"].ToString(); + } + + public string RenderJson(Hashtable pModelResult) { + return "{}"; + } + + } +} \ No newline at end of file diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs new file mode 100644 index 0000000000..13832084a9 --- /dev/null +++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs @@ -0,0 +1,1206 @@ +/* + * 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.Net; // to be used for REST-->Grid shortly +using System.Reflection; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using Mono.Data.SqliteClient; +using Mono.Addins; + +using Caps = OpenSim.Framework.Capabilities.Caps; + +using OSD = OpenMetaverse.StructuredData.OSD; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; + +[assembly: Addin("WebStats", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] + +namespace OpenSim.Region.UserStatistics +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebStatsModule")] + public class WebStatsModule : ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static SqliteConnection dbConn; + + /// + /// User statistics sessions keyed by agent ID + /// + private Dictionary m_sessions = new Dictionary(); + + private List m_scenes = new List(); + private Dictionary reports = new Dictionary(); + private Dictionary m_simstatsCounters = new Dictionary(); + private const int updateStatsMod = 6; + private int updateLogMod = 1; + private volatile int updateLogCounter = 0; + private volatile int concurrencyCounter = 0; + private bool enabled = false; + private string m_loglines = String.Empty; + private volatile int lastHit = 12000; + + #region ISharedRegionModule + + public virtual void Initialise(IConfigSource config) + { + IConfig cnfg = config.Configs["WebStats"]; + + if (cnfg != null) + enabled = cnfg.GetBoolean("enabled", false); + } + + public virtual void PostInitialise() + { + if (!enabled) + return; + + if (Util.IsWindows()) + Util.LoadArchSpecificWindowsDll("sqlite3.dll"); + + //IConfig startupConfig = config.Configs["Startup"]; + + dbConn = new SqliteConnection("URI=file:LocalUserStatistics.db,version=3"); + dbConn.Open(); + CreateTables(dbConn); + + Prototype_distributor protodep = new Prototype_distributor(); + Updater_distributor updatedep = new Updater_distributor(); + ActiveConnectionsAJAX ajConnections = new ActiveConnectionsAJAX(); + SimStatsAJAX ajSimStats = new SimStatsAJAX(); + LogLinesAJAX ajLogLines = new LogLinesAJAX(); + Default_Report defaultReport = new Default_Report(); + Clients_report clientReport = new Clients_report(); + Sessions_Report sessionsReport = new Sessions_Report(); + + reports.Add("prototype.js", protodep); + reports.Add("updater.js", updatedep); + reports.Add("activeconnectionsajax.html", ajConnections); + reports.Add("simstatsajax.html", ajSimStats); + reports.Add("activelogajax.html", ajLogLines); + reports.Add("default.report", defaultReport); + reports.Add("clients.report", clientReport); + reports.Add("sessions.report", sessionsReport); + + reports.Add("sim.css", new Prototype_distributor("sim.css")); + reports.Add("sim.html", new Prototype_distributor("sim.html")); + reports.Add("jquery.js", new Prototype_distributor("jquery.js")); + + //// + // Add Your own Reports here (Do Not Modify Lines here Devs!) + //// + + //// + // End Own reports section + //// + + MainServer.Instance.AddHTTPHandler("/SStats/", HandleStatsRequest); + MainServer.Instance.AddHTTPHandler("/CAPS/VS/", HandleUnknownCAPSRequest); + } + + public virtual void AddRegion(Scene scene) + { + if (!enabled) + return; + + lock (m_scenes) + { + m_scenes.Add(scene); + updateLogMod = m_scenes.Count * 2; + + m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID)); + + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket; + } + } + + public void RegionLoaded(Scene scene) + { + } + + public void RemoveRegion(Scene scene) + { + if (!enabled) + return; + + lock (m_scenes) + { + m_scenes.Remove(scene); + updateLogMod = m_scenes.Count * 2; + m_simstatsCounters.Remove(scene.RegionInfo.RegionID); + } + } + + public virtual void Close() + { + if (!enabled) + return; + + dbConn.Close(); + dbConn.Dispose(); + m_sessions.Clear(); + m_scenes.Clear(); + reports.Clear(); + m_simstatsCounters.Clear(); + } + + public virtual string Name + { + get { return "ViewerStatsModule"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + #endregion + + private void ReceiveClassicSimStatsPacket(SimStats stats) + { + if (!enabled) + return; + + try + { + // Ignore the update if there's a report running right now + // ignore the update if there hasn't been a hit in 30 seconds. + if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000) + return; + + // We will conduct this under lock so that fields such as updateLogCounter do not potentially get + // confused if a scene is removed. + // XXX: Possibly the scope of this lock could be reduced though it's not critical. + lock (m_scenes) + { + if (updateLogMod != 0 && updateLogCounter++ % updateLogMod == 0) + { + m_loglines = readLogLines(10); + + if (updateLogCounter > 10000) + updateLogCounter = 1; + } + + USimStatsData ss = m_simstatsCounters[stats.RegionUUID]; + + if ((++ss.StatsCounter % updateStatsMod) == 0) + { + ss.ConsumeSimStats(stats); + } + } + } + catch (KeyNotFoundException) + { + } + } + + private Hashtable HandleUnknownCAPSRequest(Hashtable request) + { + //string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/html"; + UpdateUserStats(ParseViewerStats(request["body"].ToString(), UUID.Zero), dbConn); + Hashtable responsedata = new Hashtable(); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = string.Empty; + return responsedata; + } + + private Hashtable HandleStatsRequest(Hashtable request) + { + lastHit = System.Environment.TickCount; + Hashtable responsedata = new Hashtable(); + string regpath = request["uri"].ToString(); + int response_code = 404; + string contenttype = "text/html"; + bool jsonFormatOutput = false; + + string strOut = string.Empty; + + // The request patch should be "/SStats/reportName" where 'reportName' + // is one of the names added to the 'reports' hashmap. + regpath = regpath.Remove(0, 8); + if (regpath.Length == 0) regpath = "default.report"; + if (reports.ContainsKey(regpath)) + { + IStatsController rep = reports[regpath]; + Hashtable repParams = new Hashtable(); + + if (request.ContainsKey("json")) + jsonFormatOutput = true; + + if (request.ContainsKey("requestvars")) + repParams["RequestVars"] = request["requestvars"]; + else + repParams["RequestVars"] = new Hashtable(); + + if (request.ContainsKey("querystringkeys")) + repParams["QueryStringKeys"] = request["querystringkeys"]; + else + repParams["QueryStringKeys"] = new string[0]; + + + repParams["DatabaseConnection"] = dbConn; + repParams["Scenes"] = m_scenes; + repParams["SimStats"] = m_simstatsCounters; + repParams["LogLines"] = m_loglines; + repParams["Reports"] = reports; + + concurrencyCounter++; + + if (jsonFormatOutput) + { + strOut = rep.RenderJson(rep.ProcessModel(repParams)); + contenttype = "text/json"; + } + else + { + strOut = rep.RenderView(rep.ProcessModel(repParams)); + } + + if (regpath.EndsWith("js")) + { + contenttype = "text/javascript"; + } + + if (regpath.EndsWith("css")) + { + contenttype = "text/css"; + } + + concurrencyCounter--; + + response_code = 200; + } + else + { + strOut = MainServer.Instance.GetHTTP404(""); + } + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + + return responsedata; + } + + private void CreateTables(SqliteConnection db) + { + using (SqliteCommand createcmd = new SqliteCommand(SQL_STATS_TABLE_CREATE, db)) + { + createcmd.ExecuteNonQuery(); + } + } + + private void OnRegisterCaps(UUID agentID, Caps caps) + { +// m_log.DebugFormat("[WEB STATS MODULE]: OnRegisterCaps: agentID {0} caps {1}", agentID, caps); + + string capsPath = "/CAPS/VS/" + UUID.Random(); + caps.RegisterHandler( + "ViewerStats", + new RestStreamHandler( + "POST", + capsPath, + (request, path, param, httpRequest, httpResponse) + => ViewerStatsReport(request, path, param, agentID, caps), + "ViewerStats", + agentID.ToString())); + } + + private void OnDeRegisterCaps(UUID agentID, Caps caps) + { + } + + protected virtual void AddEventHandlers() + { + lock (m_scenes) + { + updateLogMod = m_scenes.Count * 2; + foreach (Scene scene in m_scenes) + { + scene.EventManager.OnRegisterCaps += OnRegisterCaps; + scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps; + scene.EventManager.OnClientClosed += OnClientClosed; + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + } + } + } + + private void OnMakeRootAgent(ScenePresence agent) + { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Looking for session {0} for {1} in {2}", +// agent.ControllingClient.SessionId, agent.Name, agent.Scene.Name); + + lock (m_sessions) + { + UserSession uid; + + if (!m_sessions.ContainsKey(agent.UUID)) + { + UserSessionData usd = UserSessionUtil.newUserSessionData(); + uid = new UserSession(); + uid.name_f = agent.Firstname; + uid.name_l = agent.Lastname; + uid.session_data = usd; + + m_sessions.Add(agent.UUID, uid); + } + else + { + uid = m_sessions[agent.UUID]; + } + + uid.region_id = agent.Scene.RegionInfo.RegionID; + uid.session_id = agent.ControllingClient.SessionId; + } + } + + private void OnClientClosed(UUID agentID, Scene scene) + { + lock (m_sessions) + { + if (m_sessions.ContainsKey(agentID) && m_sessions[agentID].region_id == scene.RegionInfo.RegionID) + { + m_sessions.Remove(agentID); + } + } + } + + private string readLogLines(int amount) + { + Encoding encoding = Encoding.ASCII; + int sizeOfChar = encoding.GetByteCount("\n"); + byte[] buffer = encoding.GetBytes("\n"); + string logfile = Util.logFile(); + FileStream fs = new FileStream(logfile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + Int64 tokenCount = 0; + Int64 endPosition = fs.Length / sizeOfChar; + + for (Int64 position = sizeOfChar; position < endPosition; position += sizeOfChar) + { + fs.Seek(-position, SeekOrigin.End); + fs.Read(buffer, 0, buffer.Length); + + if (encoding.GetString(buffer) == "\n") + { + tokenCount++; + if (tokenCount == amount) + { + byte[] returnBuffer = new byte[fs.Length - fs.Position]; + fs.Read(returnBuffer, 0, returnBuffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(returnBuffer); + } + } + } + + // handle case where number of tokens in file is less than numberOfTokens + fs.Seek(0, SeekOrigin.Begin); + buffer = new byte[fs.Length]; + fs.Read(buffer, 0, buffer.Length); + fs.Close(); + fs.Dispose(); + return encoding.GetString(buffer); + } + + /// + /// Callback for a viewerstats cap + /// + /// + /// + /// + /// + /// + /// + private string ViewerStatsReport(string request, string path, string param, + UUID agentID, Caps caps) + { +// m_log.DebugFormat("[WEB STATS MODULE]: Received viewer starts report from {0}", agentID); + + UpdateUserStats(ParseViewerStats(request, agentID), dbConn); + + return String.Empty; + } + + private UserSession ParseViewerStats(string request, UUID agentID) + { + UserSession uid = new UserSession(); + UserSessionData usd; + OSD message = OSDParser.DeserializeLLSDXml(request); + OSDMap mmap; + + lock (m_sessions) + { + if (agentID != UUID.Zero) + { + if (!m_sessions.ContainsKey(agentID)) + { + m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID); + return new UserSession(); + } + + uid = m_sessions[agentID]; + +// m_log.DebugFormat("[WEB STATS MODULE]: Got session {0} for {1}", uid.session_id, agentID); + } + else + { + // parse through the beginning to locate the session + if (message.Type != OSDType.Map) + return new UserSession(); + + mmap = (OSDMap)message; + { + UUID sessionID = mmap["session_id"].AsUUID(); + + if (sessionID == UUID.Zero) + return new UserSession(); + + + // search through each session looking for the owner + foreach (UUID usersessionid in m_sessions.Keys) + { + // got it! + if (m_sessions[usersessionid].session_id == sessionID) + { + agentID = usersessionid; + uid = m_sessions[usersessionid]; + break; + } + + } + + // can't find a session + if (agentID == UUID.Zero) + { + return new UserSession(); + } + } + } + } + + usd = uid.session_data; + + if (message.Type != OSDType.Map) + return new UserSession(); + + mmap = (OSDMap)message; + { + if (mmap["agent"].Type != OSDType.Map) + return new UserSession(); + OSDMap agent_map = (OSDMap)mmap["agent"]; + usd.agent_id = agentID; + usd.name_f = uid.name_f; + usd.name_l = uid.name_l; + usd.region_id = uid.region_id; + usd.a_language = agent_map["language"].AsString(); + usd.mem_use = (float)agent_map["mem_use"].AsReal(); + usd.meters_traveled = (float)agent_map["meters_traveled"].AsReal(); + usd.regions_visited = agent_map["regions_visited"].AsInteger(); + usd.run_time = (float)agent_map["run_time"].AsReal(); + usd.start_time = (float)agent_map["start_time"].AsReal(); + usd.client_version = agent_map["version"].AsString(); + + UserSessionUtil.UpdateMultiItems(ref usd, agent_map["agents_in_view"].AsInteger(), + (float)agent_map["ping"].AsReal(), + (float)agent_map["sim_fps"].AsReal(), + (float)agent_map["fps"].AsReal()); + + if (mmap["downloads"].Type != OSDType.Map) + return new UserSession(); + OSDMap downloads_map = (OSDMap)mmap["downloads"]; + usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal(); + usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal(); + usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal(); + +// m_log.DebugFormat("[WEB STATS MODULE]: mmap[\"session_id\"] = [{0}]", mmap["session_id"].AsUUID()); + + usd.session_id = mmap["session_id"].AsUUID(); + + if (mmap["system"].Type != OSDType.Map) + return new UserSession(); + OSDMap system_map = (OSDMap)mmap["system"]; + + usd.s_cpu = system_map["cpu"].AsString(); + usd.s_gpu = system_map["gpu"].AsString(); + usd.s_os = system_map["os"].AsString(); + usd.s_ram = system_map["ram"].AsInteger(); + + if (mmap["stats"].Type != OSDType.Map) + return new UserSession(); + + OSDMap stats_map = (OSDMap)mmap["stats"]; + { + + if (stats_map["failures"].Type != OSDType.Map) + return new UserSession(); + OSDMap stats_failures = (OSDMap)stats_map["failures"]; + usd.f_dropped = stats_failures["dropped"].AsInteger(); + usd.f_failed_resends = stats_failures["failed_resends"].AsInteger(); + usd.f_invalid = stats_failures["invalid"].AsInteger(); + usd.f_resent = stats_failures["resent"].AsInteger(); + usd.f_send_packet = stats_failures["send_packet"].AsInteger(); + + if (stats_map["net"].Type != OSDType.Map) + return new UserSession(); + OSDMap stats_net = (OSDMap)stats_map["net"]; + { + if (stats_net["in"].Type != OSDType.Map) + return new UserSession(); + + OSDMap net_in = (OSDMap)stats_net["in"]; + usd.n_in_kb = (float)net_in["kbytes"].AsReal(); + usd.n_in_pk = net_in["packets"].AsInteger(); + + if (stats_net["out"].Type != OSDType.Map) + return new UserSession(); + OSDMap net_out = (OSDMap)stats_net["out"]; + + usd.n_out_kb = (float)net_out["kbytes"].AsReal(); + usd.n_out_pk = net_out["packets"].AsInteger(); + } + } + } + + uid.session_data = usd; + m_sessions[agentID] = uid; + +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Parse data for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + + return uid; + } + + private void UpdateUserStats(UserSession uid, SqliteConnection db) + { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Updating user stats for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + + if (uid.session_id == UUID.Zero) + return; + + lock (db) + { + using (SqliteCommand updatecmd = new SqliteCommand(SQL_STATS_TABLE_INSERT, db)) + { + updatecmd.Parameters.Add(new SqliteParameter(":session_id", uid.session_data.session_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":agent_id", uid.session_data.agent_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":region_id", uid.session_data.region_id.ToString())); + updatecmd.Parameters.Add(new SqliteParameter(":last_updated", (int) uid.session_data.last_updated)); + updatecmd.Parameters.Add(new SqliteParameter(":remote_ip", uid.session_data.remote_ip)); + updatecmd.Parameters.Add(new SqliteParameter(":name_f", uid.session_data.name_f)); + updatecmd.Parameters.Add(new SqliteParameter(":name_l", uid.session_data.name_l)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_agents_in_view", uid.session_data.avg_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":min_agents_in_view", + (int) uid.session_data.min_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":max_agents_in_view", + (int) uid.session_data.max_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_agents_in_view", + (int) uid.session_data.mode_agents_in_view)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_fps", uid.session_data.avg_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":min_fps", uid.session_data.min_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":max_fps", uid.session_data.max_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_fps", uid.session_data.mode_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":a_language", uid.session_data.a_language)); + updatecmd.Parameters.Add(new SqliteParameter(":mem_use", uid.session_data.mem_use)); + updatecmd.Parameters.Add(new SqliteParameter(":meters_traveled", uid.session_data.meters_traveled)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_ping", uid.session_data.avg_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":min_ping", uid.session_data.min_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":max_ping", uid.session_data.max_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_ping", uid.session_data.mode_ping)); + updatecmd.Parameters.Add(new SqliteParameter(":regions_visited", uid.session_data.regions_visited)); + updatecmd.Parameters.Add(new SqliteParameter(":run_time", uid.session_data.run_time)); + updatecmd.Parameters.Add(new SqliteParameter(":avg_sim_fps", uid.session_data.avg_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":min_sim_fps", uid.session_data.min_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":max_sim_fps", uid.session_data.max_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":mode_sim_fps", uid.session_data.mode_sim_fps)); + updatecmd.Parameters.Add(new SqliteParameter(":start_time", uid.session_data.start_time)); + updatecmd.Parameters.Add(new SqliteParameter(":client_version", uid.session_data.client_version)); + updatecmd.Parameters.Add(new SqliteParameter(":s_cpu", uid.session_data.s_cpu)); + updatecmd.Parameters.Add(new SqliteParameter(":s_gpu", uid.session_data.s_gpu)); + updatecmd.Parameters.Add(new SqliteParameter(":s_os", uid.session_data.s_os)); + updatecmd.Parameters.Add(new SqliteParameter(":s_ram", uid.session_data.s_ram)); + updatecmd.Parameters.Add(new SqliteParameter(":d_object_kb", uid.session_data.d_object_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":d_texture_kb", uid.session_data.d_texture_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":d_world_kb", uid.session_data.d_world_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_in_kb", uid.session_data.n_in_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_in_pk", uid.session_data.n_in_pk)); + updatecmd.Parameters.Add(new SqliteParameter(":n_out_kb", uid.session_data.n_out_kb)); + updatecmd.Parameters.Add(new SqliteParameter(":n_out_pk", uid.session_data.n_out_pk)); + updatecmd.Parameters.Add(new SqliteParameter(":f_dropped", uid.session_data.f_dropped)); + updatecmd.Parameters.Add(new SqliteParameter(":f_failed_resends", uid.session_data.f_failed_resends)); + updatecmd.Parameters.Add(new SqliteParameter(":f_invalid", uid.session_data.f_invalid)); + updatecmd.Parameters.Add(new SqliteParameter(":f_off_circuit", uid.session_data.f_off_circuit)); + updatecmd.Parameters.Add(new SqliteParameter(":f_resent", uid.session_data.f_resent)); + updatecmd.Parameters.Add(new SqliteParameter(":f_send_packet", uid.session_data.f_send_packet)); + +// StringBuilder parameters = new StringBuilder(); +// SqliteParameterCollection spc = updatecmd.Parameters; +// foreach (SqliteParameter sp in spc) +// parameters.AppendFormat("{0}={1},", sp.ParameterName, sp.Value); +// +// m_log.DebugFormat("[WEB STATS MODULE]: Parameters {0}", parameters); + +// m_log.DebugFormat("[WEB STATS MODULE]: Database stats update for {0}", uid.session_data.agent_id); + + updatecmd.ExecuteNonQuery(); + } + } + } + + #region SQL + private const string SQL_STATS_TABLE_CREATE = @"CREATE TABLE IF NOT EXISTS stats_session_data ( + session_id VARCHAR(36) NOT NULL PRIMARY KEY, + agent_id VARCHAR(36) NOT NULL DEFAULT '', + region_id VARCHAR(36) NOT NULL DEFAULT '', + last_updated INT NOT NULL DEFAULT '0', + remote_ip VARCHAR(16) NOT NULL DEFAULT '', + name_f VARCHAR(50) NOT NULL DEFAULT '', + name_l VARCHAR(50) NOT NULL DEFAULT '', + avg_agents_in_view FLOAT NOT NULL DEFAULT '0', + min_agents_in_view INT NOT NULL DEFAULT '0', + max_agents_in_view INT NOT NULL DEFAULT '0', + mode_agents_in_view INT NOT NULL DEFAULT '0', + avg_fps FLOAT NOT NULL DEFAULT '0', + min_fps FLOAT NOT NULL DEFAULT '0', + max_fps FLOAT NOT NULL DEFAULT '0', + mode_fps FLOAT NOT NULL DEFAULT '0', + a_language VARCHAR(25) NOT NULL DEFAULT '', + mem_use FLOAT NOT NULL DEFAULT '0', + meters_traveled FLOAT NOT NULL DEFAULT '0', + avg_ping FLOAT NOT NULL DEFAULT '0', + min_ping FLOAT NOT NULL DEFAULT '0', + max_ping FLOAT NOT NULL DEFAULT '0', + mode_ping FLOAT NOT NULL DEFAULT '0', + regions_visited INT NOT NULL DEFAULT '0', + run_time FLOAT NOT NULL DEFAULT '0', + avg_sim_fps FLOAT NOT NULL DEFAULT '0', + min_sim_fps FLOAT NOT NULL DEFAULT '0', + max_sim_fps FLOAT NOT NULL DEFAULT '0', + mode_sim_fps FLOAT NOT NULL DEFAULT '0', + start_time FLOAT NOT NULL DEFAULT '0', + client_version VARCHAR(255) NOT NULL DEFAULT '', + s_cpu VARCHAR(255) NOT NULL DEFAULT '', + s_gpu VARCHAR(255) NOT NULL DEFAULT '', + s_os VARCHAR(2255) NOT NULL DEFAULT '', + s_ram INT NOT NULL DEFAULT '0', + d_object_kb FLOAT NOT NULL DEFAULT '0', + d_texture_kb FLOAT NOT NULL DEFAULT '0', + d_world_kb FLOAT NOT NULL DEFAULT '0', + n_in_kb FLOAT NOT NULL DEFAULT '0', + n_in_pk INT NOT NULL DEFAULT '0', + n_out_kb FLOAT NOT NULL DEFAULT '0', + n_out_pk INT NOT NULL DEFAULT '0', + f_dropped INT NOT NULL DEFAULT '0', + f_failed_resends INT NOT NULL DEFAULT '0', + f_invalid INT NOT NULL DEFAULT '0', + f_off_circuit INT NOT NULL DEFAULT '0', + f_resent INT NOT NULL DEFAULT '0', + f_send_packet INT NOT NULL DEFAULT '0' + );"; + + private const string SQL_STATS_TABLE_INSERT = @"INSERT OR REPLACE INTO stats_session_data ( +session_id, agent_id, region_id, last_updated, remote_ip, name_f, name_l, avg_agents_in_view, min_agents_in_view, max_agents_in_view, +mode_agents_in_view, avg_fps, min_fps, max_fps, mode_fps, a_language, mem_use, meters_traveled, avg_ping, min_ping, max_ping, mode_ping, +regions_visited, run_time, avg_sim_fps, min_sim_fps, max_sim_fps, mode_sim_fps, start_time, client_version, s_cpu, s_gpu, s_os, s_ram, +d_object_kb, d_texture_kb, d_world_kb, n_in_kb, n_in_pk, n_out_kb, n_out_pk, f_dropped, f_failed_resends, f_invalid, f_off_circuit, +f_resent, f_send_packet +) +VALUES +( +:session_id, :agent_id, :region_id, :last_updated, :remote_ip, :name_f, :name_l, :avg_agents_in_view, :min_agents_in_view, :max_agents_in_view, +:mode_agents_in_view, :avg_fps, :min_fps, :max_fps, :mode_fps, :a_language, :mem_use, :meters_traveled, :avg_ping, :min_ping, :max_ping, :mode_ping, +:regions_visited, :run_time, :avg_sim_fps, :min_sim_fps, :max_sim_fps, :mode_sim_fps, :start_time, :client_version, :s_cpu, :s_gpu, :s_os, :s_ram, +:d_object_kb, :d_texture_kb, :d_world_kb, :n_in_kb, :n_in_pk, :n_out_kb, :n_out_pk, :f_dropped, :f_failed_resends, :f_invalid, :f_off_circuit, +:f_resent, :f_send_packet +) +"; + + #endregion + + } + + public static class UserSessionUtil + { + public static UserSessionData newUserSessionData() + { + UserSessionData obj = ZeroSession(new UserSessionData()); + return obj; + } + + public static void UpdateMultiItems(ref UserSessionData s, int agents_in_view, float ping, float sim_fps, float fps) + { + // don't insert zero values here or it'll skew the statistics. + if (agents_in_view == 0 && fps == 0 && sim_fps == 0 && ping == 0) + return; + s._agents_in_view.Add(agents_in_view); + s._fps.Add(fps); + s._sim_fps.Add(sim_fps); + s._ping.Add(ping); + + int[] __agents_in_view = s._agents_in_view.ToArray(); + + s.avg_agents_in_view = ArrayAvg_i(__agents_in_view); + s.min_agents_in_view = ArrayMin_i(__agents_in_view); + s.max_agents_in_view = ArrayMax_i(__agents_in_view); + s.mode_agents_in_view = ArrayMode_i(__agents_in_view); + + float[] __fps = s._fps.ToArray(); + s.avg_fps = ArrayAvg_f(__fps); + s.min_fps = ArrayMin_f(__fps); + s.max_fps = ArrayMax_f(__fps); + s.mode_fps = ArrayMode_f(__fps); + + float[] __sim_fps = s._sim_fps.ToArray(); + s.avg_sim_fps = ArrayAvg_f(__sim_fps); + s.min_sim_fps = ArrayMin_f(__sim_fps); + s.max_sim_fps = ArrayMax_f(__sim_fps); + s.mode_sim_fps = ArrayMode_f(__sim_fps); + + float[] __ping = s._ping.ToArray(); + s.avg_ping = ArrayAvg_f(__ping); + s.min_ping = ArrayMin_f(__ping); + s.max_ping = ArrayMax_f(__ping); + s.mode_ping = ArrayMode_f(__ping); + } + + #region Statistics + + public static int ArrayMin_i(int[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[0]; + } + + public static int ArrayMax_i(int[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[cnt-1]; + } + + public static float ArrayMin_f(float[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[0]; + } + + public static float ArrayMax_f(float[] arr) + { + int cnt = arr.Length; + if (cnt == 0) + return 0; + + Array.Sort(arr); + return arr[cnt - 1]; + } + + public static float ArrayAvg_i(int[] arr) + { + int cnt = arr.Length; + + if (cnt == 0) + return 0; + + float result = arr[0]; + + for (int i = 1; i < cnt; i++) + result += arr[i]; + + return result / cnt; + } + + public static float ArrayAvg_f(float[] arr) + { + int cnt = arr.Length; + + if (cnt == 0) + return 0; + + float result = arr[0]; + + for (int i = 1; i < cnt; i++) + result += arr[i]; + + return result / cnt; + } + + public static float ArrayMode_f(float[] arr) + { + List mode = new List(); + + float[] srtArr = new float[arr.Length]; + float[,] freq = new float[arr.Length, 2]; + Array.Copy(arr, srtArr, arr.Length); + Array.Sort(srtArr); + + float tmp = srtArr[0]; + int index = 0; + int i = 0; + while (i < srtArr.Length) + { + freq[index, 0] = tmp; + + while (tmp == srtArr[i]) + { + freq[index, 1]++; + i++; + + if (i > srtArr.Length - 1) + break; + } + + if (i < srtArr.Length) + { + tmp = srtArr[i]; + index++; + } + + } + + Array.Clear(srtArr, 0, srtArr.Length); + + for (i = 0; i < srtArr.Length; i++) + srtArr[i] = freq[i, 1]; + + Array.Sort(srtArr); + + if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1) + return 0; + + float freqtest = (float)freq.Length / freq.Rank; + + for (i = 0; i < freqtest; i++) + { + if (freq[i, 1] == srtArr[index]) + mode.Add(freq[i, 0]); + + } + + return mode.ToArray()[0]; + } + + public static int ArrayMode_i(int[] arr) + { + List mode = new List(); + + int[] srtArr = new int[arr.Length]; + int[,] freq = new int[arr.Length, 2]; + Array.Copy(arr, srtArr, arr.Length); + Array.Sort(srtArr); + + int tmp = srtArr[0]; + int index = 0; + int i = 0; + while (i < srtArr.Length) + { + freq[index, 0] = tmp; + + while (tmp == srtArr[i]) + { + freq[index, 1]++; + i++; + + if (i > srtArr.Length - 1) + break; + } + + if (i < srtArr.Length) + { + tmp = srtArr[i]; + index++; + } + + } + + Array.Clear(srtArr, 0, srtArr.Length); + + for (i = 0; i < srtArr.Length; i++) + srtArr[i] = freq[i, 1]; + + Array.Sort(srtArr); + + if ((srtArr[srtArr.Length - 1]) == 0 || (srtArr[srtArr.Length - 1]) == 1) + return 0; + + float freqtest = (float)freq.Length / freq.Rank; + + for (i = 0; i < freqtest; i++) + { + if (freq[i, 1] == srtArr[index]) + mode.Add(freq[i, 0]); + + } + + return mode.ToArray()[0]; + } + + #endregion + + private static UserSessionData ZeroSession(UserSessionData s) + { + s.session_id = UUID.Zero; + s.agent_id = UUID.Zero; + s.region_id = UUID.Zero; + s.last_updated = Util.UnixTimeSinceEpoch(); + s.remote_ip = ""; + s.name_f = ""; + s.name_l = ""; + s.avg_agents_in_view = 0; + s.min_agents_in_view = 0; + s.max_agents_in_view = 0; + s.mode_agents_in_view = 0; + s.avg_fps = 0; + s.min_fps = 0; + s.max_fps = 0; + s.mode_fps = 0; + s.a_language = ""; + s.mem_use = 0; + s.meters_traveled = 0; + s.avg_ping = 0; + s.min_ping = 0; + s.max_ping = 0; + s.mode_ping = 0; + s.regions_visited = 0; + s.run_time = 0; + s.avg_sim_fps = 0; + s.min_sim_fps = 0; + s.max_sim_fps = 0; + s.mode_sim_fps = 0; + s.start_time = 0; + s.client_version = ""; + s.s_cpu = ""; + s.s_gpu = ""; + s.s_os = ""; + s.s_ram = 0; + s.d_object_kb = 0; + s.d_texture_kb = 0; + s.d_world_kb = 0; + s.n_in_kb = 0; + s.n_in_pk = 0; + s.n_out_kb = 0; + s.n_out_pk = 0; + s.f_dropped = 0; + s.f_failed_resends = 0; + s.f_invalid = 0; + s.f_off_circuit = 0; + s.f_resent = 0; + s.f_send_packet = 0; + s._ping = new List(); + s._fps = new List(); + s._sim_fps = new List(); + s._agents_in_view = new List(); + return s; + } + } + #region structs + + public class UserSession + { + public UUID session_id; + public UUID region_id; + public string name_f; + public string name_l; + public UserSessionData session_data; + } + + public struct UserSessionData + { + public UUID session_id; + public UUID agent_id; + public UUID region_id; + public float last_updated; + public string remote_ip; + public string name_f; + public string name_l; + public float avg_agents_in_view; + public float min_agents_in_view; + public float max_agents_in_view; + public float mode_agents_in_view; + public float avg_fps; + public float min_fps; + public float max_fps; + public float mode_fps; + public string a_language; + public float mem_use; + public float meters_traveled; + public float avg_ping; + public float min_ping; + public float max_ping; + public float mode_ping; + public int regions_visited; + public float run_time; + public float avg_sim_fps; + public float min_sim_fps; + public float max_sim_fps; + public float mode_sim_fps; + public float start_time; + public string client_version; + public string s_cpu; + public string s_gpu; + public string s_os; + public int s_ram; + public float d_object_kb; + public float d_texture_kb; + public float d_world_kb; + public float n_in_kb; + public int n_in_pk; + public float n_out_kb; + public int n_out_pk; + public int f_dropped; + public int f_failed_resends; + public int f_invalid; + public int f_off_circuit; + public int f_resent; + public int f_send_packet; + public List _ping; + public List _fps; + public List _sim_fps; + public List _agents_in_view; + } + + #endregion + + public class USimStatsData + { + private UUID m_regionID = UUID.Zero; + private volatile int m_statcounter = 0; + private volatile float m_timeDilation; + private volatile float m_simFps; + private volatile float m_physicsFps; + private volatile float m_agentUpdates; + private volatile float m_rootAgents; + private volatile float m_childAgents; + private volatile float m_totalPrims; + private volatile float m_activePrims; + private volatile float m_totalFrameTime; + private volatile float m_netFrameTime; + private volatile float m_physicsFrameTime; + private volatile float m_otherFrameTime; + private volatile float m_imageFrameTime; + private volatile float m_inPacketsPerSecond; + private volatile float m_outPacketsPerSecond; + private volatile float m_unackedBytes; + private volatile float m_agentFrameTime; + private volatile float m_pendingDownloads; + private volatile float m_pendingUploads; + private volatile float m_activeScripts; + private volatile float m_scriptLinesPerSecond; + + public UUID RegionId { get { return m_regionID; } } + public int StatsCounter { get { return m_statcounter; } set { m_statcounter = value;}} + public float TimeDilation { get { return m_timeDilation; } } + public float SimFps { get { return m_simFps; } } + public float PhysicsFps { get { return m_physicsFps; } } + public float AgentUpdates { get { return m_agentUpdates; } } + public float RootAgents { get { return m_rootAgents; } } + public float ChildAgents { get { return m_childAgents; } } + public float TotalPrims { get { return m_totalPrims; } } + public float ActivePrims { get { return m_activePrims; } } + public float TotalFrameTime { get { return m_totalFrameTime; } } + public float NetFrameTime { get { return m_netFrameTime; } } + public float PhysicsFrameTime { get { return m_physicsFrameTime; } } + public float OtherFrameTime { get { return m_otherFrameTime; } } + public float ImageFrameTime { get { return m_imageFrameTime; } } + public float InPacketsPerSecond { get { return m_inPacketsPerSecond; } } + public float OutPacketsPerSecond { get { return m_outPacketsPerSecond; } } + public float UnackedBytes { get { return m_unackedBytes; } } + public float AgentFrameTime { get { return m_agentFrameTime; } } + public float PendingDownloads { get { return m_pendingDownloads; } } + public float PendingUploads { get { return m_pendingUploads; } } + public float ActiveScripts { get { return m_activeScripts; } } + public float ScriptLinesPerSecond { get { return m_scriptLinesPerSecond; } } + + public USimStatsData(UUID pRegionID) + { + m_regionID = pRegionID; + } + + public void ConsumeSimStats(SimStats stats) + { + m_regionID = stats.RegionUUID; + m_timeDilation = stats.StatsBlock[0].StatValue; + m_simFps = stats.StatsBlock[1].StatValue; + m_physicsFps = stats.StatsBlock[2].StatValue; + m_agentUpdates = stats.StatsBlock[3].StatValue; + m_rootAgents = stats.StatsBlock[4].StatValue; + m_childAgents = stats.StatsBlock[5].StatValue; + m_totalPrims = stats.StatsBlock[6].StatValue; + m_activePrims = stats.StatsBlock[7].StatValue; + m_totalFrameTime = stats.StatsBlock[8].StatValue; + m_netFrameTime = stats.StatsBlock[9].StatValue; + m_physicsFrameTime = stats.StatsBlock[10].StatValue; + m_otherFrameTime = stats.StatsBlock[11].StatValue; + m_imageFrameTime = stats.StatsBlock[12].StatValue; + m_inPacketsPerSecond = stats.StatsBlock[13].StatValue; + m_outPacketsPerSecond = stats.StatsBlock[14].StatValue; + m_unackedBytes = stats.StatsBlock[15].StatValue; + m_agentFrameTime = stats.StatsBlock[16].StatValue; + m_pendingDownloads = stats.StatsBlock[17].StatValue; + m_pendingUploads = stats.StatsBlock[18].StatValue; + m_activeScripts = stats.StatsBlock[19].StatValue; + m_scriptLinesPerSecond = stats.StatsBlock[20].StatValue; + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Base/CommandManager.cs b/OpenSim/Server/Base/CommandManager.cs new file mode 100644 index 0000000000..bd18485f7b --- /dev/null +++ b/OpenSim/Server/Base/CommandManager.cs @@ -0,0 +1,359 @@ +/* + * 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.Text; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Mono.Addins.Setup; +using Mono.Addins; +using Mono.Addins.Description; +using OpenSim.Framework; + +namespace OpenSim.Server.Base +{ + /// + /// Command manager - + /// Wrapper for OpenSim.Framework.PluginManager to allow + /// us to add commands to the console to perform operations + /// on our repos and plugins + /// + public class CommandManager + { + public AddinRegistry PluginRegistry; + protected PluginManager PluginManager; + + public CommandManager(AddinRegistry registry) + { + PluginRegistry = registry; + PluginManager = new PluginManager(PluginRegistry); + AddManagementCommands(); + } + + private void AddManagementCommands() + { + // add plugin + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin add", "plugin add \"plugin index\"", + "Install plugin from repository.", + HandleConsoleInstallPlugin); + + // remove plugin + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin remove", "plugin remove \"plugin index\"", + "Remove plugin from repository", + HandleConsoleUnInstallPlugin); + + // list installed plugins + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin list installed", + "plugin list installed","List install plugins", + HandleConsoleListInstalledPlugin); + + // list plugins available from registered repositories + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin list available", + "plugin list available","List available plugins", + HandleConsoleListAvailablePlugin); + // List available updates + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin updates", "plugin updates","List availble updates", + HandleConsoleListUpdates); + + // Update plugin + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin update", "plugin update \"plugin index\"","Update the plugin", + HandleConsoleUpdatePlugin); + + // Add repository + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo add", "repo add \"url\"","Add repository", + HandleConsoleAddRepo); + + // Refresh repo + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo refresh", "repo refresh \"url\"", "Sync with a registered repository", + HandleConsoleGetRepo); + + // Remove repository from registry + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo remove", + "repo remove \"[url | index]\"", + "Remove repository from registry", + HandleConsoleRemoveRepo); + + // Enable repo + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo enable", "repo enable \"[url | index]\"", + "Enable registered repository", + HandleConsoleEnableRepo); + + // Disable repo + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo disable", "repo disable\"[url | index]\"", + "Disable registered repository", + HandleConsoleDisableRepo); + + // List registered repositories + MainConsole.Instance.Commands.AddCommand("Repository", true, + "repo list", "repo list", + "List registered repositories", + HandleConsoleListRepos); + + // * + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin info", "plugin info \"plugin index\"","Show detailed information for plugin", + HandleConsoleShowAddinInfo); + + // Plugin disable + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin disable", "plugin disable \"plugin index\"", + "Disable a plugin", + HandleConsoleDisablePlugin); + + // Enable plugin + MainConsole.Instance.Commands.AddCommand("Plugin", true, + "plugin enable", "plugin enable \"plugin index\"", + "Enable the selected plugin plugin", + HandleConsoleEnablePlugin); + } + + #region console handlers + // Handle our console commands + // + // Install plugin from registered repository + /// + /// Handles the console install plugin command. Attempts to install the selected plugin + /// and + /// + /// + /// Module. + /// + /// + /// Cmd. + /// + private void HandleConsoleInstallPlugin(string module, string[] cmd) + { + Dictionary result = new Dictionary(); + + if (cmd.Length == 3) + { + int ndx = Convert.ToInt16(cmd[2]); + if (PluginManager.InstallPlugin(ndx, out result) == true) + { + ArrayList s = new ArrayList(); + s.AddRange(result.Keys); + s.Sort(); + + var list = result.Keys.ToList(); + list.Sort(); + foreach (var k in list) + { + Dictionary plugin = (Dictionary)result[k]; + bool enabled = (bool)plugin["enabled"]; + MainConsole.Instance.OutputFormat("{0}) {1} {2} rev. {3}", + k, + enabled == true ? "[ ]" : "[X]", + plugin["name"], plugin["version"]); + } + } + } + return; + } + + // Remove installed plugin + private void HandleConsoleUnInstallPlugin(string module, string[] cmd) + { + if (cmd.Length == 3) + { + int ndx = Convert.ToInt16(cmd[2]); + PluginManager.UnInstall(ndx); + } + return; + } + + // List installed plugins + private void HandleConsoleListInstalledPlugin(string module, string[] cmd) + { + Dictionary result = new Dictionary(); + PluginManager.ListInstalledAddins(out result); + + ArrayList s = new ArrayList(); + s.AddRange(result.Keys); + s.Sort(); + + var list = result.Keys.ToList(); + list.Sort(); + foreach (var k in list) + { + Dictionary plugin = (Dictionary)result[k]; + bool enabled = (bool)plugin["enabled"]; + MainConsole.Instance.OutputFormat("{0}) {1} {2} rev. {3}", + k, + enabled == true ? "[ ]" : "[X]", + plugin["name"], plugin["version"]); + } + return; + } + + // List available plugins on registered repositories + private void HandleConsoleListAvailablePlugin(string module, string[] cmd) + { + Dictionary result = new Dictionary(); + PluginManager.ListAvailable(out result); + + var list = result.Keys.ToList(); + list.Sort(); + foreach (var k in list) + { + // name, version, repository + Dictionary plugin = (Dictionary)result[k]; + MainConsole.Instance.OutputFormat("{0}) {1} rev. {2} {3}", + k, + plugin["name"], + plugin["version"], + plugin["repository"]); + } + return; + } + + // List available updates **not ready + private void HandleConsoleListUpdates(string module, string[] cmd) + { + PluginManager.ListUpdates(); + return; + } + + // Update plugin **not ready + private void HandleConsoleUpdatePlugin(string module, string[] cmd) + { + MainConsole.Instance.Output(PluginManager.Update()); + return; + } + + // Register repository + private void HandleConsoleAddRepo(string module, string[] cmd) + { + if ( cmd.Length == 3) + { + PluginManager.AddRepository(cmd[2]); + } + return; + } + + // Get repository status **not working + private void HandleConsoleGetRepo(string module, string[] cmd) + { + PluginManager.GetRepository(); + return; + } + + // Remove registered repository + private void HandleConsoleRemoveRepo(string module, string[] cmd) + { + if (cmd.Length == 3) + PluginManager.RemoveRepository(cmd); + return; + } + + // Enable repository + private void HandleConsoleEnableRepo(string module, string[] cmd) + { + PluginManager.EnableRepository(cmd); + return; + } + + // Disable repository + private void HandleConsoleDisableRepo(string module, string[] cmd) + { + PluginManager.DisableRepository(cmd); + return; + } + + // List repositories + private void HandleConsoleListRepos(string module, string[] cmd) + { + Dictionary result = new Dictionary(); + PluginManager.ListRepositories(out result); + + var list = result.Keys.ToList(); + list.Sort(); + foreach (var k in list) + { + Dictionary repo = (Dictionary)result[k]; + bool enabled = (bool)repo["enabled"]; + MainConsole.Instance.OutputFormat("{0}) {1} {2}", + k, + enabled == true ? "[ ]" : "[X]", + repo["name"], repo["url"]); + } + + return; + } + + // Show description information + private void HandleConsoleShowAddinInfo(string module, string[] cmd) + { + if (cmd.Length >= 3) + { + + Dictionary result = new Dictionary(); + + int ndx = Convert.ToInt16(cmd[2]); + PluginManager.AddinInfo(ndx, out result); + + MainConsole.Instance.OutputFormat("Name: {0}\nURL: {1}\nFile: {2}\nAuthor: {3}\nCategory: {4}\nDesc: {5}", + result["name"], + result["url"], + result["file_name"], + result["author"], + result["category"], + result["description"]); + + return; + } + } + + // Disable plugin + private void HandleConsoleDisablePlugin(string module, string[] cmd) + { + PluginManager.DisablePlugin(cmd); + return; + } + + // Enable plugin + private void HandleConsoleEnablePlugin(string module, string[] cmd) + { + PluginManager.EnablePlugin(cmd); + return; + } + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Server/Base/HttpServerBase.cs b/OpenSim/Server/Base/HttpServerBase.cs new file mode 100644 index 0000000000..44ef124a01 --- /dev/null +++ b/OpenSim/Server/Base/HttpServerBase.cs @@ -0,0 +1,152 @@ +/* + * 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.Threading; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using log4net; +using Nini.Config; + +namespace OpenSim.Server.Base +{ + public class HttpServerBase : ServicesServerBase + { +// private static readonly ILog m_Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private uint m_consolePort; + + // Handle all the automagical stuff + // + public HttpServerBase(string prompt, string[] args) : base(prompt, args) + { + } + + protected override void ReadConfig() + { + IConfig networkConfig = Config.Configs["Network"]; + + if (networkConfig == null) + { + System.Console.WriteLine("ERROR: Section [Network] not found, server can't start"); + Environment.Exit(1); + } + + uint port = (uint)networkConfig.GetInt("port", 0); + + if (port == 0) + { + System.Console.WriteLine("ERROR: No 'port' entry found in [Network]. Server can't start"); + Environment.Exit(1); + } + + bool ssl_main = networkConfig.GetBoolean("https_main",false); + bool ssl_listener = networkConfig.GetBoolean("https_listener",false); + + m_consolePort = (uint)networkConfig.GetInt("ConsolePort", 0); + + BaseHttpServer httpServer = null; + + // + // This is where to make the servers: + // + // + // Make the base server according to the port, etc. + // ADD: Possibility to make main server ssl + // Then, check for https settings and ADD a server to + // m_Servers + // + if (!ssl_main) + { + httpServer = new BaseHttpServer(port); + } + else + { + string cert_path = networkConfig.GetString("cert_path",String.Empty); + if (cert_path == String.Empty) + { + System.Console.WriteLine("ERROR: Path to X509 certificate is missing, server can't start."); + Environment.Exit(1); + } + + string cert_pass = networkConfig.GetString("cert_pass",String.Empty); + if (cert_pass == String.Empty) + { + System.Console.WriteLine("ERROR: Password for X509 certificate is missing, server can't start."); + Environment.Exit(1); + } + + httpServer = new BaseHttpServer(port, ssl_main, cert_path, cert_pass); + } + + MainServer.AddHttpServer(httpServer); + MainServer.Instance = httpServer; + + // If https_listener = true, then add an ssl listener on the https_port... + if (ssl_listener == true) + { + uint https_port = (uint)networkConfig.GetInt("https_port", 0); + + string cert_path = networkConfig.GetString("cert_path",String.Empty); + if (cert_path == String.Empty) + { + System.Console.WriteLine("ERROR: Path to X509 certificate is missing, server can't start."); + Environment.Exit(1); + } + + string cert_pass = networkConfig.GetString("cert_pass",String.Empty); + if (cert_pass == String.Empty) + { + System.Console.WriteLine("ERROR: Password for X509 certificate is missing, server can't start."); + Environment.Exit(1); + } + + MainServer.AddHttpServer(new BaseHttpServer(https_port, ssl_listener, cert_path, cert_pass)); + } + } + + protected override void Initialise() + { + foreach (BaseHttpServer s in MainServer.Servers.Values) + s.Start(); + + MainServer.RegisterHttpConsoleCommands(MainConsole.Instance); + + if (MainConsole.Instance is RemoteConsole) + { + if (m_consolePort == 0) + ((RemoteConsole)MainConsole.Instance).SetServer(MainServer.Instance); + else + ((RemoteConsole)MainConsole.Instance).SetServer(MainServer.GetHttpServer(m_consolePort)); + } + } + } +} diff --git a/OpenSim/Server/Base/Properties/AssemblyInfo.cs b/OpenSim/Server/Base/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e819a2b2fd --- /dev/null +++ b/OpenSim/Server/Base/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Server.Base")] +[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("8fbd5035-0dbc-4b9a-ad1a-a7567f254ea9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Server/Base/ProtocolVersions.cs b/OpenSim/Server/Base/ProtocolVersions.cs new file mode 100644 index 0000000000..8db5bb69d8 --- /dev/null +++ b/OpenSim/Server/Base/ProtocolVersions.cs @@ -0,0 +1,56 @@ +/* + * 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.Server.Base +{ + public class ProtocolVersions + { + /// + /// This is the external protocol versions. It is separate from the OpenSimulator project version. + /// + /// These version numbers should be increased by 1 every time a code + /// change in the Service.Connectors and Server.Handlers, espectively, + /// makes the previous OpenSimulator revision incompatible + /// with the new revision. + /// + /// Changes which are compatible with an older revision (e.g. older revisions experience degraded functionality + /// but not outright failure) do not need a version number increment. + /// + /// Having this version number allows the grid service to reject connections from regions running a version + /// of the code that is too old. + /// + /// + + // The range of acceptable servers for client-side connectors + public readonly static int ClientProtocolVersionMin = 0; + public readonly static int ClientProtocolVersionMax = 0; + + // The range of acceptable clients in server-side handlers + public readonly static int ServerProtocolVersionMin = 0; + public readonly static int ServerProtocolVersionMax = 0; + } +} diff --git a/OpenSim/Server/Base/ServerUtils.cs b/OpenSim/Server/Base/ServerUtils.cs new file mode 100644 index 0000000000..18a426676e --- /dev/null +++ b/OpenSim/Server/Base/ServerUtils.cs @@ -0,0 +1,542 @@ +/* + * 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.Reflection; +using System.Xml; +using System.Xml.Serialization; +using System.Text; +using System.Collections.Generic; +using log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenMetaverse; +using Mono.Addins; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.Servers; + + +[assembly:AddinRoot("Robust", OpenSim.VersionInfo.VersionNumber)] +namespace OpenSim.Server.Base +{ + [TypeExtensionPoint(Path="/Robust/Connector", Name="RobustConnector")] + public interface IRobustConnector + { + string ConfigName + { + get; + } + + bool Enabled + { + get; + } + + string PluginPath + { + get; + set; + } + + uint Configure(IConfigSource config); + void Initialize(IHttpServer server); + void Unload(); + } + + public class PluginLoader + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public AddinRegistry Registry + { + get; + private set; + } + + public IConfigSource Config + { + get; + private set; + } + + public PluginLoader(IConfigSource config, string registryPath) + { + Config = config; + + Registry = new AddinRegistry(registryPath, "."); + //suppress_console_output_(true); + AddinManager.Initialize(registryPath); + //suppress_console_output_(false); + AddinManager.Registry.Update(); + CommandManager commandmanager = new CommandManager(Registry); + AddinManager.AddExtensionNodeHandler("/Robust/Connector", OnExtensionChanged); + } + + private static TextWriter prev_console_; + // Temporarily masking the errors reported on start + // This is caused by a non-managed dll in the ./bin dir + // when the registry is initialized. The dll belongs to + // libomv, which has a hard-coded path to "." for pinvoke + // to load the openjpeg dll + // + // Will look for a way to fix, but for now this keeps the + // confusion to a minimum. this was copied from our region + // plugin loader, we have been doing this in there for a long time. + // + public void suppress_console_output_(bool save) + { + if (save) + { + prev_console_ = System.Console.Out; + System.Console.SetOut(new StreamWriter(Stream.Null)); + } + else + { + if (prev_console_ != null) + System.Console.SetOut(prev_console_); + } + } + + private void OnExtensionChanged(object s, ExtensionNodeEventArgs args) + { + IRobustConnector connector = (IRobustConnector)args.ExtensionObject; + Addin a = Registry.GetAddin(args.ExtensionNode.Addin.Id); + + if(a == null) + { + Registry.Rebuild(null); + a = Registry.GetAddin(args.ExtensionNode.Addin.Id); + } + + switch(args.Change) + { + case ExtensionChange.Add: + if (a.AddinFile.Contains(Registry.DefaultAddinsFolder)) + { + m_log.InfoFormat("[SERVER UTILS]: Adding {0} from registry", a.Name); + connector.PluginPath = System.IO.Path.Combine(Registry.DefaultAddinsFolder,a.Name.Replace(',', '.')); } + else + { + m_log.InfoFormat("[SERVER UTILS]: Adding {0} from ./bin", a.Name); + connector.PluginPath = a.AddinFile; + } + LoadPlugin(connector); + break; + case ExtensionChange.Remove: + m_log.InfoFormat("[SERVER UTILS]: Removing {0}", a.Name); + UnloadPlugin(connector); + break; + } + } + + private void LoadPlugin(IRobustConnector connector) + { + IHttpServer server = null; + uint port = connector.Configure(Config); + + if(connector.Enabled) + { + server = GetServer(connector, port); + connector.Initialize(server); + } + else + { + m_log.InfoFormat("[SERVER UTILS]: {0} Disabled.", connector.ConfigName); + } + } + + private void UnloadPlugin(IRobustConnector connector) + { + m_log.InfoFormat("[SERVER UTILS]: Unloading {0}", connector.ConfigName); + + connector.Unload(); + } + + private IHttpServer GetServer(IRobustConnector connector, uint port) + { + IHttpServer server; + + if(port != 0) + server = MainServer.GetHttpServer(port); + else + server = MainServer.Instance; + + return server; + } + } + + public static class ServerUtils + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static byte[] SerializeResult(XmlSerializer xs, object data) + { + using (MemoryStream ms = new MemoryStream()) + using (XmlTextWriter xw = new XmlTextWriter(ms, Util.UTF8)) + { + xw.Formatting = Formatting.Indented; + xs.Serialize(xw, data); + xw.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + byte[] ret = ms.GetBuffer(); + Array.Resize(ref ret, (int)ms.Length); + + return ret; + } + } + + /// + /// Load a plugin from a dll with the given class or interface + /// + /// + /// The arguments which control which constructor is invoked on the plugin + /// + public static T LoadPlugin (string dllName, Object[] args) where T:class + { + // This is good to debug configuration problems + //if (dllName == string.Empty) + // Util.PrintCallStack(); + + string className = String.Empty; + + // The path for a dynamic plugin will contain ":" on Windows + string[] parts = dllName.Split (new char[] {':'}); + + if (parts [0].Length > 1) + { + dllName = parts [0]; + if (parts.Length > 1) + className = parts[1]; + } + else + { + // This is Windows - we must replace the ":" in the path + dllName = String.Format ("{0}:{1}", parts [0], parts [1]); + if (parts.Length > 2) + className = parts[2]; + } + + return LoadPlugin(dllName, className, args); + } + + /// + /// Load a plugin from a dll with the given class or interface + /// + /// + /// + /// The arguments which control which constructor is invoked on the plugin + /// + public static T LoadPlugin(string dllName, string className, Object[] args) where T:class + { + string interfaceName = typeof(T).ToString(); + + try + { + Assembly pluginAssembly = Assembly.LoadFrom(dllName); + + foreach (Type pluginType in pluginAssembly.GetTypes()) + { + if (pluginType.IsPublic) + { + if (className != String.Empty + && pluginType.ToString() != pluginType.Namespace + "." + className) + continue; + + Type typeInterface = pluginType.GetInterface(interfaceName); + + if (typeInterface != null) + { + T plug = null; + try + { + plug = (T)Activator.CreateInstance(pluginType, + args); + } + catch (Exception e) + { + if (!(e is System.MissingMethodException)) + { + m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin {0} from {1}. Exception: {2}", + interfaceName, + dllName, + e.InnerException == null ? e.Message : e.InnerException.Message), + e); + } + m_log.ErrorFormat("[SERVER UTILS]: Error loading plugin {0}: {1} args.Length {2}", dllName, e.Message, args.Length); + return null; + } + + return plug; + } + } + } + + return null; + } + catch (ReflectionTypeLoadException rtle) + { + m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}:\n{1}", dllName, + String.Join("\n", Array.ConvertAll(rtle.LoaderExceptions, e => e.ToString()))), + rtle); + return null; + } + catch (Exception e) + { + m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}", dllName), e); + return null; + } + } + + public static Dictionary ParseQueryString(string query) + { + Dictionary result = new Dictionary(); + string[] terms = query.Split(new char[] {'&'}); + + if (terms.Length == 0) + return result; + + foreach (string t in terms) + { + string[] elems = t.Split(new char[] {'='}); + if (elems.Length == 0) + continue; + + string name = System.Web.HttpUtility.UrlDecode(elems[0]); + string value = String.Empty; + + if (elems.Length > 1) + value = System.Web.HttpUtility.UrlDecode(elems[1]); + + if (name.EndsWith("[]")) + { + string cleanName = name.Substring(0, name.Length - 2); + if (result.ContainsKey(cleanName)) + { + if (!(result[cleanName] is List)) + continue; + + List l = (List)result[cleanName]; + + l.Add(value); + } + else + { + List newList = new List(); + + newList.Add(value); + + result[cleanName] = newList; + } + } + else + { + if (!result.ContainsKey(name)) + result[name] = value; + } + } + + return result; + } + + public static string BuildQueryString(Dictionary data) + { + string qstring = String.Empty; + + string part; + + foreach (KeyValuePair kvp in data) + { + if (kvp.Value is List) + { + List l = (List)kvp.Value; + + foreach (string s in l) + { + part = System.Web.HttpUtility.UrlEncode(kvp.Key) + + "[]=" + System.Web.HttpUtility.UrlEncode(s); + + if (qstring != String.Empty) + qstring += "&"; + + qstring += part; + } + } + else + { + if (kvp.Value.ToString() != String.Empty) + { + part = System.Web.HttpUtility.UrlEncode(kvp.Key) + + "=" + System.Web.HttpUtility.UrlEncode(kvp.Value.ToString()); + } + else + { + part = System.Web.HttpUtility.UrlEncode(kvp.Key); + } + + if (qstring != String.Empty) + qstring += "&"; + + qstring += part; + } + } + + return qstring; + } + + public static string BuildXmlResponse(Dictionary data) + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "ServerResponse", + ""); + + doc.AppendChild(rootElement); + + BuildXmlData(rootElement, data); + + return doc.InnerXml; + } + + private static void BuildXmlData(XmlElement parent, Dictionary data) + { + foreach (KeyValuePair kvp in data) + { + if (kvp.Value == null) + continue; + + XmlElement elem = parent.OwnerDocument.CreateElement("", + XmlConvert.EncodeLocalName(kvp.Key), ""); + + if (kvp.Value is Dictionary) + { + XmlAttribute type = parent.OwnerDocument.CreateAttribute("", + "type", ""); + type.Value = "List"; + + elem.Attributes.Append(type); + + BuildXmlData(elem, (Dictionary)kvp.Value); + } + else + { + elem.AppendChild(parent.OwnerDocument.CreateTextNode( + kvp.Value.ToString())); + } + + parent.AppendChild(elem); + } + } + + public static Dictionary ParseXmlResponse(string data) + { + //m_log.DebugFormat("[XXX]: received xml string: {0}", data); + + Dictionary ret = new Dictionary(); + + XmlDocument doc = new XmlDocument(); + + doc.LoadXml(data); + + XmlNodeList rootL = doc.GetElementsByTagName("ServerResponse"); + + if (rootL.Count != 1) + return ret; + + XmlNode rootNode = rootL[0]; + + ret = ParseElement(rootNode); + + return ret; + } + + private static Dictionary ParseElement(XmlNode element) + { + Dictionary ret = new Dictionary(); + + XmlNodeList partL = element.ChildNodes; + + foreach (XmlNode part in partL) + { + XmlNode type = part.Attributes.GetNamedItem("type"); + if (type == null || type.Value != "List") + { + ret[XmlConvert.DecodeName(part.Name)] = part.InnerText; + } + else + { + ret[XmlConvert.DecodeName(part.Name)] = ParseElement(part); + } + } + + return ret; + } + + public static IConfig GetConfig(string configFile, string configName) + { + IConfig config; + + if (File.Exists(configFile)) + { + IConfigSource configsource = new IniConfigSource(configFile); + config = configsource.Configs[configName]; + } + else + config = null; + + return config; + } + + public static IConfigSource LoadInitialConfig(string url) + { + IConfigSource source = new XmlConfigSource(); + m_log.InfoFormat("[SERVER UTILS]: {0} is a http:// URI, fetching ...", url); + + // The ini file path is a http URI + // Try to read it + try + { + XmlReader r = XmlReader.Create(url); + IConfigSource cs = new XmlConfigSource(r); + source.Merge(cs); + } + catch (Exception e) + { + m_log.FatalFormat("[SERVER UTILS]: Exception reading config from URI {0}\n" + e.ToString(), url); + Environment.Exit(1); + } + + return source; + } + } +} diff --git a/OpenSim/Server/Base/ServicesServerBase.cs b/OpenSim/Server/Base/ServicesServerBase.cs new file mode 100644 index 0000000000..1f2c54d500 --- /dev/null +++ b/OpenSim/Server/Base/ServicesServerBase.cs @@ -0,0 +1,243 @@ +/* + * 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.Threading; +using System.Text; +using System.Xml; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; +using OpenSim.Framework.Servers; +using log4net; +using log4net.Config; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using Nini.Config; + +namespace OpenSim.Server.Base +{ + public class ServicesServerBase : ServerBase + { + // Logger + // + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Command line args + // + protected string[] m_Arguments; + + public string ConfigDirectory + { + get; + private set; + } + + // Run flag + // + private bool m_Running = true; + + // Handle all the automagical stuff + // + public ServicesServerBase(string prompt, string[] args) : base() + { + // Save raw arguments + m_Arguments = args; + + // Read command line + ArgvConfigSource argvConfig = new ArgvConfigSource(args); + + argvConfig.AddSwitch("Startup", "console", "c"); + argvConfig.AddSwitch("Startup", "logfile", "l"); + argvConfig.AddSwitch("Startup", "inifile", "i"); + argvConfig.AddSwitch("Startup", "prompt", "p"); + argvConfig.AddSwitch("Startup", "logconfig", "g"); + + // Automagically create the ini file name + string fileName = ""; + if (Assembly.GetEntryAssembly() != null) + fileName = Path.GetFileNameWithoutExtension(Assembly.GetEntryAssembly().Location); + string iniFile = fileName + ".ini"; + string logConfig = null; + + IConfig startupConfig = argvConfig.Configs["Startup"]; + if (startupConfig != null) + { + // Check if a file name was given on the command line + iniFile = startupConfig.GetString("inifile", iniFile); + + // Check if a prompt was given on the command line + prompt = startupConfig.GetString("prompt", prompt); + + // Check for a Log4Net config file on the command line + logConfig =startupConfig.GetString("logconfig", logConfig); + } + + // Find out of the file name is a URI and remote load it if possible. + // Load it as a local file otherwise. + Uri configUri; + + try + { + if (Uri.TryCreate(iniFile, UriKind.Absolute, out configUri) && + configUri.Scheme == Uri.UriSchemeHttp) + { + XmlReader r = XmlReader.Create(iniFile); + Config = new XmlConfigSource(r); + } + else + { + Config = new IniConfigSource(iniFile); + } + } + catch (Exception e) + { + System.Console.WriteLine("Error reading from config source. {0}", e.Message); + Environment.Exit(1); + } + + // Merge OpSys env vars + m_log.Info("[CONFIG]: Loading environment variables for Config"); + Util.MergeEnvironmentToConfig(Config); + + // Merge the configuration from the command line into the loaded file + Config.Merge(argvConfig); + + Config.ReplaceKeyValues(); + + // Refresh the startupConfig post merge + if (Config.Configs["Startup"] != null) + { + startupConfig = Config.Configs["Startup"]; + } + + ConfigDirectory = startupConfig.GetString("ConfigDirectory", "."); + + prompt = startupConfig.GetString("Prompt", prompt); + + // Allow derived classes to load config before the console is opened. + ReadConfig(); + + // Create main console + string consoleType = "local"; + if (startupConfig != null) + consoleType = startupConfig.GetString("console", consoleType); + + if (consoleType == "basic") + { + MainConsole.Instance = new CommandConsole(prompt); + } + else if (consoleType == "rest") + { + MainConsole.Instance = new RemoteConsole(prompt); + ((RemoteConsole)MainConsole.Instance).ReadConfig(Config); + } + else if (consoleType == "mock") + { + MainConsole.Instance = new MockConsole(); + } + else if (consoleType == "local") + { + MainConsole.Instance = new LocalConsole(prompt, startupConfig); + } + + m_console = MainConsole.Instance; + + if (logConfig != null) + { + FileInfo cfg = new FileInfo(logConfig); + XmlConfigurator.Configure(cfg); + } + else + { + XmlConfigurator.Configure(); + } + + LogEnvironmentInformation(); + RegisterCommonAppenders(startupConfig); + + if (startupConfig.GetString("PIDFile", String.Empty) != String.Empty) + { + CreatePIDFile(startupConfig.GetString("PIDFile")); + } + + RegisterCommonCommands(); + RegisterCommonComponents(Config); + + // Allow derived classes to perform initialization that + // needs to be done after the console has opened + Initialise(); + } + + public bool Running + { + get { return m_Running; } + } + + public virtual int Run() + { + Watchdog.Enabled = true; + MemoryWatchdog.Enabled = true; + + while (m_Running) + { + try + { + MainConsole.Instance.Prompt(); + } + catch (Exception e) + { + m_log.ErrorFormat("Command error: {0}", e); + } + } + + RemovePIDFile(); + + return 0; + } + + protected override void ShutdownSpecific() + { + m_Running = false; + m_log.Info("[CONSOLE] Quitting"); + + base.ShutdownSpecific(); + } + + protected virtual void ReadConfig() + { + } + + protected virtual void Initialise() + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServerPostHandler.cs b/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServerPostHandler.cs new file mode 100644 index 0000000000..713b75551c --- /dev/null +++ b/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServerPostHandler.cs @@ -0,0 +1,206 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.AgentPreferences +{ + public class AgentPreferencesServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAgentPreferencesService m_AgentPreferencesService; + + public AgentPreferencesServerPostHandler(IAgentPreferencesService service, IServiceAuth auth) : + base("POST", "/agentprefs", auth) + { + m_AgentPreferencesService = 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "getagentprefs": + return GetAgentPrefs(request); + case "setagentprefs": + return SetAgentPrefs(request); + case "getagentlang": + return GetAgentLang(request); + } + m_log.DebugFormat("[AGENT PREFERENCES HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[AGENT PREFERENCES HANDLER]: Exception {0}", e); + } + + return FailureResult(); + } + + byte[] GetAgentPrefs(Dictionary request) + { + if (!request.ContainsKey("UserID")) + return FailureResult(); + + UUID userID; + if (!UUID.TryParse(request["UserID"].ToString(), out userID)) + return FailureResult(); + AgentPrefs prefs = m_AgentPreferencesService.GetAgentPreferences(userID); + Dictionary result = new Dictionary(); + result = prefs.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] SetAgentPrefs(Dictionary request) + { + if (!request.ContainsKey("PrincipalID") || !request.ContainsKey("AccessPrefs") || !request.ContainsKey("HoverHeight") + || !request.ContainsKey("Language") || !request.ContainsKey("LanguageIsPublic") || !request.ContainsKey("PermEveryone") + || !request.ContainsKey("PermGroup") || !request.ContainsKey("PermNextOwner")) + { + return FailureResult(); + } + + UUID userID; + if (!UUID.TryParse(request["PrincipalID"].ToString(), out userID)) + return FailureResult(); + + AgentPrefs data = new AgentPrefs(userID); + data.AccessPrefs = request["AccessPrefs"].ToString(); + data.HoverHeight = double.Parse(request["HoverHeight"].ToString()); + data.Language = request["Language"].ToString(); + data.LanguageIsPublic = bool.Parse(request["LanguageIsPublic"].ToString()); + data.PermEveryone = int.Parse(request["PermEveryone"].ToString()); + data.PermGroup = int.Parse(request["PermGroup"].ToString()); + data.PermNextOwner = int.Parse(request["PermNextOwner"].ToString()); + + return m_AgentPreferencesService.StoreAgentPreferences(data) ? SuccessResult() : FailureResult(); + } + + byte[] GetAgentLang(Dictionary request) + { + if (!request.ContainsKey("UserID")) + return FailureResult(); + UUID userID; + if (!UUID.TryParse(request["UserID"].ToString(), out userID)) + return FailureResult(); + + string lang = "en-us"; + AgentPrefs prefs = m_AgentPreferencesService.GetAgentPreferences(userID); + if (prefs != null) + { + if (prefs.LanguageIsPublic) + lang = prefs.Language; + } + Dictionary result = new Dictionary(); + result["Language"] = lang; + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + } +} diff --git a/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServiceConnector.cs b/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServiceConnector.cs new file mode 100644 index 0000000000..a581ea2d80 --- /dev/null +++ b/OpenSim/Server/Handlers/AgentPreferences/AgentPreferencesServiceConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.AgentPreferences +{ + public class AgentPreferencesServiceConnector : ServiceConnector + { + private IAgentPreferencesService m_AgentPreferencesService; + private string m_ConfigName = "AgentPreferencesService"; + + public AgentPreferencesServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string service = serverConfig.GetString("LocalServiceModule", String.Empty); + + if (String.IsNullOrWhiteSpace(service)) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_AgentPreferencesService = ServerUtils.LoadPlugin(service, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); ; + + server.AddStreamHandler(new AgentPreferencesServerPostHandler(m_AgentPreferencesService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs new file mode 100644 index 0000000000..ab81dd69d3 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs @@ -0,0 +1,221 @@ +/* + * 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 Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Asset +{ + public class AssetServiceConnector : ServiceConnector + { + private IAssetService m_AssetService; + private string m_ConfigName = "AssetService"; + + public AssetServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (assetService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config, m_ConfigName }; + m_AssetService = + ServerUtils.LoadPlugin(assetService, args); + + if (m_AssetService == null) + throw new Exception(String.Format("Failed to load AssetService from {0}; config is {1}", assetService, m_ConfigName)); + + bool allowDelete = serverConfig.GetBoolean("AllowRemoteDelete", false); + bool allowDeleteAllTypes = serverConfig.GetBoolean("AllowRemoteDeleteAllTypes", false); + + string redirectURL = serverConfig.GetString("RedirectURL", string.Empty); + + AllowedRemoteDeleteTypes allowedRemoteDeleteTypes; + + if (!allowDelete) + { + allowedRemoteDeleteTypes = AllowedRemoteDeleteTypes.None; + } + else + { + if (allowDeleteAllTypes) + allowedRemoteDeleteTypes = AllowedRemoteDeleteTypes.All; + else + allowedRemoteDeleteTypes = AllowedRemoteDeleteTypes.MapTile; + } + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new AssetServerGetHandler(m_AssetService, auth, redirectURL)); + server.AddStreamHandler(new AssetServerPostHandler(m_AssetService, auth)); + server.AddStreamHandler(new AssetServerDeleteHandler(m_AssetService, allowedRemoteDeleteTypes, auth)); + server.AddStreamHandler(new AssetsExistHandler(m_AssetService)); + + MainConsole.Instance.Commands.AddCommand("Assets", false, + "show asset", + "show asset ", + "Show asset information", + HandleShowAsset); + + MainConsole.Instance.Commands.AddCommand("Assets", false, + "delete asset", + "delete asset ", + "Delete asset from database", + HandleDeleteAsset); + + MainConsole.Instance.Commands.AddCommand("Assets", false, + "dump asset", + "dump asset ", + "Dump asset to a file", + "The filename is the same as the ID given.", + HandleDumpAsset); + } + + void HandleDeleteAsset(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: delete asset "); + return; + } + + AssetBase asset = m_AssetService.Get(args[2]); + + if (asset == null || asset.Data.Length == 0) + { + MainConsole.Instance.OutputFormat("Could not find asset with ID {0}", args[2]); + return; + } + + if (!m_AssetService.Delete(asset.ID)) + MainConsole.Instance.OutputFormat("ERROR: Could not delete asset {0} {1}", asset.ID, asset.Name); + else + MainConsole.Instance.OutputFormat("Deleted asset {0} {1}", asset.ID, asset.Name); + } + + void HandleDumpAsset(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Usage is dump asset "); + return; + } + + UUID assetId; + string rawAssetId = args[2]; + + if (!UUID.TryParse(rawAssetId, out assetId)) + { + MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid ID format", rawAssetId); + return; + } + + AssetBase asset = m_AssetService.Get(assetId.ToString()); + if (asset == null) + { + MainConsole.Instance.OutputFormat("ERROR: No asset found with ID {0}", assetId); + return; + } + + string fileName = rawAssetId; + + if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, fileName)) + return; + + using (FileStream fs = new FileStream(fileName, FileMode.CreateNew)) + { + using (BinaryWriter bw = new BinaryWriter(fs)) + { + bw.Write(asset.Data); + } + } + + MainConsole.Instance.OutputFormat("Asset dumped to file {0}", fileName); + } + + void HandleShowAsset(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: show asset "); + return; + } + + AssetBase asset = m_AssetService.Get(args[2]); + + if (asset == null || asset.Data.Length == 0) + { + MainConsole.Instance.Output("Asset not found"); + return; + } + + int i; + + MainConsole.Instance.OutputFormat("Name: {0}", asset.Name); + MainConsole.Instance.OutputFormat("Description: {0}", asset.Description); + MainConsole.Instance.OutputFormat("Type: {0} (type number = {1})", (AssetType)asset.Type, asset.Type); + MainConsole.Instance.OutputFormat("Content-type: {0}", asset.Metadata.ContentType); + MainConsole.Instance.OutputFormat("Size: {0} bytes", asset.Data.Length); + MainConsole.Instance.OutputFormat("Temporary: {0}", asset.Temporary ? "yes" : "no"); + MainConsole.Instance.OutputFormat("Flags: {0}", asset.Metadata.Flags); + + for (i = 0 ; i < 5 ; i++) + { + int off = i * 16; + if (asset.Data.Length <= off) + break; + int len = 16; + if (asset.Data.Length < off + len) + len = asset.Data.Length - off; + + byte[] line = new byte[len]; + Array.Copy(asset.Data, off, line, 0, len); + + string text = BitConverter.ToString(line); + MainConsole.Instance.Output(String.Format("{0:x4}: {1}", off, text)); + } + } + } +} diff --git a/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs b/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs new file mode 100644 index 0000000000..d85d4713f1 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/AssetServerDeleteHandler.cs @@ -0,0 +1,115 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.Asset +{ + /// + /// Remote deletes allowed. + /// + public enum AllowedRemoteDeleteTypes + { + None, + MapTile, + All + } + + public class AssetServerDeleteHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAssetService m_AssetService; + + /// + /// Asset types that can be deleted remotely. + /// + private AllowedRemoteDeleteTypes m_allowedTypes; + + public AssetServerDeleteHandler(IAssetService service, AllowedRemoteDeleteTypes allowedTypes) : + base("DELETE", "/assets") + { + m_AssetService = service; + m_allowedTypes = allowedTypes; + } + + public AssetServerDeleteHandler(IAssetService service, AllowedRemoteDeleteTypes allowedTypes, IServiceAuth auth) : + base("DELETE", "/assets", auth) + { + m_AssetService = service; + m_allowedTypes = allowedTypes; + } + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + bool result = false; + + string[] p = SplitParams(path); + + if (p.Length > 0) + { + if (m_allowedTypes != AllowedRemoteDeleteTypes.None) + { + string assetID = p[0]; + + AssetBase asset = m_AssetService.Get(assetID); + if (asset != null) + { + if (m_allowedTypes == AllowedRemoteDeleteTypes.All + || (int)(asset.Flags & AssetFlags.Maptile) != 0) + { + result = m_AssetService.Delete(assetID); + } + else + { + m_log.DebugFormat( + "[ASSET SERVER DELETE HANDLER]: Request to delete asset {0}, but type is {1} and allowed remote delete types are {2}", + assetID, (AssetFlags)asset.Flags, m_allowedTypes); + } + } + } + } + + XmlSerializer xs = new XmlSerializer(typeof(bool)); + return ServerUtils.SerializeResult(xs, result); + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs b/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs new file mode 100644 index 0000000000..91c5c54ca8 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/AssetServerGetHandler.cs @@ -0,0 +1,172 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.IO; +using System.Reflection; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.Asset +{ + public class AssetServerGetHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAssetService m_AssetService; + private string m_RedirectURL; + + public AssetServerGetHandler(IAssetService service) : + base("GET", "/assets") + { + m_AssetService = service; + } + + public AssetServerGetHandler(IAssetService service, IServiceAuth auth, string redirectURL) : + base("GET", "/assets", auth) + { + m_AssetService = service; + m_RedirectURL = redirectURL; + if (!m_RedirectURL.EndsWith("/")) + m_RedirectURL = m_RedirectURL.TrimEnd('/'); + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result = new byte[0]; + + string[] p = SplitParams(path); + + if (p.Length == 0) + return result; + + string id = string.Empty; + if (p.Length > 1) + { + id = p[0]; + string cmd = p[1]; + + if (cmd == "data") + { + result = m_AssetService.GetData(id); + if (result == null) + { + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + httpResponse.ContentType = "text/plain"; + result = new byte[0]; + } + else + { + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = "application/octet-stream"; + } + } + else if (cmd == "metadata") + { + AssetMetadata metadata = m_AssetService.GetMetadata(id); + + if (metadata != null) + { + XmlSerializer xs = + new XmlSerializer(typeof(AssetMetadata)); + result = ServerUtils.SerializeResult(xs, metadata); + + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = + SLUtil.SLAssetTypeToContentType(metadata.Type); + } + else + { + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + httpResponse.ContentType = "text/plain"; + result = new byte[0]; + } + } + else + { + // Unknown request + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + httpResponse.ContentType = "text/plain"; + result = new byte[0]; + } + } + else if (p.Length == 1) + { + // Get the entire asset (metadata + data) + + id = p[0]; + AssetBase asset = m_AssetService.Get(id); + + if (asset != null) + { + XmlSerializer xs = new XmlSerializer(typeof(AssetBase)); + result = ServerUtils.SerializeResult(xs, asset); + + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = + SLUtil.SLAssetTypeToContentType(asset.Type); + } + else + { + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + httpResponse.ContentType = "text/plain"; + result = new byte[0]; + } + } + else + { + // Unknown request + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + httpResponse.ContentType = "text/plain"; + result = new byte[0]; + } + + if (httpResponse.StatusCode == (int)HttpStatusCode.NotFound && !string.IsNullOrEmpty(m_RedirectURL) && !string.IsNullOrEmpty(id)) + { + httpResponse.StatusCode = (int)HttpStatusCode.Redirect; + string rurl = m_RedirectURL; + if (!path.StartsWith("/")) + rurl += "/"; + rurl += path; + httpResponse.AddHeader("Location", rurl); + m_log.DebugFormat("[ASSET GET HANDLER]: Asset not found, redirecting to {0} ({1})", rurl, httpResponse.StatusCode); + } + return result; + } + } +} diff --git a/OpenSim/Server/Handlers/Asset/AssetServerPostHandler.cs b/OpenSim/Server/Handlers/Asset/AssetServerPostHandler.cs new file mode 100644 index 0000000000..1c706a7208 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/AssetServerPostHandler.cs @@ -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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.Asset +{ + public class AssetServerPostHandler : BaseStreamHandler + { + // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAssetService m_AssetService; + + public AssetServerPostHandler(IAssetService service) : + base("POST", "/assets") + { + m_AssetService = service; + } + + public AssetServerPostHandler(IAssetService service, IServiceAuth auth) : + base("POST", "/assets", auth) + { + m_AssetService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + AssetBase asset; + XmlSerializer xs = new XmlSerializer(typeof(AssetBase)); + + try + { + asset = (AssetBase)xs.Deserialize(request); + } + catch (Exception) + { + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + return null; + } + + string[] p = SplitParams(path); + if (p.Length > 0) + { + string id = p[0]; + bool result = m_AssetService.UpdateContent(id, asset.Data); + + xs = new XmlSerializer(typeof(bool)); + return ServerUtils.SerializeResult(xs, result); + } + else + { + string id = m_AssetService.Store(asset); + + xs = new XmlSerializer(typeof(string)); + return ServerUtils.SerializeResult(xs, id); + } + } + } +} diff --git a/OpenSim/Server/Handlers/Asset/AssetsExistHandler.cs b/OpenSim/Server/Handlers/Asset/AssetsExistHandler.cs new file mode 100644 index 0000000000..32901b3b47 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/AssetsExistHandler.cs @@ -0,0 +1,87 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Asset +{ + public class AssetsExistHandler : BaseStreamHandler + { + //private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAssetService m_AssetService; + + public AssetsExistHandler(IAssetService service) : + base("POST", "/get_assets_exist") + { + m_AssetService = service; + } + + public AssetsExistHandler(IAssetService service, IServiceAuth auth) : + base("POST", "/get_assets_exist", auth) + { + m_AssetService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + XmlSerializer xs; + + string[] ids; + try + { + xs = new XmlSerializer(typeof(string[])); + ids = (string[])xs.Deserialize(request); + } + catch (Exception) + { + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + return null; + } + + bool[] exist = m_AssetService.AssetsExist(ids); + + xs = new XmlSerializer(typeof(bool[])); + return ServerUtils.SerializeResult(xs, exist); + } + } +} diff --git a/OpenSim/Server/Handlers/Asset/Tests/AssetServerPostHandlerTests.cs b/OpenSim/Server/Handlers/Asset/Tests/AssetServerPostHandlerTests.cs new file mode 100644 index 0000000000..faa6fb7ac4 --- /dev/null +++ b/OpenSim/Server/Handlers/Asset/Tests/AssetServerPostHandlerTests.cs @@ -0,0 +1,109 @@ +/* + * 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.Text; +using System.Xml; +using System.Xml.Serialization; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Server.Handlers.Asset; +using OpenSim.Services.AssetService; +using OpenSim.Services.Interfaces; +using OpenSim.Tests.Common; + +namespace OpenSim.Server.Handlers.Asset.Test +{ + [TestFixture] + public class AssetServerPostHandlerTests : OpenSimTestCase + { + [Test] + public void TestGoodAssetStoreRequest() + { + TestHelpers.InMethod(); + + UUID assetId = TestHelpers.ParseTail(0x1); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("AssetService"); + config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + + AssetService assetService = new AssetService(config); + + AssetServerPostHandler asph = new AssetServerPostHandler(assetService); + + AssetBase asset = AssetHelpers.CreateNotecardAsset(assetId, "Hello World"); + + MemoryStream buffer = new MemoryStream(); + + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(typeof(AssetBase)); + serializer.Serialize(writer, asset); + writer.Flush(); + } + + buffer.Position = 0; + asph.Handle(null, buffer, null, null); + + AssetBase retrievedAsset = assetService.Get(assetId.ToString()); + + Assert.That(retrievedAsset, Is.Not.Null); + } + + [Test] + public void TestBadXmlAssetStoreRequest() + { + TestHelpers.InMethod(); + + IConfigSource config = new IniConfigSource(); + config.AddConfig("AssetService"); + config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + + AssetService assetService = new AssetService(config); + + AssetServerPostHandler asph = new AssetServerPostHandler(assetService); + + MemoryStream buffer = new MemoryStream(); + byte[] badData = new byte[] { 0x48, 0x65, 0x6c, 0x6c, 0x6f }; + buffer.Write(badData, 0, badData.Length); + buffer.Position = 0; + + TestOSHttpResponse response = new TestOSHttpResponse(); + asph.Handle(null, buffer, null, response); + + Assert.That(response.StatusCode, Is.EqualTo((int)HttpStatusCode.BadRequest)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs b/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs new file mode 100644 index 0000000000..c9a8dce46f --- /dev/null +++ b/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs @@ -0,0 +1,67 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Authentication +{ + public class AuthenticationServiceConnector : ServiceConnector + { + private IAuthenticationService m_AuthenticationService; + private string m_ConfigName = "AuthenticationService"; + + public AuthenticationServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string authenticationService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (authenticationService == String.Empty) + throw new Exception("No AuthenticationService in config file"); + + Object[] args = new Object[] { config }; + m_AuthenticationService = ServerUtils.LoadPlugin(authenticationService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new AuthenticationServerPostHandler(m_AuthenticationService, serverConfig, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs b/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs new file mode 100644 index 0000000000..6ee98b34b2 --- /dev/null +++ b/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs @@ -0,0 +1,318 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Authentication +{ + public class AuthenticationServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAuthenticationService m_AuthenticationService; + + private bool m_AllowGetAuthInfo = false; + private bool m_AllowSetAuthInfo = false; + private bool m_AllowSetPassword = false; + + public AuthenticationServerPostHandler(IAuthenticationService service) : + this(service, null, null) {} + + public AuthenticationServerPostHandler(IAuthenticationService service, IConfig config, IServiceAuth auth) : + base("POST", "/auth", auth) + { + m_AuthenticationService = service; + + if (config != null) + { + m_AllowGetAuthInfo = config.GetBoolean("AllowGetAuthInfo", m_AllowGetAuthInfo); + m_AllowSetAuthInfo = config.GetBoolean("AllowSetAuthInfo", m_AllowSetAuthInfo); + m_AllowSetPassword = config.GetBoolean("AllowSetPassword", m_AllowSetPassword); + } + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { +// m_log.Error("[XXX]: Authenticating..."); + string[] p = SplitParams(path); + + if (p.Length > 0) + { + switch (p[0]) + { + case "plain": + StreamReader sr = new StreamReader(request); + string body = sr.ReadToEnd(); + sr.Close(); + + return DoPlainMethods(body); + case "crypt": + byte[] buffer = new byte[request.Length]; + long length = request.Length; + if (length > 16384) + length = 16384; + request.Read(buffer, 0, (int)length); + + return DoEncryptedMethods(buffer); + } + } + return new byte[0]; + } + + private byte[] DoPlainMethods(string body) + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + int lifetime = 30; + + if (request.ContainsKey("LIFETIME")) + { + lifetime = Convert.ToInt32(request["LIFETIME"].ToString()); + if (lifetime > 30) + lifetime = 30; + } + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + if (!request.ContainsKey("PRINCIPAL")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + UUID principalID; + string token; + + if (!UUID.TryParse(request["PRINCIPAL"].ToString(), out principalID)) + return FailureResult(); + + switch (method) + { + case "authenticate": + if (!request.ContainsKey("PASSWORD")) + return FailureResult(); + + token = m_AuthenticationService.Authenticate(principalID, request["PASSWORD"].ToString(), lifetime); + + if (token != String.Empty) + return SuccessResult(token); + return FailureResult(); + + case "setpassword": + if (!m_AllowSetPassword) + return FailureResult(); + + if (!request.ContainsKey("PASSWORD")) + return FailureResult(); + + if (m_AuthenticationService.SetPassword(principalID, request["PASSWORD"].ToString())) + return SuccessResult(); + else + return FailureResult(); + + case "verify": + if (!request.ContainsKey("TOKEN")) + return FailureResult(); + + if (m_AuthenticationService.Verify(principalID, request["TOKEN"].ToString(), lifetime)) + return SuccessResult(); + + return FailureResult(); + + case "release": + if (!request.ContainsKey("TOKEN")) + return FailureResult(); + + if (m_AuthenticationService.Release(principalID, request["TOKEN"].ToString())) + return SuccessResult(); + + return FailureResult(); + + case "getauthinfo": + if (m_AllowGetAuthInfo) + return GetAuthInfo(principalID); + + break; + + case "setauthinfo": + if (m_AllowSetAuthInfo) + return SetAuthInfo(principalID, request); + + break; + } + + return FailureResult(); + } + + private byte[] DoEncryptedMethods(byte[] ciphertext) + { + return new byte[0]; + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + byte[] GetAuthInfo(UUID principalID) + { + AuthInfo info = m_AuthenticationService.GetAuthInfo(principalID); + + if (info != null) + { + Dictionary result = new Dictionary(); + result["result"] = info.ToKeyValuePairs(); + + return ResultToBytes(result); + } + else + { + return FailureResult(); + } + } + + byte[] SetAuthInfo(UUID principalID, Dictionary request) + { + AuthInfo existingInfo = m_AuthenticationService.GetAuthInfo(principalID); + + if (existingInfo == null) + return FailureResult(); + + if (request.ContainsKey("AccountType")) + existingInfo.AccountType = request["AccountType"].ToString(); + + if (request.ContainsKey("PasswordHash")) + existingInfo.PasswordHash = request["PasswordHash"].ToString(); + + if (request.ContainsKey("PasswordSalt")) + existingInfo.PasswordSalt = request["PasswordSalt"].ToString(); + + if (request.ContainsKey("WebLoginKey")) + existingInfo.WebLoginKey = request["WebLoginKey"].ToString(); + + if (!m_AuthenticationService.SetAuthInfo(existingInfo)) + { + m_log.ErrorFormat( + "[AUTHENTICATION SERVER POST HANDLER]: Authentication info store failed for account {0} {1} {2}", + existingInfo.PrincipalID); + + return FailureResult(); + } + + return SuccessResult(); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] SuccessResult(string token) + { + 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("Success")); + + rootElement.AppendChild(result); + + XmlElement t = doc.CreateElement("", "Token", ""); + t.AppendChild(doc.CreateTextNode(token)); + + rootElement.AppendChild(t); + + return Util.DocToBytes(doc); + } + + private byte[] ResultToBytes(Dictionary result) + { + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + } +} diff --git a/OpenSim/Server/Handlers/Authentication/OpenIdServerConnector.cs b/OpenSim/Server/Handlers/Authentication/OpenIdServerConnector.cs new file mode 100644 index 0000000000..6464399f93 --- /dev/null +++ b/OpenSim/Server/Handlers/Authentication/OpenIdServerConnector.cs @@ -0,0 +1,77 @@ +/* + * 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 log4net; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Authentication +{ + public class OpenIdServerConnector : ServiceConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private IAuthenticationService m_AuthenticationService; + private IUserAccountService m_UserAccountService; + private string m_ConfigName = "OpenIdService"; + + public OpenIdServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string authService = serverConfig.GetString("AuthenticationServiceModule", + String.Empty); + string userService = serverConfig.GetString("UserAccountServiceModule", + String.Empty); + + if (authService == String.Empty || userService == String.Empty) + throw new Exception("No AuthenticationServiceModule or no UserAccountServiceModule in config file for OpenId authentication"); + + Object[] args = new Object[] { config }; + m_AuthenticationService = ServerUtils.LoadPlugin(authService, args); + m_UserAccountService = ServerUtils.LoadPlugin(userService, args); + + // Handler for OpenID user identity pages + server.AddStreamHandler(new OpenIdStreamHandler("GET", "/users/", m_UserAccountService, m_AuthenticationService)); + // Handlers for the OpenID endpoint server + server.AddStreamHandler(new OpenIdStreamHandler("POST", "/openid/server/", m_UserAccountService, m_AuthenticationService)); + server.AddStreamHandler(new OpenIdStreamHandler("GET", "/openid/server/", m_UserAccountService, m_AuthenticationService)); + + m_log.Info("[OPENID]: OpenId service enabled"); + } + } +} diff --git a/OpenSim/Server/Handlers/Authentication/OpenIdServerHandler.cs b/OpenSim/Server/Handlers/Authentication/OpenIdServerHandler.cs new file mode 100644 index 0000000000..b201dc7510 --- /dev/null +++ b/OpenSim/Server/Handlers/Authentication/OpenIdServerHandler.cs @@ -0,0 +1,339 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Web; +using DotNetOpenId; +using DotNetOpenId.Provider; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using Nini.Config; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Authentication +{ + /// + /// Temporary, in-memory store for OpenID associations + /// + public class ProviderMemoryStore : IAssociationStore + { + private class AssociationItem + { + public AssociationRelyingPartyType DistinguishingFactor; + public string Handle; + public DateTime Expires; + public byte[] PrivateData; + } + + Dictionary m_store = new Dictionary(); + SortedList m_sortedStore = new SortedList(); + object m_syncRoot = new object(); + + #region IAssociationStore Members + + public void StoreAssociation(AssociationRelyingPartyType distinguishingFactor, Association assoc) + { + AssociationItem item = new AssociationItem(); + item.DistinguishingFactor = distinguishingFactor; + item.Handle = assoc.Handle; + item.Expires = assoc.Expires.ToLocalTime(); + item.PrivateData = assoc.SerializePrivateData(); + + lock (m_syncRoot) + { + m_store[item.Handle] = item; + m_sortedStore[item.Expires] = item; + } + } + + public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor) + { + lock (m_syncRoot) + { + if (m_sortedStore.Count > 0) + { + AssociationItem item = m_sortedStore.Values[m_sortedStore.Count - 1]; + return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); + } + else + { + return null; + } + } + } + + public Association GetAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) + { + AssociationItem item; + bool success = false; + lock (m_syncRoot) + success = m_store.TryGetValue(handle, out item); + + if (success) + return Association.Deserialize(item.Handle, item.Expires.ToUniversalTime(), item.PrivateData); + else + return null; + } + + public bool RemoveAssociation(AssociationRelyingPartyType distinguishingFactor, string handle) + { + lock (m_syncRoot) + { + for (int i = 0; i < m_sortedStore.Values.Count; i++) + { + AssociationItem item = m_sortedStore.Values[i]; + if (item.Handle == handle) + { + m_sortedStore.RemoveAt(i); + break; + } + } + + return m_store.Remove(handle); + } + } + + public void ClearExpiredAssociations() + { + lock (m_syncRoot) + { + List itemsCopy = new List(m_sortedStore.Values); + DateTime now = DateTime.Now; + + for (int i = 0; i < itemsCopy.Count; i++) + { + AssociationItem item = itemsCopy[i]; + + if (item.Expires <= now) + { + m_sortedStore.RemoveAt(i); + m_store.Remove(item.Handle); + } + } + } + } + + #endregion + } + + public class OpenIdStreamHandler : BaseOutputStreamHandler, IStreamHandler + { + #region HTML + + /// Login form used to authenticate OpenID requests + const string LOGIN_PAGE = +@" +OpenSim OpenID Login + +

OpenSim Login

+
+ + + + +
+ +"; + + /// Page shown for a valid OpenID identity + const string OPENID_PAGE = +@" + +{2} {3} + + +OpenID identifier for {2} {3} + +"; + + /// Page shown for an invalid OpenID identity + const string INVALID_OPENID_PAGE = +@"Identity not found +Invalid OpenID identity"; + + /// Page shown if the OpenID endpoint is requested directly + const string ENDPOINT_PAGE = +@"OpenID Endpoint +This is an OpenID server endpoint, not a human-readable resource. +For more information, see http://openid.net/. +"; + + #endregion HTML + + IAuthenticationService m_authenticationService; + IUserAccountService m_userAccountService; + ProviderMemoryStore m_openidStore = new ProviderMemoryStore(); + + public override string ContentType { get { return "text/html"; } } + + /// + /// Constructor + /// + public OpenIdStreamHandler( + string httpMethod, string path, IUserAccountService userService, IAuthenticationService authService) + : base(httpMethod, path, "OpenId", "OpenID stream handler") + { + m_authenticationService = authService; + m_userAccountService = userService; + } + + /// + /// Handles all GET and POST requests for OpenID identifier pages and endpoint + /// server communication + /// + protected override void ProcessRequest( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + Uri providerEndpoint = new Uri(String.Format("{0}://{1}{2}", httpRequest.Url.Scheme, httpRequest.Url.Authority, httpRequest.Url.AbsolutePath)); + + // Defult to returning HTML content + httpResponse.ContentType = ContentType; + + try + { + NameValueCollection postQuery = HttpUtility.ParseQueryString(new StreamReader(httpRequest.InputStream).ReadToEnd()); + NameValueCollection getQuery = HttpUtility.ParseQueryString(httpRequest.Url.Query); + NameValueCollection openIdQuery = (postQuery.GetValues("openid.mode") != null ? postQuery : getQuery); + + OpenIdProvider provider = new OpenIdProvider(m_openidStore, providerEndpoint, httpRequest.Url, openIdQuery); + + if (provider.Request != null) + { + if (!provider.Request.IsResponseReady && provider.Request is IAuthenticationRequest) + { + IAuthenticationRequest authRequest = (IAuthenticationRequest)provider.Request; + string[] passwordValues = postQuery.GetValues("pass"); + + UserAccount account; + if (TryGetAccount(new Uri(authRequest.ClaimedIdentifier.ToString()), out account)) + { + // Check for form POST data + if (passwordValues != null && passwordValues.Length == 1) + { + if (account != null && + (m_authenticationService.Authenticate(account.PrincipalID,Util.Md5Hash(passwordValues[0]), 30) != string.Empty)) + authRequest.IsAuthenticated = true; + else + authRequest.IsAuthenticated = false; + } + else + { + // Authentication was requested, send the client a login form + using (StreamWriter writer = new StreamWriter(response)) + writer.Write(String.Format(LOGIN_PAGE, account.FirstName, account.LastName)); + return; + } + } + else + { + // Cannot find an avatar matching the claimed identifier + authRequest.IsAuthenticated = false; + } + } + + // Add OpenID headers to the response + foreach (string key in provider.Request.Response.Headers.Keys) + httpResponse.AddHeader(key, provider.Request.Response.Headers[key]); + + string[] contentTypeValues = provider.Request.Response.Headers.GetValues("Content-Type"); + if (contentTypeValues != null && contentTypeValues.Length == 1) + httpResponse.ContentType = contentTypeValues[0]; + + // Set the response code and document body based on the OpenID result + httpResponse.StatusCode = (int)provider.Request.Response.Code; + response.Write(provider.Request.Response.Body, 0, provider.Request.Response.Body.Length); + response.Close(); + } + else if (httpRequest.Url.AbsolutePath.Contains("/openid/server")) + { + // Standard HTTP GET was made on the OpenID endpoint, send the client the default error page + using (StreamWriter writer = new StreamWriter(response)) + writer.Write(ENDPOINT_PAGE); + } + else + { + // Try and lookup this avatar + UserAccount account; + if (TryGetAccount(httpRequest.Url, out account)) + { + using (StreamWriter writer = new StreamWriter(response)) + { + // TODO: Print out a full profile page for this avatar + writer.Write(String.Format(OPENID_PAGE, httpRequest.Url.Scheme, + httpRequest.Url.Authority, account.FirstName, account.LastName)); + } + } + else + { + // Couldn't parse an avatar name, or couldn't find the avatar in the user server + using (StreamWriter writer = new StreamWriter(response)) + writer.Write(INVALID_OPENID_PAGE); + } + } + } + catch (Exception ex) + { + httpResponse.StatusCode = (int)HttpStatusCode.InternalServerError; + using (StreamWriter writer = new StreamWriter(response)) + writer.Write(ex.Message); + } + } + + /// + /// Parse a URL with a relative path of the form /users/First_Last and try to + /// retrieve the profile matching that avatar name + /// + /// URL to parse for an avatar name + /// Profile data for the avatar + /// True if the parse and lookup were successful, otherwise false + bool TryGetAccount(Uri requestUrl, out UserAccount account) + { + if (requestUrl.Segments.Length == 3 && requestUrl.Segments[1] == "users/") + { + // Parse the avatar name from the path + string username = requestUrl.Segments[requestUrl.Segments.Length - 1]; + string[] name = username.Split('_'); + + if (name.Length == 2) + { + account = m_userAccountService.GetUserAccount(UUID.Zero, name[0], name[1]); + return (account != null); + } + } + + account = null; + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Authorization/AuthorizationServerConnector.cs b/OpenSim/Server/Handlers/Authorization/AuthorizationServerConnector.cs new file mode 100644 index 0000000000..20fd0f78fd --- /dev/null +++ b/OpenSim/Server/Handlers/Authorization/AuthorizationServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Authorization +{ + public class AuthorizationServerConnector : ServiceConnector + { + private IAuthorizationService m_AuthorizationService; + private string m_ConfigName = "AuthorizationService"; + + public AuthorizationServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string authorizationService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (authorizationService == String.Empty) + throw new Exception("No AuthorizationService in config file"); + + Object[] args = new Object[] { config }; + m_AuthorizationService = + ServerUtils.LoadPlugin(authorizationService, args); + + server.AddStreamHandler(new AuthorizationServerPostHandler(m_AuthorizationService)); + } + } +} diff --git a/OpenSim/Server/Handlers/Authorization/AuthorizationServerPostHandler.cs b/OpenSim/Server/Handlers/Authorization/AuthorizationServerPostHandler.cs new file mode 100644 index 0000000000..c9b4e9b0d2 --- /dev/null +++ b/OpenSim/Server/Handlers/Authorization/AuthorizationServerPostHandler.cs @@ -0,0 +1,73 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.Authorization +{ + public class AuthorizationServerPostHandler : BaseStreamHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAuthorizationService m_AuthorizationService; + + public AuthorizationServerPostHandler(IAuthorizationService service) : + base("POST", "/authorization") + { + m_AuthorizationService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + XmlSerializer xs = new XmlSerializer(typeof (AuthorizationRequest)); + AuthorizationRequest Authorization = (AuthorizationRequest) xs.Deserialize(request); + + string message = String.Empty; + bool authorized = m_AuthorizationService.IsAuthorizedForRegion(Authorization.ID, Authorization.FirstName, Authorization.SurName, Authorization.RegionID, out message); + + AuthorizationResponse result = new AuthorizationResponse(authorized, Authorization.ID + " has been authorized"); + + xs = new XmlSerializer(typeof(AuthorizationResponse)); + return ServerUtils.SerializeResult(xs, result); + + } + } +} diff --git a/OpenSim/Server/Handlers/Avatar/AvatarServerConnector.cs b/OpenSim/Server/Handlers/Avatar/AvatarServerConnector.cs new file mode 100644 index 0000000000..1831e84d59 --- /dev/null +++ b/OpenSim/Server/Handlers/Avatar/AvatarServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Avatar +{ + public class AvatarServiceConnector : ServiceConnector + { + private IAvatarService m_AvatarService; + private string m_ConfigName = "AvatarService"; + + public AvatarServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string avatarService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (avatarService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_AvatarService = ServerUtils.LoadPlugin(avatarService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new AvatarServerPostHandler(m_AvatarService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs b/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs new file mode 100644 index 0000000000..ff8699fbc6 --- /dev/null +++ b/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs @@ -0,0 +1,276 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Avatar +{ + public class AvatarServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAvatarService m_AvatarService; + + public AvatarServerPostHandler(IAvatarService service, IServiceAuth auth) : + base("POST", "/avatar", auth) + { + m_AvatarService = 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "getavatar": + return GetAvatar(request); + case "setavatar": + return SetAvatar(request); + case "resetavatar": + return ResetAvatar(request); + case "setitems": + return SetItems(request); + case "removeitems": + return RemoveItems(request); + } + m_log.DebugFormat("[AVATAR HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.Debug("[AVATAR HANDLER]: Exception {0}" + e); + } + + return FailureResult(); + + } + + byte[] GetAvatar(Dictionary request) + { + UUID user = UUID.Zero; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + if (UUID.TryParse(request["UserID"].ToString(), out user)) + { + AvatarData avatar = m_AvatarService.GetAvatar(user); + if (avatar == null) + return FailureResult(); + + Dictionary result = new Dictionary(); + if (avatar == null) + result["result"] = "null"; + else + result["result"] = avatar.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + return FailureResult(); + } + + byte[] SetAvatar(Dictionary request) + { + UUID user = UUID.Zero; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + if (!UUID.TryParse(request["UserID"].ToString(), out user)) + return FailureResult(); + + RemoveRequestParamsNotForStorage(request); + + AvatarData avatar = new AvatarData(request); + if (m_AvatarService.SetAvatar(user, avatar)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] ResetAvatar(Dictionary request) + { + UUID user = UUID.Zero; + if (!request.ContainsKey("UserID")) + return FailureResult(); + + if (!UUID.TryParse(request["UserID"].ToString(), out user)) + return FailureResult(); + + RemoveRequestParamsNotForStorage(request); + + if (m_AvatarService.ResetAvatar(user)) + return SuccessResult(); + + return FailureResult(); + } + + /// + /// Remove parameters that were used to invoke the method and should not in themselves be persisted. + /// + /// + private void RemoveRequestParamsNotForStorage(Dictionary request) + { + request.Remove("VERSIONMAX"); + request.Remove("VERSIONMIN"); + request.Remove("METHOD"); + request.Remove("UserID"); + } + + byte[] SetItems(Dictionary request) + { + UUID user = UUID.Zero; + string[] names, values; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("Names") || !request.ContainsKey("Values")) + return FailureResult(); + + if (!UUID.TryParse(request["UserID"].ToString(), out user)) + return FailureResult(); + + if (!(request["Names"] is List || request["Values"] is List)) + return FailureResult(); + + RemoveRequestParamsNotForStorage(request); + + List _names = (List)request["Names"]; + names = _names.ToArray(); + List _values = (List)request["Values"]; + values = _values.ToArray(); + + if (m_AvatarService.SetItems(user, names, values)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] RemoveItems(Dictionary request) + { + UUID user = UUID.Zero; + string[] names; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("Names")) + return FailureResult(); + + if (!UUID.TryParse(request["UserID"].ToString(), out user)) + return FailureResult(); + + if (!(request["Names"] is List)) + return FailureResult(); + + List _names = (List)request["Names"]; + names = _names.ToArray(); + + if (m_AvatarService.RemoveItems(user, names)) + return SuccessResult(); + + return FailureResult(); + } + + + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + } +} diff --git a/OpenSim/Server/Handlers/BakedTextures/XBakes.cs b/OpenSim/Server/Handlers/BakedTextures/XBakes.cs new file mode 100644 index 0000000000..4e554336ae --- /dev/null +++ b/OpenSim/Server/Handlers/BakedTextures/XBakes.cs @@ -0,0 +1,148 @@ +/* + * 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.Diagnostics; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using Nini.Config; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.BakedTextures +{ + public class XBakes : ServiceBase, IBakedTextureService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_FSBase; + + private System.Text.UTF8Encoding utf8encoding = + new System.Text.UTF8Encoding(); + + public XBakes(IConfigSource config) : base(config) + { + MainConsole.Instance.Commands.AddCommand("fs", false, + "delete bakes", "delete bakes ", + "Delete agent's baked textures from server", + HandleDeleteBakes); + + IConfig assetConfig = config.Configs["BakedTextureService"]; + if (assetConfig == null) + { + throw new Exception("No BakedTextureService configuration"); + } + + m_FSBase = assetConfig.GetString("BaseDirectory", String.Empty); + if (m_FSBase == String.Empty) + { + m_log.ErrorFormat("[BAKES]: BaseDirectory not specified"); + throw new Exception("Configuration error"); + } + + m_log.Info("[BAKES]: XBakes service enabled"); + } + + public string Get(string id) + { + string file = HashToFile(id); + string diskFile = Path.Combine(m_FSBase, file); + + if (File.Exists(diskFile)) + { + try + { + byte[] content = File.ReadAllBytes(diskFile); + + return utf8encoding.GetString(content); + } + catch + { + } + } + return String.Empty; + } + + public void Store(string id, string sdata) + { + string file = HashToFile(id); + string diskFile = Path.Combine(m_FSBase, file); + + Directory.CreateDirectory(Path.GetDirectoryName(diskFile)); + + File.Delete(diskFile); + + byte[] data = utf8encoding.GetBytes(sdata); + + using (FileStream fs = File.Create(diskFile)) + fs.Write(data, 0, data.Length); + } + + private void HandleDeleteBakes(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: delete bakes "); + return; + } + + string file = HashToFile(args[2]); + string diskFile = Path.Combine(m_FSBase, file); + + if (File.Exists(diskFile)) + { + File.Delete(diskFile); + MainConsole.Instance.Output("Bakes deleted"); + return; + } + MainConsole.Instance.Output("Bakes not found"); + } + + public string HashToPath(string hash) + { + return Path.Combine(hash.Substring(0, 2), + Path.Combine(hash.Substring(2, 2), + Path.Combine(hash.Substring(4, 2), + hash.Substring(6, 4)))); + } + + public string HashToFile(string hash) + { + return Path.Combine(HashToPath(hash), hash); + } + } +} diff --git a/OpenSim/Server/Handlers/BakedTextures/XBakesGetHandler.cs b/OpenSim/Server/Handlers/BakedTextures/XBakesGetHandler.cs new file mode 100644 index 0000000000..3c6167384f --- /dev/null +++ b/OpenSim/Server/Handlers/BakedTextures/XBakesGetHandler.cs @@ -0,0 +1,71 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.IO; +using System.Reflection; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.BakedTextures +{ + public class BakesServerGetHandler : BaseStreamHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IBakedTextureService m_BakesService; + private System.Text.UTF8Encoding utf8 = + new System.Text.UTF8Encoding(); + + public BakesServerGetHandler(IBakedTextureService service, IServiceAuth auth) : + base("GET", "/bakes", auth) + { + m_BakesService = service; + } + + protected override byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + string[] p = SplitParams(path); + + if (p.Length == 0) + return new byte[0]; + + return utf8.GetBytes(m_BakesService.Get(p[0])); + } + } +} diff --git a/OpenSim/Server/Handlers/BakedTextures/XBakesHandler.cs b/OpenSim/Server/Handlers/BakedTextures/XBakesHandler.cs new file mode 100644 index 0000000000..4c129673dc --- /dev/null +++ b/OpenSim/Server/Handlers/BakedTextures/XBakesHandler.cs @@ -0,0 +1,69 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.BakedTextures +{ + public class XBakesConnector : ServiceConnector + { + private IBakedTextureService m_BakesService; + private string m_ConfigName = "BakedTextureService"; + + public XBakesConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string assetService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (assetService == String.Empty) + throw new Exception("No BakedTextureService in config file"); + + Object[] args = new Object[] { config }; + m_BakesService = + ServerUtils.LoadPlugin(assetService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new BakesServerGetHandler(m_BakesService, auth)); + server.AddStreamHandler(new BakesServerPostHandler(m_BakesService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/BakedTextures/XBakesPostHandler.cs b/OpenSim/Server/Handlers/BakedTextures/XBakesPostHandler.cs new file mode 100644 index 0000000000..e38543b41c --- /dev/null +++ b/OpenSim/Server/Handlers/BakedTextures/XBakesPostHandler.cs @@ -0,0 +1,76 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.BakedTextures +{ + public class BakesServerPostHandler : BaseStreamHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IBakedTextureService m_BakesService; + + public BakesServerPostHandler(IBakedTextureService service, IServiceAuth auth) : + base("POST", "/bakes", auth) + { + m_BakesService = service; + } + + protected override byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + string[] p = SplitParams(path); + + if (p.Length == 0) + { + return new byte[0]; + } + + StreamReader sr = new StreamReader(request); + + m_BakesService.Store(p[0], sr.ReadToEnd()); + sr.Close(); + + return new byte[0]; + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Base/ServerConnector.cs b/OpenSim/Server/Handlers/Base/ServerConnector.cs new file mode 100644 index 0000000000..72014dba7d --- /dev/null +++ b/OpenSim/Server/Handlers/Base/ServerConnector.cs @@ -0,0 +1,113 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Server.Handlers.Base +{ + public interface IServiceConnector + { + } + + public class ServiceConnector : IServiceConnector + { + public virtual string ConfigURL + { + get { return String.Empty; } + } + + public virtual string ConfigName + { + get; + protected set; + } + + public virtual string ConfigFile + { + get; + protected set; + } + + public virtual IConfigSource Config + { + get; + protected set; + } + + public ServiceConnector() + { + } + + public ServiceConnector(IConfigSource config, IHttpServer server, string configName) + { + } + + // We call this from our plugin module to get our configuration + public IConfig GetConfig() + { + IConfig config = null; + config = ServerUtils.GetConfig(ConfigFile, ConfigName); + + // Our file is not here? We can get one to bootstrap our plugin module + if ( config == null ) + { + IConfigSource remotesource = GetConfigSource(); + + if (remotesource != null) + { + IniConfigSource initialconfig = new IniConfigSource(); + initialconfig.Merge (remotesource); + initialconfig.Save(ConfigFile); + } + + config = remotesource.Configs[ConfigName]; + } + + return config; + } + + // We get our remote initial configuration for bootstrapping in case + // we have no configuration in our main file or in an existing + // modular config file. This is the last resort to bootstrap the + // configuration, likely a new plugin loading for the first time. + private IConfigSource GetConfigSource() + { + IConfigSource source = null; + + source = ServerUtils.LoadInitialConfig(ConfigURL); + + if (source == null) + System.Console.WriteLine(String.Format ("Config Url: {0} Not found!", ConfigURL)); + + return source; + } + } +} diff --git a/OpenSim/Server/Handlers/Base/Utils.cs b/OpenSim/Server/Handlers/Base/Utils.cs new file mode 100644 index 0000000000..48d923f9ba --- /dev/null +++ b/OpenSim/Server/Handlers/Base/Utils.cs @@ -0,0 +1,97 @@ +/* + * 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.Net; + +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Base +{ + public class RestHandlerUtils + { + /// + /// Extract the param from an uri. + /// + /// Something like this: /uuid/ or /uuid/handle/release + /// uuid on uuid field + /// optional action + public static bool GetParams(string path, out UUID uuid, out ulong regionHandle, out string action) + { + uuid = UUID.Zero; + action = ""; + regionHandle = 0; + + path = path.Trim(new char[] { '/' }); + string[] parts = path.Split('/'); + if (parts.Length <= 1) + { + return false; + } + else + { + if (!UUID.TryParse(parts[0], out uuid)) + return false; + + if (parts.Length >= 2) + UInt64.TryParse(parts[1], out regionHandle); + if (parts.Length >= 3) + action = parts[2]; + + return true; + } + } + + public static bool GetAuthentication(IOSHttpRequest httpRequest, out string authority, out string authKey) + { + authority = string.Empty; + authKey = string.Empty; + + Uri authUri; + + string auth = httpRequest.Headers["authentication"]; + // Authentication keys look like this: + // http://orgrid.org:8002/ + if ((auth != null) && (!string.Empty.Equals(auth)) && auth != "None") + { + if (Uri.TryCreate(auth, UriKind.Absolute, out authUri)) + { + authority = authUri.Authority; + authKey = authUri.PathAndQuery.Trim('/'); + return true; + } + } + + return false; + } + + } +} diff --git a/OpenSim/Server/Handlers/Estate/EstateDataRobustConnector.cs b/OpenSim/Server/Handlers/Estate/EstateDataRobustConnector.cs new file mode 100644 index 0000000000..e0c281044f --- /dev/null +++ b/OpenSim/Server/Handlers/Estate/EstateDataRobustConnector.cs @@ -0,0 +1,343 @@ +/* + * 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.Net; + +using Nini.Config; +using log4net; +using OpenMetaverse; + +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers +{ + public class EstateDataRobustConnector : ServiceConnector + { + private string m_ConfigName = "EstateService"; + + public EstateDataRobustConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string service = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (service == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + IEstateDataService e_service = ServerUtils.LoadPlugin(service, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); ; + + server.AddStreamHandler(new EstateServerGetHandler(e_service, auth)); + server.AddStreamHandler(new EstateServerPostHandler(e_service, auth)); + } + } + + + public class EstateServerGetHandler : BaseStreamHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + IEstateDataService m_EstateService; + + // Possibilities + // /estates/estate/?region=uuid&create=[t|f] + // /estates/estate/?eid=int + // /estates/?name=string + // /estates/?owner=uuid + // /estates/ (all) + // /estates/regions/?eid=int + + public EstateServerGetHandler(IEstateDataService service, IServiceAuth auth) : + base("GET", "/estates", auth) + { + m_EstateService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + Dictionary data = null; + + string[] p = SplitParams(path); + + // /estates/ (all) + // /estates/?name=string + // /estates/?owner=uuid + if (p.Length == 0) + data = GetEstates(httpRequest, httpResponse); + else + { + string resource = p[0]; + + // /estates/estate/?region=uuid&create=[t|f] + // /estates/estate/?eid=int + if ("estate".Equals(resource)) + data = GetEstate(httpRequest, httpResponse); + // /estates/regions/?eid=int + else if ("regions".Equals(resource)) + data = GetRegions(httpRequest, httpResponse); + } + + if (data == null) + data = new Dictionary(); + + string xmlString = ServerUtils.BuildXmlResponse(data); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + + } + + private Dictionary GetEstates(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // /estates/ (all) + // /estates/?name=string + // /estates/?owner=uuid + + Dictionary data = null; + string name = (string)httpRequest.Query["name"]; + string owner = (string)httpRequest.Query["owner"]; + + if (!string.IsNullOrEmpty(name) || !string.IsNullOrEmpty(owner)) + { + List estateIDs = null; + if (!string.IsNullOrEmpty(name)) + { + estateIDs = m_EstateService.GetEstates(name); + } + else if (!string.IsNullOrEmpty(owner)) + { + UUID ownerID = UUID.Zero; + if (UUID.TryParse(owner, out ownerID)) + estateIDs = m_EstateService.GetEstatesByOwner(ownerID); + } + + if (estateIDs == null || (estateIDs != null && estateIDs.Count == 0)) + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + else + { + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = "text/xml"; + data = new Dictionary(); + int i = 0; + foreach (int id in estateIDs) + data["estate" + i++] = id; + } + } + else + { + List estates = m_EstateService.LoadEstateSettingsAll(); + if (estates == null || estates.Count == 0) + { + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + } + else + { + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = "text/xml"; + data = new Dictionary(); + int i = 0; + foreach (EstateSettings es in estates) + data["estate" + i++] = es.ToMap(); + + } + } + + return data; + } + + private Dictionary GetEstate(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // /estates/estate/?region=uuid&create=[t|f] + // /estates/estate/?eid=int + Dictionary data = null; + string region = (string)httpRequest.Query["region"]; + string eid = (string)httpRequest.Query["eid"]; + + EstateSettings estate = null; + + if (!string.IsNullOrEmpty(region)) + { + UUID regionID = UUID.Zero; + if (UUID.TryParse(region, out regionID)) + { + string create = (string)httpRequest.Query["create"]; + bool createYN = false; + Boolean.TryParse(create, out createYN); + estate = m_EstateService.LoadEstateSettings(regionID, createYN); + } + } + else if (!string.IsNullOrEmpty(eid)) + { + int id = 0; + if (Int32.TryParse(eid, out id)) + estate = m_EstateService.LoadEstateSettings(id); + } + + if (estate != null) + { + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = "text/xml"; + data = estate.ToMap(); + } + else + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + + return data; + } + + private Dictionary GetRegions(IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // /estates/regions/?eid=int + Dictionary data = null; + string eid = (string)httpRequest.Query["eid"]; + + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + if (!string.IsNullOrEmpty(eid)) + { + int id = 0; + if (Int32.TryParse(eid, out id)) + { + List regions = m_EstateService.GetRegions(id); + if (regions != null && regions.Count > 0) + { + data = new Dictionary(); + int i = 0; + foreach (UUID uuid in regions) + data["region" + i++] = uuid.ToString(); + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.ContentType = "text/xml"; + } + } + } + + return data; + } + } + + public class EstateServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + IEstateDataService m_EstateService; + + // Possibilities + // /estates/estate/ (post an estate) + // /estates/estate/?eid=int®ion=uuid (link a region to an estate) + + public EstateServerPostHandler(IEstateDataService service, IServiceAuth auth) : + base("POST", "/estates", auth) + { + m_EstateService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + Dictionary data = null; + + string[] p = SplitParams(path); + + if (p.Length > 0) + { + string resource = p[0]; + + // /estates/estate/ + // /estates/estate/?eid=int®ion=uuid + if ("estate".Equals(resource)) + { + StreamReader sr = new StreamReader(request); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + Dictionary requestData = ServerUtils.ParseQueryString(body); + + data = UpdateEstate(requestData, httpRequest, httpResponse); + } + } + + if (data == null) + data = new Dictionary(); + + string xmlString = ServerUtils.BuildXmlResponse(data); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + + } + + private Dictionary UpdateEstate(Dictionary requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // /estates/estate/ + // /estates/estate/?eid=int®ion=uuid + Dictionary result = new Dictionary(); + string eid = (string)httpRequest.Query["eid"]; + string region = (string)httpRequest.Query["region"]; + + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + + if (string.IsNullOrEmpty(eid) && string.IsNullOrEmpty(region) && + requestData.ContainsKey("OP") && requestData["OP"] != null && "STORE".Equals(requestData["OP"])) + { + // /estates/estate/ + EstateSettings es = new EstateSettings(requestData); + m_EstateService.StoreEstateSettings(es); + //m_log.DebugFormat("[EstateServerPostHandler]: Store estate {0}", es.ToString()); + httpResponse.StatusCode = (int)HttpStatusCode.OK; + result["Result"] = true; + } + else if (!string.IsNullOrEmpty(region) && !string.IsNullOrEmpty(eid) && + requestData.ContainsKey("OP") && requestData["OP"] != null && "LINK".Equals(requestData["OP"])) + { + int id = 0; + UUID regionID = UUID.Zero; + if (UUID.TryParse(region, out regionID) && Int32.TryParse(eid, out id)) + { + m_log.DebugFormat("[EstateServerPostHandler]: Link region {0} to estate {1}", regionID, id); + httpResponse.StatusCode = (int)HttpStatusCode.OK; + result["Result"] = m_EstateService.LinkRegion(regionID, id); + } + } + else + m_log.WarnFormat("[EstateServerPostHandler]: something wrong with POST request {0}", httpRequest.RawUrl); + + return result; + } + + } +} diff --git a/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs b/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs new file mode 100644 index 0000000000..da56b8778d --- /dev/null +++ b/OpenSim/Server/Handlers/Freeswitch/FreeswitchServerConnector.cs @@ -0,0 +1,128 @@ +/* + * 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.Web; +using System.Reflection; +using Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Server.Handlers.Freeswitch +{ + public class FreeswitchServerConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IFreeswitchService m_FreeswitchService; + private string m_ConfigName = "FreeswitchService"; + protected readonly string m_freeSwitchAPIPrefix = "/fsapi"; + + public FreeswitchServerConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string freeswitchService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (freeswitchService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_FreeswitchService = + ServerUtils.LoadPlugin(freeswitchService, args); + + server.AddHTTPHandler(String.Format("{0}/freeswitch-config", m_freeSwitchAPIPrefix), FreeSwitchConfigHTTPHandler); + server.AddHTTPHandler(String.Format("{0}/region-config", m_freeSwitchAPIPrefix), RegionConfigHTTPHandler); + } + + public Hashtable FreeSwitchConfigHTTPHandler(Hashtable request) + { + Hashtable response = new Hashtable(); + response["str_response_string"] = string.Empty; + response["content_type"] = "text/plain"; + response["keepalive"] = false; + response["int_response_code"] = 500; + + Hashtable requestBody = ParseRequestBody((string) request["body"]); + + string section = (string) requestBody["section"]; + + if (section == "directory") + response = m_FreeswitchService.HandleDirectoryRequest(requestBody); + else if (section == "dialplan") + response = m_FreeswitchService.HandleDialplanRequest(requestBody); + else + m_log.WarnFormat("[FreeSwitchVoice]: section was {0}", section); + + return response; + } + + private Hashtable ParseRequestBody(string body) + { + Hashtable bodyParams = new Hashtable(); + // split string + string [] nvps = body.Split(new Char [] {'&'}); + + foreach (string s in nvps) + { + if (s.Trim() != "") + { + string [] nvp = s.Split(new Char [] {'='}); + bodyParams.Add(HttpUtility.UrlDecode(nvp[0]), HttpUtility.UrlDecode(nvp[1])); + } + } + + return bodyParams; + } + + public Hashtable RegionConfigHTTPHandler(Hashtable request) + { + Hashtable response = new Hashtable(); + response["content_type"] = "text/json"; + response["keepalive"] = false; + response["int_response_code"] = 200; + + response["str_response_string"] = m_FreeswitchService.GetJsonConfig(); + + return response; + } + + } +} diff --git a/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs b/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs new file mode 100644 index 0000000000..b0e6c7dea3 --- /dev/null +++ b/OpenSim/Server/Handlers/Friends/FriendServerConnector.cs @@ -0,0 +1,63 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Friends +{ + public class FriendsServiceConnector : ServiceConnector + { + private IFriendsService m_FriendsService; + private string m_ConfigName = "FriendsService"; + + public FriendsServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string theService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (theService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_FriendsService = ServerUtils.LoadPlugin(theService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + server.AddStreamHandler(new FriendsServerPostHandler(m_FriendsService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs b/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs new file mode 100644 index 0000000000..3aab30b3c3 --- /dev/null +++ b/OpenSim/Server/Handlers/Friends/FriendsServerPostHandler.cs @@ -0,0 +1,282 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Friends +{ + public class FriendsServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IFriendsService m_FriendsService; + + public FriendsServerPostHandler(IFriendsService service, IServiceAuth auth) : + base("POST", "/friends", auth) + { + m_FriendsService = 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "getfriends": + return GetFriends(request); + + case "getfriends_string": + return GetFriendsString(request); + + case "storefriend": + return StoreFriend(request); + + case "deletefriend": + return DeleteFriend(request); + + case "deletefriend_string": + return DeleteFriendString(request); + + } + + m_log.DebugFormat("[FRIENDS HANDLER]: unknown method request {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[FRIENDS HANDLER]: Exception {0}", e); + } + + return FailureResult(); + } + + #region Method-specific handlers + + byte[] GetFriends(Dictionary request) + { + UUID principalID = UUID.Zero; + if (request.ContainsKey("PRINCIPALID")) + UUID.TryParse(request["PRINCIPALID"].ToString(), out principalID); + else + m_log.WarnFormat("[FRIENDS HANDLER]: no principalID in request to get friends"); + + FriendInfo[] finfos = m_FriendsService.GetFriends(principalID); + + return PackageFriends(finfos); + } + + byte[] GetFriendsString(Dictionary request) + { + string principalID = string.Empty; + if (request.ContainsKey("PRINCIPALID")) + principalID = request["PRINCIPALID"].ToString(); + else + m_log.WarnFormat("[FRIENDS HANDLER]: no principalID in request to get friends"); + + FriendInfo[] finfos = m_FriendsService.GetFriends(principalID); + + return PackageFriends(finfos); + } + + private byte[] PackageFriends(FriendInfo[] finfos) + { + + Dictionary result = new Dictionary(); + if ((finfos == null) || ((finfos != null) && (finfos.Length == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (FriendInfo finfo in finfos) + { + Dictionary rinfoDict = finfo.ToKeyValuePairs(); + result["friend" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[FRIENDS HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] StoreFriend(Dictionary request) + { + string principalID = string.Empty, friend = string.Empty; int flags = 0; + FromKeyValuePairs(request, out principalID, out friend, out flags); + bool success = m_FriendsService.StoreFriend(principalID, friend, flags); + + if (success) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] DeleteFriend(Dictionary request) + { + UUID principalID = UUID.Zero; + if (request.ContainsKey("PRINCIPALID")) + UUID.TryParse(request["PRINCIPALID"].ToString(), out principalID); + else + m_log.WarnFormat("[FRIENDS HANDLER]: no principalID in request to delete friend"); + string friend = string.Empty; + if (request.ContainsKey("FRIEND")) + friend = request["FRIEND"].ToString(); + + bool success = m_FriendsService.Delete(principalID, friend); + if (success) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] DeleteFriendString(Dictionary request) + { + string principalID = string.Empty; + if (request.ContainsKey("PRINCIPALID")) + principalID = request["PRINCIPALID"].ToString(); + else + m_log.WarnFormat("[FRIENDS HANDLER]: no principalID in request to delete friend"); + string friend = string.Empty; + if (request.ContainsKey("FRIEND")) + friend = request["FRIEND"].ToString(); + + bool success = m_FriendsService.Delete(principalID, friend); + if (success) + return SuccessResult(); + else + return FailureResult(); + } + + #endregion + + #region Misc + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + return FailureResult(String.Empty); + } + + private byte[] FailureResult(string msg) + { + 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("Failure")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Message", ""); + message.AppendChild(doc.CreateTextNode(msg)); + + rootElement.AppendChild(message); + + return Util.DocToBytes(doc); + } + + void FromKeyValuePairs(Dictionary kvp, out string principalID, out string friend, out int flags) + { + principalID = string.Empty; + if (kvp.ContainsKey("PrincipalID") && kvp["PrincipalID"] != null) + principalID = kvp["PrincipalID"].ToString(); + friend = string.Empty; + if (kvp.ContainsKey("Friend") && kvp["Friend"] != null) + friend = kvp["Friend"].ToString(); + flags = 0; + if (kvp.ContainsKey("MyFlags") && kvp["MyFlags"] != null) + Int32.TryParse(kvp["MyFlags"].ToString(), out flags); + } + + #endregion + } +} diff --git a/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs new file mode 100644 index 0000000000..346af32aa3 --- /dev/null +++ b/OpenSim/Server/Handlers/Grid/GridInfoHandlers.cs @@ -0,0 +1,199 @@ +/* + * 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.IO; +using System.Net; +using System.Reflection; +using System.Security; +using System.Text; +using log4net; +using Nini.Config; +using Nwc.XmlRpc; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Server.Handlers.Grid +{ + public class GridInfoHandlers + { + private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IConfigSource m_Config; + private Hashtable _info = new Hashtable(); + + /// + /// Instantiate a GridInfoService object. + /// + /// path to config path containing + /// grid information + /// + /// GridInfoService uses the [GridInfo] section of the + /// standard OpenSim.ini file --- which is not optimal, but + /// anything else requires a general redesign of the config + /// system. + /// + public GridInfoHandlers(IConfigSource configSource) + { + m_Config = configSource; + loadGridInfo(configSource); + } + + private void loadGridInfo(IConfigSource configSource) + { + _info["platform"] = "OpenSim"; + try + { + IConfig gridCfg = configSource.Configs["GridInfoService"]; + IConfig netCfg = configSource.Configs["Network"]; + + if (null != gridCfg) + { + foreach (string k in gridCfg.GetKeys()) + { + _info[k] = gridCfg.GetString(k); + } + } + else if (null != netCfg) + { + _info["login"] + = String.Format( + "http://127.0.0.1:{0}/", + netCfg.GetString( + "http_listener_port", ConfigSettings.DefaultRegionHttpPort.ToString())); + + IssueWarning(); + } + else + { + _info["login"] = "http://127.0.0.1:9000/"; + IssueWarning(); + } + } + catch (Exception) + { + _log.Warn("[GRID INFO SERVICE]: Cannot get grid info from config source, using minimal defaults"); + } + + _log.DebugFormat("[GRID INFO SERVICE]: Grid info service initialized with {0} keys", _info.Count); + } + + private void IssueWarning() + { + _log.Warn("[GRID INFO SERVICE]: found no [GridInfo] section in your configuration files"); + _log.Warn("[GRID INFO SERVICE]: trying to guess sensible defaults, you might want to provide better ones:"); + + foreach (string k in _info.Keys) + { + _log.WarnFormat("[GRID INFO SERVICE]: {0}: {1}", k, _info[k]); + } + } + + public XmlRpcResponse XmlRpcGridInfoMethod(XmlRpcRequest request, IPEndPoint remoteClient) + { + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable responseData = new Hashtable(); + + _log.Debug("[GRID INFO SERVICE]: Request for grid info"); + + foreach (string k in _info.Keys) + { + responseData[k] = _info[k]; + } + response.Value = responseData; + + return response; + } + + public string RestGetGridInfoMethod(string request, string path, string param, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + StringBuilder sb = new StringBuilder(); + + sb.Append("\n"); + foreach (string k in _info.Keys) + { + sb.AppendFormat("<{0}>{1}\n", k, SecurityElement.Escape(_info[k].ToString())); + } + sb.Append("\n"); + + return sb.ToString(); + } + + /// + /// Get GridInfo in json format: Used bu the OSSL osGetGrid* + /// Adding the SRV_HomeIRI to the kvp returned for use in scripts + /// + /// + /// json string + /// + /// + /// Request. + /// + /// + /// /json_grid_info + /// + /// + /// Parameter. + /// + /// + /// Http request. + /// + /// + /// Http response. + /// + public string JsonGetGridInfoMethod(string request, string path, string param, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + OSDMap map = new OSDMap(); + + foreach (string k in _info.Keys) + { + map[k] = OSD.FromString(_info[k].ToString()); + } + + string HomeURI = Util.GetConfigVarFromSections(m_Config, "HomeURI", + new string[] { "Startup", "Hypergrid" }, String.Empty); + + if (!String.IsNullOrEmpty(HomeURI)) + map["home"] = OSD.FromString(HomeURI); + else // Legacy. Remove soon! + { + IConfig cfg = m_Config.Configs["LoginService"]; + + if (null != cfg) + HomeURI = cfg.GetString("SRV_HomeURI", HomeURI); + + if (!String.IsNullOrEmpty(HomeURI)) + map["home"] = OSD.FromString(HomeURI); + } + + return OSDParser.SerializeJsonString(map).ToString(); + } + } +} diff --git a/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs b/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs new file mode 100644 index 0000000000..f9b5915d69 --- /dev/null +++ b/OpenSim/Server/Handlers/Grid/GridInfoServerInConnector.cs @@ -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. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using log4net; +using OpenMetaverse; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Grid +{ + public class GridInfoServerInConnector : ServiceConnector + { +// private string m_ConfigName = "GridInfoService"; + + public GridInfoServerInConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + GridInfoHandlers handlers = new GridInfoHandlers(config); + + server.AddStreamHandler(new RestStreamHandler("GET", "/get_grid_info", + handlers.RestGetGridInfoMethod)); + server.AddStreamHandler(new RestStreamHandler("GET", "/json_grid_info", + handlers.JsonGetGridInfoMethod)); + server.AddXmlRPCHandler("get_grid_info", handlers.XmlRpcGridInfoMethod); + } + + } +} diff --git a/OpenSim/Server/Handlers/Grid/GridServerConnector.cs b/OpenSim/Server/Handlers/Grid/GridServerConnector.cs new file mode 100644 index 0000000000..6eb6a79b14 --- /dev/null +++ b/OpenSim/Server/Handlers/Grid/GridServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Grid +{ + public class GridServiceConnector : ServiceConnector + { + private IGridService m_GridService; + private string m_ConfigName = "GridService"; + + public GridServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string gridService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (gridService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_GridService = ServerUtils.LoadPlugin(gridService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new GridServerPostHandler(m_GridService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs new file mode 100644 index 0000000000..849fa94030 --- /dev/null +++ b/OpenSim/Server/Handlers/Grid/GridServerPostHandler.cs @@ -0,0 +1,661 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Grid +{ + public class GridServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + +#pragma warning disable 414 + private static string LogHeader = "[GRID HANDLER]"; +#pragma warning restore 414 + + private IGridService m_GridService; + + public GridServerPostHandler(IGridService service, IServiceAuth auth) : + base("POST", "/grid", auth) + { + m_GridService = 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "register": + return Register(request); + + case "deregister": + return Deregister(request); + + case "get_neighbours": + return GetNeighbours(request); + + case "get_region_by_uuid": + return GetRegionByUUID(request); + + case "get_region_by_position": + return GetRegionByPosition(request); + + case "get_region_by_name": + return GetRegionByName(request); + + case "get_regions_by_name": + return GetRegionsByName(request); + + case "get_region_range": + return GetRegionRange(request); + + case "get_default_regions": + return GetDefaultRegions(request); + + case "get_default_hypergrid_regions": + return GetDefaultHypergridRegions(request); + + case "get_fallback_regions": + return GetFallbackRegions(request); + + case "get_hyperlinks": + return GetHyperlinks(request); + + case "get_region_flags": + return GetRegionFlags(request); + + case "get_grid_extra_features": + return GetGridExtraFeatures(request); + } + + m_log.DebugFormat("[GRID HANDLER]: unknown method request {0}", method); + } + catch (Exception e) + { + m_log.ErrorFormat("[GRID HANDLER]: Exception {0} {1}", e.Message, e.StackTrace); + } + + return FailureResult(); + } + + #region Method-specific handlers + + byte[] Register(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to register region"); + + int versionNumberMin = 0, versionNumberMax = 0; + if (request.ContainsKey("VERSIONMIN")) + Int32.TryParse(request["VERSIONMIN"].ToString(), out versionNumberMin); + else + m_log.WarnFormat("[GRID HANDLER]: no minimum protocol version in request to register region"); + + if (request.ContainsKey("VERSIONMAX")) + Int32.TryParse(request["VERSIONMAX"].ToString(), out versionNumberMax); + else + m_log.WarnFormat("[GRID HANDLER]: no maximum protocol version in request to register region"); + + // Check the protocol version + if ((versionNumberMin > ProtocolVersions.ServerProtocolVersionMax && versionNumberMax < ProtocolVersions.ServerProtocolVersionMax)) + { + // Can't do, there is no overlap in the acceptable ranges + return FailureResult(); + } + + Dictionary rinfoData = new Dictionary(); + GridRegion rinfo = null; + try + { + foreach (KeyValuePair kvp in request) + rinfoData[kvp.Key] = kvp.Value.ToString(); + rinfo = new GridRegion(rinfoData); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID HANDLER]: exception unpacking region data: {0}", e); + } + + string result = "Error communicating with grid service"; + if (rinfo != null) + result = m_GridService.RegisterRegion(scopeID, rinfo); + + if (result == String.Empty) + return SuccessResult(); + else + return FailureResult(result); + } + + byte[] Deregister(Dictionary request) + { + UUID regionID = UUID.Zero; + if (request.ContainsKey("REGIONID")) + UUID.TryParse(request["REGIONID"].ToString(), out regionID); + else + m_log.WarnFormat("[GRID HANDLER]: no regionID in request to deregister region"); + + bool result = m_GridService.DeregisterRegion(regionID); + + if (result) + return SuccessResult(); + else + return FailureResult(); + + } + + byte[] GetNeighbours(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get neighbours"); + + UUID regionID = UUID.Zero; + if (request.ContainsKey("REGIONID")) + UUID.TryParse(request["REGIONID"].ToString(), out regionID); + else + m_log.WarnFormat("[GRID HANDLER]: no regionID in request to get neighbours"); + + List rinfos = m_GridService.GetNeighbours(scopeID, regionID); + //m_log.DebugFormat("[GRID HANDLER]: neighbours for region {0}: {1}", regionID, rinfos.Count); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionByUUID(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get neighbours"); + + UUID regionID = UUID.Zero; + if (request.ContainsKey("REGIONID")) + UUID.TryParse(request["REGIONID"].ToString(), out regionID); + else + m_log.WarnFormat("[GRID HANDLER]: no regionID in request to get neighbours"); + + GridRegion rinfo = m_GridService.GetRegionByUUID(scopeID, regionID); + //m_log.DebugFormat("[GRID HANDLER]: neighbours for region {0}: {1}", regionID, rinfos.Count); + + Dictionary result = new Dictionary(); + if (rinfo == null) + result["result"] = "null"; + else + result["result"] = rinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionByPosition(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region by position"); + + int x = 0, y = 0; + if (request.ContainsKey("X")) + Int32.TryParse(request["X"].ToString(), out x); + else + m_log.WarnFormat("[GRID HANDLER]: no X in request to get region by position"); + if (request.ContainsKey("Y")) + Int32.TryParse(request["Y"].ToString(), out y); + else + m_log.WarnFormat("[GRID HANDLER]: no Y in request to get region by position"); + + // m_log.DebugFormat("{0} GetRegionByPosition: loc=<{1},{2}>", LogHeader, x, y); + GridRegion rinfo = m_GridService.GetRegionByPosition(scopeID, x, y); + + Dictionary result = new Dictionary(); + if (rinfo == null) + result["result"] = "null"; + else + result["result"] = rinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionByName(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region by name"); + + string regionName = string.Empty; + if (request.ContainsKey("NAME")) + regionName = request["NAME"].ToString(); + else + m_log.WarnFormat("[GRID HANDLER]: no name in request to get region by name"); + + GridRegion rinfo = m_GridService.GetRegionByName(scopeID, regionName); + //m_log.DebugFormat("[GRID HANDLER]: neighbours for region {0}: {1}", regionID, rinfos.Count); + + Dictionary result = new Dictionary(); + if (rinfo == null) + result["result"] = "null"; + else + result["result"] = rinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionsByName(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get regions by name"); + + string regionName = string.Empty; + if (request.ContainsKey("NAME")) + regionName = request["NAME"].ToString(); + else + m_log.WarnFormat("[GRID HANDLER]: no NAME in request to get regions by name"); + + int max = 0; + if (request.ContainsKey("MAX")) + Int32.TryParse(request["MAX"].ToString(), out max); + else + m_log.WarnFormat("[GRID HANDLER]: no MAX in request to get regions by name"); + + List rinfos = m_GridService.GetRegionsByName(scopeID, regionName, max); + //m_log.DebugFormat("[GRID HANDLER]: neighbours for region {0}: {1}", regionID, rinfos.Count); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionRange(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetRegionRange"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region range"); + + int xmin = 0, xmax = 0, ymin = 0, ymax = 0; + if (request.ContainsKey("XMIN")) + Int32.TryParse(request["XMIN"].ToString(), out xmin); + else + m_log.WarnFormat("[GRID HANDLER]: no XMIN in request to get region range"); + if (request.ContainsKey("XMAX")) + Int32.TryParse(request["XMAX"].ToString(), out xmax); + else + m_log.WarnFormat("[GRID HANDLER]: no XMAX in request to get region range"); + if (request.ContainsKey("YMIN")) + Int32.TryParse(request["YMIN"].ToString(), out ymin); + else + m_log.WarnFormat("[GRID HANDLER]: no YMIN in request to get region range"); + if (request.ContainsKey("YMAX")) + Int32.TryParse(request["YMAX"].ToString(), out ymax); + else + m_log.WarnFormat("[GRID HANDLER]: no YMAX in request to get region range"); + + + List rinfos = m_GridService.GetRegionRange(scopeID, xmin, xmax, ymin, ymax); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetDefaultRegions(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetDefaultRegions"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region range"); + + List rinfos = m_GridService.GetDefaultRegions(scopeID); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetDefaultHypergridRegions(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetDefaultRegions"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get region range"); + + List rinfos = m_GridService.GetDefaultHypergridRegions(scopeID); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetFallbackRegions(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetRegionRange"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get fallback regions"); + + int x = 0, y = 0; + if (request.ContainsKey("X")) + Int32.TryParse(request["X"].ToString(), out x); + else + m_log.WarnFormat("[GRID HANDLER]: no X in request to get fallback regions"); + if (request.ContainsKey("Y")) + Int32.TryParse(request["Y"].ToString(), out y); + else + m_log.WarnFormat("[GRID HANDLER]: no Y in request to get fallback regions"); + + + List rinfos = m_GridService.GetFallbackRegions(scopeID, x, y); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetHyperlinks(Dictionary request) + { + //m_log.DebugFormat("[GRID HANDLER]: GetHyperlinks"); + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get linked regions"); + + List rinfos = m_GridService.GetHyperlinks(scopeID); + + Dictionary result = new Dictionary(); + if ((rinfos == null) || ((rinfos != null) && (rinfos.Count == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridRegion rinfo in rinfos) + { + Dictionary rinfoDict = rinfo.ToKeyValuePairs(); + result["region" + i] = rinfoDict; + i++; + } + } + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetRegionFlags(Dictionary request) + { + UUID scopeID = UUID.Zero; + if (request.ContainsKey("SCOPEID")) + UUID.TryParse(request["SCOPEID"].ToString(), out scopeID); + else + m_log.WarnFormat("[GRID HANDLER]: no scopeID in request to get neighbours"); + + UUID regionID = UUID.Zero; + if (request.ContainsKey("REGIONID")) + UUID.TryParse(request["REGIONID"].ToString(), out regionID); + else + m_log.WarnFormat("[GRID HANDLER]: no regionID in request to get neighbours"); + + int flags = m_GridService.GetRegionFlags(scopeID, regionID); + // m_log.DebugFormat("[GRID HANDLER]: flags for region {0}: {1}", regionID, flags); + + Dictionary result = new Dictionary(); + result["result"] = flags.ToString(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetGridExtraFeatures(Dictionary request) + { + + Dictionary result = new Dictionary (); + Dictionary extraFeatures = m_GridService.GetExtraFeatures (); + + foreach (string key in extraFeatures.Keys) + { + result [key] = extraFeatures [key]; + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #endregion + + #region Misc + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + return FailureResult(String.Empty); + } + + private byte[] FailureResult(string msg) + { + 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("Failure")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Message", ""); + message.AppendChild(doc.CreateTextNode(msg)); + + rootElement.AppendChild(message); + + return Util.DocToBytes(doc); + } + + #endregion + } +} diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs new file mode 100644 index 0000000000..1e29378c26 --- /dev/null +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.GridUser +{ + public class GridUserServiceConnector : ServiceConnector + { + private IGridUserService m_GridUserService; + private string m_ConfigName = "GridUserService"; + + public GridUserServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string service = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (service == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_GridUserService = ServerUtils.LoadPlugin(service, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); ; + + server.AddStreamHandler(new GridUserServerPostHandler(m_GridUserService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs new file mode 100644 index 0000000000..9237c6334e --- /dev/null +++ b/OpenSim/Server/Handlers/GridUser/GridUserServerPostHandler.cs @@ -0,0 +1,308 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.GridUser +{ + public class GridUserServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGridUserService m_GridUserService; + + public GridUserServerPostHandler(IGridUserService service, IServiceAuth auth) : + base("POST", "/griduser", auth) + { + m_GridUserService = 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); + string method = string.Empty; + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + method = request["METHOD"].ToString(); + + switch (method) + { + case "loggedin": + return LoggedIn(request); + case "loggedout": + return LoggedOut(request); + case "sethome": + return SetHome(request); + case "setposition": + return SetPosition(request); + case "getgriduserinfo": + return GetGridUserInfo(request); + case "getgriduserinfos": + return GetGridUserInfos(request); + } + m_log.DebugFormat("[GRID USER HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER HANDLER]: Exception in method {0}: {1}", method, e); + } + + return FailureResult(); + + } + + byte[] LoggedIn(Dictionary request) + { + string user = String.Empty; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + user = request["UserID"].ToString(); + + GridUserInfo guinfo = m_GridUserService.LoggedIn(user); + + Dictionary result = new Dictionary(); + result["result"] = guinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID USER HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] LoggedOut(Dictionary request) + { + string userID = string.Empty; + UUID regionID = UUID.Zero; + Vector3 position = Vector3.Zero; + Vector3 lookat = Vector3.Zero; + + if (!UnpackArgs(request, out userID, out regionID, out position, out lookat)) + return FailureResult(); + + if (m_GridUserService.LoggedOut(userID, UUID.Zero, regionID, position, lookat)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] SetHome(Dictionary request) + { + string user = string.Empty; + UUID region = UUID.Zero; + Vector3 position = new Vector3(128, 128, 70); + Vector3 look = Vector3.Zero; + + if (!UnpackArgs(request, out user, out region, out position, out look)) + return FailureResult(); + + if (m_GridUserService.SetHome(user, region, position, look)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] SetPosition(Dictionary request) + { + string user = string.Empty; + UUID region = UUID.Zero; + Vector3 position = new Vector3(128, 128, 70); + Vector3 look = Vector3.Zero; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("RegionID")) + return FailureResult(); + + if (!UnpackArgs(request, out user, out region, out position, out look)) + return FailureResult(); + + if (m_GridUserService.SetLastPosition(user, UUID.Zero, region, position, look)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] GetGridUserInfo(Dictionary request) + { + string user = String.Empty; + + if (!request.ContainsKey("UserID")) + return FailureResult(); + + user = request["UserID"].ToString(); + + GridUserInfo guinfo = m_GridUserService.GetGridUserInfo(user); + + Dictionary result = new Dictionary(); + if (guinfo != null) + result["result"] = guinfo.ToKeyValuePairs(); + else + result["result"] = "null"; + + string xmlString = ServerUtils.BuildXmlResponse(result); + //m_log.DebugFormat("[GRID USER HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetGridUserInfos(Dictionary request) + { + + string[] userIDs; + + if (!request.ContainsKey("AgentIDs")) + { + m_log.DebugFormat("[GRID USER HANDLER]: GetGridUserInfos called without required uuids argument"); + return FailureResult(); + } + + if (!(request["AgentIDs"] is List)) + { + m_log.DebugFormat("[GRID USER HANDLER]: GetGridUserInfos input argument was of unexpected type {0}", request["uuids"].GetType().ToString()); + return FailureResult(); + } + + userIDs = ((List)request["AgentIDs"]).ToArray(); + + GridUserInfo[] pinfos = m_GridUserService.GetGridUserInfo(userIDs); + + Dictionary result = new Dictionary(); + if ((pinfos == null) || ((pinfos != null) && (pinfos.Length == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (GridUserInfo pinfo in pinfos) + { + Dictionary rinfoDict = pinfo.ToKeyValuePairs(); + result["griduser" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + private bool UnpackArgs(Dictionary request, out string user, out UUID region, out Vector3 position, out Vector3 lookAt) + { + user = string.Empty; + region = UUID.Zero; + position = new Vector3(128, 128, 70); + lookAt = Vector3.Zero; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("RegionID")) + return false; + + user = request["UserID"].ToString(); + + if (!UUID.TryParse(request["RegionID"].ToString(), out region)) + return false; + + if (request.ContainsKey("Position")) + Vector3.TryParse(request["Position"].ToString(), out position); + + if (request.ContainsKey("LookAt")) + Vector3.TryParse(request["LookAt"].ToString(), out lookAt); + + return true; + } + + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/AgentHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/AgentHandlers.cs new file mode 100644 index 0000000000..95a0510595 --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/AgentHandlers.cs @@ -0,0 +1,70 @@ +/* + * 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.IO; +using System.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Simulation; +using Utils = OpenSim.Server.Handlers.Simulation.Utils; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class GatekeeperAgentHandler : OpenSim.Server.Handlers.Simulation.AgentPostHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGatekeeperService m_GatekeeperService; + + public GatekeeperAgentHandler(IGatekeeperService gatekeeper, bool proxy) : base("/foreignagent") + { + m_GatekeeperService = gatekeeper; + m_Proxy = proxy; + } + + protected override bool CreateAgent(GridRegion source, GridRegion gatekeeper, GridRegion destination, + AgentCircuitData aCircuit, uint teleportFlags, bool fromLogin, out string reason) + { + return m_GatekeeperService.LoginAgent(source, aCircuit, destination, out reason); + } + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs new file mode 100644 index 0000000000..ffe2f367ba --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/GatekeeperServerConnector.cs @@ -0,0 +1,89 @@ +/* + * 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 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; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class GatekeeperServiceInConnector : ServiceConnector + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private IGatekeeperService m_GatekeeperService; + public IGatekeeperService GateKeeper + { + get { return m_GatekeeperService; } + } + + bool m_Proxy = false; + + public GatekeeperServiceInConnector(IConfigSource config, IHttpServer server, ISimulationService simService) : + base(config, server, String.Empty) + { + IConfig gridConfig = config.Configs["GatekeeperService"]; + if (gridConfig != null) + { + string serviceDll = gridConfig.GetString("LocalServiceModule", string.Empty); + Object[] args = new Object[] { config, simService }; + m_GatekeeperService = ServerUtils.LoadPlugin(serviceDll, args); + + } + if (m_GatekeeperService == null) + throw new Exception("Gatekeeper server connector cannot proceed because of missing service"); + + m_Proxy = gridConfig.GetBoolean("HasProxy", false); + + HypergridHandlers hghandlers = new HypergridHandlers(m_GatekeeperService); + server.AddXmlRPCHandler("link_region", hghandlers.LinkRegionRequest, false); + server.AddXmlRPCHandler("get_region", hghandlers.GetRegion, false); + + server.AddStreamHandler(new GatekeeperAgentHandler(m_GatekeeperService, m_Proxy)); + } + + public GatekeeperServiceInConnector(IConfigSource config, IHttpServer server, string configName) + : this(config, server, (ISimulationService)null) + { + } + + public GatekeeperServiceInConnector(IConfigSource config, IHttpServer server) + : this(config, server, String.Empty) + { + } + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs new file mode 100644 index 0000000000..6c79c607af --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendServerConnector.cs @@ -0,0 +1,77 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HGFriendsServerConnector : ServiceConnector + { + private IUserAgentService m_UserAgentService; + private IHGFriendsService m_TheService; + private string m_ConfigName = "HGFriendsService"; + + // Called from Robust + public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName) : + this(config, server, configName, null) + { + + } + + // Called from standalone configurations + public HGFriendsServerConnector(IConfigSource config, IHttpServer server, string configName, IFriendsSimConnector localConn) + : base(config, server, configName) + { + if (configName != string.Empty) + m_ConfigName = configName; + + Object[] args = new Object[] { config, m_ConfigName, localConn }; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string theService = serverConfig.GetString("LocalServiceModule", + String.Empty); + if (theService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + m_TheService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("UserAgentService", string.Empty); + if (theService == String.Empty) + throw new Exception("No UserAgentService in " + m_ConfigName); + m_UserAgentService = ServerUtils.LoadPlugin(theService, new Object[] { config, localConn }); + + server.AddStreamHandler(new HGFriendsServerPostHandler(m_TheService, m_UserAgentService, localConn)); + } + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs new file mode 100644 index 0000000000..37b47edcd5 --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HGFriendsServerPostHandler.cs @@ -0,0 +1,425 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HGFriendsServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IUserAgentService m_UserAgentService; + private IFriendsSimConnector m_FriendsLocalSimConnector; + private IHGFriendsService m_TheService; + + public HGFriendsServerPostHandler(IHGFriendsService service, IUserAgentService uas, IFriendsSimConnector friendsConn) : + base("POST", "/hgfriends") + { + m_TheService = service; + m_UserAgentService = uas; + m_FriendsLocalSimConnector = friendsConn; + + m_log.DebugFormat("[HGFRIENDS HANDLER]: HGFriendsServerPostHandler is On ({0})", + (m_FriendsLocalSimConnector == null ? "robust" : "standalone")); + + if (m_TheService == null) + m_log.ErrorFormat("[HGFRIENDS HANDLER]: TheService is null!"); + } + + 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + + switch (method) + { + case "getfriendperms": + return GetFriendPerms(request); + + case "newfriendship": + return NewFriendship(request); + + case "deletefriendship": + return DeleteFriendship(request); + + /* Same as inter-sim */ + case "friendship_offered": + return FriendshipOffered(request); + + case "validate_friendship_offered": + return ValidateFriendshipOffered(request); + + case "statusnotification": + return StatusNotification(request); + /* + case "friendship_approved": + return FriendshipApproved(request); + + case "friendship_denied": + return FriendshipDenied(request); + + case "friendship_terminated": + return FriendshipTerminated(request); + + case "grant_rights": + return GrantRights(request); + */ + } + + m_log.DebugFormat("[HGFRIENDS HANDLER]: unknown method {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS HANDLER]: Exception {0}", e); + } + + return FailureResult(); + } + + #region Method-specific handlers + + byte[] GetFriendPerms(Dictionary request) + { + if (!VerifyServiceKey(request)) + return FailureResult(); + + UUID principalID = UUID.Zero; + if (request.ContainsKey("PRINCIPALID")) + UUID.TryParse(request["PRINCIPALID"].ToString(), out principalID); + else + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: no principalID in request to get friend perms"); + return FailureResult(); + } + + UUID friendID = UUID.Zero; + if (request.ContainsKey("FRIENDID")) + UUID.TryParse(request["FRIENDID"].ToString(), out friendID); + else + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: no friendID in request to get friend perms"); + return FailureResult(); + } + + int perms = m_TheService.GetFriendPerms(principalID, friendID); + if (perms < 0) + return FailureResult("Friend not found"); + + return SuccessResult(perms.ToString()); + } + + byte[] NewFriendship(Dictionary request) + { + bool verified = VerifyServiceKey(request); + + FriendInfo friend = new FriendInfo(request); + + bool success = m_TheService.NewFriendship(friend, verified); + + if (success) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] DeleteFriendship(Dictionary request) + { + FriendInfo friend = new FriendInfo(request); + string secret = string.Empty; + if (request.ContainsKey("SECRET")) + secret = request["SECRET"].ToString(); + + if (secret == string.Empty) + return BoolResult(false); + + bool success = m_TheService.DeleteFriendship(friend, secret); + + return BoolResult(success); + } + + byte[] FriendshipOffered(Dictionary request) + { + UUID fromID = UUID.Zero; + UUID toID = UUID.Zero; + string message = string.Empty; + string name = string.Empty; + + if (!request.ContainsKey("FromID") || !request.ContainsKey("ToID")) + return BoolResult(false); + + if (!UUID.TryParse(request["ToID"].ToString(), out toID)) + return BoolResult(false); + + message = request["Message"].ToString(); + + if (!UUID.TryParse(request["FromID"].ToString(), out fromID)) + return BoolResult(false); + + if (request.ContainsKey("FromName")) + name = request["FromName"].ToString(); + + bool success = m_TheService.FriendshipOffered(fromID, name, toID, message); + + return BoolResult(success); + } + + byte[] ValidateFriendshipOffered(Dictionary request) + { + FriendInfo friend = new FriendInfo(request); + UUID friendID = UUID.Zero; + if (!UUID.TryParse(friend.Friend, out friendID)) + return BoolResult(false); + + bool success = m_TheService.ValidateFriendshipOffered(friend.PrincipalID, friendID); + + return BoolResult(success); + } + + byte[] StatusNotification(Dictionary request) + { + UUID principalID = UUID.Zero; + if (request.ContainsKey("userID")) + UUID.TryParse(request["userID"].ToString(), out principalID); + else + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: no userID in request to notify"); + return FailureResult(); + } + + bool online = true; + if (request.ContainsKey("online")) + Boolean.TryParse(request["online"].ToString(), out online); + else + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: no online in request to notify"); + return FailureResult(); + } + + List friends = new List(); + int i = 0; + foreach (KeyValuePair kvp in request) + { + if (kvp.Key.Equals("friend_" + i.ToString())) + { + friends.Add(kvp.Value.ToString()); + i++; + } + } + + List onlineFriends = m_TheService.StatusNotification(friends, principalID, online); + + Dictionary result = new Dictionary(); + if ((onlineFriends == null) || ((onlineFriends != null) && (onlineFriends.Count == 0))) + result["RESULT"] = "NULL"; + else + { + i = 0; + foreach (UUID f in onlineFriends) + { + result["friend_" + i] = f.ToString(); + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + #endregion + + #region Misc + + private bool VerifyServiceKey(Dictionary request) + { + if (!request.ContainsKey("KEY") || !request.ContainsKey("SESSIONID")) + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: ignoring request without Key or SessionID"); + return false; + } + + if (request["KEY"] == null || request["SESSIONID"] == null) + return false; + + string serviceKey = request["KEY"].ToString(); + string sessionStr = request["SESSIONID"].ToString(); + + UUID sessionID; + if (!UUID.TryParse(sessionStr, out sessionID) || serviceKey == string.Empty) + return false; + + if (!m_UserAgentService.VerifyAgent(sessionID, serviceKey)) + { + m_log.WarnFormat("[HGFRIENDS HANDLER]: Key {0} for session {1} did not match existing key. Ignoring request", serviceKey, sessionID); + return false; + } + + m_log.DebugFormat("[HGFRIENDS HANDLER]: Verification ok"); + return true; + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] SuccessResult(string 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("Success")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Value", ""); + message.AppendChild(doc.CreateTextNode(value)); + + rootElement.AppendChild(message); + + return Util.DocToBytes(doc); + } + + + private byte[] FailureResult() + { + return FailureResult(String.Empty); + } + + private byte[] FailureResult(string msg) + { + 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("Failure")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Message", ""); + message.AppendChild(doc.CreateTextNode(msg)); + + rootElement.AppendChild(message); + + return Util.DocToBytes(doc); + } + + 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 + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HeloServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/HeloServerConnector.cs new file mode 100644 index 0000000000..dac4ca85dc --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HeloServerConnector.cs @@ -0,0 +1,113 @@ +/* + * 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.Net; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HeloServiceInConnector : ServiceConnector + { + public HeloServiceInConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { +#pragma warning disable 0612 + server.AddStreamHandler(new HeloServerGetHandler("opensim-robust")); +#pragma warning restore 0612 + server.AddStreamHandler(new HeloServerHeadHandler("opensim-robust")); + } + } + + [Obsolete] + public class HeloServerGetHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_HandlersType; + + public HeloServerGetHandler(string handlersType) : + base("GET", "/helo") + { + m_HandlersType = handlersType; + } + + public override byte[] Handle(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return OKResponse(httpResponse); + } + + private byte[] OKResponse(IOSHttpResponse httpResponse) + { + m_log.Debug("[HELO]: hi, GET was called"); + httpResponse.AddHeader("X-Handlers-Provided", m_HandlersType); + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.StatusDescription = "OK"; + return new byte[0]; + } + + } + + public class HeloServerHeadHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_HandlersType; + + public HeloServerHeadHandler(string handlersType) : + base("HEAD", "/helo") + { + m_HandlersType = handlersType; + } + + protected override byte[] ProcessRequest(string path, Stream requestData, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return OKResponse(httpResponse); + } + + private byte[] OKResponse(IOSHttpResponse httpResponse) + { + m_log.Debug("[HELO]: hi, HEAD was called"); + httpResponse.AddHeader("X-Handlers-Provided", m_HandlersType); + httpResponse.StatusCode = (int)HttpStatusCode.OK; + httpResponse.StatusDescription = "OK"; + return new byte[0]; + } + + } + +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs new file mode 100644 index 0000000000..e787f7ced3 --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HomeAgentHandlers.cs @@ -0,0 +1,137 @@ +/* + * 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.IO; +using System.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Simulation; +using Utils = OpenSim.Server.Handlers.Simulation.Utils; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HomeAgentHandler : AgentPostHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IUserAgentService m_UserAgentService; + + private string m_LoginServerIP; + + public HomeAgentHandler(IUserAgentService userAgentService, string loginServerIP, bool proxy) : + base("/homeagent") + { + m_UserAgentService = userAgentService; + m_LoginServerIP = loginServerIP; + m_Proxy = proxy; + } + + protected override AgentDestinationData CreateAgentDestinationData() + { + return new ExtendedAgentDestinationData(); + } + + protected override void UnpackData(OSDMap args, AgentDestinationData d, Hashtable request) + { + base.UnpackData(args, d, request); + ExtendedAgentDestinationData data = (ExtendedAgentDestinationData)d; + try + { + if (args.ContainsKey("gatekeeper_host") && args["gatekeeper_host"] != null) + data.host = args["gatekeeper_host"].AsString(); + if (args.ContainsKey("gatekeeper_port") && args["gatekeeper_port"] != null) + Int32.TryParse(args["gatekeeper_port"].AsString(), out data.port); + if (args.ContainsKey("gatekeeper_serveruri") && args["gatekeeper_serveruri"] != null) + data.gatekeeperServerURI = args["gatekeeper_serveruri"]; + if (args.ContainsKey("destination_serveruri") && args["destination_serveruri"] != null) + data.destinationServerURI = args["destination_serveruri"]; + + } + catch (InvalidCastException) + { + m_log.ErrorFormat("[HOME AGENT HANDLER]: Bad cast in UnpackData"); + } + + string callerIP = GetCallerIP(request); + // Verify if this call came from the login server + if (callerIP == m_LoginServerIP) + data.fromLogin = true; + + } + + protected override GridRegion ExtractGatekeeper(AgentDestinationData d) + { + if (d is ExtendedAgentDestinationData) + { + ExtendedAgentDestinationData data = (ExtendedAgentDestinationData)d; + GridRegion gatekeeper = new GridRegion(); + gatekeeper.ServerURI = data.gatekeeperServerURI; + gatekeeper.ExternalHostName = data.host; + gatekeeper.HttpPort = (uint)data.port; + gatekeeper.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0); + + return gatekeeper; + } + else + m_log.WarnFormat("[HOME AGENT HANDLER]: Wrong data type"); + + return null; + } + + + protected override bool CreateAgent(GridRegion source, GridRegion gatekeeper, GridRegion destination, + AgentCircuitData aCircuit, uint teleportFlags, bool fromLogin, out string reason) + { + return m_UserAgentService.LoginAgentToGrid(source, aCircuit, gatekeeper, destination, fromLogin, out reason); + } + + } + + public class ExtendedAgentDestinationData : AgentDestinationData + { + public string host; + public int port; + public string gatekeeperServerURI; + public string destinationServerURI; + + } + +} diff --git a/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs b/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs new file mode 100644 index 0000000000..c7ac9be132 --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/HypergridHandlers.cs @@ -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.Collections; +using System.Collections.Generic; +using System.Net; +using System.Reflection; + +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class HypergridHandlers + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGatekeeperService m_GatekeeperService; + + public HypergridHandlers(IGatekeeperService gatekeeper) + { + m_GatekeeperService = gatekeeper; + m_log.DebugFormat("[HYPERGRID HANDLERS]: Active"); + } + + /// + /// Someone wants to link to us + /// + /// + /// + public XmlRpcResponse LinkRegionRequest(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string name = (string)requestData["region_name"]; + if (name == null) + name = string.Empty; + + UUID regionID = UUID.Zero; + string externalName = string.Empty; + string imageURL = string.Empty; + ulong regionHandle = 0; + string reason = string.Empty; + + bool success = m_GatekeeperService.LinkRegion(name, out regionID, out regionHandle, out externalName, out imageURL, out reason); + + Hashtable hash = new Hashtable(); + hash["result"] = success.ToString(); + hash["uuid"] = regionID.ToString(); + hash["handle"] = regionHandle.ToString(); + hash["region_image"] = imageURL; + hash["external_name"] = externalName; + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + } + + public XmlRpcResponse GetRegion(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string regionID_str = (string)requestData["region_uuid"]; + UUID regionID = UUID.Zero; + UUID.TryParse(regionID_str, out regionID); + + UUID agentID = UUID.Zero; + string agentHomeURI = null; + if (requestData.ContainsKey("agent_id")) + agentID = UUID.Parse((string)requestData["agent_id"]); + if (requestData.ContainsKey("agent_home_uri")) + agentHomeURI = (string)requestData["agent_home_uri"]; + + string message; + GridRegion regInfo = m_GatekeeperService.GetHyperlinkRegion(regionID, agentID, agentHomeURI, out message); + + Hashtable hash = new Hashtable(); + if (regInfo == null) + { + hash["result"] = "false"; + } + else + { + hash["result"] = "true"; + hash["uuid"] = regInfo.RegionID.ToString(); + hash["x"] = regInfo.RegionLocX.ToString(); + hash["y"] = regInfo.RegionLocY.ToString(); + hash["size_x"] = regInfo.RegionSizeX.ToString(); + hash["size_y"] = regInfo.RegionSizeY.ToString(); + hash["region_name"] = regInfo.RegionName; + hash["hostname"] = regInfo.ExternalHostName; + hash["http_port"] = regInfo.HttpPort.ToString(); + hash["internal_port"] = regInfo.InternalEndPoint.Port.ToString(); + } + + if (message != null) + hash["message"] = message; + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/InstantMessageServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/InstantMessageServerConnector.cs new file mode 100644 index 0000000000..8145a21ecb --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/InstantMessageServerConnector.cs @@ -0,0 +1,248 @@ +/* + * 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 Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class InstantMessageServerConnector : ServiceConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private IInstantMessage m_IMService; + + public InstantMessageServerConnector(IConfigSource config, IHttpServer server) : + this(config, server, (IInstantMessageSimConnector)null) + { + } + + public InstantMessageServerConnector(IConfigSource config, IHttpServer server, string configName) : + this(config, server) + { + } + + public InstantMessageServerConnector(IConfigSource config, IHttpServer server, IInstantMessageSimConnector simConnector) : + base(config, server, String.Empty) + { + IConfig gridConfig = config.Configs["HGInstantMessageService"]; + if (gridConfig != null) + { + string serviceDll = gridConfig.GetString("LocalServiceModule", string.Empty); + + Object[] args = new Object[] { config, simConnector }; + m_IMService = ServerUtils.LoadPlugin(serviceDll, args); + } + if (m_IMService == null) + throw new Exception("InstantMessage server connector cannot proceed because of missing service"); + + server.AddXmlRPCHandler("grid_instant_message", ProcessInstantMessage, false); + + } + + public IInstantMessage GetService() + { + return m_IMService; + } + + protected virtual XmlRpcResponse ProcessInstantMessage(XmlRpcRequest request, IPEndPoint remoteClient) + { + bool successful = false; + + try + { + // various rational defaults + UUID fromAgentID = UUID.Zero; + UUID toAgentID = UUID.Zero; + UUID imSessionID = UUID.Zero; + uint timestamp = 0; + string fromAgentName = ""; + string message = ""; + byte dialog = (byte)0; + bool fromGroup = false; + byte offline = (byte)0; + uint ParentEstateID = 0; + Vector3 Position = Vector3.Zero; + UUID RegionID = UUID.Zero; + byte[] binaryBucket = new byte[0]; + + float pos_x = 0; + float pos_y = 0; + float pos_z = 0; + //m_log.Info("Processing IM"); + + + Hashtable requestData = (Hashtable)request.Params[0]; + // Check if it's got all the data + if (requestData.ContainsKey("from_agent_id") + && requestData.ContainsKey("to_agent_id") && requestData.ContainsKey("im_session_id") + && requestData.ContainsKey("timestamp") && requestData.ContainsKey("from_agent_name") + && requestData.ContainsKey("message") && requestData.ContainsKey("dialog") + && requestData.ContainsKey("from_group") + && requestData.ContainsKey("offline") && requestData.ContainsKey("parent_estate_id") + && requestData.ContainsKey("position_x") && requestData.ContainsKey("position_y") + && requestData.ContainsKey("position_z") && requestData.ContainsKey("region_id") + && requestData.ContainsKey("binary_bucket")) + { + // Do the easy way of validating the UUIDs + UUID.TryParse((string)requestData["from_agent_id"], out fromAgentID); + UUID.TryParse((string)requestData["to_agent_id"], out toAgentID); + UUID.TryParse((string)requestData["im_session_id"], out imSessionID); + UUID.TryParse((string)requestData["region_id"], out RegionID); + try + { + timestamp = (uint)Convert.ToInt32((string)requestData["timestamp"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + fromAgentName = (string)requestData["from_agent_name"]; + message = (string)requestData["message"]; + if (message == null) + message = string.Empty; + + // Bytes don't transfer well over XMLRPC, so, we Base64 Encode them. + string requestData1 = (string)requestData["dialog"]; + if (string.IsNullOrEmpty(requestData1)) + { + dialog = 0; + } + else + { + byte[] dialogdata = Convert.FromBase64String(requestData1); + dialog = dialogdata[0]; + } + + if ((string)requestData["from_group"] == "TRUE") + fromGroup = true; + + string requestData2 = (string)requestData["offline"]; + if (String.IsNullOrEmpty(requestData2)) + { + offline = 0; + } + else + { + byte[] offlinedata = Convert.FromBase64String(requestData2); + offline = offlinedata[0]; + } + + try + { + ParentEstateID = (uint)Convert.ToInt32((string)requestData["parent_estate_id"]); + } + catch (ArgumentException) + { + } + catch (FormatException) + { + } + catch (OverflowException) + { + } + + float.TryParse((string)requestData["position_x"], out pos_x); + float.TryParse((string)requestData["position_y"], out pos_y); + float.TryParse((string)requestData["position_z"], out pos_z); + + Position = new Vector3(pos_x, pos_y, pos_z); + + string requestData3 = (string)requestData["binary_bucket"]; + if (string.IsNullOrEmpty(requestData3)) + { + binaryBucket = new byte[0]; + } + else + { + binaryBucket = Convert.FromBase64String(requestData3); + } + + // Create a New GridInstantMessageObject the the data + GridInstantMessage gim = new GridInstantMessage(); + gim.fromAgentID = fromAgentID.Guid; + gim.fromAgentName = fromAgentName; + gim.fromGroup = fromGroup; + gim.imSessionID = imSessionID.Guid; + gim.RegionID = RegionID.Guid; + gim.timestamp = timestamp; + gim.toAgentID = toAgentID.Guid; + gim.message = message; + gim.dialog = dialog; + gim.offline = offline; + gim.ParentEstateID = ParentEstateID; + gim.Position = Position; + gim.binaryBucket = binaryBucket; + + successful = m_IMService.IncomingInstantMessage(gim); + + } + } + catch (Exception e) + { + m_log.Error("[INSTANT MESSAGE]: Caught unexpected exception:", e); + successful = false; + } + + //Send response back to region calling if it was successful + // calling region uses this to know when to look up a user's location again. + XmlRpcResponse resp = new XmlRpcResponse(); + Hashtable respdata = new Hashtable(); + if (successful) + respdata["success"] = "TRUE"; + else + respdata["success"] = "FALSE"; + resp.Value = respdata; + + return resp; + } + + } +} diff --git a/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs new file mode 100644 index 0000000000..e112e0e1ff --- /dev/null +++ b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs @@ -0,0 +1,488 @@ +/* + * 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 Nini.Config; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using log4net; +using Nwc.XmlRpc; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Hypergrid +{ + public class UserAgentServerConnector : ServiceConnector + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private IUserAgentService m_HomeUsersService; + public IUserAgentService HomeUsersService + { + get { return m_HomeUsersService; } + } + + private string[] m_AuthorizedCallers; + + private bool m_VerifyCallers = false; + + public UserAgentServerConnector(IConfigSource config, IHttpServer server) : + this(config, server, (IFriendsSimConnector)null) + { + } + + public UserAgentServerConnector(IConfigSource config, IHttpServer server, string configName) : + this(config, server) + { + } + + public UserAgentServerConnector(IConfigSource config, IHttpServer server, IFriendsSimConnector friendsConnector) : + base(config, server, String.Empty) + { + IConfig gridConfig = config.Configs["UserAgentService"]; + if (gridConfig != null) + { + string serviceDll = gridConfig.GetString("LocalServiceModule", string.Empty); + + Object[] args = new Object[] { config, friendsConnector }; + m_HomeUsersService = ServerUtils.LoadPlugin(serviceDll, args); + } + if (m_HomeUsersService == null) + throw new Exception("UserAgent server connector cannot proceed because of missing service"); + + string loginServerIP = gridConfig.GetString("LoginServerIP", "127.0.0.1"); + bool proxy = gridConfig.GetBoolean("HasProxy", false); + + m_VerifyCallers = gridConfig.GetBoolean("VerifyCallers", false); + string csv = gridConfig.GetString("AuthorizedCallers", "127.0.0.1"); + csv = csv.Replace(" ", ""); + m_AuthorizedCallers = csv.Split(','); + + server.AddXmlRPCHandler("agent_is_coming_home", AgentIsComingHome, false); + server.AddXmlRPCHandler("get_home_region", GetHomeRegion, false); + server.AddXmlRPCHandler("verify_agent", VerifyAgent, false); + server.AddXmlRPCHandler("verify_client", VerifyClient, false); + server.AddXmlRPCHandler("logout_agent", LogoutAgent, false); + +#pragma warning disable 0612 + server.AddXmlRPCHandler("status_notification", StatusNotification, false); + server.AddXmlRPCHandler("get_online_friends", GetOnlineFriends, false); +#pragma warning restore 0612 + server.AddXmlRPCHandler("get_user_info", GetUserInfo, false); + server.AddXmlRPCHandler("get_server_urls", GetServerURLs, false); + + server.AddXmlRPCHandler("locate_user", LocateUser, false); + server.AddXmlRPCHandler("get_uui", GetUUI, false); + server.AddXmlRPCHandler("get_uuid", GetUUID, false); + + server.AddStreamHandler(new HomeAgentHandler(m_HomeUsersService, loginServerIP, proxy)); + } + + public XmlRpcResponse GetHomeRegion(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + Vector3 position = Vector3.UnitY, lookAt = Vector3.UnitY; + GridRegion regInfo = m_HomeUsersService.GetHomeRegion(userID, out position, out lookAt); + + Hashtable hash = new Hashtable(); + if (regInfo == null) + hash["result"] = "false"; + else + { + hash["result"] = "true"; + hash["uuid"] = regInfo.RegionID.ToString(); + hash["x"] = regInfo.RegionLocX.ToString(); + hash["y"] = regInfo.RegionLocY.ToString(); + hash["size_x"] = regInfo.RegionSizeX.ToString(); + hash["size_y"] = regInfo.RegionSizeY.ToString(); + hash["region_name"] = regInfo.RegionName; + hash["hostname"] = regInfo.ExternalHostName; + hash["http_port"] = regInfo.HttpPort.ToString(); + hash["internal_port"] = regInfo.InternalEndPoint.Port.ToString(); + hash["position"] = position.ToString(); + hash["lookAt"] = lookAt.ToString(); + } + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + public XmlRpcResponse AgentIsComingHome(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string sessionID_str = (string)requestData["sessionID"]; + UUID sessionID = UUID.Zero; + UUID.TryParse(sessionID_str, out sessionID); + string gridName = (string)requestData["externalName"]; + + bool success = m_HomeUsersService.IsAgentComingHome(sessionID, gridName); + + Hashtable hash = new Hashtable(); + hash["result"] = success.ToString(); + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + public XmlRpcResponse VerifyAgent(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string sessionID_str = (string)requestData["sessionID"]; + UUID sessionID = UUID.Zero; + UUID.TryParse(sessionID_str, out sessionID); + string token = (string)requestData["token"]; + + bool success = m_HomeUsersService.VerifyAgent(sessionID, token); + + Hashtable hash = new Hashtable(); + hash["result"] = success.ToString(); + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + public XmlRpcResponse VerifyClient(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string sessionID_str = (string)requestData["sessionID"]; + UUID sessionID = UUID.Zero; + UUID.TryParse(sessionID_str, out sessionID); + string token = (string)requestData["token"]; + + bool success = m_HomeUsersService.VerifyClient(sessionID, token); + + Hashtable hash = new Hashtable(); + hash["result"] = success.ToString(); + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + public XmlRpcResponse LogoutAgent(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + string sessionID_str = (string)requestData["sessionID"]; + UUID sessionID = UUID.Zero; + UUID.TryParse(sessionID_str, out sessionID); + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + m_HomeUsersService.LogoutAgent(userID, sessionID); + + Hashtable hash = new Hashtable(); + hash["result"] = "true"; + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + [Obsolete] + public XmlRpcResponse StatusNotification(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + hash["result"] = "false"; + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("userID") && requestData.ContainsKey("online")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + List ids = new List(); + foreach (object key in requestData.Keys) + { + if (key is string && ((string)key).StartsWith("friend_") && requestData[key] != null) + ids.Add(requestData[key].ToString()); + } + bool online = false; + bool.TryParse(requestData["online"].ToString(), out online); + + // let's spawn a thread for this, because it may take a long time... + List friendsOnline = m_HomeUsersService.StatusNotification(ids, userID, online); + if (friendsOnline.Count > 0) + { + int i = 0; + foreach (UUID id in friendsOnline) + { + hash["friend_" + i.ToString()] = id.ToString(); + i++; + } + } + else + hash["result"] = "No Friends Online"; + + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + [Obsolete] + public XmlRpcResponse GetOnlineFriends(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("userID")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + List ids = new List(); + foreach (object key in requestData.Keys) + { + if (key is string && ((string)key).StartsWith("friend_") && requestData[key] != null) + ids.Add(requestData[key].ToString()); + } + + //List online = m_HomeUsersService.GetOnlineFriends(userID, ids); + //if (online.Count > 0) + //{ + // int i = 0; + // foreach (UUID id in online) + // { + // hash["friend_" + i.ToString()] = id.ToString(); + // i++; + // } + //} + //else + // hash["result"] = "No Friends Online"; + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + public XmlRpcResponse GetUserInfo(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + Hashtable requestData = (Hashtable)request.Params[0]; + + // This needs checking! + if (requestData.ContainsKey("userID")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + //int userFlags = m_HomeUsersService.GetUserFlags(userID); + Dictionary userInfo = m_HomeUsersService.GetUserInfo(userID); + if (userInfo.Count > 0) + { + foreach (KeyValuePair kvp in userInfo) + { + hash[kvp.Key] = kvp.Value; + } + } + else + { + hash["result"] = "failure"; + } + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + } + + public XmlRpcResponse GetServerURLs(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("userID")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + Dictionary serverURLs = m_HomeUsersService.GetServerURLs(userID); + if (serverURLs.Count > 0) + { + foreach (KeyValuePair kvp in serverURLs) + hash["SRV_" + kvp.Key] = kvp.Value.ToString(); + } + else + hash["result"] = "No Service URLs"; + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + /// + /// Locates the user. + /// This is a sensitive operation, only authorized IP addresses can perform it. + /// + /// + /// + /// + public XmlRpcResponse LocateUser(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + bool authorized = true; + if (m_VerifyCallers) + { + authorized = false; + foreach (string s in m_AuthorizedCallers) + if (s == remoteClient.Address.ToString()) + { + authorized = true; + break; + } + } + + if (authorized) + { + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("userID")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + string url = m_HomeUsersService.LocateUser(userID); + if (url != string.Empty) + hash["URL"] = url; + else + hash["result"] = "Unable to locate user"; + } + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + + } + + /// + /// Returns the UUI of a user given a UUID. + /// + /// + /// + /// + public XmlRpcResponse GetUUI(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("userID") && requestData.ContainsKey("targetUserID")) + { + string userID_str = (string)requestData["userID"]; + UUID userID = UUID.Zero; + UUID.TryParse(userID_str, out userID); + + string tuserID_str = (string)requestData["targetUserID"]; + UUID targetUserID = UUID.Zero; + UUID.TryParse(tuserID_str, out targetUserID); + string uui = m_HomeUsersService.GetUUI(userID, targetUserID); + if (uui != string.Empty) + hash["UUI"] = uui; + else + hash["result"] = "User unknown"; + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + } + + /// + /// Gets the UUID of a user given First name, Last name. + /// + /// + /// + /// + public XmlRpcResponse GetUUID(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable hash = new Hashtable(); + + Hashtable requestData = (Hashtable)request.Params[0]; + //string host = (string)requestData["host"]; + //string portstr = (string)requestData["port"]; + if (requestData.ContainsKey("first") && requestData.ContainsKey("last")) + { + string first = (string)requestData["first"]; + string last = (string)requestData["last"]; + UUID uuid = m_HomeUsersService.GetUUID(first, last); + hash["UUID"] = uuid.ToString(); + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs b/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs new file mode 100644 index 0000000000..b295446c19 --- /dev/null +++ b/OpenSim/Server/Handlers/Inventory/InventoryServerInConnector.cs @@ -0,0 +1,330 @@ +/* + * 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 Nwc.XmlRpc; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Inventory +{ + public class InventoryServiceInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected IInventoryService m_InventoryService; + + private bool m_doLookup = false; + + //private static readonly int INVENTORY_DEFAULT_SESSION_TIME = 30; // secs + //private AuthedSessionCache m_session_cache = new AuthedSessionCache(INVENTORY_DEFAULT_SESSION_TIME); + + private string m_userserver_url; + protected string m_ConfigName = "InventoryService"; + + public InventoryServiceInConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != string.Empty) + m_ConfigName = configName; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string inventoryService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (inventoryService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_InventoryService = + ServerUtils.LoadPlugin(inventoryService, args); + + m_userserver_url = serverConfig.GetString("UserServerURI", String.Empty); + m_doLookup = serverConfig.GetBoolean("SessionAuthentication", false); + + AddHttpHandlers(server); + m_log.Debug("[INVENTORY HANDLER]: handlers initialized"); + } + + protected virtual void AddHttpHandlers(IHttpServer m_httpServer) + { + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler>( + "POST", "/SystemFolders/", GetSystemFolders, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/GetFolderContent/", GetFolderContent, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/UpdateFolder/", m_InventoryService.UpdateFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/MoveFolder/", m_InventoryService.MoveFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/PurgeFolder/", m_InventoryService.PurgeFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler, bool>( + "POST", "/DeleteFolders/", DeleteFolders, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler, bool>( + "POST", "/DeleteItem/", DeleteItems, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/QueryItem/", m_InventoryService.GetItem, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/QueryFolder/", m_InventoryService.GetFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler( + "POST", "/CreateInventory/", CreateUsersInventory, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/NewFolder/", m_InventoryService.AddFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/CreateFolder/", m_InventoryService.AddFolder, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler( + "POST", "/NewItem/", m_InventoryService.AddItem, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler( + "POST", "/AddNewItem/", m_InventoryService.AddItem, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler>( + "POST", "/GetItems/", GetFolderItems, CheckAuthSession)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseSecureHandler, bool>( + "POST", "/MoveItems/", MoveItems, CheckAuthSession)); + + m_httpServer.AddStreamHandler(new InventoryServerMoveItemsHandler(m_InventoryService)); + + + // for persistent active gestures + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler> + ("POST", "/ActiveGestures/", GetActiveGestures, CheckTrustSource)); + + // WARNING: Root folders no longer just delivers the root and immediate child folders (e.g + // system folders such as Objects, Textures), but it now returns the entire inventory skeleton. + // It would have been better to rename this request, but complexities in the BaseHttpServer + // (e.g. any http request not found is automatically treated as an xmlrpc request) make it easier + // to do this for now. + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler> + ("POST", "/RootFolders/", GetInventorySkeleton, CheckTrustSource)); + + m_httpServer.AddStreamHandler( + new RestDeserialiseTrustedHandler + ("POST", "/AssetPermissions/", GetAssetPermissions, CheckTrustSource)); + + } + + #region Wrappers for converting the Guid parameter + + public List GetSystemFolders(Guid guid) + { + UUID userID = new UUID(guid); + return new List(GetSystemFolders(userID).Values); + } + + // This shouldn't be here, it should be in the inventory service. + // But I don't want to deal with types and dependencies for now. + private Dictionary GetSystemFolders(UUID userID) + { + InventoryFolderBase root = m_InventoryService.GetRootFolder(userID); + if (root != null) + { + InventoryCollection content = m_InventoryService.GetFolderContent(userID, root.ID); + if (content != null) + { + Dictionary folders = new Dictionary(); + foreach (InventoryFolderBase folder in content.Folders) + { + if ((folder.Type != (short)AssetType.Folder) && (folder.Type != (short)AssetType.Unknown)) + folders[(AssetType)folder.Type] = folder; + } + // Put the root folder there, as type Folder + folders[AssetType.Folder] = root; + return folders; + } + } + m_log.WarnFormat("[INVENTORY SERVICE]: System folders for {0} not found", userID); + return new Dictionary(); + } + + public InventoryCollection GetFolderContent(Guid guid) + { + return m_InventoryService.GetFolderContent(UUID.Zero, new UUID(guid)); + } + + public List GetFolderItems(Guid folderID) + { + List allItems = new List(); + + // TODO: UUID.Zero is passed as the userID here, making the old assumption that the OpenSim + // inventory server only has a single inventory database and not per-user inventory databases. + // This could be changed but it requirs a bit of hackery to pass another parameter into this + // callback + List items = m_InventoryService.GetFolderItems(UUID.Zero, new UUID(folderID)); + + if (items != null) + { + allItems.InsertRange(0, items); + } + return allItems; + } + + public bool CreateUsersInventory(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + + + return m_InventoryService.CreateUserInventory(userID); + } + + public List GetActiveGestures(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + + return m_InventoryService.GetActiveGestures(userID); + } + + public List GetInventorySkeleton(Guid rawUserID) + { + UUID userID = new UUID(rawUserID); + return m_InventoryService.GetInventorySkeleton(userID); + } + + public int GetAssetPermissions(InventoryItemBase item) + { + return m_InventoryService.GetAssetPermissions(item.Owner, item.AssetID); + } + + public bool DeleteFolders(List items) + { + List uuids = new List(); + foreach (Guid g in items) + uuids.Add(new UUID(g)); + // oops we lost the user info here. Bad bad handlers + return m_InventoryService.DeleteFolders(UUID.Zero, uuids); + } + + public bool DeleteItems(List items) + { + List uuids = new List(); + foreach (Guid g in items) + uuids.Add(new UUID(g)); + // oops we lost the user info here. Bad bad handlers + return m_InventoryService.DeleteItems(UUID.Zero, uuids); + } + + public bool MoveItems(List items) + { + // oops we lost the user info here. Bad bad handlers + // let's peek at one item + UUID ownerID = UUID.Zero; + if (items.Count > 0) + ownerID = items[0].Owner; + return m_InventoryService.MoveItems(ownerID, items); + } + #endregion + + /// + /// Check that the source of an inventory request is one that we trust. + /// + /// + /// + public bool CheckTrustSource(IPEndPoint peer) + { + if (m_doLookup) + { + m_log.InfoFormat("[INVENTORY IN CONNECTOR]: Checking trusted source {0}", peer); + UriBuilder ub = new UriBuilder(m_userserver_url); + IPAddress[] uaddrs = Dns.GetHostAddresses(ub.Host); + foreach (IPAddress uaddr in uaddrs) + { + if (uaddr.Equals(peer.Address)) + { + return true; + } + } + + m_log.WarnFormat( + "[INVENTORY IN CONNECTOR]: Rejecting request since source {0} was not in the list of trusted sources", + peer); + + return false; + } + else + { + return true; + } + } + + /// + /// Check that the source of an inventory request for a particular agent is a current session belonging to + /// that agent. + /// + /// + /// + /// + public virtual bool CheckAuthSession(string session_id, string avatar_id) + { + return true; + } + + } +} diff --git a/OpenSim/Server/Handlers/Inventory/InventoryServerMoveItemsHandler.cs b/OpenSim/Server/Handlers/Inventory/InventoryServerMoveItemsHandler.cs new file mode 100644 index 0000000000..e2c50fe1fa --- /dev/null +++ b/OpenSim/Server/Handlers/Inventory/InventoryServerMoveItemsHandler.cs @@ -0,0 +1,81 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Collections.Generic; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Inventory +{ + public class InventoryServerMoveItemsHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + + public InventoryServerMoveItemsHandler(IInventoryService service) : + base("PUT", "/inventory") + { + m_InventoryService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + XmlSerializer xs = new XmlSerializer(typeof (List)); + List items = (List)xs.Deserialize(request); + + bool result = false; + string[] p = SplitParams(path); + + if (p.Length > 0) + { + UUID ownerID = UUID.Zero; + UUID.TryParse(p[0], out ownerID); + result = m_InventoryService.MoveItems(ownerID, items); + } + else + m_log.WarnFormat("[MOVEITEMS HANDLER]: ownerID not provided in request. Unable to serve."); + + xs = new XmlSerializer(typeof(bool)); + return ServerUtils.SerializeResult(xs, result); + } + } +} diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs new file mode 100644 index 0000000000..5c4e7a9be4 --- /dev/null +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -0,0 +1,767 @@ +/* + * 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.Framework.ServiceAuth; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; +using log4net; +using OpenMetaverse; + +using System.Threading; + +namespace OpenSim.Server.Handlers.Inventory +{ + public class XInventoryInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + private string m_ConfigName = "InventoryService"; + + public XInventoryInConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + if (configName != String.Empty) + m_ConfigName = configName; + + m_log.DebugFormat("[XInventoryInConnector]: Starting with config name {0}", m_ConfigName); + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section '{0}' in config file", m_ConfigName)); + + string inventoryService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (inventoryService == String.Empty) + throw new Exception("No InventoryService in config file"); + + Object[] args = new Object[] { config, m_ConfigName }; + m_InventoryService = + ServerUtils.LoadPlugin(inventoryService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new XInventoryConnectorPostHandler(m_InventoryService, auth)); + } + } + + public class XInventoryConnectorPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IInventoryService m_InventoryService; + + public XInventoryConnectorPostHandler(IInventoryService service, IServiceAuth auth) : + base("POST", "/xinventory", auth) + { + m_InventoryService = 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 request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + string method = request["METHOD"].ToString(); + request.Remove("METHOD"); + + switch (method) + { + case "CREATEUSERINVENTORY": + return HandleCreateUserInventory(request); + case "GETINVENTORYSKELETON": + return HandleGetInventorySkeleton(request); + case "GETROOTFOLDER": + return HandleGetRootFolder(request); + case "GETFOLDERFORTYPE": + return HandleGetFolderForType(request); + case "GETFOLDERCONTENT": + return HandleGetFolderContent(request); + case "GETMULTIPLEFOLDERSCONTENT": + return HandleGetMultipleFoldersContent(request); + case "GETFOLDERITEMS": + return HandleGetFolderItems(request); + case "ADDFOLDER": + return HandleAddFolder(request); + case "UPDATEFOLDER": + return HandleUpdateFolder(request); + case "MOVEFOLDER": + return HandleMoveFolder(request); + case "DELETEFOLDERS": + return HandleDeleteFolders(request); + case "PURGEFOLDER": + return HandlePurgeFolder(request); + case "ADDITEM": + return HandleAddItem(request); + case "UPDATEITEM": + return HandleUpdateItem(request); + case "MOVEITEMS": + return HandleMoveItems(request); + case "DELETEITEMS": + return HandleDeleteItems(request); + case "GETITEM": + return HandleGetItem(request); + case "GETMULTIPLEITEMS": + return HandleGetMultipleItems(request); + case "GETFOLDER": + return HandleGetFolder(request); + case "GETACTIVEGESTURES": + return HandleGetActiveGestures(request); + case "GETASSETPERMISSIONS": + return HandleGetAssetPermissions(request); + } + m_log.DebugFormat("[XINVENTORY HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.Error(string.Format("[XINVENTORY HANDLER]: Exception {0} ", e.Message), e); + } + + return FailureResult(); + } + + 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); + } + + byte[] HandleCreateUserInventory(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("PRINCIPAL")) + return FailureResult(); + + if (m_InventoryService.CreateUserInventory(new UUID(request["PRINCIPAL"].ToString()))) + result["RESULT"] = "True"; + else + result["RESULT"] = "False"; + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetInventorySkeleton(Dictionary request) + { + Dictionary result = new Dictionary(); + + if (!request.ContainsKey("PRINCIPAL")) + return FailureResult(); + + + List folders = m_InventoryService.GetInventorySkeleton(new UUID(request["PRINCIPAL"].ToString())); + + Dictionary sfolders = new Dictionary(); + if (folders != null) + { + int i = 0; + foreach (InventoryFolderBase f in folders) + { + sfolders["folder_" + i.ToString()] = EncodeFolder(f); + i++; + } + } + result["FOLDERS"] = sfolders; + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetRootFolder(Dictionary request) + { + Dictionary result = new Dictionary(); + + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + InventoryFolderBase rfolder = m_InventoryService.GetRootFolder(principal); + if (rfolder != null) + result["folder"] = EncodeFolder(rfolder); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetFolderForType(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + int type = 0; + Int32.TryParse(request["TYPE"].ToString(), out type); + InventoryFolderBase folder = m_InventoryService.GetFolderForType(principal, (FolderType)type); + if (folder != null) + result["folder"] = EncodeFolder(folder); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetFolderContent(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + UUID folderID = UUID.Zero; + UUID.TryParse(request["FOLDER"].ToString(), out folderID); + + InventoryCollection icoll = m_InventoryService.GetFolderContent(principal, folderID); + if (icoll != null) + { + result["FID"] = icoll.FolderID.ToString(); + result["VERSION"] = icoll.Version.ToString(); + Dictionary folders = new Dictionary(); + int i = 0; + if (icoll.Folders != null) + { + foreach (InventoryFolderBase f in icoll.Folders) + { + folders["folder_" + i.ToString()] = EncodeFolder(f); + i++; + } + result["FOLDERS"] = folders; + } + if (icoll.Items != null) + { + i = 0; + Dictionary items = new Dictionary(); + foreach (InventoryItemBase it in icoll.Items) + { + items["item_" + i.ToString()] = EncodeItem(it); + i++; + } + result["ITEMS"] = items; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetMultipleFoldersContent(Dictionary request) + { + Dictionary resultSet = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + string folderIDstr = request["FOLDERS"].ToString(); + int count = 0; + Int32.TryParse(request["COUNT"].ToString(), out count); + + UUID[] fids = new UUID[count]; + string[] uuids = folderIDstr.Split(','); + int i = 0; + foreach (string id in uuids) + { + UUID fid = UUID.Zero; + if (UUID.TryParse(id, out fid)) + fids[i] = fid; + i += 1; + } + + count = 0; + InventoryCollection[] icollList = m_InventoryService.GetMultipleFoldersContent(principal, fids); + if (icollList != null && icollList.Length > 0) + { + foreach (InventoryCollection icoll in icollList) + { + Dictionary result = new Dictionary(); + result["FID"] = icoll.FolderID.ToString(); + result["VERSION"] = icoll.Version.ToString(); + result["OWNER"] = icoll.OwnerID.ToString(); + Dictionary folders = new Dictionary(); + i = 0; + if (icoll.Folders != null) + { + foreach (InventoryFolderBase f in icoll.Folders) + { + folders["folder_" + i.ToString()] = EncodeFolder(f); + i++; + } + result["FOLDERS"] = folders; + } + i = 0; + if (icoll.Items != null) + { + Dictionary items = new Dictionary(); + foreach (InventoryItemBase it in icoll.Items) + { + items["item_" + i.ToString()] = EncodeItem(it); + i++; + } + result["ITEMS"] = items; + } + + resultSet["F_" + fids[count++]] = result; + //m_log.DebugFormat("[XXX]: Sending {0} {1}", fids[count-1], icoll.FolderID); + } + } + + string xmlString = ServerUtils.BuildXmlResponse(resultSet); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetFolderItems(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + UUID folderID = UUID.Zero; + UUID.TryParse(request["FOLDER"].ToString(), out folderID); + + List items = m_InventoryService.GetFolderItems(principal, folderID); + Dictionary sitems = new Dictionary(); + + if (items != null) + { + int i = 0; + foreach (InventoryItemBase item in items) + { + sitems["item_" + i.ToString()] = EncodeItem(item); + i++; + } + } + result["ITEMS"] = sitems; + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleAddFolder(Dictionary request) + { + InventoryFolderBase folder = BuildFolder(request); + + if (m_InventoryService.AddFolder(folder)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleUpdateFolder(Dictionary request) + { + InventoryFolderBase folder = BuildFolder(request); + + if (m_InventoryService.UpdateFolder(folder)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleMoveFolder(Dictionary request) + { + UUID parentID = UUID.Zero; + UUID.TryParse(request["ParentID"].ToString(), out parentID); + UUID folderID = UUID.Zero; + UUID.TryParse(request["ID"].ToString(), out folderID); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + + InventoryFolderBase folder = new InventoryFolderBase(folderID, "", principal, parentID); + if (m_InventoryService.MoveFolder(folder)) + return SuccessResult(); + else + return FailureResult(); + + } + + byte[] HandleDeleteFolders(Dictionary request) + { + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + List slist = (List)request["FOLDERS"]; + List uuids = new List(); + foreach (string s in slist) + { + UUID u = UUID.Zero; + if (UUID.TryParse(s, out u)) + uuids.Add(u); + } + + if (m_InventoryService.DeleteFolders(principal, uuids)) + return SuccessResult(); + else + return + FailureResult(); + } + + byte[] HandlePurgeFolder(Dictionary request) + { + UUID folderID = UUID.Zero; + UUID.TryParse(request["ID"].ToString(), out folderID); + + InventoryFolderBase folder = new InventoryFolderBase(folderID); + if (m_InventoryService.PurgeFolder(folder)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleAddItem(Dictionary request) + { + InventoryItemBase item = BuildItem(request); + + if (m_InventoryService.AddItem(item)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleUpdateItem(Dictionary request) + { + InventoryItemBase item = BuildItem(request); + + if (m_InventoryService.UpdateItem(item)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleMoveItems(Dictionary request) + { + List idlist = (List)request["IDLIST"]; + List destlist = (List)request["DESTLIST"]; + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + + List items = new List(); + int n = 0; + try + { + foreach (string s in idlist) + { + UUID u = UUID.Zero; + if (UUID.TryParse(s, out u)) + { + UUID fid = UUID.Zero; + if (UUID.TryParse(destlist[n++], out fid)) + { + InventoryItemBase item = new InventoryItemBase(u, principal); + item.Folder = fid; + items.Add(item); + } + } + } + } + catch (Exception e) + { + m_log.DebugFormat("[XINVENTORY IN CONNECTOR]: Exception in HandleMoveItems: {0}", e.Message); + return FailureResult(); + } + + if (m_InventoryService.MoveItems(principal, items)) + return SuccessResult(); + else + return FailureResult(); + } + + byte[] HandleDeleteItems(Dictionary request) + { + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + List slist = (List)request["ITEMS"]; + List uuids = new List(); + foreach (string s in slist) + { + UUID u = UUID.Zero; + if (UUID.TryParse(s, out u)) + uuids.Add(u); + } + + if (m_InventoryService.DeleteItems(principal, uuids)) + return SuccessResult(); + else + return + FailureResult(); + } + + byte[] HandleGetItem(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID id = UUID.Zero; + UUID.TryParse(request["ID"].ToString(), out id); + + InventoryItemBase item = new InventoryItemBase(id); + item = m_InventoryService.GetItem(item); + if (item != null) + result["item"] = EncodeItem(item); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetMultipleItems(Dictionary request) + { + Dictionary resultSet = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + string itemIDstr = request["ITEMS"].ToString(); + int count = 0; + Int32.TryParse(request["COUNT"].ToString(), out count); + + UUID[] fids = new UUID[count]; + string[] uuids = itemIDstr.Split(','); + int i = 0; + foreach (string id in uuids) + { + UUID fid = UUID.Zero; + if (UUID.TryParse(id, out fid)) + fids[i] = fid; + i += 1; + } + + InventoryItemBase[] itemsList = m_InventoryService.GetMultipleItems(principal, fids); + if (itemsList != null && itemsList.Length > 0) + { + count = 0; + foreach (InventoryItemBase item in itemsList) + resultSet["item_" + count++] = (item == null) ? (object)"NULL" : EncodeItem(item); + } + + string xmlString = ServerUtils.BuildXmlResponse(resultSet); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetFolder(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID id = UUID.Zero; + UUID.TryParse(request["ID"].ToString(), out id); + + InventoryFolderBase folder = new InventoryFolderBase(id); + folder = m_InventoryService.GetFolder(folder); + if (folder != null) + result["folder"] = EncodeFolder(folder); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetActiveGestures(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + + List gestures = m_InventoryService.GetActiveGestures(principal); + Dictionary items = new Dictionary(); + if (gestures != null) + { + int i = 0; + foreach (InventoryItemBase item in gestures) + { + items["item_" + i.ToString()] = EncodeItem(item); + i++; + } + } + result["ITEMS"] = items; + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] HandleGetAssetPermissions(Dictionary request) + { + Dictionary result = new Dictionary(); + UUID principal = UUID.Zero; + UUID.TryParse(request["PRINCIPAL"].ToString(), out principal); + UUID assetID = UUID.Zero; + UUID.TryParse(request["ASSET"].ToString(), out assetID); + + int perms = m_InventoryService.GetAssetPermissions(principal, assetID); + + result["RESULT"] = perms.ToString(); + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + private Dictionary EncodeFolder(InventoryFolderBase f) + { + Dictionary ret = new Dictionary(); + + ret["ParentID"] = f.ParentID.ToString(); + ret["Type"] = f.Type.ToString(); + ret["Version"] = f.Version.ToString(); + ret["Name"] = f.Name; + ret["Owner"] = f.Owner.ToString(); + ret["ID"] = f.ID.ToString(); + + return ret; + } + + private Dictionary EncodeItem(InventoryItemBase item) + { + Dictionary ret = new Dictionary(); + + ret["AssetID"] = item.AssetID.ToString(); + ret["AssetType"] = item.AssetType.ToString(); + ret["BasePermissions"] = item.BasePermissions.ToString(); + ret["CreationDate"] = item.CreationDate.ToString(); + if (item.CreatorId != null) + ret["CreatorId"] = item.CreatorId.ToString(); + else + ret["CreatorId"] = String.Empty; + if (item.CreatorData != null) + ret["CreatorData"] = item.CreatorData; + else + ret["CreatorData"] = String.Empty; + ret["CurrentPermissions"] = item.CurrentPermissions.ToString(); + ret["Description"] = item.Description.ToString(); + ret["EveryOnePermissions"] = item.EveryOnePermissions.ToString(); + ret["Flags"] = item.Flags.ToString(); + ret["Folder"] = item.Folder.ToString(); + ret["GroupID"] = item.GroupID.ToString(); + ret["GroupOwned"] = item.GroupOwned.ToString(); + ret["GroupPermissions"] = item.GroupPermissions.ToString(); + ret["ID"] = item.ID.ToString(); + ret["InvType"] = item.InvType.ToString(); + ret["Name"] = item.Name.ToString(); + ret["NextPermissions"] = item.NextPermissions.ToString(); + ret["Owner"] = item.Owner.ToString(); + ret["SalePrice"] = item.SalePrice.ToString(); + ret["SaleType"] = item.SaleType.ToString(); + + return ret; + } + + private InventoryFolderBase BuildFolder(Dictionary data) + { + InventoryFolderBase folder = new InventoryFolderBase(); + + folder.ParentID = new UUID(data["ParentID"].ToString()); + folder.Type = short.Parse(data["Type"].ToString()); + folder.Version = ushort.Parse(data["Version"].ToString()); + folder.Name = data["Name"].ToString(); + folder.Owner = new UUID(data["Owner"].ToString()); + folder.ID = new UUID(data["ID"].ToString()); + + return folder; + } + + private InventoryItemBase BuildItem(Dictionary data) + { + InventoryItemBase item = new InventoryItemBase(); + + item.AssetID = new UUID(data["AssetID"].ToString()); + item.AssetType = int.Parse(data["AssetType"].ToString()); + item.Name = data["Name"].ToString(); + item.Owner = new UUID(data["Owner"].ToString()); + item.ID = new UUID(data["ID"].ToString()); + item.InvType = int.Parse(data["InvType"].ToString()); + item.Folder = new UUID(data["Folder"].ToString()); + item.CreatorId = data["CreatorId"].ToString(); + item.CreatorData = data["CreatorData"].ToString(); + item.Description = data["Description"].ToString(); + item.NextPermissions = uint.Parse(data["NextPermissions"].ToString()); + item.CurrentPermissions = uint.Parse(data["CurrentPermissions"].ToString()); + item.BasePermissions = uint.Parse(data["BasePermissions"].ToString()); + item.EveryOnePermissions = uint.Parse(data["EveryOnePermissions"].ToString()); + item.GroupPermissions = uint.Parse(data["GroupPermissions"].ToString()); + item.GroupID = new UUID(data["GroupID"].ToString()); + item.GroupOwned = bool.Parse(data["GroupOwned"].ToString()); + item.SalePrice = int.Parse(data["SalePrice"].ToString()); + item.SaleType = byte.Parse(data["SaleType"].ToString()); + item.Flags = uint.Parse(data["Flags"].ToString()); + item.CreationDate = int.Parse(data["CreationDate"].ToString()); + + return item; + } + + } +} diff --git a/OpenSim/Server/Handlers/Land/LandHandlers.cs b/OpenSim/Server/Handlers/Land/LandHandlers.cs new file mode 100644 index 0000000000..b45289a788 --- /dev/null +++ b/OpenSim/Server/Handlers/Land/LandHandlers.cs @@ -0,0 +1,96 @@ +/* + * 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.IO; +using System.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nwc.XmlRpc; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Land +{ + public class LandHandlers + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ILandService m_LocalService; + + public LandHandlers(ILandService service) + { + m_LocalService = service; + } + + public XmlRpcResponse GetLandData(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + ulong regionHandle = Convert.ToUInt64(requestData["region_handle"]); + uint x = Convert.ToUInt32(requestData["x"]); + uint y = Convert.ToUInt32(requestData["y"]); + m_log.DebugFormat("[LAND HANDLER]: Got request for land data at {0}, {1} for region {2}", x, y, regionHandle); + + byte regionAccess; + LandData landData = m_LocalService.GetLandData(UUID.Zero, regionHandle, x, y, out regionAccess); + Hashtable hash = new Hashtable(); + if (landData != null) + { + // for now, only push out the data we need for answering a ParcelInfoReqeust + hash["AABBMax"] = landData.AABBMax.ToString(); + hash["AABBMin"] = landData.AABBMin.ToString(); + hash["Area"] = landData.Area.ToString(); + hash["AuctionID"] = landData.AuctionID.ToString(); + hash["Description"] = landData.Description; + hash["Flags"] = landData.Flags.ToString(); + hash["GlobalID"] = landData.GlobalID.ToString(); + hash["Name"] = landData.Name; + hash["OwnerID"] = landData.OwnerID.ToString(); + hash["SalePrice"] = landData.SalePrice.ToString(); + hash["SnapshotID"] = landData.SnapshotID.ToString(); + hash["UserLocation"] = landData.UserLocation.ToString(); + hash["RegionAccess"] = regionAccess.ToString(); + } + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + return response; + } + } + +} diff --git a/OpenSim/Server/Handlers/Land/LandServiceInConnector.cs b/OpenSim/Server/Handlers/Land/LandServiceInConnector.cs new file mode 100644 index 0000000000..d368bd39ad --- /dev/null +++ b/OpenSim/Server/Handlers/Land/LandServiceInConnector.cs @@ -0,0 +1,66 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Land +{ + public class LandServiceInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ILandService m_LandService; + // TODO : private IAuthenticationService m_AuthenticationService; + + public LandServiceInConnector(IConfigSource source, IHttpServer server, ILandService service, IScene scene) : + base(source, server, String.Empty) + { + m_LandService = service; + if (m_LandService == null) + { + m_log.Error("[LAND IN CONNECTOR]: Land service was not provided"); + return; + } + + //bool authentication = neighbourConfig.GetBoolean("RequireAuthentication", false); + //if (authentication) + // m_AuthenticationService = scene.RequestModuleInterface(); + + LandHandlers landHandlers = new LandHandlers(m_LandService); + server.AddXmlRPCHandler("land_data", landHandlers.GetLandData, false); + } + } +} diff --git a/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs new file mode 100644 index 0000000000..f2a5678852 --- /dev/null +++ b/OpenSim/Server/Handlers/Login/LLLoginHandlers.cs @@ -0,0 +1,309 @@ +/* + * 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.IO; +using System.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nwc.XmlRpc; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Login +{ + public class LLLoginHandlers + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ILoginService m_LocalService; + private bool m_Proxy; + + + public LLLoginHandlers(ILoginService service, bool hasProxy) + { + m_LocalService = service; + m_Proxy = hasProxy; + } + + public XmlRpcResponse HandleXMLRPCLogin(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + if (m_Proxy && request.Params[3] != null) + { + IPEndPoint ep = Util.GetClientIPFromXFF((string)request.Params[3]); + if (ep != null) + // Bang! + remoteClient = ep; + } + + if (requestData != null) + { + // Debug code to show exactly what login parameters the viewer is sending us. + // TODO: Extract into a method that can be generally applied if one doesn't already exist. +// foreach (string key in requestData.Keys) +// { +// object value = requestData[key]; +// Console.WriteLine("{0}:{1}", key, value); +// if (value is ArrayList) +// { +// ICollection col = value as ICollection; +// foreach (object item in col) +// Console.WriteLine(" {0}", item); +// } +// } + + if (requestData.ContainsKey("first") && requestData["first"] != null && + requestData.ContainsKey("last") && requestData["last"] != null && ( + (requestData.ContainsKey("passwd") && requestData["passwd"] != null) || + (!requestData.ContainsKey("passwd") && requestData.ContainsKey("web_login_key") && requestData["web_login_key"] != null && requestData["web_login_key"].ToString() != UUID.Zero.ToString()) + )) + { + string first = requestData["first"].ToString(); + string last = requestData["last"].ToString(); + string passwd = null; + if (requestData.ContainsKey("passwd")) + { + passwd = requestData["passwd"].ToString(); + } + else if (requestData.ContainsKey("web_login_key")) + { + passwd = "$1$" + requestData["web_login_key"].ToString(); + m_log.InfoFormat("[LOGIN]: XMLRPC Login Req key {0}", passwd); + } + string startLocation = string.Empty; + UUID scopeID = UUID.Zero; + if (requestData["scope_id"] != null) + scopeID = new UUID(requestData["scope_id"].ToString()); + if (requestData.ContainsKey("start")) + startLocation = requestData["start"].ToString(); + + string clientVersion = "Unknown"; + if (requestData.Contains("version") && requestData["version"] != null) + clientVersion = requestData["version"].ToString(); + // We should do something interesting with the client version... + + string channel = "Unknown"; + if (requestData.Contains("channel") && requestData["channel"] != null) + channel = requestData["channel"].ToString(); + + string mac = "Unknown"; + if (requestData.Contains("mac") && requestData["mac"] != null) + mac = requestData["mac"].ToString(); + + string id0 = "Unknown"; + if (requestData.Contains("id0") && requestData["id0"] != null) + id0 = requestData["id0"].ToString(); + + //m_log.InfoFormat("[LOGIN]: XMLRPC Login Requested for {0} {1}, starting in {2}, using {3}", first, last, startLocation, clientVersion); + + LoginResponse reply = null; + reply = m_LocalService.Login(first, last, passwd, startLocation, scopeID, clientVersion, channel, mac, id0, remoteClient); + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = reply.ToHashtable(); + return response; + + } + } + + return FailedXMLRPCResponse(); + + } + public XmlRpcResponse HandleXMLRPCLoginBlocked(XmlRpcRequest request, IPEndPoint client) + { + XmlRpcResponse response = new XmlRpcResponse(); + Hashtable resp = new Hashtable(); + + resp["reason"] = "presence"; + resp["message"] = "Logins are currently restricted. Please try again later."; + resp["login"] = "false"; + response.Value = resp; + return response; + } + + public XmlRpcResponse HandleXMLRPCSetLoginLevel(XmlRpcRequest request, IPEndPoint remoteClient) + { + Hashtable requestData = (Hashtable)request.Params[0]; + + if (requestData != null) + { + if (requestData.ContainsKey("first") && requestData["first"] != null && + requestData.ContainsKey("last") && requestData["last"] != null && + requestData.ContainsKey("level") && requestData["level"] != null && + requestData.ContainsKey("passwd") && requestData["passwd"] != null) + { + string first = requestData["first"].ToString(); + string last = requestData["last"].ToString(); + string passwd = requestData["passwd"].ToString(); + int level = Int32.Parse(requestData["level"].ToString()); + + m_log.InfoFormat("[LOGIN]: XMLRPC Set Level to {2} Requested by {0} {1}", first, last, level); + + Hashtable reply = m_LocalService.SetLevel(first, last, passwd, level, remoteClient); + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = reply; + + return response; + + } + } + + XmlRpcResponse failResponse = new XmlRpcResponse(); + Hashtable failHash = new Hashtable(); + failHash["success"] = "false"; + failResponse.Value = failHash; + + return failResponse; + + } + + public OSD HandleLLSDLogin(OSD request, IPEndPoint remoteClient) + { + if (request.Type == OSDType.Map) + { + OSDMap map = (OSDMap)request; + + if (map.ContainsKey("first") && map.ContainsKey("last") && map.ContainsKey("passwd")) + { + string startLocation = string.Empty; + + if (map.ContainsKey("start")) + startLocation = map["start"].AsString(); + + UUID scopeID = UUID.Zero; + + if (map.ContainsKey("scope_id")) + scopeID = new UUID(map["scope_id"].AsString()); + + m_log.Info("[LOGIN]: LLSD Login Requested for: '" + map["first"].AsString() + "' '" + map["last"].AsString() + "' / " + startLocation); + + LoginResponse reply = null; + reply = m_LocalService.Login(map["first"].AsString(), map["last"].AsString(), map["passwd"].AsString(), startLocation, scopeID, + map["version"].AsString(), map["channel"].AsString(), map["mac"].AsString(), map["id0"].AsString(), remoteClient); + return reply.ToOSDMap(); + + } + } + + return FailedOSDResponse(); + } + + public void HandleWebSocketLoginEvents(string path, WebSocketHttpServerHandler sock) + { + sock.MaxPayloadSize = 16384; //16 kb payload + sock.InitialMsgTimeout = 5000; //5 second first message to trigger at least one of these events + sock.NoDelay_TCP_Nagle = true; + sock.OnData += delegate(object sender, WebsocketDataEventArgs data) { sock.Close("fail"); }; + sock.OnPing += delegate(object sender, PingEventArgs pingdata) { sock.Close("fail"); }; + sock.OnPong += delegate(object sender, PongEventArgs pongdata) { sock.Close("fail"); }; + sock.OnText += delegate(object sender, WebsocketTextEventArgs text) + { + OSD request = null; + try + { + request = OSDParser.DeserializeJson(text.Data); + if (!(request is OSDMap)) + { + sock.SendMessage(OSDParser.SerializeJsonString(FailedOSDResponse())); + } + else + { + OSDMap req = request as OSDMap; + string first = req["firstname"].AsString(); + string last = req["lastname"].AsString(); + string passwd = req["passwd"].AsString(); + string start = req["startlocation"].AsString(); + string version = req["version"].AsString(); + string channel = req["channel"].AsString(); + string mac = req["mac"].AsString(); + string id0 = req["id0"].AsString(); + UUID scope = UUID.Zero; + IPEndPoint endPoint = + (sender as WebSocketHttpServerHandler).GetRemoteIPEndpoint(); + LoginResponse reply = null; + reply = m_LocalService.Login(first, last, passwd, start, scope, version, + channel, mac, id0, endPoint); + sock.SendMessage(OSDParser.SerializeJsonString(reply.ToOSDMap())); + + } + + } + catch (Exception) + { + sock.SendMessage(OSDParser.SerializeJsonString(FailedOSDResponse())); + } + finally + { + sock.Close("success"); + } + }; + + sock.HandshakeAndUpgrade(); + + } + + + private XmlRpcResponse FailedXMLRPCResponse() + { + Hashtable hash = new Hashtable(); + hash["reason"] = "key"; + hash["message"] = "Incomplete login credentials. Check your username and password."; + hash["login"] = "false"; + + XmlRpcResponse response = new XmlRpcResponse(); + response.Value = hash; + + return response; + } + + private OSD FailedOSDResponse() + { + OSDMap map = new OSDMap(); + + map["reason"] = OSD.FromString("key"); + map["message"] = OSD.FromString("Invalid login credentials. Check your username and passwd."); + map["login"] = OSD.FromString("false"); + + return map; + } + + } + +} diff --git a/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs new file mode 100644 index 0000000000..f60e8923db --- /dev/null +++ b/OpenSim/Server/Handlers/Login/LLLoginServiceInConnector.cs @@ -0,0 +1,117 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Login +{ + public class LLLoginServiceInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ILoginService m_LoginService; + private bool m_Proxy; + private BasicDosProtectorOptions m_DosProtectionOptions; + + public LLLoginServiceInConnector(IConfigSource config, IHttpServer server, IScene scene) : + base(config, server, String.Empty) + { + m_log.Debug("[LLLOGIN IN CONNECTOR]: Starting..."); + string loginService = ReadLocalServiceFromConfig(config); + + ISimulationService simService = scene.RequestModuleInterface(); + ILibraryService libService = scene.RequestModuleInterface(); + + Object[] args = new Object[] { config, simService, libService }; + m_LoginService = ServerUtils.LoadPlugin(loginService, args); + + InitializeHandlers(server); + } + + public LLLoginServiceInConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + string loginService = ReadLocalServiceFromConfig(config); + + Object[] args = new Object[] { config }; + + m_LoginService = ServerUtils.LoadPlugin(loginService, args); + + InitializeHandlers(server); + } + + public LLLoginServiceInConnector(IConfigSource config, IHttpServer server) : + this(config, server, String.Empty) + { + } + + private string ReadLocalServiceFromConfig(IConfigSource config) + { + IConfig serverConfig = config.Configs["LoginService"]; + if (serverConfig == null) + throw new Exception(String.Format("No section LoginService in config file")); + + string loginService = serverConfig.GetString("LocalServiceModule", String.Empty); + if (loginService == string.Empty) + throw new Exception(String.Format("No LocalServiceModule for LoginService in config file")); + + m_Proxy = serverConfig.GetBoolean("HasProxy", false); + m_DosProtectionOptions = new BasicDosProtectorOptions(); + // Dos Protection Options + m_DosProtectionOptions.AllowXForwardedFor = serverConfig.GetBoolean("DOSAllowXForwardedForHeader", false); + m_DosProtectionOptions.RequestTimeSpan = + TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSRequestTimeFrameMS", 10000)); + m_DosProtectionOptions.MaxRequestsInTimeframe = serverConfig.GetInt("DOSMaxRequestsInTimeFrame", 5); + m_DosProtectionOptions.ForgetTimeSpan = + TimeSpan.FromMilliseconds(serverConfig.GetInt("DOSForgiveClientAfterMS", 120000)); + m_DosProtectionOptions.ReportingName = "LOGINDOSPROTECTION"; + + + return loginService; + } + + private void InitializeHandlers(IHttpServer server) + { + LLLoginHandlers loginHandlers = new LLLoginHandlers(m_LoginService, m_Proxy); + server.AddXmlRPCHandler("login_to_simulator", + new XmlRpcBasicDOSProtector(loginHandlers.HandleXMLRPCLogin,loginHandlers.HandleXMLRPCLoginBlocked, + m_DosProtectionOptions).Process, false); + server.AddXmlRPCHandler("set_login_level", loginHandlers.HandleXMLRPCSetLoginLevel, false); + server.SetDefaultLLSDHandler(loginHandlers.HandleLLSDLogin); + server.AddWebSocketHandler("/WebSocket/GridLogin", loginHandlers.HandleWebSocketLoginEvents); + } + } +} diff --git a/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs b/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs new file mode 100644 index 0000000000..649a27e7b9 --- /dev/null +++ b/OpenSim/Server/Handlers/Map/MapAddServerConnector.cs @@ -0,0 +1,247 @@ +/* + * 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 Nini.Config; +using log4net; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Server.Handlers.MapImage +{ + public class MapAddServiceConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IMapImageService m_MapService; + private IGridService m_GridService; + private string m_ConfigName = "MapImageService"; + + public MapAddServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string mapService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (mapService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_MapService = ServerUtils.LoadPlugin(mapService, args); + + string gridService = serverConfig.GetString("GridService", String.Empty); + if (gridService != string.Empty) + m_GridService = ServerUtils.LoadPlugin(gridService, args); + + if (m_GridService != null) + m_log.InfoFormat("[MAP IMAGE HANDLER]: GridService check is ON"); + else + m_log.InfoFormat("[MAP IMAGE HANDLER]: GridService check is OFF"); + + bool proxy = serverConfig.GetBoolean("HasProxy", false); + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + server.AddStreamHandler(new MapServerPostHandler(m_MapService, m_GridService, proxy, auth)); + + } + } + + class MapServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private IMapImageService m_MapService; + private IGridService m_GridService; + bool m_Proxy; + + public MapServerPostHandler(IMapImageService service, IGridService grid, bool proxy, IServiceAuth auth) : + base("POST", "/map", auth) + { + m_MapService = service; + m_GridService = grid; + m_Proxy = proxy; + } + + protected override byte[] ProcessRequest(string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { +// m_log.DebugFormat("[MAP SERVICE IMAGE HANDLER]: Received {0}", path); + StreamReader sr = new StreamReader(requestData); + string body = sr.ReadToEnd(); + sr.Close(); + body = body.Trim(); + + try + { + Dictionary request = ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("X") || !request.ContainsKey("Y") || !request.ContainsKey("DATA")) + { + httpResponse.StatusCode = (int)OSHttpStatusCode.ClientErrorBadRequest; + return FailureResult("Bad request."); + } + uint x = 0, y = 0; + UInt32.TryParse(request["X"].ToString(), out x); + UInt32.TryParse(request["Y"].ToString(), out y); + + m_log.DebugFormat("[MAP ADD SERVER CONNECTOR]: Received map data for region at {0}-{1}", x, y); + +// string type = "image/jpeg"; +// +// if (request.ContainsKey("TYPE")) +// type = request["TYPE"].ToString(); + + if (m_GridService != null) + { + System.Net.IPAddress ipAddr = GetCallerIP(httpRequest); + GridRegion r = m_GridService.GetRegionByPosition(UUID.Zero, (int)Util.RegionToWorldLoc(x), (int)Util.RegionToWorldLoc(y)); + if (r != null) + { + if (r.ExternalEndPoint.Address.ToString() != ipAddr.ToString()) + { + m_log.WarnFormat("[MAP IMAGE HANDLER]: IP address {0} may be trying to impersonate region in IP {1}", ipAddr, r.ExternalEndPoint.Address); + return FailureResult("IP address of caller does not match IP address of registered region"); + } + + } + else + { + m_log.WarnFormat("[MAP IMAGE HANDLER]: IP address {0} may be rogue. Region not found at coordinates {1}-{2}", + ipAddr, x, y); + return FailureResult("Region not found at given coordinates"); + } + } + + byte[] data = Convert.FromBase64String(request["DATA"].ToString()); + + string reason = string.Empty; + bool result = m_MapService.AddMapTile((int)x, (int)y, data, out reason); + + if (result) + return SuccessResult(); + else + return FailureResult(reason); + + } + catch (Exception e) + { + m_log.ErrorFormat("[MAP SERVICE IMAGE HANDLER]: Exception {0} {1}", e.Message, e.StackTrace); + } + + return FailureResult("Unexpected server error"); + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult(string msg) + { + 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("Failure")); + + rootElement.AppendChild(result); + + XmlElement message = doc.CreateElement("", "Message", ""); + message.AppendChild(doc.CreateTextNode(msg)); + + rootElement.AppendChild(message); + + return Util.DocToBytes(doc); + } + + private System.Net.IPAddress GetCallerIP(IOSHttpRequest request) + { + if (!m_Proxy) + return request.RemoteIPEndPoint.Address; + + // We're behind a proxy + string xff = "X-Forwarded-For"; + string xffValue = request.Headers[xff.ToLower()]; + if (xffValue == null || (xffValue != null && xffValue == string.Empty)) + xffValue = request.Headers[xff]; + + if (xffValue == null || (xffValue != null && xffValue == string.Empty)) + { + m_log.WarnFormat("[MAP IMAGE HANDLER]: No XFF header"); + return request.RemoteIPEndPoint.Address; + } + + System.Net.IPEndPoint ep = Util.GetClientIPFromXFF(xffValue); + if (ep != null) + return ep.Address; + + // Oops + return request.RemoteIPEndPoint.Address; + } + + } +} diff --git a/OpenSim/Server/Handlers/Map/MapGetServerConnector.cs b/OpenSim/Server/Handlers/Map/MapGetServerConnector.cs new file mode 100644 index 0000000000..7bb2f39820 --- /dev/null +++ b/OpenSim/Server/Handlers/Map/MapGetServerConnector.cs @@ -0,0 +1,107 @@ +/* + * 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 Nini.Config; +using log4net; + +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.MapImage +{ + public class MapGetServiceConnector : ServiceConnector + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IMapImageService m_MapService; + + private string m_ConfigName = "MapImageService"; + + public MapGetServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string gridService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (gridService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_MapService = ServerUtils.LoadPlugin(gridService, args); + + server.AddStreamHandler(new MapServerGetHandler(m_MapService)); + } + } + + class MapServerGetHandler : BaseStreamHandler + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IMapImageService m_MapService; + + public MapServerGetHandler(IMapImageService service) : + base("GET", "/map") + { + m_MapService = service; + } + + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result = new byte[0]; + + string format = string.Empty; + result = m_MapService.GetMapTile(path.Trim('/'), out format); + if (result.Length > 0) + { + httpResponse.StatusCode = (int)HttpStatusCode.OK; + if (format.Equals(".png")) + httpResponse.ContentType = "image/png"; + else if (format.Equals(".jpg") || format.Equals(".jpeg")) + httpResponse.ContentType = "image/jpeg"; + } + else + { + httpResponse.StatusCode = (int)HttpStatusCode.NotFound; + httpResponse.ContentType = "text/plain"; + } + + return result; + } + + } +} diff --git a/OpenSim/Server/Handlers/Neighbour/NeighbourHandlers.cs b/OpenSim/Server/Handlers/Neighbour/NeighbourHandlers.cs new file mode 100644 index 0000000000..3525a01a71 --- /dev/null +++ b/OpenSim/Server/Handlers/Neighbour/NeighbourHandlers.cs @@ -0,0 +1,208 @@ +/* + * 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.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Neighbour +{ + public class NeighbourGetHandler : BaseStreamHandler + { + // TODO: unused: private ISimulationService m_SimulationService; + // TODO: unused: private IAuthenticationService m_AuthenticationService; + + public NeighbourGetHandler(INeighbourService service, IAuthenticationService authentication) : + base("GET", "/region") + { + // TODO: unused: m_SimulationService = service; + // TODO: unused: m_AuthenticationService = authentication; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // Not implemented yet + Console.WriteLine("--- Get region --- " + path); + httpResponse.StatusCode = (int)HttpStatusCode.NotImplemented; + return new byte[] { }; + } + } + + public class NeighbourPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private INeighbourService m_NeighbourService; + private IAuthenticationService m_AuthenticationService; + // TODO: unused: private bool m_AllowForeignGuests; + + public NeighbourPostHandler(INeighbourService service, IAuthenticationService authentication) : + base("POST", "/region") + { + m_NeighbourService = service; + m_AuthenticationService = authentication; + // TODO: unused: m_AllowForeignGuests = foreignGuests; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result = new byte[0]; + + UUID regionID; + string action; + ulong regionHandle; + if (RestHandlerUtils.GetParams(path, out regionID, out regionHandle, out action)) + { + m_log.InfoFormat("[RegionPostHandler]: Invalid parameters for neighbour message {0}", path); + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + httpResponse.StatusDescription = "Invalid parameters for neighbour message " + path; + + return result; + } + + if (m_AuthenticationService != null) + { + // Authentication + string authority = string.Empty; + string authToken = string.Empty; + if (!RestHandlerUtils.GetAuthentication(httpRequest, out authority, out authToken)) + { + m_log.InfoFormat("[RegionPostHandler]: Authentication failed for neighbour message {0}", path); + httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; + return result; + } + // TODO: Rethink this + //if (!m_AuthenticationService.VerifyKey(regionID, authToken)) + //{ + // m_log.InfoFormat("[RegionPostHandler]: Authentication failed for neighbour message {0}", path); + // httpResponse.StatusCode = (int)HttpStatusCode.Forbidden; + // return result; + //} + m_log.DebugFormat("[RegionPostHandler]: Authentication succeeded for {0}", regionID); + } + + OSDMap args = Util.GetOSDMap(request, (int)httpRequest.ContentLength); + if (args == null) + { + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + httpResponse.StatusDescription = "Unable to retrieve data"; + m_log.DebugFormat("[RegionPostHandler]: Unable to retrieve data for post {0}", path); + return result; + } + + // retrieve the regionhandle + ulong regionhandle = 0; + if (args["destination_handle"] != null) + UInt64.TryParse(args["destination_handle"].AsString(), out regionhandle); + + RegionInfo aRegion = new RegionInfo(); + try + { + aRegion.UnpackRegionInfoData(args); + } + catch (Exception ex) + { + m_log.InfoFormat("[RegionPostHandler]: exception on unpacking region info {0}", ex.Message); + httpResponse.StatusCode = (int)HttpStatusCode.BadRequest; + httpResponse.StatusDescription = "Problems with data deserialization"; + return result; + } + + // Finally! + GridRegion thisRegion = m_NeighbourService.HelloNeighbour(regionhandle, aRegion); + + OSDMap resp = new OSDMap(1); + + if (thisRegion != null) + resp["success"] = OSD.FromBoolean(true); + else + resp["success"] = OSD.FromBoolean(false); + + httpResponse.StatusCode = (int)HttpStatusCode.OK; + + return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(resp)); + } + } + + public class NeighbourPutHandler : BaseStreamHandler + { + // TODO: unused: private ISimulationService m_SimulationService; + // TODO: unused: private IAuthenticationService m_AuthenticationService; + + public NeighbourPutHandler(INeighbourService service, IAuthenticationService authentication) : + base("PUT", "/region") + { + // TODO: unused: m_SimulationService = service; + // TODO: unused: m_AuthenticationService = authentication; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // Not implemented yet + httpResponse.StatusCode = (int)HttpStatusCode.NotImplemented; + return new byte[] { }; + } + } + + public class NeighbourDeleteHandler : BaseStreamHandler + { + // TODO: unused: private ISimulationService m_SimulationService; + // TODO: unused: private IAuthenticationService m_AuthenticationService; + + public NeighbourDeleteHandler(INeighbourService service, IAuthenticationService authentication) : + base("DELETE", "/region") + { + // TODO: unused: m_SimulationService = service; + // TODO: unused: m_AuthenticationService = authentication; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + // Not implemented yet + httpResponse.StatusCode = (int)HttpStatusCode.NotImplemented; + return new byte[] { }; + } + } +} diff --git a/OpenSim/Server/Handlers/Neighbour/NeighbourServiceInConnector.cs b/OpenSim/Server/Handlers/Neighbour/NeighbourServiceInConnector.cs new file mode 100644 index 0000000000..ac2e75f042 --- /dev/null +++ b/OpenSim/Server/Handlers/Neighbour/NeighbourServiceInConnector.cs @@ -0,0 +1,68 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Neighbour +{ + public class NeighbourServiceInConnector : ServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private INeighbourService m_NeighbourService; + private IAuthenticationService m_AuthenticationService = null; + + public NeighbourServiceInConnector(IConfigSource source, IHttpServer server, INeighbourService nService, IScene scene) : + base(source, server, String.Empty) + { + + m_NeighbourService = nService; + if (m_NeighbourService == null) + { + m_log.Error("[NEIGHBOUR IN CONNECTOR]: neighbour service was not provided"); + return; + } + + //bool authentication = neighbourConfig.GetBoolean("RequireAuthentication", false); + //if (authentication) + // m_AuthenticationService = scene.RequestModuleInterface(); + + + server.AddStreamHandler(new NeighbourPostHandler(m_NeighbourService, m_AuthenticationService)); + server.AddStreamHandler(new NeighbourGetHandler(m_NeighbourService, m_AuthenticationService)); + } + } +} diff --git a/OpenSim/Server/Handlers/Presence/PresenceServerConnector.cs b/OpenSim/Server/Handlers/Presence/PresenceServerConnector.cs new file mode 100644 index 0000000000..7a63c3605a --- /dev/null +++ b/OpenSim/Server/Handlers/Presence/PresenceServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Presence +{ + public class PresenceServiceConnector : ServiceConnector + { + private IPresenceService m_PresenceService; + private string m_ConfigName = "PresenceService"; + + public PresenceServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string gridService = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (gridService == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_PresenceService = ServerUtils.LoadPlugin(gridService, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new PresenceServerPostHandler(m_PresenceService, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs b/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs new file mode 100644 index 0000000000..49dbcb509f --- /dev/null +++ b/OpenSim/Server/Handlers/Presence/PresenceServerPostHandler.cs @@ -0,0 +1,294 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.ServiceAuth; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Presence +{ + public class PresenceServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IPresenceService m_PresenceService; + + public PresenceServerPostHandler(IPresenceService service, IServiceAuth auth) : + base("POST", "/presence", auth) + { + m_PresenceService = 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); + string method = string.Empty; + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + method = request["METHOD"].ToString(); + + switch (method) + { + case "login": + return LoginAgent(request); + case "logout": + return LogoutAgent(request); + case "logoutregion": + return LogoutRegionAgents(request); + case "report": + return Report(request); + case "getagent": + return GetAgent(request); + case "getagents": + return GetAgents(request); + } + m_log.DebugFormat("[PRESENCE HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE HANDLER]: Exception in method {0}: {1}", method, e); + } + + return FailureResult(); + + } + + byte[] LoginAgent(Dictionary request) + { + string user = String.Empty; + UUID session = UUID.Zero; + UUID ssession = UUID.Zero; + + if (!request.ContainsKey("UserID") || !request.ContainsKey("SessionID")) + return FailureResult(); + + user = request["UserID"].ToString(); + + if (!UUID.TryParse(request["SessionID"].ToString(), out session)) + return FailureResult(); + + if (request.ContainsKey("SecureSessionID")) + // If it's malformed, we go on with a Zero on it + UUID.TryParse(request["SecureSessionID"].ToString(), out ssession); + + if (m_PresenceService.LoginAgent(user, session, ssession)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] LogoutAgent(Dictionary request) + { + UUID session = UUID.Zero; + + if (!request.ContainsKey("SessionID")) + return FailureResult(); + + if (!UUID.TryParse(request["SessionID"].ToString(), out session)) + return FailureResult(); + + if (m_PresenceService.LogoutAgent(session)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] LogoutRegionAgents(Dictionary request) + { + UUID region = UUID.Zero; + + if (!request.ContainsKey("RegionID")) + return FailureResult(); + + if (!UUID.TryParse(request["RegionID"].ToString(), out region)) + return FailureResult(); + + if (m_PresenceService.LogoutRegionAgents(region)) + return SuccessResult(); + + return FailureResult(); + } + + byte[] Report(Dictionary request) + { + UUID session = UUID.Zero; + UUID region = UUID.Zero; + + if (!request.ContainsKey("SessionID") || !request.ContainsKey("RegionID")) + return FailureResult(); + + if (!UUID.TryParse(request["SessionID"].ToString(), out session)) + return FailureResult(); + + if (!UUID.TryParse(request["RegionID"].ToString(), out region)) + return FailureResult(); + + if (m_PresenceService.ReportAgent(session, region)) + { + return SuccessResult(); + } + + return FailureResult(); + } + + byte[] GetAgent(Dictionary request) + { + UUID session = UUID.Zero; + + if (!request.ContainsKey("SessionID")) + return FailureResult(); + + if (!UUID.TryParse(request["SessionID"].ToString(), out session)) + return FailureResult(); + + PresenceInfo pinfo = m_PresenceService.GetAgent(session); + + Dictionary result = new Dictionary(); + if (pinfo == null) + result["result"] = "null"; + else + result["result"] = pinfo.ToKeyValuePairs(); + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] GetAgents(Dictionary request) + { + + string[] userIDs; + + if (!request.ContainsKey("uuids")) + { + m_log.DebugFormat("[PRESENCE HANDLER]: GetAgents called without required uuids argument"); + return FailureResult(); + } + + if (!(request["uuids"] is List)) + { + m_log.DebugFormat("[PRESENCE HANDLER]: GetAgents input argument was of unexpected type {0}", request["uuids"].GetType().ToString()); + return FailureResult(); + } + + userIDs = ((List)request["uuids"]).ToArray(); + + PresenceInfo[] pinfos = m_PresenceService.GetAgents(userIDs); + + Dictionary result = new Dictionary(); + if ((pinfos == null) || ((pinfos != null) && (pinfos.Length == 0))) + result["result"] = "null"; + else + { + int i = 0; + foreach (PresenceInfo pinfo in pinfos) + { + Dictionary rinfoDict = pinfo.ToKeyValuePairs(); + result["presence" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + } +} diff --git a/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs b/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs new file mode 100644 index 0000000000..2dfb862751 --- /dev/null +++ b/OpenSim/Server/Handlers/Profiles/UserProfilesConnector.cs @@ -0,0 +1,109 @@ +/* + * 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.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; +using OpenSim.Server.Handlers.Base; +using log4net; + +namespace OpenSim.Server.Handlers.Profiles +{ + public class UserProfilesConnector: ServiceConnector + { +// static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Our Local Module + public IUserProfilesService ServiceModule + { + get; private set; + } + + // The HTTP server. + public IHttpServer Server + { + get; private set; + } + + public bool Enabled + { + get; private set; + } + + public UserProfilesConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + ConfigName = "UserProfilesService"; + if(!string.IsNullOrEmpty(configName)) + ConfigName = configName; + + IConfig serverConfig = config.Configs[ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", ConfigName)); + + if(!serverConfig.GetBoolean("Enabled",false)) + { + Enabled = false; + return; + } + + Enabled = true; + + Server = server; + + string service = serverConfig.GetString("LocalServiceModule", String.Empty); + + Object[] args = new Object[] { config, ConfigName }; + ServiceModule = ServerUtils.LoadPlugin(service, args); + + JsonRpcProfileHandlers handler = new JsonRpcProfileHandlers(ServiceModule); + + Server.AddJsonRPCHandler("avatarclassifiedsrequest", handler.AvatarClassifiedsRequest); + Server.AddJsonRPCHandler("classified_update", handler.ClassifiedUpdate); + Server.AddJsonRPCHandler("classifieds_info_query", handler.ClassifiedInfoRequest); + Server.AddJsonRPCHandler("classified_delete", handler.ClassifiedDelete); + Server.AddJsonRPCHandler("avatarpicksrequest", handler.AvatarPicksRequest); + Server.AddJsonRPCHandler("pickinforequest", handler.PickInfoRequest); + Server.AddJsonRPCHandler("picks_update", handler.PicksUpdate); + Server.AddJsonRPCHandler("picks_delete", handler.PicksDelete); + Server.AddJsonRPCHandler("avatarnotesrequest", handler.AvatarNotesRequest); + Server.AddJsonRPCHandler("avatar_notes_update", handler.NotesUpdate); + Server.AddJsonRPCHandler("avatar_properties_request", handler.AvatarPropertiesRequest); + Server.AddJsonRPCHandler("avatar_properties_update", handler.AvatarPropertiesUpdate); + Server.AddJsonRPCHandler("avatar_interests_update", handler.AvatarInterestsUpdate); + Server.AddJsonRPCHandler("user_preferences_update", handler.UserPreferenecesUpdate); + Server.AddJsonRPCHandler("user_preferences_request", handler.UserPreferencesRequest); + Server.AddJsonRPCHandler("image_assets_request", handler.AvatarImageAssetsRequest); + Server.AddJsonRPCHandler("user_data_request", handler.RequestUserAppData); + Server.AddJsonRPCHandler("user_data_update", handler.UpdateUserAppData); + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs b/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs new file mode 100644 index 0000000000..49aa8bafcd --- /dev/null +++ b/OpenSim/Server/Handlers/Profiles/UserProfilesHandlers.cs @@ -0,0 +1,513 @@ +/* + * 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 OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework; + +namespace OpenSim.Server.Handlers +{ + public class UserProfilesHandlers + { + public UserProfilesHandlers () + { + } + } + + public class JsonRpcProfileHandlers + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public IUserProfilesService Service + { + get; private set; + } + + public JsonRpcProfileHandlers(IUserProfilesService service) + { + Service = service; + } + + #region Classifieds + /// + /// Request avatar's classified ads. + /// + /// + /// An array containing all the calassified uuid and it's name created by the creator id + /// + /// + /// Our parameters are in the OSDMap json["params"] + /// + /// + /// If set to true response. + /// + public bool AvatarClassifiedsRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Classified Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID creatorId = new UUID(request["creatorId"].AsString()); + + + OSDArray data = (OSDArray) Service.AvatarClassifiedsRequest(creatorId); + response.Result = data; + + return true; + } + + public bool ClassifiedUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "Error parsing classified update request"; + m_log.DebugFormat ("Classified Update Request"); + return false; + } + + string result = string.Empty; + UserClassifiedAdd ad = new UserClassifiedAdd(); + object Ad = (object)ad; + OSD.DeserializeMembers(ref Ad, (OSDMap)json["params"]); + if(Service.ClassifiedUpdate(ad, ref result)) + { + response.Result = OSD.SerializeMembers(ad); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool ClassifiedDelete(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Classified Delete Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID classifiedId = new UUID(request["classifiedId"].AsString()); + + if (Service.ClassifiedDelete(classifiedId)) + return true; + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "data error removing record"; + return false; + } + + public bool ClassifiedInfoRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Classified Info Request"); + return false; + } + + string result = string.Empty; + UserClassifiedAdd ad = new UserClassifiedAdd(); + object Ad = (object)ad; + OSD.DeserializeMembers(ref Ad, (OSDMap)json["params"]); + if(Service.ClassifiedInfoRequest(ref ad, ref result)) + { + response.Result = OSD.SerializeMembers(ad); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Classifieds + + #region Picks + public bool AvatarPicksRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Picks Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID creatorId = new UUID(request["creatorId"].AsString()); + + + OSDArray data = (OSDArray) Service.AvatarPicksRequest(creatorId); + response.Result = data; + + return true; + } + + public bool PickInfoRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Picks Info Request"); + return false; + } + + string result = string.Empty; + UserProfilePick pick = new UserProfilePick(); + object Pick = (object)pick; + OSD.DeserializeMembers(ref Pick, (OSDMap)json["params"]); + if(Service.PickInfoRequest(ref pick, ref result)) + { + response.Result = OSD.SerializeMembers(pick); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool PicksUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Picks Update Request"); + return false; + } + + string result = string.Empty; + UserProfilePick pick = new UserProfilePick(); + object Pick = (object)pick; + OSD.DeserializeMembers(ref Pick, (OSDMap)json["params"]); + if(Service.PicksUpdate(ref pick, ref result)) + { + response.Result = OSD.SerializeMembers(pick); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "unable to update pick"; + + return false; + } + + public bool PicksDelete(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Picks Delete Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID pickId = new UUID(request["pickId"].AsString()); + if(Service.PicksDelete(pickId)) + return true; + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "data error removing record"; + return false; + } + #endregion Picks + + #region Notes + public bool AvatarNotesRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "Params missing"; + m_log.DebugFormat ("Avatar Notes Request"); + return false; + } + + UserProfileNotes note = new UserProfileNotes(); + object Note = (object)note; + OSD.DeserializeMembers(ref Note, (OSDMap)json["params"]); + if(Service.AvatarNotesRequest(ref note)) + { + response.Result = OSD.SerializeMembers(note); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = "Error reading notes"; + return false; + } + + public bool NotesUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "No parameters"; + m_log.DebugFormat ("Avatar Notes Update Request"); + return false; + } + + string result = string.Empty; + UserProfileNotes note = new UserProfileNotes(); + object Notes = (object) note; + OSD.DeserializeMembers(ref Notes, (OSDMap)json["params"]); + if(Service.NotesUpdate(ref note, ref result)) + { + response.Result = OSD.SerializeMembers(note); + return true; + } + return true; + } + #endregion Notes + + #region Profile Properties + public bool AvatarPropertiesRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Properties Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarPropertiesRequest(ref props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool AvatarPropertiesUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Properties Update Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarPropertiesUpdate(ref props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Profile Properties + + #region Interests + public bool AvatarInterestsUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("Avatar Interests Update Request"); + return false; + } + + string result = string.Empty; + UserProfileProperties props = new UserProfileProperties(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.AvatarInterestsUpdate(props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion Interests + + #region User Preferences + public bool UserPreferencesRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("User Preferences Request"); + return false; + } + + string result = string.Empty; + UserPreferences prefs = new UserPreferences(); + object Prefs = (object)prefs; + OSD.DeserializeMembers(ref Prefs, (OSDMap)json["params"]); + if(Service.UserPreferencesRequest(ref prefs, ref result)) + { + response.Result = OSD.SerializeMembers(prefs); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); +// m_log.InfoFormat("[PROFILES]: User preferences request error - {0}", response.Error.Message); + return false; + } + + public bool UserPreferenecesUpdate(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("User Preferences Update Request"); + return false; + } + + string result = string.Empty; + UserPreferences prefs = new UserPreferences(); + object Prefs = (object)prefs; + OSD.DeserializeMembers(ref Prefs, (OSDMap)json["params"]); + if(Service.UserPreferencesUpdate(ref prefs, ref result)) + { + response.Result = OSD.SerializeMembers(prefs); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + m_log.InfoFormat("[PROFILES]: User preferences update error - {0}", response.Error.Message); + return false; + } + #endregion User Preferences + + #region Utility + public bool AvatarImageAssetsRequest(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + m_log.DebugFormat ("Avatar Image Assets Request"); + return false; + } + + OSDMap request = (OSDMap)json["params"]; + UUID avatarId = new UUID(request["avatarId"].AsString()); + + OSDArray data = (OSDArray) Service.AvatarImageAssetsRequest(avatarId); + response.Result = data; + + return true; + } + #endregion Utiltiy + + #region UserData + public bool RequestUserAppData(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("User Application Service URL Request: No Parameters!"); + return false; + } + + string result = string.Empty; + UserAppData props = new UserAppData(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.RequestUserAppData(ref props, ref result)) + { + OSDMap res = new OSDMap(); + res["result"] = OSD.FromString("success"); + res["token"] = OSD.FromString (result); + response.Result = res; + + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + + public bool UpdateUserAppData(OSDMap json, ref JsonRpcResponse response) + { + if(!json.ContainsKey("params")) + { + response.Error.Code = ErrorCode.ParseError; + response.Error.Message = "no parameters supplied"; + m_log.DebugFormat ("User App Data Update Request"); + return false; + } + + string result = string.Empty; + UserAppData props = new UserAppData(); + object Props = (object)props; + OSD.DeserializeMembers(ref Props, (OSDMap)json["params"]); + if(Service.SetUserAppData(props, ref result)) + { + response.Result = OSD.SerializeMembers(props); + return true; + } + + response.Error.Code = ErrorCode.InternalError; + response.Error.Message = string.Format("{0}", result); + return false; + } + #endregion UserData + } +} + diff --git a/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs b/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..780e454e42 --- /dev/null +++ b/OpenSim/Server/Handlers/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Server.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("6319afca-d740-4468-a95d-d7f87a081cb3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs new file mode 100644 index 0000000000..e7544b5b85 --- /dev/null +++ b/OpenSim/Server/Handlers/Simulation/AgentHandlers.cs @@ -0,0 +1,647 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.IO.Compression; +using System.Reflection; +using System.Net; +using System.Text; +using System.Web; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Simulation +{ + public class AgentHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ISimulationService m_SimulationService; + + public AgentHandler() { } + + public AgentHandler(ISimulationService sim) + { + m_SimulationService = sim; + } + + public Hashtable Handler(Hashtable request) + { +// m_log.Debug("[CONNECTION DEBUGGING]: AgentHandler Called"); +// +// m_log.Debug("---------------------------"); +// m_log.Debug(" >> uri=" + request["uri"]); +// m_log.Debug(" >> content-type=" + request["content-type"]); +// m_log.Debug(" >> http-method=" + request["http-method"]); +// m_log.Debug("---------------------------\n"); + + Hashtable responsedata = new Hashtable(); + responsedata["content_type"] = "text/html"; + responsedata["keepalive"] = false; + + + UUID agentID; + UUID regionID; + string action; + if (!Utils.GetParams((string)request["uri"], out agentID, out regionID, out action)) + { + m_log.InfoFormat("[AGENT HANDLER]: Invalid parameters for agent message {0}", request["uri"]); + responsedata["int_response_code"] = 404; + responsedata["str_response_string"] = "false"; + + return responsedata; + } + + // Next, let's parse the verb + string method = (string)request["http-method"]; + if (method.Equals("DELETE")) + { + string auth_token = string.Empty; + if (request.ContainsKey("auth")) + auth_token = request["auth"].ToString(); + + DoAgentDelete(request, responsedata, agentID, action, regionID, auth_token); + return responsedata; + } + else if (method.Equals("QUERYACCESS")) + { + DoQueryAccess(request, responsedata, agentID, regionID); + return responsedata; + } + else + { + m_log.ErrorFormat("[AGENT HANDLER]: method {0} not supported in agent message {1} (caller is {2})", method, (string)request["uri"], Util.GetCallerIP(request)); + responsedata["int_response_code"] = HttpStatusCode.MethodNotAllowed; + responsedata["str_response_string"] = "Method not allowed"; + + return responsedata; + } + + } + + protected virtual void DoQueryAccess(Hashtable request, Hashtable responsedata, UUID agentID, UUID regionID) + { + if (m_SimulationService == null) + { + m_log.Debug("[AGENT HANDLER]: Agent QUERY called. Harmless but useless."); + responsedata["content_type"] = "application/json"; + responsedata["int_response_code"] = HttpStatusCode.NotImplemented; + responsedata["str_response_string"] = string.Empty; + + return; + } + + // m_log.DebugFormat("[AGENT HANDLER]: Received QUERYACCESS with {0}", (string)request["body"]); + OSDMap args = Utils.GetOSDMap((string)request["body"]); + + bool viaTeleport = true; + if (args.ContainsKey("viaTeleport")) + viaTeleport = args["viaTeleport"].AsBoolean(); + + Vector3 position = Vector3.Zero; + if (args.ContainsKey("position")) + position = Vector3.Parse(args["position"].AsString()); + + string agentHomeURI = null; + if (args.ContainsKey("agent_home_uri")) + agentHomeURI = args["agent_home_uri"].AsString(); + + string theirVersion = string.Empty; + if (args.ContainsKey("my_version")) + theirVersion = args["my_version"].AsString(); + + List features = new List(); + + if (args.ContainsKey("features")) + { + OSDArray array = (OSDArray)args["features"]; + + foreach (OSD o in array) + features.Add(new UUID(o.AsString())); + } + + GridRegion destination = new GridRegion(); + destination.RegionID = regionID; + + string reason; + string version; + bool result = m_SimulationService.QueryAccess(destination, agentID, agentHomeURI, viaTeleport, position, theirVersion, features, out version, out reason); + + responsedata["int_response_code"] = HttpStatusCode.OK; + + OSDMap resp = new OSDMap(3); + + resp["success"] = OSD.FromBoolean(result); + resp["reason"] = OSD.FromString(reason); + resp["version"] = OSD.FromString(version); + + OSDArray featuresWanted = new OSDArray(); + foreach (UUID feature in features) + featuresWanted.Add(OSD.FromString(feature.ToString())); + + resp["features"] = featuresWanted; + + // We must preserve defaults here, otherwise a false "success" will not be put into the JSON map! + responsedata["str_response_string"] = OSDParser.SerializeJsonString(resp, true); + +// Console.WriteLine("str_response_string [{0}]", responsedata["str_response_string"]); + } + + protected void DoAgentDelete(Hashtable request, Hashtable responsedata, UUID id, string action, UUID regionID, string auth_token) + { + if (string.IsNullOrEmpty(action)) + m_log.DebugFormat("[AGENT HANDLER]: >>> DELETE <<< RegionID: {0}; from: {1}; auth_code: {2}", regionID, Util.GetCallerIP(request), auth_token); + else + m_log.DebugFormat("[AGENT HANDLER]: Release {0} to RegionID: {1}", id, regionID); + + GridRegion destination = new GridRegion(); + destination.RegionID = regionID; + + if (action.Equals("release")) + ReleaseAgent(regionID, id); + else + Util.FireAndForget( + o => m_SimulationService.CloseAgent(destination, id, auth_token), null, "AgentHandler.DoAgentDelete"); + + responsedata["int_response_code"] = HttpStatusCode.OK; + responsedata["str_response_string"] = "OpenSim agent " + id.ToString(); + + //m_log.DebugFormat("[AGENT HANDLER]: Agent {0} Released/Deleted from region {1}", id, regionID); + } + + protected virtual void ReleaseAgent(UUID regionID, UUID id) + { + m_SimulationService.ReleaseAgent(regionID, id, ""); + } + } + + public class AgentPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ISimulationService m_SimulationService; + protected bool m_Proxy = false; + + public AgentPostHandler(ISimulationService service) : + base("POST", "/agent") + { + m_SimulationService = service; + } + + public AgentPostHandler(string path) : + base("POST", path) + { + m_SimulationService = null; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { +// m_log.DebugFormat("[SIMULATION]: Stream handler called"); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + + string[] querystringkeys = httpRequest.QueryString.AllKeys; + string[] rHeaders = httpRequest.Headers.AllKeys; + + keysvals.Add("uri", httpRequest.RawUrl); + keysvals.Add("content-type", httpRequest.ContentType); + keysvals.Add("http-method", httpRequest.HttpMethod); + + foreach (string queryname in querystringkeys) + keysvals.Add(queryname, httpRequest.QueryString[queryname]); + + foreach (string headername in rHeaders) + headervals[headername] = httpRequest.Headers[headername]; + + keysvals.Add("headers", headervals); + keysvals.Add("querystringkeys", querystringkeys); + + httpResponse.StatusCode = 200; + httpResponse.ContentType = "text/html"; + httpResponse.KeepAlive = false; + Encoding encoding = Encoding.UTF8; + + if (httpRequest.ContentType != "application/json") + { + httpResponse.StatusCode = 406; + return encoding.GetBytes("false"); + } + + string requestBody; + + Stream inputStream = request; + Stream innerStream = null; + try + { + if ((httpRequest.ContentType == "application/x-gzip" || httpRequest.Headers["Content-Encoding"] == "gzip") || (httpRequest.Headers["X-Content-Encoding"] == "gzip")) + { + innerStream = inputStream; + inputStream = new GZipStream(innerStream, CompressionMode.Decompress); + } + + using (StreamReader reader = new StreamReader(inputStream, encoding)) + { + requestBody = reader.ReadToEnd(); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + inputStream.Dispose(); + } + + keysvals.Add("body", requestBody); + + Hashtable responsedata = new Hashtable(); + + UUID agentID; + UUID regionID; + string action; + + if (!Utils.GetParams((string)keysvals["uri"], out agentID, out regionID, out action)) + { + m_log.InfoFormat("[AGENT HANDLER]: Invalid parameters for agent message {0}", keysvals["uri"]); + + httpResponse.StatusCode = 404; + + return encoding.GetBytes("false"); + } + + DoAgentPost(keysvals, responsedata, agentID); + + httpResponse.StatusCode = (int)responsedata["int_response_code"]; + return encoding.GetBytes((string)responsedata["str_response_string"]); + } + + protected void DoAgentPost(Hashtable request, Hashtable responsedata, UUID id) + { + OSDMap args = Utils.GetOSDMap((string)request["body"]); + if (args == null) + { + responsedata["int_response_code"] = HttpStatusCode.BadRequest; + responsedata["str_response_string"] = "Bad request"; + return; + } + + AgentDestinationData data = CreateAgentDestinationData(); + UnpackData(args, data, request); + + GridRegion destination = new GridRegion(); + destination.RegionID = data.uuid; + destination.RegionLocX = data.x; + destination.RegionLocY = data.y; + destination.RegionName = data.name; + + GridRegion gatekeeper = ExtractGatekeeper(data); + + AgentCircuitData aCircuit = new AgentCircuitData(); + try + { + aCircuit.UnpackAgentCircuitData(args); + } + catch (Exception ex) + { + m_log.InfoFormat("[AGENT HANDLER]: exception on unpacking ChildCreate message {0}", ex.Message); + responsedata["int_response_code"] = HttpStatusCode.BadRequest; + responsedata["str_response_string"] = "Bad request"; + return; + } + + GridRegion source = null; + + if (args.ContainsKey("source_uuid")) + { + source = new GridRegion(); + source.RegionLocX = Int32.Parse(args["source_x"].AsString()); + source.RegionLocY = Int32.Parse(args["source_y"].AsString()); + source.RegionName = args["source_name"].AsString(); + source.RegionID = UUID.Parse(args["source_uuid"].AsString()); + + if (args.ContainsKey("source_server_uri")) + source.RawServerURI = args["source_server_uri"].AsString(); + else + source.RawServerURI = null; + } + + OSDMap resp = new OSDMap(2); + string reason = String.Empty; + + // This is the meaning of POST agent + //m_regionClient.AdjustUserInformation(aCircuit); + //bool result = m_SimulationService.CreateAgent(destination, aCircuit, teleportFlags, out reason); + bool result = CreateAgent(source, gatekeeper, destination, aCircuit, data.flags, data.fromLogin, out reason); + + resp["reason"] = OSD.FromString(reason); + resp["success"] = OSD.FromBoolean(result); + // Let's also send out the IP address of the caller back to the caller (HG 1.5) + resp["your_ip"] = OSD.FromString(GetCallerIP(request)); + + // TODO: add reason if not String.Empty? + responsedata["int_response_code"] = HttpStatusCode.OK; + responsedata["str_response_string"] = OSDParser.SerializeJsonString(resp); + } + + protected virtual AgentDestinationData CreateAgentDestinationData() + { + return new AgentDestinationData(); + } + + protected virtual void UnpackData(OSDMap args, AgentDestinationData data, Hashtable request) + { + // retrieve the input arguments + if (args.ContainsKey("destination_x") && args["destination_x"] != null) + Int32.TryParse(args["destination_x"].AsString(), out data.x); + else + m_log.WarnFormat(" -- request didn't have destination_x"); + if (args.ContainsKey("destination_y") && args["destination_y"] != null) + Int32.TryParse(args["destination_y"].AsString(), out data.y); + else + m_log.WarnFormat(" -- request didn't have destination_y"); + if (args.ContainsKey("destination_uuid") && args["destination_uuid"] != null) + UUID.TryParse(args["destination_uuid"].AsString(), out data.uuid); + if (args.ContainsKey("destination_name") && args["destination_name"] != null) + data.name = args["destination_name"].ToString(); + if (args.ContainsKey("teleport_flags") && args["teleport_flags"] != null) + data.flags = args["teleport_flags"].AsUInteger(); + } + + protected virtual GridRegion ExtractGatekeeper(AgentDestinationData data) + { + return null; + } + + protected string GetCallerIP(Hashtable request) + { + if (!m_Proxy) + return Util.GetCallerIP(request); + + // We're behind a proxy + Hashtable headers = (Hashtable)request["headers"]; + + //// DEBUG + //foreach (object o in headers.Keys) + // m_log.DebugFormat("XXX {0} = {1}", o.ToString(), (headers[o] == null? "null" : headers[o].ToString())); + + string xff = "X-Forwarded-For"; + if (headers.ContainsKey(xff.ToLower())) + xff = xff.ToLower(); + + if (!headers.ContainsKey(xff) || headers[xff] == null) + { + m_log.WarnFormat("[AGENT HANDLER]: No XFF header"); + return Util.GetCallerIP(request); + } + + m_log.DebugFormat("[AGENT HANDLER]: XFF is {0}", headers[xff]); + + IPEndPoint ep = Util.GetClientIPFromXFF((string)headers[xff]); + if (ep != null) + return ep.Address.ToString(); + + // Oops + return Util.GetCallerIP(request); + } + + // subclasses can override this + protected virtual bool CreateAgent(GridRegion source, GridRegion gatekeeper, GridRegion destination, + AgentCircuitData aCircuit, uint teleportFlags, bool fromLogin, out string reason) + { + return m_SimulationService.CreateAgent(source, destination, aCircuit, teleportFlags, out reason); + } + } + + public class AgentPutHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private ISimulationService m_SimulationService; + protected bool m_Proxy = false; + + public AgentPutHandler(ISimulationService service) : + base("PUT", "/agent") + { + m_SimulationService = service; + } + + public AgentPutHandler(string path) : + base("PUT", path) + { + m_SimulationService = null; + } + + protected override byte[] ProcessRequest(string path, Stream request, + IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { +// m_log.DebugFormat("[SIMULATION]: Stream handler called"); + + Hashtable keysvals = new Hashtable(); + Hashtable headervals = new Hashtable(); + + string[] querystringkeys = httpRequest.QueryString.AllKeys; + string[] rHeaders = httpRequest.Headers.AllKeys; + + keysvals.Add("uri", httpRequest.RawUrl); + keysvals.Add("content-type", httpRequest.ContentType); + keysvals.Add("http-method", httpRequest.HttpMethod); + + foreach (string queryname in querystringkeys) + keysvals.Add(queryname, httpRequest.QueryString[queryname]); + + foreach (string headername in rHeaders) + headervals[headername] = httpRequest.Headers[headername]; + + keysvals.Add("headers", headervals); + keysvals.Add("querystringkeys", querystringkeys); + + String requestBody; + Encoding encoding = Encoding.UTF8; + + Stream inputStream = request; + Stream innerStream = null; + try + { + if ((httpRequest.ContentType == "application/x-gzip" || httpRequest.Headers["Content-Encoding"] == "gzip") || (httpRequest.Headers["X-Content-Encoding"] == "gzip")) + { + innerStream = inputStream; + inputStream = new GZipStream(innerStream, CompressionMode.Decompress); + } + + using (StreamReader reader = new StreamReader(inputStream, encoding)) + { + requestBody = reader.ReadToEnd(); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + inputStream.Dispose(); + } + + keysvals.Add("body", requestBody); + + httpResponse.StatusCode = 200; + httpResponse.ContentType = "text/html"; + httpResponse.KeepAlive = false; + + Hashtable responsedata = new Hashtable(); + + UUID agentID; + UUID regionID; + string action; + + if (!Utils.GetParams((string)keysvals["uri"], out agentID, out regionID, out action)) + { + m_log.InfoFormat("[AGENT HANDLER]: Invalid parameters for agent message {0}", keysvals["uri"]); + + httpResponse.StatusCode = 404; + + return encoding.GetBytes("false"); + } + + DoAgentPut(keysvals, responsedata); + + httpResponse.StatusCode = (int)responsedata["int_response_code"]; + return encoding.GetBytes((string)responsedata["str_response_string"]); + } + + protected void DoAgentPut(Hashtable request, Hashtable responsedata) + { + OSDMap args = Utils.GetOSDMap((string)request["body"]); + if (args == null) + { + responsedata["int_response_code"] = HttpStatusCode.BadRequest; + responsedata["str_response_string"] = "Bad request"; + return; + } + + // retrieve the input arguments + int x = 0, y = 0; + UUID uuid = UUID.Zero; + string regionname = string.Empty; + if (args.ContainsKey("destination_x") && args["destination_x"] != null) + Int32.TryParse(args["destination_x"].AsString(), out x); + if (args.ContainsKey("destination_y") && args["destination_y"] != null) + Int32.TryParse(args["destination_y"].AsString(), out y); + if (args.ContainsKey("destination_uuid") && args["destination_uuid"] != null) + UUID.TryParse(args["destination_uuid"].AsString(), out uuid); + if (args.ContainsKey("destination_name") && args["destination_name"] != null) + regionname = args["destination_name"].ToString(); + + GridRegion destination = new GridRegion(); + destination.RegionID = uuid; + destination.RegionLocX = x; + destination.RegionLocY = y; + destination.RegionName = regionname; + + string messageType; + if (args["message_type"] != null) + messageType = args["message_type"].AsString(); + else + { + m_log.Warn("[AGENT HANDLER]: Agent Put Message Type not found. "); + messageType = "AgentData"; + } + + bool result = true; + if ("AgentData".Equals(messageType)) + { + AgentData agent = new AgentData(); + try + { + agent.Unpack(args, m_SimulationService.GetScene(destination.RegionID)); + } + catch (Exception ex) + { + m_log.InfoFormat("[AGENT HANDLER]: exception on unpacking ChildAgentUpdate message {0}", ex.Message); + responsedata["int_response_code"] = HttpStatusCode.BadRequest; + responsedata["str_response_string"] = "Bad request"; + return; + } + + //agent.Dump(); + // This is one of the meanings of PUT agent + result = UpdateAgent(destination, agent); + } + else if ("AgentPosition".Equals(messageType)) + { + AgentPosition agent = new AgentPosition(); + try + { + agent.Unpack(args, m_SimulationService.GetScene(destination.RegionID)); + } + catch (Exception ex) + { + m_log.InfoFormat("[AGENT HANDLER]: exception on unpacking ChildAgentUpdate message {0}", ex.Message); + return; + } + //agent.Dump(); + // This is one of the meanings of PUT agent + result = m_SimulationService.UpdateAgent(destination, agent); + + } + + responsedata["int_response_code"] = HttpStatusCode.OK; + responsedata["str_response_string"] = result.ToString(); + //responsedata["str_response_string"] = OSDParser.SerializeJsonString(resp); ??? instead + } + + // subclasses can override this + protected virtual bool UpdateAgent(GridRegion destination, AgentData agent) + { + return m_SimulationService.UpdateAgent(destination, agent); + } + } + + public class AgentDestinationData + { + public int x; + public int y; + public string name; + public UUID uuid; + public uint flags; + public bool fromLogin; + } +} diff --git a/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs b/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs new file mode 100644 index 0000000000..dbb1a15ae7 --- /dev/null +++ b/OpenSim/Server/Handlers/Simulation/ObjectHandlers.cs @@ -0,0 +1,218 @@ +/* + * 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.IO; +using System.Reflection; +using System.Net; +using System.Text; + +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using Nini.Config; +using log4net; + + +namespace OpenSim.Server.Handlers.Simulation +{ + public class ObjectHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private ISimulationService m_SimulationService; + + public ObjectHandler() { } + + public ObjectHandler(ISimulationService sim) + { + m_SimulationService = sim; + } + + public Hashtable Handler(Hashtable request) + { + //m_log.Debug("[CONNECTION DEBUGGING]: ObjectHandler Called"); + + //m_log.Debug("---------------------------"); + //m_log.Debug(" >> uri=" + request["uri"]); + //m_log.Debug(" >> content-type=" + request["content-type"]); + //m_log.Debug(" >> http-method=" + request["http-method"]); + //m_log.Debug("---------------------------\n"); + + Hashtable responsedata = new Hashtable(); + responsedata["content_type"] = "text/html"; + + UUID objectID; + UUID regionID; + string action; + if (!Utils.GetParams((string)request["uri"], out objectID, out regionID, out action)) + { + m_log.InfoFormat("[OBJECT HANDLER]: Invalid parameters for object message {0}", request["uri"]); + responsedata["int_response_code"] = 404; + responsedata["str_response_string"] = "false"; + + return responsedata; + } + + try + { + // Next, let's parse the verb + string method = (string)request["http-method"]; + if (method.Equals("POST")) + { + DoObjectPost(request, responsedata, regionID); + return responsedata; + } + //else if (method.Equals("DELETE")) + //{ + // DoObjectDelete(request, responsedata, agentID, action, regionHandle); + // return responsedata; + //} + else + { + m_log.InfoFormat("[OBJECT HANDLER]: method {0} not supported in object message", method); + responsedata["int_response_code"] = HttpStatusCode.MethodNotAllowed; + responsedata["str_response_string"] = "Method not allowed"; + + return responsedata; + } + } + catch (Exception e) + { + m_log.WarnFormat("[OBJECT HANDLER]: Caught exception {0}", e.StackTrace); + responsedata["int_response_code"] = HttpStatusCode.InternalServerError; + responsedata["str_response_string"] = "Internal server error"; + + return responsedata; + + } + } + + protected void DoObjectPost(Hashtable request, Hashtable responsedata, UUID regionID) + { + OSDMap args = Utils.GetOSDMap((string)request["body"]); + if (args == null) + { + responsedata["int_response_code"] = 400; + responsedata["str_response_string"] = "false"; + return; + } + // retrieve the input arguments + int x = 0, y = 0; + UUID uuid = UUID.Zero; + string regionname = string.Empty; + Vector3 newPosition = Vector3.Zero; + + if (args.ContainsKey("destination_x") && args["destination_x"] != null) + Int32.TryParse(args["destination_x"].AsString(), out x); + if (args.ContainsKey("destination_y") && args["destination_y"] != null) + Int32.TryParse(args["destination_y"].AsString(), out y); + if (args.ContainsKey("destination_uuid") && args["destination_uuid"] != null) + UUID.TryParse(args["destination_uuid"].AsString(), out uuid); + if (args.ContainsKey("destination_name") && args["destination_name"] != null) + regionname = args["destination_name"].ToString(); + if (args.ContainsKey("new_position") && args["new_position"] != null) + Vector3.TryParse(args["new_position"], out newPosition); + + GridRegion destination = new GridRegion(); + destination.RegionID = uuid; + destination.RegionLocX = x; + destination.RegionLocY = y; + destination.RegionName = regionname; + + string sogXmlStr = "", extraStr = "", stateXmlStr = ""; + if (args.ContainsKey("sog") && args["sog"] != null) + sogXmlStr = args["sog"].AsString(); + if (args.ContainsKey("extra") && args["extra"] != null) + extraStr = args["extra"].AsString(); + + IScene s = m_SimulationService.GetScene(destination.RegionID); + ISceneObject sog = null; + try + { + //m_log.DebugFormat("[OBJECT HANDLER]: received {0}", sogXmlStr); + sog = s.DeserializeObject(sogXmlStr); + sog.ExtraFromXmlString(extraStr); + } + catch (Exception ex) + { + m_log.InfoFormat("[OBJECT HANDLER]: exception on deserializing scene object {0}", ex.Message); + responsedata["int_response_code"] = HttpStatusCode.BadRequest; + responsedata["str_response_string"] = "Bad request"; + return; + } + + if (args.ContainsKey("modified")) + sog.HasGroupChanged = args["modified"].AsBoolean(); + else + sog.HasGroupChanged = false; + + if ((args["state"] != null) && s.AllowScriptCrossings) + { + stateXmlStr = args["state"].AsString(); + if (stateXmlStr != "") + { + try + { + sog.SetState(stateXmlStr, s); + } + catch (Exception ex) + { + m_log.InfoFormat("[OBJECT HANDLER]: exception on setting state for scene object {0}", ex.Message); + // ignore and continue + } + } + } + + bool result = false; + try + { + // This is the meaning of POST object + result = CreateObject(destination, newPosition, sog); + } + catch (Exception e) + { + m_log.DebugFormat("[OBJECT HANDLER]: Exception in CreateObject: {0}", e.StackTrace); + } + + responsedata["int_response_code"] = HttpStatusCode.OK; + responsedata["str_response_string"] = result.ToString(); + } + + // subclasses can override this + protected virtual bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog) + { + return m_SimulationService.CreateObject(destination, newPosition, sog, false); + } + } +} diff --git a/OpenSim/Server/Handlers/Simulation/SimulationServiceInConnector.cs b/OpenSim/Server/Handlers/Simulation/SimulationServiceInConnector.cs new file mode 100644 index 0000000000..0da08f8a70 --- /dev/null +++ b/OpenSim/Server/Handlers/Simulation/SimulationServiceInConnector.cs @@ -0,0 +1,58 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.Simulation +{ + public class SimulationServiceInConnector : ServiceConnector + { + private ISimulationService m_LocalSimulationService; +// private IAuthenticationService m_AuthenticationService; + + public SimulationServiceInConnector(IConfigSource config, IHttpServer server, IScene scene) : + base(config, server, String.Empty) + { + m_LocalSimulationService = scene.RequestModuleInterface(); + m_LocalSimulationService = m_LocalSimulationService.GetInnerService(); + + // This one MUST be a stream handler because compressed fatpacks + // are pure binary and shoehorning that into a string with UTF-8 + // encoding breaks it + server.AddStreamHandler(new AgentPostHandler(m_LocalSimulationService)); + server.AddStreamHandler(new AgentPutHandler(m_LocalSimulationService)); + server.AddHTTPHandler("/agent/", new AgentHandler(m_LocalSimulationService).Handler); + server.AddHTTPHandler("/object/", new ObjectHandler(m_LocalSimulationService).Handler); + } + } +} diff --git a/OpenSim/Server/Handlers/Simulation/Utils.cs b/OpenSim/Server/Handlers/Simulation/Utils.cs new file mode 100644 index 0000000000..ed379da92d --- /dev/null +++ b/OpenSim/Server/Handlers/Simulation/Utils.cs @@ -0,0 +1,103 @@ +/* + * 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 OpenMetaverse; +using OpenMetaverse.StructuredData; + +using log4net; + +namespace OpenSim.Server.Handlers.Simulation +{ + public class Utils + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Extract the param from an uri. + /// + /// Something like this: /agent/uuid/ or /agent/uuid/handle/release + /// uuid on uuid field + /// optional action + public static bool GetParams(string uri, out UUID uuid, out UUID regionID, out string action) + { + uuid = UUID.Zero; + regionID = UUID.Zero; + action = ""; + + uri = uri.Trim(new char[] { '/' }); + string[] parts = uri.Split('/'); + if (parts.Length <= 1) + { + return false; + } + else + { + if (!UUID.TryParse(parts[1], out uuid)) + return false; + + if (parts.Length >= 3) + UUID.TryParse(parts[2], out regionID); + if (parts.Length >= 4) + action = parts[3]; + + return true; + } + } + + public static OSDMap GetOSDMap(string data) + { + OSDMap args = null; + try + { + OSD buffer; + // We should pay attention to the content-type, but let's assume we know it's Json + buffer = OSDParser.DeserializeJson(data); + if (buffer.Type == OSDType.Map) + { + args = (OSDMap)buffer; + return args; + } + else + { + // uh? + m_log.Debug(("[REST COMMS]: Got OSD of unexpected type " + buffer.Type.ToString())); + return null; + } + } + catch (Exception ex) + { + m_log.Debug("[REST COMMS]: exception on parse of REST message " + ex.Message); + return null; + } + } + + } +} diff --git a/OpenSim/Server/Handlers/UserAccounts/UserAccountServerConnector.cs b/OpenSim/Server/Handlers/UserAccounts/UserAccountServerConnector.cs new file mode 100644 index 0000000000..e95e3dccc2 --- /dev/null +++ b/OpenSim/Server/Handlers/UserAccounts/UserAccountServerConnector.cs @@ -0,0 +1,64 @@ +/* + * 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 Nini.Config; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Server.Handlers.Base; + +namespace OpenSim.Server.Handlers.UserAccounts +{ + public class UserAccountServiceConnector : ServiceConnector + { + private IUserAccountService m_UserAccountService; + private string m_ConfigName = "UserAccountService"; + + public UserAccountServiceConnector(IConfigSource config, IHttpServer server, string configName) : + base(config, server, configName) + { + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string service = serverConfig.GetString("LocalServiceModule", + String.Empty); + + if (service == String.Empty) + throw new Exception("No LocalServiceModule in config file"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(service, args); + + IServiceAuth auth = ServiceAuth.Create(config, m_ConfigName); + + server.AddStreamHandler(new UserAccountServerPostHandler(m_UserAccountService, serverConfig, auth)); + } + } +} diff --git a/OpenSim/Server/Handlers/UserAccounts/UserAccountServerPostHandler.cs b/OpenSim/Server/Handlers/UserAccounts/UserAccountServerPostHandler.cs new file mode 100644 index 0000000000..21eb790643 --- /dev/null +++ b/OpenSim/Server/Handlers/UserAccounts/UserAccountServerPostHandler.cs @@ -0,0 +1,348 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.UserAccountService; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Framework.ServiceAuth; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.UserAccounts +{ + public class UserAccountServerPostHandler : BaseStreamHandler + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IUserAccountService m_UserAccountService; + private bool m_AllowCreateUser = false; + private bool m_AllowSetAccount = false; + + public UserAccountServerPostHandler(IUserAccountService service) + : this(service, null, null) {} + + public UserAccountServerPostHandler(IUserAccountService service, IConfig config, IServiceAuth auth) : + base("POST", "/accounts", auth) + { + m_UserAccountService = service; + + if (config != null) + { + m_AllowCreateUser = config.GetBoolean("AllowCreateUser", m_AllowCreateUser); + m_AllowSetAccount = config.GetBoolean("AllowSetAccount", m_AllowSetAccount); + } + } + + 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(); + + // We need to check the authorization header + //httpRequest.Headers["authorization"] ... + + //m_log.DebugFormat("[XXX]: query String: {0}", body); + string method = string.Empty; + try + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + + method = request["METHOD"].ToString(); + + switch (method) + { + case "createuser": + if (m_AllowCreateUser) + return CreateUser(request); + else + break; + case "getaccount": + return GetAccount(request); + case "getaccounts": + return GetAccounts(request); + case "setaccount": + if (m_AllowSetAccount) + return StoreAccount(request); + else + break; + } + + m_log.DebugFormat("[USER SERVICE HANDLER]: unknown method request: {0}", method); + } + catch (Exception e) + { + m_log.DebugFormat("[USER SERVICE HANDLER]: Exception in method {0}: {1}", method, e); + } + + return FailureResult(); + } + + byte[] GetAccount(Dictionary request) + { + UserAccount account = null; + UUID scopeID = UUID.Zero; + Dictionary result = new Dictionary(); + + if (request.ContainsKey("ScopeID") && !UUID.TryParse(request["ScopeID"].ToString(), out scopeID)) + { + result["result"] = "null"; + return ResultToBytes(result); + } + + if (request.ContainsKey("UserID") && request["UserID"] != null) + { + UUID userID; + if (UUID.TryParse(request["UserID"].ToString(), out userID)) + account = m_UserAccountService.GetUserAccount(scopeID, userID); + } + else if (request.ContainsKey("PrincipalID") && request["PrincipalID"] != null) + { + UUID userID; + if (UUID.TryParse(request["PrincipalID"].ToString(), out userID)) + account = m_UserAccountService.GetUserAccount(scopeID, userID); + } + else if (request.ContainsKey("Email") && request["Email"] != null) + { + account = m_UserAccountService.GetUserAccount(scopeID, request["Email"].ToString()); + } + else if (request.ContainsKey("FirstName") && request.ContainsKey("LastName") && + request["FirstName"] != null && request["LastName"] != null) + { + account = m_UserAccountService.GetUserAccount(scopeID, request["FirstName"].ToString(), request["LastName"].ToString()); + } + + if (account == null) + { + result["result"] = "null"; + } + else + { + result["result"] = account.ToKeyValuePairs(); + } + + return ResultToBytes(result); + } + + byte[] GetAccounts(Dictionary request) + { + if (!request.ContainsKey("query")) + return FailureResult(); + + UUID scopeID = UUID.Zero; + if (request.ContainsKey("ScopeID") && !UUID.TryParse(request["ScopeID"].ToString(), out scopeID)) + return FailureResult(); + + string query = request["query"].ToString(); + + List accounts = m_UserAccountService.GetUserAccounts(scopeID, query); + + Dictionary result = new Dictionary(); + if ((accounts == null) || ((accounts != null) && (accounts.Count == 0))) + { + result["result"] = "null"; + } + else + { + int i = 0; + foreach (UserAccount acc in accounts) + { + Dictionary rinfoDict = acc.ToKeyValuePairs(); + result["account" + i] = rinfoDict; + i++; + } + } + + string xmlString = ServerUtils.BuildXmlResponse(result); + + //m_log.DebugFormat("[GRID HANDLER]: resp string: {0}", xmlString); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + + byte[] StoreAccount(Dictionary request) + { + UUID principalID = UUID.Zero; + if (request.ContainsKey("PrincipalID") && !UUID.TryParse(request["PrincipalID"].ToString(), out principalID)) + return FailureResult(); + + UUID scopeID = UUID.Zero; + if (request.ContainsKey("ScopeID") && !UUID.TryParse(request["ScopeID"].ToString(), out scopeID)) + return FailureResult(); + + UserAccount existingAccount = m_UserAccountService.GetUserAccount(scopeID, principalID); + if (existingAccount == null) + return FailureResult(); + + Dictionary result = new Dictionary(); + + if (request.ContainsKey("FirstName")) + existingAccount.FirstName = request["FirstName"].ToString(); + + if (request.ContainsKey("LastName")) + existingAccount.LastName = request["LastName"].ToString(); + + if (request.ContainsKey("Email")) + existingAccount.Email = request["Email"].ToString(); + + int created = 0; + if (request.ContainsKey("Created") && int.TryParse(request["Created"].ToString(), out created)) + existingAccount.Created = created; + + int userLevel = 0; + if (request.ContainsKey("UserLevel") && int.TryParse(request["UserLevel"].ToString(), out userLevel)) + existingAccount.UserLevel = userLevel; + + int userFlags = 0; + if (request.ContainsKey("UserFlags") && int.TryParse(request["UserFlags"].ToString(), out userFlags)) + existingAccount.UserFlags = userFlags; + + if (request.ContainsKey("UserTitle")) + existingAccount.UserTitle = request["UserTitle"].ToString(); + + if (!m_UserAccountService.StoreUserAccount(existingAccount)) + { + m_log.ErrorFormat( + "[USER ACCOUNT SERVER POST HANDLER]: Account store failed for account {0} {1} {2}", + existingAccount.FirstName, existingAccount.LastName, existingAccount.PrincipalID); + + return FailureResult(); + } + + result["result"] = existingAccount.ToKeyValuePairs(); + + return ResultToBytes(result); + } + + byte[] CreateUser(Dictionary request) + { + if (! request.ContainsKey("FirstName") + && request.ContainsKey("LastName") + && request.ContainsKey("Password")) + return FailureResult(); + + Dictionary result = new Dictionary(); + + UUID scopeID = UUID.Zero; + if (request.ContainsKey("ScopeID") && !UUID.TryParse(request["ScopeID"].ToString(), out scopeID)) + return FailureResult(); + + UUID principalID = UUID.Random(); + if (request.ContainsKey("PrincipalID") && !UUID.TryParse(request["PrincipalID"].ToString(), out principalID)) + return FailureResult(); + + string firstName = request["FirstName"].ToString(); + string lastName = request["LastName"].ToString(); + string password = request["Password"].ToString(); + + string email = ""; + if (request.ContainsKey("Email")) + email = request["Email"].ToString(); + + UserAccount createdUserAccount = null; + + if (m_UserAccountService is UserAccountService) + createdUserAccount + = ((UserAccountService)m_UserAccountService).CreateUser( + scopeID, principalID, firstName, lastName, password, email); + + if (createdUserAccount == null) + return FailureResult(); + + result["result"] = createdUserAccount.ToKeyValuePairs(); + + return ResultToBytes(result); + } + + private byte[] SuccessResult() + { + 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("Success")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] FailureResult() + { + 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("Failure")); + + rootElement.AppendChild(result); + + return Util.DocToBytes(doc); + } + + private byte[] ResultToBytes(Dictionary result) + { + string xmlString = ServerUtils.BuildXmlResponse(result); + return Util.UTF8NoBomEncoding.GetBytes(xmlString); + } + } +} \ No newline at end of file diff --git a/OpenSim/Server/Properties/AssemblyInfo.cs b/OpenSim/Server/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ee45e10a49 --- /dev/null +++ b/OpenSim/Server/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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("Robust")] +[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("d347c5cb-baf8-4566-a221-35d948e1776f")] + +// 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")] diff --git a/OpenSim/Server/ServerMain.cs b/OpenSim/Server/ServerMain.cs new file mode 100644 index 0000000000..65e92874e8 --- /dev/null +++ b/OpenSim/Server/ServerMain.cs @@ -0,0 +1,161 @@ +/* + * 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 Nini.Config; +using log4net; +using System.Reflection; +using System; +using System.Collections.Generic; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Server.Base; +using OpenSim.Server.Handlers.Base; +using Mono.Addins; + +namespace OpenSim.Server +{ + public class OpenSimServer + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected static HttpServerBase m_Server = null; + + protected static List m_ServiceConnectors = + new List(); + + protected static PluginLoader loader; + + public static int Main(string[] args) + { + m_Server = new HttpServerBase("R.O.B.U.S.T.", args); + + string registryLocation; + + IConfig serverConfig = m_Server.Config.Configs["Startup"]; + if (serverConfig == null) + { + System.Console.WriteLine("Startup config section missing in .ini file"); + throw new Exception("Configuration error"); + } + + string connList = serverConfig.GetString("ServiceConnectors", String.Empty); + + registryLocation = serverConfig.GetString("RegistryLocation","."); + + IConfig servicesConfig = m_Server.Config.Configs["ServiceList"]; + if (servicesConfig != null) + { + List servicesList = new List(); + if (connList != String.Empty) + servicesList.Add(connList); + + foreach (string k in servicesConfig.GetKeys()) + { + string v = servicesConfig.GetString(k); + if (v != String.Empty) + servicesList.Add(v); + } + + connList = String.Join(",", servicesList.ToArray()); + } + + string[] conns = connList.Split(new char[] {',', ' ', '\n', '\r', '\t'}); + +// int i = 0; + foreach (string c in conns) + { + if (c == String.Empty) + continue; + + string configName = String.Empty; + string conn = c; + uint port = 0; + + string[] split1 = conn.Split(new char[] {'/'}); + if (split1.Length > 1) + { + conn = split1[1]; + + string[] split2 = split1[0].Split(new char[] {'@'}); + if (split2.Length > 1) + { + configName = split2[0]; + port = Convert.ToUInt32(split2[1]); + } + else + { + port = Convert.ToUInt32(split1[0]); + } + } + string[] parts = conn.Split(new char[] {':'}); + string friendlyName = parts[0]; + if (parts.Length > 1) + friendlyName = parts[1]; + + IHttpServer server; + + if (port != 0) + server = MainServer.GetHttpServer(port); + else + server = MainServer.Instance; + + m_log.InfoFormat("[SERVER]: Loading {0} on port {1}", friendlyName, server.Port); + + IServiceConnector connector = null; + + Object[] modargs = new Object[] { m_Server.Config, server, configName }; + connector = ServerUtils.LoadPlugin(conn, modargs); + + if (connector == null) + { + modargs = new Object[] { m_Server.Config, server }; + connector = ServerUtils.LoadPlugin(conn, modargs); + } + + if (connector != null) + { + m_ServiceConnectors.Add(connector); + m_log.InfoFormat("[SERVER]: {0} loaded successfully", friendlyName); + } + else + { + m_log.ErrorFormat("[SERVER]: Failed to load {0}", conn); + } + } + + loader = new PluginLoader(m_Server.Config, registryLocation); + + int res = m_Server.Run(); + + Environment.Exit(res); + + return 0; + } + } +} diff --git a/OpenSim/Services/AssetService/AssetService.cs b/OpenSim/Services/AssetService/AssetService.cs new file mode 100644 index 0000000000..0aefa16fd2 --- /dev/null +++ b/OpenSim/Services/AssetService/AssetService.cs @@ -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 System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.AssetService +{ + public class AssetService : AssetServiceBase, IAssetService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected static AssetService m_RootInstance; + + public AssetService(IConfigSource config) + : this(config, "AssetService") + { + } + + public AssetService(IConfigSource config, string configName) : base(config, configName) + { + if (m_RootInstance == null) + { + m_RootInstance = this; + + if (m_AssetLoader != null) + { + IConfig assetConfig = config.Configs[m_ConfigName]; + if (assetConfig == null) + throw new Exception("No " + m_ConfigName + " configuration"); + + string loaderArgs = assetConfig.GetString("AssetLoaderArgs", + String.Empty); + + bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true); + + if (assetLoaderEnabled) + { + m_log.DebugFormat("[ASSET SERVICE]: Loading default asset set from {0}", loaderArgs); + + m_AssetLoader.ForEachDefaultXmlAsset( + loaderArgs, + delegate(AssetBase a) + { + AssetBase existingAsset = Get(a.ID); +// AssetMetadata existingMetadata = GetMetadata(a.ID); + + if (existingAsset == null || Util.SHA1Hash(existingAsset.Data) != Util.SHA1Hash(a.Data)) + { +// m_log.DebugFormat("[ASSET]: Storing {0} {1}", a.Name, a.ID); + Store(a); + } + }); + } + + m_log.Debug("[ASSET SERVICE]: Local asset service enabled"); + } + } + } + + public virtual AssetBase Get(string id) + { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset for {0}", id); + + UUID assetID; + + if (!UUID.TryParse(id, out assetID)) + { + m_log.WarnFormat("[ASSET SERVICE]: Could not parse requested asset id {0}", id); + return null; + } + + try + { + return m_Database.GetAsset(assetID); + } + catch (Exception e) + { + m_log.ErrorFormat("[ASSET SERVICE]: Exception getting asset {0} {1}", assetID, e); + return null; + } + } + + public virtual AssetBase GetCached(string id) + { + return Get(id); + } + + public virtual AssetMetadata GetMetadata(string id) + { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset metadata for {0}", id); + + AssetBase asset = Get(id); + + if (asset != null) + return asset.Metadata; + else + return null; + } + + public virtual byte[] GetData(string id) + { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset data for {0}", id); + + AssetBase asset = Get(id); + + if (asset != null) + return asset.Data; + else + return null; + } + + public virtual bool Get(string id, Object sender, AssetRetrieved handler) + { + //m_log.DebugFormat("[AssetService]: Get asset async {0}", id); + + handler(id, sender, Get(id)); + + return true; + } + + public virtual bool[] AssetsExist(string[] ids) + { + try + { + UUID[] uuid = Array.ConvertAll(ids, id => UUID.Parse(id)); + return m_Database.AssetsExist(uuid); + } + catch (Exception e) + { + m_log.Error("[ASSET SERVICE]: Exception getting assets ", e); + return new bool[ids.Length]; + } + } + + public virtual string Store(AssetBase asset) + { + bool exists = m_Database.AssetsExist(new[] { asset.FullID })[0]; + if (!exists) + { +// m_log.DebugFormat( +// "[ASSET SERVICE]: Storing asset {0} {1}, bytes {2}", asset.Name, asset.FullID, asset.Data.Length); + m_Database.StoreAsset(asset); + } +// else +// { +// m_log.DebugFormat( +// "[ASSET SERVICE]: Not storing asset {0} {1}, bytes {2} as it already exists", asset.Name, asset.FullID, asset.Data.Length); +// } + + return asset.ID; + } + + public bool UpdateContent(string id, byte[] data) + { + return false; + } + + public virtual bool Delete(string id) + { +// m_log.DebugFormat("[ASSET SERVICE]: Deleting asset {0}", id); + + UUID assetID; + if (!UUID.TryParse(id, out assetID)) + return false; + + return m_Database.Delete(id); + } + } +} diff --git a/OpenSim/Services/AssetService/AssetServiceBase.cs b/OpenSim/Services/AssetService/AssetServiceBase.cs new file mode 100644 index 0000000000..58ab052ebe --- /dev/null +++ b/OpenSim/Services/AssetService/AssetServiceBase.cs @@ -0,0 +1,103 @@ +/* + * 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.Services.AssetService +{ + public class AssetServiceBase : ServiceBase + { + protected IAssetDataPlugin m_Database = null; + protected IAssetLoader m_AssetLoader = null; + protected string m_ConfigName = "AssetService"; + + public AssetServiceBase(IConfigSource config) + : this(config, "AssetService") + { + } + + public AssetServiceBase(IConfigSource config, string configName) : base(config) + { + if (configName != string.Empty) + m_ConfigName = configName; + + string dllName = String.Empty; + string connString = String.Empty; + + // + // Try reading the [AssetService] section, if it exists + // + IConfig assetConfig = config.Configs[m_ConfigName]; + if (assetConfig != null) + { + dllName = assetConfig.GetString("StorageProvider", dllName); + connString = assetConfig.GetString("ConnectionString", connString); + } + + // + // 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); + } + + // + // 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(dllName); + if (m_Database == null) + throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName)); + + m_Database.Initialise(connString); + + string loaderName = assetConfig.GetString("DefaultAssetLoader", + String.Empty); + + if (loaderName != String.Empty) + { + m_AssetLoader = LoadPlugin(loaderName); + + if (m_AssetLoader == null) + throw new Exception(string.Format("Asset loader could not be loaded from {0}", loaderName)); + } + } + } +} diff --git a/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs b/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..97173198ca --- /dev/null +++ b/OpenSim/Services/AssetService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.AssetService")] +[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("fe57c0df-6101-4c23-ae1a-7b3e937843f9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/AssetService/XAssetService.cs b/OpenSim/Services/AssetService/XAssetService.cs new file mode 100644 index 0000000000..f58b769d25 --- /dev/null +++ b/OpenSim/Services/AssetService/XAssetService.cs @@ -0,0 +1,227 @@ +/* + * 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 Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.AssetService +{ + /// + /// A de-duplicating asset service. + /// + public class XAssetService : XAssetServiceBase, IAssetService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected static XAssetService m_RootInstance; + + public XAssetService(IConfigSource config) : this(config, "AssetService") {} + + public XAssetService(IConfigSource config, string configName) : base(config, configName) + { + if (m_RootInstance == null) + { + m_RootInstance = this; + + if (m_AssetLoader != null) + { + IConfig assetConfig = config.Configs[configName]; + if (assetConfig == null) + throw new Exception("No AssetService configuration"); + + string loaderArgs = assetConfig.GetString("AssetLoaderArgs", String.Empty); + + bool assetLoaderEnabled = assetConfig.GetBoolean("AssetLoaderEnabled", true); + + if (assetLoaderEnabled && !HasChainedAssetService) + { + m_log.DebugFormat("[XASSET SERVICE]: Loading default asset set from {0}", loaderArgs); + + m_AssetLoader.ForEachDefaultXmlAsset( + loaderArgs, + a => + { + AssetBase existingAsset = Get(a.ID); +// AssetMetadata existingMetadata = GetMetadata(a.ID); + + if (existingAsset == null || Util.SHA1Hash(existingAsset.Data) != Util.SHA1Hash(a.Data)) + { +// m_log.DebugFormat("[ASSET]: Storing {0} {1}", a.Name, a.ID); + Store(a); + } + }); + } + + m_log.Debug("[XASSET SERVICE]: Local asset service enabled"); + } + } + } + + public virtual AssetBase Get(string id) + { +// m_log.DebugFormat("[ASSET SERVICE]: Get asset for {0}", id); + + UUID assetID; + + if (!UUID.TryParse(id, out assetID)) + { + m_log.WarnFormat("[XASSET SERVICE]: Could not parse requested asset id {0}", id); + return null; + } + + try + { + AssetBase asset = m_Database.GetAsset(assetID); + + if (asset != null) + { + return asset; + } + else if (HasChainedAssetService) + { + asset = m_ChainedAssetService.Get(id); + + if (asset != null) + MigrateFromChainedService(asset); + + return asset; + } + + return null; + } + catch (Exception e) + { + m_log.ErrorFormat("[XASSET SERVICE]: Exception getting asset {0} {1}", assetID, e); + return null; + } + } + + public virtual AssetBase GetCached(string id) + { + return Get(id); + } + + public virtual AssetMetadata GetMetadata(string id) + { +// m_log.DebugFormat("[XASSET SERVICE]: Get asset metadata for {0}", id); + + AssetBase asset = Get(id); + + if (asset != null) + return asset.Metadata; + else + return null; + } + + public virtual byte[] GetData(string id) + { +// m_log.DebugFormat("[XASSET SERVICE]: Get asset data for {0}", id); + + AssetBase asset = Get(id); + + if (asset != null) + return asset.Data; + else + return null; + } + + public virtual bool Get(string id, Object sender, AssetRetrieved handler) + { + //m_log.DebugFormat("[XASSET SERVICE]: Get asset async {0}", id); + + UUID assetID; + + if (!UUID.TryParse(id, out assetID)) + return false; + + AssetBase asset = Get(id); + + //m_log.DebugFormat("[XASSET SERVICE]: Got asset {0}", asset); + + handler(id, sender, asset); + + return true; + } + + public virtual bool[] AssetsExist(string[] ids) + { + UUID[] uuid = Array.ConvertAll(ids, id => UUID.Parse(id)); + return m_Database.AssetsExist(uuid); + } + + public virtual string Store(AssetBase asset) + { + bool exists = m_Database.AssetsExist(new[] { asset.FullID })[0]; + if (!exists) + { +// m_log.DebugFormat( +// "[XASSET SERVICE]: Storing asset {0} {1}, bytes {2}", asset.Name, asset.FullID, asset.Data.Length); + m_Database.StoreAsset(asset); + } +// else +// { +// m_log.DebugFormat( +// "[XASSET SERVICE]: Not storing asset {0} {1}, bytes {2} as it already exists", asset.Name, asset.FullID, asset.Data.Length); +// } + + return asset.ID; + } + + public bool UpdateContent(string id, byte[] data) + { + return false; + } + + public virtual bool Delete(string id) + { +// m_log.DebugFormat("[XASSET SERVICE]: Deleting asset {0}", id); + + UUID assetID; + if (!UUID.TryParse(id, out assetID)) + return false; + + if (HasChainedAssetService) + m_ChainedAssetService.Delete(id); + + return m_Database.Delete(id); + } + + private void MigrateFromChainedService(AssetBase asset) + { + Store(asset); + m_ChainedAssetService.Delete(asset.ID); + } + } +} diff --git a/OpenSim/Services/AssetService/XAssetServiceBase.cs b/OpenSim/Services/AssetService/XAssetServiceBase.cs new file mode 100644 index 0000000000..c118c9dac1 --- /dev/null +++ b/OpenSim/Services/AssetService/XAssetServiceBase.cs @@ -0,0 +1,119 @@ +/* + * 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 log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Services.AssetService +{ + public class XAssetServiceBase : ServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected IXAssetDataPlugin m_Database; + protected IAssetLoader m_AssetLoader; + protected IAssetService m_ChainedAssetService; + + protected bool HasChainedAssetService { get { return m_ChainedAssetService != null; } } + + public XAssetServiceBase(IConfigSource config, string configName) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + + // + // Try reading the [AssetService] section first, if it exists + // + IConfig assetConfig = config.Configs[configName]; + if (assetConfig != null) + { + dllName = assetConfig.GetString("StorageProvider", dllName); + connString = assetConfig.GetString("ConnectionString", connString); + } + + // + // 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); + } + + // + // 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(dllName); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + + string chainedAssetServiceDesignator = assetConfig.GetString("ChainedServiceModule", null); + + if (chainedAssetServiceDesignator != null) + { + m_log.InfoFormat( + "[XASSET SERVICE BASE]: Loading chained asset service from {0}", chainedAssetServiceDesignator); + + Object[] args = new Object[] { config, configName }; + m_ChainedAssetService = ServerUtils.LoadPlugin(chainedAssetServiceDesignator, args); + + if (!HasChainedAssetService) + throw new Exception( + String.Format("Failed to load ChainedAssetService from {0}", chainedAssetServiceDesignator)); + } + + m_Database.Initialise(connString); + + if (HasChainedAssetService) + { + string loaderName = assetConfig.GetString("DefaultAssetLoader", + String.Empty); + + if (loaderName != String.Empty) + { + m_AssetLoader = LoadPlugin(loaderName); + + if (m_AssetLoader == null) + throw new Exception("Asset loader could not be loaded"); + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs new file mode 100644 index 0000000000..229f5578f7 --- /dev/null +++ b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs @@ -0,0 +1,186 @@ +/* + * 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 log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.AuthenticationService +{ + // Generic Authentication service used for identifying + // and authenticating principals. + // Principals may be clients acting on users' behalf, + // or any other components that need + // verifiable identification. + // + public class AuthenticationServiceBase : ServiceBase + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected IAuthenticationData m_Database; + + public AuthenticationServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "auth"; + + // + // Try reading the [AuthenticationService] section first, if it exists + // + IConfig authConfig = config.Configs["AuthenticationService"]; + if (authConfig != null) + { + dllName = authConfig.GetString("StorageProvider", dllName); + connString = authConfig.GetString("ConnectionString", connString); + realm = authConfig.GetString("Realm", realm); + } + + // + // 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); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName == String.Empty || realm == String.Empty) + throw new Exception("No StorageProvider configured"); + + m_Database = LoadPlugin(dllName, + new Object[] {connString, realm}); + if (m_Database == null) + throw new Exception(string.Format("Could not find a storage interface in module {0}", dllName)); + } + + public bool Verify(UUID principalID, string token, int lifetime) + { + return m_Database.CheckToken(principalID, token, lifetime); + } + + public virtual bool Release(UUID principalID, string token) + { + return m_Database.CheckToken(principalID, token, 0); + } + + public virtual bool SetPassword(UUID principalID, string password) + { + string passwordSalt = Util.Md5Hash(UUID.Random().ToString()); + string md5PasswdHash = Util.Md5Hash(Util.Md5Hash(password) + ":" + passwordSalt); + + AuthenticationData auth = m_Database.Get(principalID); + if (auth == null) + { + auth = new AuthenticationData(); + auth.PrincipalID = principalID; + auth.Data = new System.Collections.Generic.Dictionary(); + auth.Data["accountType"] = "UserAccount"; + auth.Data["webLoginKey"] = UUID.Zero.ToString(); + } + auth.Data["passwordHash"] = md5PasswdHash; + auth.Data["passwordSalt"] = passwordSalt; + if (!m_Database.Store(auth)) + { + m_log.DebugFormat("[AUTHENTICATION DB]: Failed to store authentication data"); + return false; + } + + m_log.InfoFormat("[AUTHENTICATION DB]: Set password for principalID {0}", principalID); + return true; + } + + public virtual AuthInfo GetAuthInfo(UUID principalID) + { + AuthenticationData data = m_Database.Get(principalID); + + if (data == null) + { + return null; + } + else + { + AuthInfo info + = new AuthInfo() + { + PrincipalID = data.PrincipalID, + AccountType = data.Data["accountType"] as string, + PasswordHash = data.Data["passwordHash"] as string, + PasswordSalt = data.Data["passwordSalt"] as string, + WebLoginKey = data.Data["webLoginKey"] as string + }; + + return info; + } + } + + public virtual bool SetAuthInfo(AuthInfo info) + { + AuthenticationData auth = new AuthenticationData(); + auth.PrincipalID = info.PrincipalID; + auth.Data = new System.Collections.Generic.Dictionary(); + auth.Data["accountType"] = info.AccountType; + auth.Data["webLoginKey"] = info.WebLoginKey; + auth.Data["passwordHash"] = info.PasswordHash; + auth.Data["passwordSalt"] = info.PasswordSalt; + + if (!m_Database.Store(auth)) + { + m_log.ErrorFormat("[AUTHENTICATION DB]: Failed to store authentication info."); + return false; + } + + m_log.DebugFormat("[AUTHENTICATION DB]: Set authentication info for principalID {0}", info.PrincipalID); + return true; + } + + protected string GetToken(UUID principalID, int lifetime) + { + UUID token = UUID.Random(); + + if (m_Database.SetToken(principalID, token.ToString(), lifetime)) + return token.ToString(); + + return String.Empty; + } + + } +} diff --git a/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs new file mode 100644 index 0000000000..5f1bde1387 --- /dev/null +++ b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs @@ -0,0 +1,99 @@ +/* + * 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.Services.Interfaces; +using log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Framework.Console; + +namespace OpenSim.Services.AuthenticationService +{ + // Generic Authentication service used for identifying + // and authenticating principals. + // Principals may be clients acting on users' behalf, + // or any other components that need + // verifiable identification. + // + public class PasswordAuthenticationService : + AuthenticationServiceBase, IAuthenticationService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public PasswordAuthenticationService(IConfigSource config) : + base(config) + { + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + AuthenticationData data = m_Database.Get(principalID); + + if (data == null) + { + m_log.DebugFormat("[AUTH SERVICE]: PrincipalID {0} not found", principalID); + return String.Empty; + } + else if (data.Data == null) + { + m_log.DebugFormat("[AUTH SERVICE]: PrincipalID {0} data not found", principalID); + return String.Empty; + } + else if (!data.Data.ContainsKey("passwordHash") || !data.Data.ContainsKey("passwordSalt")) + { + m_log.DebugFormat( + "[AUTH SERVICE]: PrincipalID {0} data didn't contain either passwordHash or passwordSalt", principalID); + return String.Empty; + } + else + { + string hashed = Util.Md5Hash(password + ":" + data.Data["passwordSalt"].ToString()); + + m_log.DebugFormat("[PASS AUTH]: got {0}; hashed = {1}; stored = {2}", password, hashed, data.Data["passwordHash"].ToString()); + + if (data.Data["passwordHash"].ToString() == hashed) + { + return GetToken(principalID, lifetime); + } + else + { + m_log.DebugFormat( + "[AUTH SERVICE]: Salted hash {0} of given password did not match salted hash of {1} for PrincipalID {2}. Authentication failure.", + hashed, data.Data["passwordHash"], principalID); + return String.Empty; + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs b/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8c63adc6b0 --- /dev/null +++ b/OpenSim/Services/AuthenticationService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.AuthenticationService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim")] +[assembly: AssemblyCopyright("Copyright © 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("74497b6f-8844-4ed4-8f0d-2caf7f42b760")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs b/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs new file mode 100644 index 0000000000..2344c0ed4e --- /dev/null +++ b/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs @@ -0,0 +1,91 @@ +/* + * 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.Services.Interfaces; +using log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Framework.Console; + +namespace OpenSim.Services.AuthenticationService +{ + // Generic Authentication service used for identifying + // and authenticating principals. + // Principals may be clients acting on users' behalf, + // or any other components that need + // verifiable identification. + // + public class WebkeyAuthenticationService : + AuthenticationServiceBase, IAuthenticationService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public WebkeyAuthenticationService(IConfigSource config) : + base(config) + { + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + if (new UUID(password) == UUID.Zero) + { + m_log.DebugFormat("[AUTH SERVICE]: UUID.Zero is not a valid web_login_key on PrincipalID {0}", principalID); + } + else + { + AuthenticationData data = m_Database.Get(principalID); + if (data != null && data.Data != null) + { + if (data.Data.ContainsKey("webLoginKey")) + { + string key = data.Data["webLoginKey"].ToString(); + if (key == password) + { + data.Data["webLoginKey"] = UUID.Zero.ToString(); + m_Database.Store(data); + return GetToken(principalID, lifetime); + } + else + { + m_log.DebugFormat("[AUTH SERVICE]: web login auth failed, got PrincipalID {0} gave {1} instead of {2}", principalID, password, key); + } + }else{ + m_log.DebugFormat("[AUTH SERVICE]: no col webLoginKey in passwd.db"); + } + } + m_log.DebugFormat("[AUTH SERVICE]: PrincipalID {0} or its data not found", principalID); + } + return String.Empty; + } + } +} diff --git a/OpenSim/Services/AuthenticationService/WebkeyOrPasswordAuthenticationService.cs b/OpenSim/Services/AuthenticationService/WebkeyOrPasswordAuthenticationService.cs new file mode 100644 index 0000000000..2c6cebdb7a --- /dev/null +++ b/OpenSim/Services/AuthenticationService/WebkeyOrPasswordAuthenticationService.cs @@ -0,0 +1,92 @@ +/* + * 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.Services.Interfaces; +using log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; + +namespace OpenSim.Services.AuthenticationService +{ + public class WebkeyOrPasswordAuthenticationService : AuthenticationServiceBase, IAuthenticationService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary m_svcChecks + = new Dictionary(); + + public WebkeyOrPasswordAuthenticationService(IConfigSource config) + : base(config) + { + m_svcChecks["web_login_key"] = new WebkeyAuthenticationService(config); + m_svcChecks["password"] = new PasswordAuthenticationService(config); + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + AuthenticationData data = m_Database.Get(principalID); + string result = String.Empty; + if (data != null && data.Data != null) + { + if (data.Data.ContainsKey("webLoginKey")) + { + m_log.DebugFormat("[AUTH SERVICE]: Attempting web key authentication for PrincipalID {0}", principalID); + result = m_svcChecks["web_login_key"].Authenticate(principalID, password, lifetime); + if (result == String.Empty) + { + m_log.DebugFormat("[AUTH SERVICE]: Web Login failed for PrincipalID {0}", principalID); + } + } + if (result == string.Empty && data.Data.ContainsKey("passwordHash") && data.Data.ContainsKey("passwordSalt")) + { + m_log.DebugFormat("[AUTH SERVICE]: Attempting password authentication for PrincipalID {0}", principalID); + result = m_svcChecks["password"].Authenticate(principalID, password, lifetime); + if (result == String.Empty) + { + m_log.DebugFormat("[AUTH SERVICE]: Password login failed for PrincipalID {0}", principalID); + } + } + if (result == string.Empty) + { + m_log.DebugFormat("[AUTH SERVICE]: Both password and webLoginKey-based authentication failed for PrincipalID {0}", principalID); + } + } + else + { + m_log.DebugFormat("[AUTH SERVICE]: PrincipalID {0} or its data not found", principalID); + } + return result; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/AuthorizationService/AuthorizationService.cs b/OpenSim/Services/AuthorizationService/AuthorizationService.cs new file mode 100644 index 0000000000..03da6e144f --- /dev/null +++ b/OpenSim/Services/AuthorizationService/AuthorizationService.cs @@ -0,0 +1,58 @@ +/* + * 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 log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.AuthorizationService +{ + public class AuthorizationService : AuthorizationServiceBase, IAuthorizationService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public AuthorizationService(IConfigSource config) : base(config) + { + m_log.Info("[AUTHORIZATION CONNECTOR]: Local Authorization service enabled"); + } + + public bool IsAuthorizedForRegion( + string userID, string firstName, string lastName, string regionID, out string message) + { + message = "Authorized"; + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/AuthorizationService/AuthorizationServiceBase.cs b/OpenSim/Services/AuthorizationService/AuthorizationServiceBase.cs new file mode 100644 index 0000000000..9e6d070b4b --- /dev/null +++ b/OpenSim/Services/AuthorizationService/AuthorizationServiceBase.cs @@ -0,0 +1,83 @@ +/* + * 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.Services.AuthorizationService +{ + public class AuthorizationServiceBase : ServiceBase + { + protected IAssetDataPlugin m_Database = null; + + public AuthorizationServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + + // + // Try reading the [AuthorizationService] section first, if it exists + // + IConfig assetConfig = config.Configs["AuthorizationService"]; + if (assetConfig != null) + { + dllName = assetConfig.GetString("StorageProvider", dllName); + connString = assetConfig.GetString("ConnectionString", connString); + } + + // + // 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); + } + + // + // 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(dllName); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + + m_Database.Initialise(connString); + + } + } +} diff --git a/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs b/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ce91b56b08 --- /dev/null +++ b/OpenSim/Services/AuthorizationService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.AuthorizationService")] +[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("822586bb-cf25-4a2a-ac3e-59edaf147be3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/AvatarService/AvatarService.cs b/OpenSim/Services/AvatarService/AvatarService.cs new file mode 100644 index 0000000000..423c7818f0 --- /dev/null +++ b/OpenSim/Services/AvatarService/AvatarService.cs @@ -0,0 +1,185 @@ +/* + * 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.Net; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.AvatarService +{ + public class AvatarService : AvatarServiceBase, IAvatarService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public AvatarService(IConfigSource config) + : base(config) + { + m_log.Debug("[AVATAR SERVICE]: Starting avatar service"); + } + + public AvatarAppearance GetAppearance(UUID principalID) + { + AvatarData avatar = GetAvatar(principalID); + return avatar.ToAvatarAppearance(); + } + + public bool SetAppearance(UUID principalID, AvatarAppearance appearance) + { + AvatarData avatar = new AvatarData(appearance); + return SetAvatar(principalID,avatar); + } + + public AvatarData GetAvatar(UUID principalID) + { + AvatarBaseData[] av = m_Database.Get("PrincipalID", principalID.ToString()); + AvatarData ret = new AvatarData(); + ret.Data = new Dictionary(); + + if (av.Length == 0) + { + ret.AvatarType = 1; // SL avatar + return ret; + } + + foreach (AvatarBaseData b in av) + { + if (b.Data["Name"] == "AvatarType") + ret.AvatarType = Convert.ToInt32(b.Data["Value"]); + else + ret.Data[b.Data["Name"]] = b.Data["Value"]; + } + + return ret; + } + + public bool SetAvatar(UUID principalID, AvatarData avatar) + { + int count = 0; + foreach (KeyValuePair kvp in avatar.Data) + if (kvp.Key.StartsWith("_")) + count++; + +// m_log.DebugFormat("[AVATAR SERVICE]: SetAvatar for {0}, attachs={1}", principalID, count); + m_Database.Delete("PrincipalID", principalID.ToString()); + + AvatarBaseData av = new AvatarBaseData(); + av.Data = new Dictionary(); + + av.PrincipalID = principalID; + av.Data["Name"] = "AvatarType"; + av.Data["Value"] = avatar.AvatarType.ToString(); + + if (!m_Database.Store(av)) + return false; + + foreach (KeyValuePair kvp in avatar.Data) + { + av.Data["Name"] = kvp.Key; + + // justincc 20110730. Yes, this is a hack to get around the fact that a bug in OpenSim is causing + // various simulators on osgrid to inject bad values. Since these simulators might be around for a + // long time, we are going to manually police the value. + // + // It should be possible to remove this in half a year if we don't want to police values server side. + if (kvp.Key == "AvatarHeight") + { + float height; + if (!float.TryParse(kvp.Value, out height) || height < 0 || height > 10) + { + string rawHeight = kvp.Value.Replace(",", "."); + + if (!float.TryParse(rawHeight, out height) || height < 0 || height > 10) + height = 1.771488f; + + m_log.DebugFormat( + "[AVATAR SERVICE]: Rectifying height of avatar {0} from {1} to {2}", + principalID, kvp.Value, height); + } + + av.Data["Value"] = height.ToString(); + } + else + { + av.Data["Value"] = kvp.Value; + } + + if (!m_Database.Store(av)) + { + m_Database.Delete("PrincipalID", principalID.ToString()); + return false; + } + } + + return true; + } + + public bool ResetAvatar(UUID principalID) + { + return m_Database.Delete("PrincipalID", principalID.ToString()); + } + + public bool SetItems(UUID principalID, string[] names, string[] values) + { + AvatarBaseData av = new AvatarBaseData(); + av.Data = new Dictionary(); + av.PrincipalID = principalID; + + if (names.Length != values.Length) + return false; + + for (int i = 0 ; i < names.Length ; i++) + { + av.Data["Name"] = names[i]; + av.Data["Value"] = values[i]; + + if (!m_Database.Store(av)) + return false; + } + + return true; + } + + public bool RemoveItems(UUID principalID, string[] names) + { + foreach (string name in names) + { + m_Database.Delete(principalID, name); + } + return true; + } + } +} diff --git a/OpenSim/Services/AvatarService/AvatarServiceBase.cs b/OpenSim/Services/AvatarService/AvatarServiceBase.cs new file mode 100644 index 0000000000..ab9d7cdc62 --- /dev/null +++ b/OpenSim/Services/AvatarService/AvatarServiceBase.cs @@ -0,0 +1,84 @@ +/* + * 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.Services.AvatarService +{ + public class AvatarServiceBase : ServiceBase + { + protected IAvatarData m_Database = null; + + public AvatarServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "Avatars"; + + // + // 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); + } + + // + // [AvatarService] section overrides [DatabaseService], if it exists + // + IConfig presenceConfig = config.Configs["AvatarService"]; + if (presenceConfig != null) + { + dllName = presenceConfig.GetString("StorageProvider", dllName); + connString = presenceConfig.GetString("ConnectionString", connString); + realm = presenceConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + + } + } +} diff --git a/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs b/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1778863554 --- /dev/null +++ b/OpenSim/Services/AvatarService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.AvatarService")] +[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("0c9462ad-a5f3-46d1-ae9e-d6901fa33aa4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/Base/Properties/AssemblyInfo.cs b/OpenSim/Services/Base/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..a3fb11b01d --- /dev/null +++ b/OpenSim/Services/Base/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.Base")] +[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("db9f6f73-3a56-497f-a465-4bea9cb86062")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/Base/ServiceBase.cs b/OpenSim/Services/Base/ServiceBase.cs new file mode 100644 index 0000000000..a7eb2be9f9 --- /dev/null +++ b/OpenSim/Services/Base/ServiceBase.cs @@ -0,0 +1,121 @@ +/* + * 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 log4net; +using Nini.Config; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.Base +{ + public class ServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public T LoadPlugin(string dllName) where T:class + { + return LoadPlugin(dllName, new Object[0]); + } + + public T LoadPlugin(string dllName, Object[] args) where T:class + { + // The path:type separator : is unfortunate because it collides + // with Windows paths like C:\... + // When the path provided includes the drive, this fails. + // Hence the root/noroot thing going on here. + string pathRoot = Path.GetPathRoot(dllName); + string noRoot = dllName.Substring(pathRoot.Length); + string[] parts = noRoot.Split(new char[] {':'}); + + dllName = pathRoot + parts[0]; + + string className = String.Empty; + + if (parts.Length > 1) + className = parts[1]; + + return LoadPlugin(dllName, className, args); + } + + public T LoadPlugin(string dllName, string className, Object[] args) where T:class + { + string interfaceName = typeof(T).ToString(); + + try + { + Assembly pluginAssembly = Assembly.LoadFrom(dllName); + +// m_log.DebugFormat("[SERVICE BASE]: Found assembly {0}", dllName); + + foreach (Type pluginType in pluginAssembly.GetTypes()) + { +// m_log.DebugFormat("[SERVICE BASE]: Found type {0}", pluginType); + + if (pluginType.IsPublic) + { + if (className != String.Empty && + pluginType.ToString() != + pluginType.Namespace + "." + className) + continue; + + Type typeInterface = + pluginType.GetInterface(interfaceName); + if (typeInterface != null) + { + T plug = (T)Activator.CreateInstance(pluginType, + args); + + return plug; + } + } + } + + return null; + } + catch (Exception e) + { + List strArgs = new List(); + foreach (Object arg in args) + strArgs.Add(arg.ToString()); + + m_log.Error( + string.Format( + "[SERVICE BASE]: Failed to load plugin {0} from {1} with args {2}", + interfaceName, dllName, string.Join(", ", strArgs.ToArray())), e); + + return null; + } + } + + public ServiceBase(IConfigSource config) + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/AgentPreferences/AgentPreferencesConnector.cs b/OpenSim/Services/Connectors/AgentPreferences/AgentPreferencesConnector.cs new file mode 100644 index 0000000000..1dbc0c8fe2 --- /dev/null +++ b/OpenSim/Services/Connectors/AgentPreferences/AgentPreferencesConnector.cs @@ -0,0 +1,230 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using IAvatarService = OpenSim.Services.Interfaces.IAvatarService; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class AgentPreferencesServicesConnector : BaseServiceConnector, IAgentPreferencesService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public AgentPreferencesServicesConnector() + { + } + + public AgentPreferencesServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public AgentPreferencesServicesConnector(IConfigSource source) + : base(source, "AgentPreferencesService") + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["AgentPreferencesService"]; + if (gridConfig == null) + { + m_log.Error("[AGENT PREFERENCES CONNECTOR]: AgentPreferencesService missing from OpenSim.ini"); + throw new Exception("Agent Preferences connector init error"); + } + + string serviceURI = gridConfig.GetString("AgentPreferencesServerURI", String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[AGENT PREFERENCES CONNECTOR]: No Server URI named in section AgentPreferences"); + throw new Exception("Agent Preferences connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "AgentPreferencesService"); + } + + #region IAgentPreferencesService + + public AgentPrefs GetAgentPreferences(UUID principalID) + { + Dictionary sendData = new Dictionary(); + + string reply = string.Empty; + string uri = String.Concat(m_ServerURI, "/agentprefs"); + + sendData["METHOD"] = "getagentprefs"; + sendData["UserID"] = principalID; + string reqString = ServerUtils.BuildQueryString(sendData); + // m_log.DebugFormat("[AGENT PREFS CONNECTOR]: queryString = {0}", reqString); + + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (String.IsNullOrEmpty(reply)) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetAgentPreferences received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: Exception when contacting agent preferences server at {0}: {1}", uri, e.Message); + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + if (replyData != null) + { + if (replyData.ContainsKey("result") && + (replyData["result"].ToString() == "null" || replyData["result"].ToString() == "Failure")) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetAgentPreferences received Failure response"); + return null; + } + } + else + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetAgentPreferences received null response"); + return null; + } + AgentPrefs prefs = new AgentPrefs(replyData); + return prefs; + } + + public bool StoreAgentPreferences(AgentPrefs data) + { + Dictionary sendData = new Dictionary(); + + sendData["METHOD"] = "setagentprefs"; + + sendData["PrincipalID"] = data.PrincipalID.ToString(); + sendData["AccessPrefs"] = data.AccessPrefs; + sendData["HoverHeight"] = data.HoverHeight.ToString(); + sendData["Language"] = data.Language; + sendData["LanguageIsPublic"] = data.LanguageIsPublic.ToString(); + sendData["PermEveryone"] = data.PermEveryone.ToString(); + sendData["PermGroup"] = data.PermGroup.ToString(); + sendData["PermNextOwner"] = data.PermNextOwner.ToString(); + + string uri = String.Concat(m_ServerURI, "/agentprefs"); + string reqString = ServerUtils.BuildQueryString(sendData); + // m_log.DebugFormat("[AGENT PREFS CONNECTOR]: queryString = {0}", reqString); + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: StoreAgentPreferences reply data does not contain result field"); + } + + } + else + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: StoreAgentPreferences received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: Exception when contacting agent preferences server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public string GetLang(UUID principalID) + { + Dictionary sendData = new Dictionary(); + string reply = string.Empty; + + sendData["METHOD"] = "getagentlang"; + sendData["UserID"] = principalID.ToString(); + + string uri = String.Concat(m_ServerURI, "/agentprefs"); + string reqString = ServerUtils.BuildQueryString(sendData); + + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (String.IsNullOrEmpty(reply)) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetLang received null or empty reply"); + return "en-us"; // I guess? Gotta return somethin'! + } + } + catch (Exception e) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: Exception when contacting agent preferences server at {0}: {1}", uri, e.Message); + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + if (replyData != null) + { + if (replyData.ContainsKey("result") && + (replyData["result"].ToString() == "null" || replyData["result"].ToString() == "Failure")) + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetLang received Failure response"); + return "en-us"; + } + if (replyData.ContainsKey("Language")) + return replyData["Language"].ToString(); + } + else + { + m_log.DebugFormat("[AGENT PREFERENCES CONNECTOR]: GetLang received null response"); + + } + return "en-us"; + } + + #endregion IAgentPreferencesService + } +} diff --git a/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs new file mode 100644 index 0000000000..badacb7b8c --- /dev/null +++ b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs @@ -0,0 +1,373 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class AssetServicesConnector : BaseServiceConnector, IAssetService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + private IImprovedAssetCache m_Cache = null; + private int m_maxAssetRequestConcurrency = 30; + + private delegate void AssetRetrievedEx(AssetBase asset); + + // Keeps track of concurrent requests for the same asset, so that it's only loaded once. + // Maps: Asset ID -> Handlers which will be called when the asset has been loaded + private Dictionary m_AssetHandlers = new Dictionary(); + + public int MaxAssetRequestConcurrency + { + get { return m_maxAssetRequestConcurrency; } + set { m_maxAssetRequestConcurrency = value; } + } + + public AssetServicesConnector() + { + } + + public AssetServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public AssetServicesConnector(IConfigSource source) + : base(source, "AssetService") + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig netconfig = source.Configs["Network"]; + if (netconfig != null) + m_maxAssetRequestConcurrency = netconfig.GetInt("MaxRequestConcurrency",m_maxAssetRequestConcurrency); + + IConfig assetConfig = source.Configs["AssetService"]; + if (assetConfig == null) + { + m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini"); + throw new Exception("Asset connector init error"); + } + + string serviceURI = assetConfig.GetString("AssetServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[ASSET CONNECTOR]: No Server URI named in section AssetService"); + throw new Exception("Asset connector init error"); + } + + m_ServerURI = serviceURI; + } + + protected void SetCache(IImprovedAssetCache cache) + { + m_Cache = cache; + } + + public AssetBase Get(string id) + { +// m_log.DebugFormat("[ASSET SERVICE CONNECTOR]: Synchronous get request for {0}", id); + + string uri = m_ServerURI + "/assets/" + id; + + AssetBase asset = null; + if (m_Cache != null) + asset = m_Cache.Get(id); + + if (asset == null) + { + // XXX: Commented out for now since this has either never been properly operational or not for some time + // as m_maxAssetRequestConcurrency was being passed as the timeout, not a concurrency limiting option. + // Wasn't noticed before because timeout wasn't actually used. + // Not attempting concurrency setting for now as this omission was discovered in release candidate + // phase for OpenSimulator 0.8. Need to revisit afterwards. +// asset +// = SynchronousRestObjectRequester.MakeRequest( +// "GET", uri, 0, m_maxAssetRequestConcurrency); + + asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); + + if (m_Cache != null) + m_Cache.Cache(asset); + } + return asset; + } + + public AssetBase GetCached(string id) + { +// m_log.DebugFormat("[ASSET SERVICE CONNECTOR]: Cache request for {0}", id); + + if (m_Cache != null) + return m_Cache.Get(id); + + return null; + } + + public AssetMetadata GetMetadata(string id) + { + if (m_Cache != null) + { + AssetBase fullAsset = m_Cache.Get(id); + + if (fullAsset != null) + return fullAsset.Metadata; + } + + string uri = m_ServerURI + "/assets/" + id + "/metadata"; + + AssetMetadata asset = SynchronousRestObjectRequester.MakeRequest("GET", uri, 0, m_Auth); + return asset; + } + + public byte[] GetData(string id) + { + if (m_Cache != null) + { + AssetBase fullAsset = m_Cache.Get(id); + + if (fullAsset != null) + return fullAsset.Data; + } + + using (RestClient rc = new RestClient(m_ServerURI)) + { + rc.AddResourcePath("assets"); + rc.AddResourcePath(id); + rc.AddResourcePath("data"); + + rc.RequestMethod = "GET"; + + Stream s = rc.Request(m_Auth); + + if (s == null) + return null; + + if (s.Length > 0) + { + byte[] ret = new byte[s.Length]; + s.Read(ret, 0, (int)s.Length); + + return ret; + } + + return null; + } + } + + public bool Get(string id, Object sender, AssetRetrieved handler) + { +// m_log.DebugFormat("[ASSET SERVICE CONNECTOR]: Potentially asynchronous get request for {0}", id); + + string uri = m_ServerURI + "/assets/" + id; + + AssetBase asset = null; + if (m_Cache != null) + asset = m_Cache.Get(id); + + if (asset == null) + { + lock (m_AssetHandlers) + { + AssetRetrievedEx handlerEx = new AssetRetrievedEx(delegate(AssetBase _asset) { handler(id, sender, _asset); }); + + AssetRetrievedEx handlers; + if (m_AssetHandlers.TryGetValue(id, out handlers)) + { + // Someone else is already loading this asset. It will notify our handler when done. + handlers += handlerEx; + return true; + } + + // Load the asset ourselves + handlers += handlerEx; + m_AssetHandlers.Add(id, handlers); + } + + bool success = false; + try + { + AsynchronousRestObjectRequester.MakeRequest("GET", uri, 0, + delegate(AssetBase a) + { + if (a != null && m_Cache != null) + m_Cache.Cache(a); + + AssetRetrievedEx handlers; + lock (m_AssetHandlers) + { + handlers = m_AssetHandlers[id]; + m_AssetHandlers.Remove(id); + } + handlers.Invoke(a); + }, m_maxAssetRequestConcurrency, m_Auth); + + success = true; + } + finally + { + if (!success) + { + lock (m_AssetHandlers) + { + m_AssetHandlers.Remove(id); + } + } + } + } + else + { + handler(id, sender, asset); + } + + return true; + } + + public virtual bool[] AssetsExist(string[] ids) + { + string uri = m_ServerURI + "/get_assets_exist"; + + bool[] exist = null; + try + { + exist = SynchronousRestObjectRequester.MakeRequest("POST", uri, ids, m_Auth); + } + catch (Exception) + { + // This is most likely to happen because the server doesn't support this function, + // so just silently return "doesn't exist" for all the assets. + } + + if (exist == null) + exist = new bool[ids.Length]; + + return exist; + } + + public string Store(AssetBase asset) + { + if (asset.Local) + { + if (m_Cache != null) + m_Cache.Cache(asset); + + return asset.ID; + } + + string uri = m_ServerURI + "/assets/"; + + string newID; + try + { + newID = SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, m_Auth); + } + catch (Exception e) + { + m_log.Warn(string.Format("[ASSET CONNECTOR]: Unable to send asset {0} to asset server. Reason: {1} ", asset.ID, e.Message), e); + return string.Empty; + } + + // TEMPORARY: SRAS returns 'null' when it's asked to store existing assets + if (newID == null) + { + m_log.DebugFormat("[ASSET CONNECTOR]: Storing of asset {0} returned null; assuming the asset already exists", asset.ID); + return asset.ID; + } + + if (string.IsNullOrEmpty(newID)) + return string.Empty; + + asset.ID = newID; + + if (m_Cache != null) + m_Cache.Cache(asset); + + return newID; + } + + public bool UpdateContent(string id, byte[] data) + { + AssetBase asset = null; + + if (m_Cache != null) + asset = m_Cache.Get(id); + + if (asset == null) + { + AssetMetadata metadata = GetMetadata(id); + if (metadata == null) + return false; + + asset = new AssetBase(metadata.FullID, metadata.Name, metadata.Type, UUID.Zero.ToString()); + asset.Metadata = metadata; + } + asset.Data = data; + + string uri = m_ServerURI + "/assets/" + id; + + if (SynchronousRestObjectRequester.MakeRequest("POST", uri, asset, m_Auth)) + { + if (m_Cache != null) + m_Cache.Cache(asset); + + return true; + } + return false; + } + + public bool Delete(string id) + { + string uri = m_ServerURI + "/assets/" + id; + + if (SynchronousRestObjectRequester.MakeRequest("DELETE", uri, 0, m_Auth)) + { + if (m_Cache != null) + m_Cache.Expire(id); + + return true; + } + return false; + } + } +} diff --git a/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs new file mode 100644 index 0000000000..3710c86d22 --- /dev/null +++ b/OpenSim/Services/Connectors/Asset/HGAssetServiceConnector.cs @@ -0,0 +1,263 @@ +/* + * 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 log4net; +using Nini.Config; +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Reflection; +using System.Web; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Hypergrid; +using OpenSim.Services.Connectors.SimianGrid; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class HGAssetServiceConnector : IAssetService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private Dictionary m_endpointSerializer = new Dictionary(); + private object EndPointLock(IAssetService connector) + { + lock (m_endpointSerializer) + { + object eplock = null; + + if (! m_endpointSerializer.TryGetValue(connector, out eplock)) + { + eplock = new object(); + m_endpointSerializer.Add(connector, eplock); + // m_log.WarnFormat("[WEB UTIL] add a new host to end point serializer {0}",endpoint); + } + + return eplock; + } + } + + private Dictionary m_connectors = new Dictionary(); + + public HGAssetServiceConnector(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + // string name = moduleConfig.GetString("AssetServices", ""); + + IConfig assetConfig = source.Configs["AssetService"]; + if (assetConfig == null) + { + m_log.Error("[HG ASSET SERVICE]: AssetService missing from OpenSim.ini"); + return; + } + + m_log.Info("[HG ASSET SERVICE]: HG asset service enabled"); + } + } + + private IAssetService GetConnector(string url) + { + IAssetService connector = null; + lock (m_connectors) + { + if (m_connectors.ContainsKey(url)) + { + connector = m_connectors[url]; + } + else + { + // Still not as flexible as I would like this to be, + // but good enough for now + string connectorType = new HeloServicesConnector(url).Helo(); + m_log.DebugFormat("[HG ASSET SERVICE]: HELO returned {0}", connectorType); + if (connectorType == "opensim-simian") + { + connector = new SimianAssetServiceConnector(url); + } + else + connector = new AssetServicesConnector(url); + + m_connectors.Add(url, connector); + } + } + return connector; + } + + public AssetBase Get(string id) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(id, out url, out assetID)) + { + IAssetService connector = GetConnector(url); + return connector.Get(assetID); + } + + return null; + } + + public AssetBase GetCached(string id) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(id, out url, out assetID)) + { + IAssetService connector = GetConnector(url); + return connector.GetCached(assetID); + } + + return null; + } + + public AssetMetadata GetMetadata(string id) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(id, out url, out assetID)) + { + IAssetService connector = GetConnector(url); + return connector.GetMetadata(assetID); + } + + return null; + } + + public byte[] GetData(string id) + { + return null; + } + + public bool Get(string id, Object sender, AssetRetrieved handler) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(id, out url, out assetID)) + { + IAssetService connector = GetConnector(url); + return connector.Get(assetID, sender, handler); + } + + return false; + } + + + private struct AssetAndIndex + { + public UUID assetID; + public int index; + + public AssetAndIndex(UUID assetID, int index) + { + this.assetID = assetID; + this.index = index; + } + } + + public virtual bool[] AssetsExist(string[] ids) + { + // This method is a bit complicated because it works even if the assets belong to different + // servers; that requires sending separate requests to each server. + + // Group the assets by the server they belong to + + var url2assets = new Dictionary>(); + + for (int i = 0; i < ids.Length; i++) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(ids[i], out url, out assetID)) + { + if (!url2assets.ContainsKey(url)) + url2assets.Add(url, new List()); + url2assets[url].Add(new AssetAndIndex(UUID.Parse(assetID), i)); + } + } + + // Query each of the servers in turn + + bool[] exist = new bool[ids.Length]; + + foreach (string url in url2assets.Keys) + { + IAssetService connector = GetConnector(url); + lock (EndPointLock(connector)) + { + List curAssets = url2assets[url]; + string[] assetIDs = curAssets.ConvertAll(a => a.assetID.ToString()).ToArray(); + bool[] curExist = connector.AssetsExist(assetIDs); + + int i = 0; + foreach (AssetAndIndex ai in curAssets) + { + exist[ai.index] = curExist[i]; + ++i; + } + } + } + + return exist; + } + + public string Store(AssetBase asset) + { + string url = string.Empty; + string assetID = string.Empty; + + if (Util.ParseForeignAssetID(asset.ID, out url, out assetID)) + { + IAssetService connector = GetConnector(url); + // Restore the assetID to a simple UUID + asset.ID = assetID; + lock (EndPointLock(connector)) + return connector.Store(asset); + } + + return String.Empty; + } + + public bool UpdateContent(string id, byte[] data) + { + return false; + } + + public bool Delete(string id) + { + return false; + } + } +} diff --git a/OpenSim/Services/Connectors/Authentication/AuthenticationServicesConnector.cs b/OpenSim/Services/Connectors/Authentication/AuthenticationServicesConnector.cs new file mode 100644 index 0000000000..c8a4912e8c --- /dev/null +++ b/OpenSim/Services/Connectors/Authentication/AuthenticationServicesConnector.cs @@ -0,0 +1,171 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class AuthenticationServicesConnector : BaseServiceConnector, IAuthenticationService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public AuthenticationServicesConnector() + { + } + + public AuthenticationServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public AuthenticationServicesConnector(IConfigSource source) + : base(source, "AuthenticationService") + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig assetConfig = source.Configs["AuthenticationService"]; + if (assetConfig == null) + { + m_log.Error("[AUTH CONNECTOR]: AuthenticationService missing from OpenSim.ini"); + throw new Exception("Authentication connector init error"); + } + + string serviceURI = assetConfig.GetString("AuthenticationServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[AUTH CONNECTOR]: No Server URI named in section AuthenticationService"); + throw new Exception("Authentication connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "AuthenticationService"); + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + Dictionary sendData = new Dictionary(); + sendData["LIFETIME"] = lifetime.ToString(); + sendData["PRINCIPAL"] = principalID.ToString(); + sendData["PASSWORD"] = password; + + sendData["METHOD"] = "authenticate"; + + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/auth/plain", + ServerUtils.BuildQueryString(sendData), m_Auth); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + if (replyData["Result"].ToString() != "Success") + return String.Empty; + + return replyData["Token"].ToString(); + } + + public bool Verify(UUID principalID, string token, int lifetime) + { +// m_log.Error("[XXX]: Verify"); + Dictionary sendData = new Dictionary(); + sendData["LIFETIME"] = lifetime.ToString(); + sendData["PRINCIPAL"] = principalID.ToString(); + sendData["TOKEN"] = token; + + sendData["METHOD"] = "verify"; + + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/auth/plain", + ServerUtils.BuildQueryString(sendData), m_Auth); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + if (replyData["Result"].ToString() != "Success") + return false; + + return true; + } + + public bool Release(UUID principalID, string token) + { + Dictionary sendData = new Dictionary(); + sendData["PRINCIPAL"] = principalID.ToString(); + sendData["TOKEN"] = token; + + sendData["METHOD"] = "release"; + + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + m_ServerURI + "/auth/plain", + ServerUtils.BuildQueryString(sendData), m_Auth); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + if (replyData["Result"].ToString() != "Success") + return false; + + return true; + } + + public bool SetPassword(UUID principalID, string passwd) + { + // nope, we don't do this + return false; + } + + public AuthInfo GetAuthInfo(UUID principalID) + { + // not done from remote simulators + return null; + } + + public bool SetAuthInfo(AuthInfo info) + { + // not done from remote simulators + return false; + } + } +} diff --git a/OpenSim/Services/Connectors/Authorization/AuthorizationServicesConnector.cs b/OpenSim/Services/Connectors/Authorization/AuthorizationServicesConnector.cs new file mode 100644 index 0000000000..63730b3a83 --- /dev/null +++ b/OpenSim/Services/Connectors/Authorization/AuthorizationServicesConnector.cs @@ -0,0 +1,123 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class AuthorizationServicesConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + private bool m_ResponseOnFailure = true; + + public AuthorizationServicesConnector() + { + } + + public AuthorizationServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public AuthorizationServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig authorizationConfig = source.Configs["AuthorizationService"]; + if (authorizationConfig == null) + { + //m_log.Info("[AUTHORIZATION CONNECTOR]: AuthorizationService missing from OpenSim.ini"); + throw new Exception("Authorization connector init error"); + } + + string serviceURI = authorizationConfig.GetString("AuthorizationServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[AUTHORIZATION CONNECTOR]: No Server URI named in section AuthorizationService"); + throw new Exception("Authorization connector init error"); + } + m_ServerURI = serviceURI; + + // this dictates what happens if the remote service fails, if the service fails and the value is true + // the user is authorized for the region. + bool responseOnFailure = authorizationConfig.GetBoolean("ResponseOnFailure",true); + + m_ResponseOnFailure = responseOnFailure; + m_log.Info("[AUTHORIZATION CONNECTOR]: AuthorizationService initialized"); + } + + public bool IsAuthorizedForRegion(string userID, string firstname, string surname, string email, string regionName, string regionID, out string message) + { + // do a remote call to the authorization server specified in the AuthorizationServerURI + m_log.InfoFormat("[AUTHORIZATION CONNECTOR]: IsAuthorizedForRegion checking {0} at remote server {1}", userID, m_ServerURI); + + string uri = m_ServerURI; + + AuthorizationRequest req = new AuthorizationRequest(userID, firstname, surname, email, regionName, regionID); + + AuthorizationResponse response; + try + { + response = SynchronousRestObjectRequester.MakeRequest("POST", uri, req); + } + catch (Exception e) + { + m_log.WarnFormat("[AUTHORIZATION CONNECTOR]: Unable to send authorize {0} for region {1} error thrown during comms with remote server. Reason: {2}", userID, regionID, e.Message); + message = e.Message; + return m_ResponseOnFailure; + } + if (response == null) + { + message = "Null response"; + return m_ResponseOnFailure; + } + m_log.DebugFormat("[AUTHORIZATION CONNECTOR] response from remote service was {0}", response.Message); + message = response.Message; + + return response.IsAuthorized; + } + + } +} diff --git a/OpenSim/Services/Connectors/Avatar/AvatarServicesConnector.cs b/OpenSim/Services/Connectors/Avatar/AvatarServicesConnector.cs new file mode 100644 index 0000000000..3f44efab73 --- /dev/null +++ b/OpenSim/Services/Connectors/Avatar/AvatarServicesConnector.cs @@ -0,0 +1,329 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using IAvatarService = OpenSim.Services.Interfaces.IAvatarService; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class AvatarServicesConnector : BaseServiceConnector, IAvatarService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public AvatarServicesConnector() + { + } + + public AvatarServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public AvatarServicesConnector(IConfigSource source) + : base(source, "AvatarService") + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["AvatarService"]; + if (gridConfig == null) + { + m_log.Error("[AVATAR CONNECTOR]: AvatarService missing from OpenSim.ini"); + throw new Exception("Avatar connector init error"); + } + + string serviceURI = gridConfig.GetString("AvatarServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[AVATAR CONNECTOR]: No Server URI named in section AvatarService"); + throw new Exception("Avatar connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "AvatarService"); + } + + + #region IAvatarService + + public AvatarAppearance GetAppearance(UUID userID) + { + AvatarData avatar = GetAvatar(userID); + return avatar.ToAvatarAppearance(); + } + + public bool SetAppearance(UUID userID, AvatarAppearance appearance) + { + AvatarData avatar = new AvatarData(appearance); + return SetAvatar(userID,avatar); + } + + public AvatarData GetAvatar(UUID userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getavatar"; + + sendData["UserID"] = userID; + + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/avatar"; + // m_log.DebugFormat("[AVATAR CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: GetAgent received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + AvatarData avatar = null; + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + { + avatar = new AvatarData((Dictionary)replyData["result"]); + } + } + + return avatar; + + } + + public bool SetAvatar(UUID userID, AvatarData avatar) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "setavatar"; + + sendData["UserID"] = userID.ToString(); + + Dictionary structData = avatar.ToKeyValuePairs(); + + foreach (KeyValuePair kvp in structData) + sendData[kvp.Key] = kvp.Value.ToString(); + + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/avatar"; + //m_log.DebugFormat("[AVATAR CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + { + m_log.DebugFormat("[AVATAR CONNECTOR]: SetAvatar reply data does not contain result field"); + } + } + else + { + m_log.DebugFormat("[AVATAR CONNECTOR]: SetAvatar received empty reply"); + } + } + catch (Exception e) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public bool ResetAvatar(UUID userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "resetavatar"; + + sendData["UserID"] = userID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/avatar"; + // m_log.DebugFormat("[AVATAR CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: SetItems reply data does not contain result field"); + + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: SetItems received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public bool SetItems(UUID userID, string[] names, string[] values) + { + Dictionary sendData = new Dictionary(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "setitems"; + + sendData["UserID"] = userID.ToString(); + sendData["Names"] = new List(names); + sendData["Values"] = new List(values); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/avatar"; + // m_log.DebugFormat("[AVATAR CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: SetItems reply data does not contain result field"); + + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: SetItems received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public bool RemoveItems(UUID userID, string[] names) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "removeitems"; + + sendData["UserID"] = userID.ToString(); + sendData["Names"] = new List(names); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/avatar"; + // m_log.DebugFormat("[AVATAR CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: RemoveItems reply data does not contain result field"); + + } + else + m_log.DebugFormat("[AVATAR CONNECTOR]: RemoveItems received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[AVATAR CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + #endregion + + } +} diff --git a/OpenSim/Services/Connectors/BaseServiceConnector.cs b/OpenSim/Services/Connectors/BaseServiceConnector.cs new file mode 100644 index 0000000000..98cd4897bf --- /dev/null +++ b/OpenSim/Services/Connectors/BaseServiceConnector.cs @@ -0,0 +1,33 @@ +using System; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; + +using Nini.Config; + +namespace OpenSim.Services.Connectors +{ + public class BaseServiceConnector + { + protected IServiceAuth m_Auth; + + public BaseServiceConnector() { } + + public BaseServiceConnector(IConfigSource config, string section) + { + Initialise(config, section); + } + + public void Initialise(IConfigSource config, string section) + { + string authType = Util.GetConfigVarFromSections(config, "AuthType", new string[] { "Network", section }, "None"); + + switch (authType) + { + case "BasicHttpAuthentication": + m_Auth = new BasicHttpAuthentication(config, section); + break; + } + + } + } +} diff --git a/OpenSim/Services/Connectors/Estate/EstateDataConnector.cs b/OpenSim/Services/Connectors/Estate/EstateDataConnector.cs new file mode 100644 index 0000000000..6412bcd72d --- /dev/null +++ b/OpenSim/Services/Connectors/Estate/EstateDataConnector.cs @@ -0,0 +1,338 @@ +/* + * 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.Net; +using System.Reflection; + +using log4net; + +using OpenMetaverse; +using Nini.Config; + +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Connectors; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; + +namespace OpenSim.Services.Connectors +{ + public class EstateDataRemoteConnector : BaseServiceConnector, IEstateDataService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + private ExpiringCache> m_EstateCache = new ExpiringCache>(); + private const int EXPIRATION = 5 * 60; // 5 minutes in secs + + public EstateDataRemoteConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["EstateService"]; + if (gridConfig == null) + { + m_log.Error("[ESTATE CONNECTOR]: EstateService missing from OpenSim.ini"); + throw new Exception("Estate connector init error"); + } + + string serviceURI = gridConfig.GetString("EstateServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[ESTATE CONNECTOR]: No Server URI named in section EstateService"); + throw new Exception("Estate connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "EstateService"); + } + + #region IEstateDataService + + public List LoadEstateSettingsAll() + { + string reply = string.Empty; + string uri = m_ServerURI + "/estates"; + + reply = MakeRequest("GET", uri, string.Empty); + if (String.IsNullOrEmpty(reply)) + return new List(); + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + List estates = new List(); + if (replyData != null && replyData.Count > 0) + { + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettingsAll returned {0} elements", replyData.Count); + Dictionary.ValueCollection estateData = replyData.Values; + foreach (object r in estateData) + { + if (r is Dictionary) + { + EstateSettings es = new EstateSettings((Dictionary)r); + estates.Add(es); + } + } + m_EstateCache.AddOrUpdate("estates", estates, EXPIRATION); + } + else + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettingsAll from {0} received null or zero response", uri); + + return estates; + + } + + public List GetEstatesAll() + { + List eids = new List(); + // If we don't have them, load them from the server + List estates = null; + if (!m_EstateCache.TryGetValue("estates", out estates)) + LoadEstateSettingsAll(); + + foreach (EstateSettings es in estates) + eids.Add((int)es.EstateID); + + return eids; + } + + public List GetEstates(string search) + { + // If we don't have them, load them from the server + List estates = null; + if (!m_EstateCache.TryGetValue("estates", out estates)) + LoadEstateSettingsAll(); + + List eids = new List(); + foreach (EstateSettings es in estates) + if (es.EstateName == search) + eids.Add((int)es.EstateID); + + return eids; + } + + public List GetEstatesByOwner(UUID ownerID) + { + // If we don't have them, load them from the server + List estates = null; + if (!m_EstateCache.TryGetValue("estates", out estates)) + LoadEstateSettingsAll(); + + List eids = new List(); + foreach (EstateSettings es in estates) + if (es.EstateOwner == ownerID) + eids.Add((int)es.EstateID); + + return eids; + } + + public List GetRegions(int estateID) + { + string reply = string.Empty; + // /estates/regions/?eid=int + string uri = m_ServerURI + "/estates/regions/?eid=" + estateID.ToString(); + + reply = MakeRequest("GET", uri, string.Empty); + if (String.IsNullOrEmpty(reply)) + return new List(); + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + List regions = new List(); + if (replyData != null && replyData.Count > 0) + { + m_log.DebugFormat("[ESTATE CONNECTOR]: GetRegions for estate {0} returned {1} elements", estateID, replyData.Count); + Dictionary.ValueCollection data = replyData.Values; + foreach (object r in data) + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(r.ToString(), out uuid)) + regions.Add(uuid); + } + } + else + m_log.DebugFormat("[ESTATE CONNECTOR]: GetRegions from {0} received null or zero response", uri); + + return regions; + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + string reply = string.Empty; + // /estates/estate/?region=uuid&create=[t|f] + string uri = m_ServerURI + string.Format("/estates/estate/?region={0}&create={1}", regionID, create); + + reply = MakeRequest("GET", uri, string.Empty); + if (String.IsNullOrEmpty(reply)) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null && replyData.Count > 0) + { + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettings({0}) returned {1} elements", regionID, replyData.Count); + EstateSettings es = new EstateSettings(replyData); + return es; + } + else + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettings(regionID) from {0} received null or zero response", uri); + + return null; + } + + public EstateSettings LoadEstateSettings(int estateID) + { + string reply = string.Empty; + // /estates/estate/?eid=int + string uri = m_ServerURI + string.Format("/estates/estate/?eid={0}", estateID); + + reply = MakeRequest("GET", uri, string.Empty); + if (String.IsNullOrEmpty(reply)) + return null; + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null && replyData.Count > 0) + { + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettings({0}) returned {1} elements", estateID, replyData.Count); + EstateSettings es = new EstateSettings(replyData); + return es; + } + else + m_log.DebugFormat("[ESTATE CONNECTOR]: LoadEstateSettings(estateID) from {0} received null or zero response", uri); + + return null; + } + + /// + /// Forbidden operation + /// + /// + public EstateSettings CreateNewEstate() + { + // No can do + return null; + } + + public void StoreEstateSettings(EstateSettings es) + { + // /estates/estate/ + string uri = m_ServerURI + ("/estates/estate"); + + Dictionary formdata = es.ToMap(); + formdata["OP"] = "STORE"; + + PostRequest(uri, formdata); + } + + public bool LinkRegion(UUID regionID, int estateID) + { + // /estates/estate/?eid=int®ion=uuid + string uri = m_ServerURI + String.Format("/estates/estate/?eid={0}®ion={1}", estateID, regionID); + + Dictionary formdata = new Dictionary(); + formdata["OP"] = "LINK"; + return PostRequest(uri, formdata); + } + + private bool PostRequest(string uri, Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + + string reply = MakeRequest("POST", uri, reqString); + if (String.IsNullOrEmpty(reply)) + return false; + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + bool result = false; + if (replyData != null && replyData.Count > 0) + { + if (replyData.ContainsKey("Result")) + { + if (Boolean.TryParse(replyData["Result"].ToString(), out result)) + m_log.DebugFormat("[ESTATE CONNECTOR]: PostRequest {0} returned {1}", uri, result); + } + } + else + m_log.DebugFormat("[ESTATE CONNECTOR]: PostRequest {0} received null or zero response", uri); + + return result; + } + + /// + /// Forbidden operation + /// + /// + public bool DeleteEstate(int estateID) + { + return false; + } + + #endregion + + private string MakeRequest(string verb, string uri, string formdata) + { + string reply = string.Empty; + try + { + reply = SynchronousRestFormsRequester.MakeRequest(verb, uri, formdata, m_Auth); + } + catch (WebException e) + { + using (HttpWebResponse hwr = (HttpWebResponse)e.Response) + { + if (hwr != null) + { + if (hwr.StatusCode == HttpStatusCode.NotFound) + m_log.Error(string.Format("[ESTATE CONNECTOR]: Resource {0} not found ", uri)); + if (hwr.StatusCode == HttpStatusCode.Unauthorized) + m_log.Error(string.Format("[ESTATE CONNECTOR]: Web request {0} requires authentication ", uri)); + } + else + m_log.Error(string.Format( + "[ESTATE CONNECTOR]: WebException for {0} {1} {2} ", + verb, uri, formdata, e)); + } + } + catch (Exception e) + { + m_log.DebugFormat("[ESTATE CONNECTOR]: Exception when contacting estate server at {0}: {1}", uri, e.Message); + } + + return reply; + } + } +} diff --git a/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs b/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs new file mode 100644 index 0000000000..d68829996d --- /dev/null +++ b/OpenSim/Services/Connectors/Freeswitch/RemoteFreeswitchConnector.cs @@ -0,0 +1,103 @@ +/* + * 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 log4net; +using System; +using System.IO; +using System.Collections; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class RemoteFreeswitchConnector : IFreeswitchService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public RemoteFreeswitchConnector() + { + } + + public RemoteFreeswitchConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/') + "/region-config"; + } + + public RemoteFreeswitchConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig freeswitchConfig = source.Configs["FreeSwitchVoice"]; + if (freeswitchConfig == null) + { + m_log.Error("[FREESWITCH CONNECTOR]: FreeSwitchVoice missing from OpenSim.ini"); + throw new Exception("Freeswitch connector init error"); + } + + string serviceURI = freeswitchConfig.GetString("FreeswitchServiceURL", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[FREESWITCH CONNECTOR]: No FreeswitchServiceURL named in section FreeSwitchVoice"); + throw new Exception("Freeswitch connector init error"); + } + m_ServerURI = serviceURI.TrimEnd('/') + "/region-config"; + } + + public Hashtable HandleDirectoryRequest(Hashtable requestBody) + { + // not used here + return new Hashtable(); + } + + public Hashtable HandleDialplanRequest(Hashtable requestBody) + { + // not used here + return new Hashtable(); + } + + public string GetJsonConfig() + { + m_log.DebugFormat("[FREESWITCH CONNECTOR]: Requesting config from {0}", m_ServerURI); + return SynchronousRestFormsRequester.MakeRequest("GET", + m_ServerURI, String.Empty); + } + } +} diff --git a/OpenSim/Services/Connectors/Friends/FriendsServicesConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsServicesConnector.cs new file mode 100644 index 0000000000..74851a94db --- /dev/null +++ b/OpenSim/Services/Connectors/Friends/FriendsServicesConnector.cs @@ -0,0 +1,269 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors.Friends +{ + public class FriendsServicesConnector : BaseServiceConnector, IFriendsService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public FriendsServicesConnector() + { + } + + public FriendsServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public FriendsServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["FriendsService"]; + if (gridConfig == null) + { + m_log.Error("[FRIENDS SERVICE CONNECTOR]: FriendsService missing from OpenSim.ini"); + throw new Exception("Friends connector init error"); + } + + string serviceURI = gridConfig.GetString("FriendsServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[FRIENDS SERVICE CONNECTOR]: No Server URI named in section FriendsService"); + throw new Exception("Friends connector init error"); + } + m_ServerURI = serviceURI; + base.Initialise(source, "FriendsService"); + } + + + #region IFriendsService + + public FriendInfo[] GetFriends(UUID PrincipalID) + { + Dictionary sendData = new Dictionary(); + + sendData["PRINCIPALID"] = PrincipalID.ToString(); + sendData["METHOD"] = "getfriends"; + + return GetFriends(sendData, PrincipalID.ToString()); + } + + public FriendInfo[] GetFriends(string PrincipalID) + { + Dictionary sendData = new Dictionary(); + + sendData["PRINCIPALID"] = PrincipalID; + sendData["METHOD"] = "getfriends_string"; + + return GetFriends(sendData, PrincipalID); + } + + protected FriendInfo[] GetFriends(Dictionary sendData, string PrincipalID) + { + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/friends"; + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + if (replyData.ContainsKey("result") && (replyData["result"].ToString().ToLower() == "null")) + { + return new FriendInfo[0]; + } + + List finfos = new List(); + Dictionary.ValueCollection finfosList = replyData.Values; + //m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: get neighbours returned {0} elements", rinfosList.Count); + foreach (object f in finfosList) + { + if (f is Dictionary) + { + FriendInfo finfo = new FriendInfo((Dictionary)f); + finfos.Add(finfo); + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: GetFriends {0} received invalid response type {1}", + PrincipalID, f.GetType()); + } + + // Success + return finfos.ToArray(); + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: GetFriends {0} received null response", + PrincipalID); + + } + } + catch (Exception e) + { + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + } + + return new FriendInfo[0]; + + } + + public bool StoreFriend(string PrincipalID, string Friend, int flags) + { + + Dictionary sendData = ToKeyValuePairs(PrincipalID, Friend, flags); + + sendData["METHOD"] = "storefriend"; + + string reply = string.Empty; + string uri = m_ServerURI + "/friends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) + { + bool success = false; + Boolean.TryParse(replyData["Result"].ToString(), out success); + return success; + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: StoreFriend {0} {1} received null response", + PrincipalID, Friend); + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: StoreFriend received null reply"); + + return false; + + } + + public bool Delete(string PrincipalID, string Friend) + { + Dictionary sendData = new Dictionary(); + sendData["PRINCIPALID"] = PrincipalID.ToString(); + sendData["FRIEND"] = Friend; + sendData["METHOD"] = "deletefriend_string"; + + return Delete(sendData, PrincipalID, Friend); + } + + public bool Delete(UUID PrincipalID, string Friend) + { + Dictionary sendData = new Dictionary(); + sendData["PRINCIPALID"] = PrincipalID.ToString(); + sendData["FRIEND"] = Friend; + sendData["METHOD"] = "deletefriend"; + + return Delete(sendData, PrincipalID.ToString(), Friend); + } + + public bool Delete(Dictionary sendData, string PrincipalID, string Friend) + { + string reply = string.Empty; + string uri = m_ServerURI + "/friends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) + { + bool success = false; + Boolean.TryParse(replyData["Result"].ToString(), out success); + return success; + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: DeleteFriend {0} {1} received null response", + PrincipalID, Friend); + } + else + m_log.DebugFormat("[FRIENDS SERVICE CONNECTOR]: DeleteFriend received null reply"); + + return false; + } + + #endregion + + public Dictionary ToKeyValuePairs(string principalID, string friend, int flags) + { + Dictionary result = new Dictionary(); + result["PrincipalID"] = principalID; + result["Friend"] = friend; + result["MyFlags"] = flags; + + return result; + } + + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs new file mode 100644 index 0000000000..6d5ce4be42 --- /dev/null +++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs @@ -0,0 +1,187 @@ +/* + * 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.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenSim.Framework; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Services.Connectors.Friends +{ + public class FriendsSimConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected virtual string ServicePath() + { + return "friends"; + } + + public bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message) + { + return FriendshipOffered(region, userID, friendID, message, String.Empty); + } + + public virtual bool FriendshipOffered(GridRegion region, UUID userID, UUID friendID, string message, string userName) + { + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "friendship_offered"; + + sendData["FromID"] = userID.ToString(); + sendData["ToID"] = friendID.ToString(); + sendData["Message"] = message; + if (userName != String.Empty) + sendData["FromName"] = userName; + + return Call(region, sendData); + } + + public bool FriendshipApproved(GridRegion region, UUID userID, string userName, UUID friendID) + { + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "friendship_approved"; + + sendData["FromID"] = userID.ToString(); + sendData["FromName"] = userName; + sendData["ToID"] = friendID.ToString(); + + return Call(region, sendData); + } + + public bool FriendshipDenied(GridRegion region, UUID userID, string userName, UUID friendID) + { + if (region == null) + return false; + + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "friendship_denied"; + + sendData["FromID"] = userID.ToString(); + sendData["FromName"] = userName; + sendData["ToID"] = friendID.ToString(); + + return Call(region, sendData); + } + + public bool FriendshipTerminated(GridRegion region, UUID userID, UUID friendID) + { + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "friendship_terminated"; + + sendData["FromID"] = userID.ToString(); + sendData["ToID"] = friendID.ToString(); + + return Call(region, sendData); + } + + public bool GrantRights(GridRegion region, UUID userID, UUID friendID, int userFlags, int rights) + { + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "grant_rights"; + + sendData["FromID"] = userID.ToString(); + sendData["ToID"] = friendID.ToString(); + sendData["UserFlags"] = userFlags.ToString(); + sendData["Rights"] = rights.ToString(); + + return Call(region, sendData); + } + + public bool StatusNotify(GridRegion region, UUID userID, string friendID, bool online) + { + Dictionary sendData = new Dictionary(); + //sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + //sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "status"; + + sendData["FromID"] = userID.ToString(); + sendData["ToID"] = friendID; + sendData["Online"] = online.ToString(); + + return Call(region, sendData); + } + + private bool Call(GridRegion region, Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + //m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: queryString = {0}", reqString); + if (region == null) + return false; + + string path = ServicePath(); + if (!region.ServerURI.EndsWith("/")) + path = "/" + path; + string uri = region.ServerURI + path; +// m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("RESULT")) + { + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; + } + else + m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: reply data does not contain result field"); + + } + else + m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: Exception when contacting remote sim at {0}: {1}", uri, e.Message); + } + + return false; + } + } +} diff --git a/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs b/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs new file mode 100644 index 0000000000..9e553568bd --- /dev/null +++ b/OpenSim/Services/Connectors/Grid/GridServicesConnector.cs @@ -0,0 +1,764 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class GridServicesConnector : BaseServiceConnector, IGridService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public GridServicesConnector() + { + } + + public GridServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public GridServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["GridService"]; + if (gridConfig == null) + { + m_log.Error("[GRID CONNECTOR]: GridService missing from OpenSim.ini"); + throw new Exception("Grid connector init error"); + } + + string serviceURI = gridConfig.GetString("GridServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[GRID CONNECTOR]: No Server URI named in section GridService"); + throw new Exception("Grid connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "GridService"); + } + + + #region IGridService + + public string RegisterRegion(UUID scopeID, GridRegion regionInfo) + { + Dictionary rinfo = regionInfo.ToKeyValuePairs(); + Dictionary sendData = new Dictionary(); + foreach (KeyValuePair kvp in rinfo) + sendData[kvp.Key] = (string)kvp.Value; + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "register"; + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/grid"; + // m_log.DebugFormat("[GRID CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("Result")&& (replyData["Result"].ToString().ToLower() == "success")) + { + return String.Empty; + } + else if (replyData.ContainsKey("Result")&& (replyData["Result"].ToString().ToLower() == "failure")) + { + m_log.ErrorFormat( + "[GRID CONNECTOR]: Registration failed: {0} when contacting {1}", replyData["Message"], uri); + + return replyData["Message"].ToString(); + } + else if (!replyData.ContainsKey("Result")) + { + m_log.ErrorFormat( + "[GRID CONNECTOR]: reply data does not contain result field when contacting {0}", uri); + } + else + { + m_log.ErrorFormat( + "[GRID CONNECTOR]: unexpected result {0} when contacting {1}", replyData["Result"], uri); + + return "Unexpected result " + replyData["Result"].ToString(); + } + } + else + { + m_log.ErrorFormat( + "[GRID CONNECTOR]: RegisterRegion received null reply when contacting grid server at {0}", uri); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + } + + return string.Format("Error communicating with the grid service at {0}", uri); + } + + public bool DeregisterRegion(UUID regionID) + { + Dictionary sendData = new Dictionary(); + + sendData["REGIONID"] = regionID.ToString(); + + sendData["METHOD"] = "deregister"; + + string uri = m_ServerURI + "/grid"; + + try + { + string reply + = SynchronousRestFormsRequester.MakeRequest("POST", uri, ServerUtils.BuildQueryString(sendData), m_Auth); + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData["Result"] != null) && (replyData["Result"].ToString().ToLower() == "success")) + return true; + } + else + m_log.DebugFormat("[GRID CONNECTOR]: DeregisterRegion received null reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public List GetNeighbours(UUID scopeID, UUID regionID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["REGIONID"] = regionID.ToString(); + + sendData["METHOD"] = "get_neighbours"; + + List rinfos = new List(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, reqString, m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + //m_log.DebugFormat("[GRID CONNECTOR]: get neighbours returned {0} elements", rinfosList.Count); + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetNeighbours {0}, {1} received null response", + scopeID, regionID); + + return rinfos; + } + + public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["REGIONID"] = regionID.ToString(); + + sendData["METHOD"] = "get_region_by_uuid"; + + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", uri, ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return null; + } + + GridRegion rinfo = null; + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + rinfo = new GridRegion((Dictionary)replyData["result"]); + //else + // m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByUUID {0}, {1} received null response", + // scopeID, regionID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByUUID {0}, {1} received null response", + scopeID, regionID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByUUID received null reply"); + + return rinfo; + } + + public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["X"] = x.ToString(); + sendData["Y"] = y.ToString(); + + sendData["METHOD"] = "get_region_by_position"; + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return null; + } + + GridRegion rinfo = null; + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + rinfo = new GridRegion((Dictionary)replyData["result"]); + //else + // m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition {0}, {1}-{2} received no region", + // scopeID, x, y); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition {0}, {1}-{2} received null response", + scopeID, x, y); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition received null reply"); + + return rinfo; + } + + public GridRegion GetRegionByName(UUID scopeID, string regionName) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["NAME"] = regionName; + + sendData["METHOD"] = "get_region_by_name"; + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return null; + } + + GridRegion rinfo = null; + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + rinfo = new GridRegion((Dictionary)replyData["result"]); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByPosition {0}, {1} received null response", + scopeID, regionName); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionByName received null reply"); + + return rinfo; + } + + public List GetRegionsByName(UUID scopeID, string name, int maxNumber) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["NAME"] = name; + sendData["MAX"] = maxNumber.ToString(); + + sendData["METHOD"] = "get_regions_by_name"; + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionsByName {0}, {1}, {2} received null response", + scopeID, name, maxNumber); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionsByName received null reply"); + + return rinfos; + } + + public List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["XMIN"] = xmin.ToString(); + sendData["XMAX"] = xmax.ToString(); + sendData["YMIN"] = ymin.ToString(); + sendData["YMAX"] = ymax.ToString(); + + sendData["METHOD"] = "get_region_range"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionRange {0}, {1}-{2} {3}-{4} received null response", + scopeID, xmin, xmax, ymin, ymax); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionRange received null reply"); + + return rinfos; + } + + public List GetDefaultRegions(UUID scopeID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + + sendData["METHOD"] = "get_default_regions"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultRegions {0} received null response", + scopeID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultRegions received null reply"); + + return rinfos; + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + + sendData["METHOD"] = "get_default_hypergrid_regions"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultHypergridRegions {0} received null response", + scopeID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetDefaultHypergridRegions received null reply"); + + return rinfos; + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["X"] = x.ToString(); + sendData["Y"] = y.ToString(); + + sendData["METHOD"] = "get_fallback_regions"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetFallbackRegions {0}, {1}-{2} received null response", + scopeID, x, y); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetFallbackRegions received null reply"); + + return rinfos; + } + + public List GetHyperlinks(UUID scopeID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + + sendData["METHOD"] = "get_hyperlinks"; + + List rinfos = new List(); + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + + //m_log.DebugFormat("[GRID CONNECTOR]: reply was {0}", reply); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return rinfos; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + Dictionary.ValueCollection rinfosList = replyData.Values; + foreach (object r in rinfosList) + { + if (r is Dictionary) + { + GridRegion rinfo = new GridRegion((Dictionary)r); + rinfos.Add(rinfo); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetHyperlinks {0} received null response", + scopeID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetHyperlinks received null reply"); + + return rinfos; + } + + public int GetRegionFlags(UUID scopeID, UUID regionID) + { + Dictionary sendData = new Dictionary(); + + sendData["SCOPEID"] = scopeID.ToString(); + sendData["REGIONID"] = regionID.ToString(); + + sendData["METHOD"] = "get_region_flags"; + + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: Exception when contacting grid server at {0}: {1}", uri, e.Message); + return -1; + } + + int flags = -1; + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + Int32.TryParse((string)replyData["result"], out flags); + //else + // m_log.DebugFormat("[GRID CONNECTOR]: GetRegionFlags {0}, {1} received wrong type {2}", + // scopeID, regionID, replyData["result"].GetType()); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionFlags {0}, {1} received null response", + scopeID, regionID); + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetRegionFlags received null reply"); + + return flags; + } + + public Dictionary GetExtraFeatures() + { + Dictionary sendData = new Dictionary(); + Dictionary extraFeatures = new Dictionary(); + + sendData["METHOD"] = "get_grid_extra_features"; + + string reply = string.Empty; + string uri = m_ServerURI + "/grid"; + + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), m_Auth); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID CONNECTOR]: GetExtraFeatures - Exception when contacting grid server at {0}: {1}", uri, e.Message); + return extraFeatures; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.Count > 0) + { + foreach (string key in replyData.Keys) + { + extraFeatures[key] = replyData[key].ToString(); + } + } + } + else + m_log.DebugFormat("[GRID CONNECTOR]: GetExtraServiceURLs received null reply"); + + return extraFeatures; + } + #endregion + + } +} diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs new file mode 100644 index 0000000000..ffdd94aca8 --- /dev/null +++ b/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs @@ -0,0 +1,300 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class GridUserServicesConnector : BaseServiceConnector, IGridUserService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public GridUserServicesConnector() + { + } + + public GridUserServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public GridUserServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["GridUserService"]; + if (gridConfig == null) + { + m_log.Error("[GRID USER CONNECTOR]: GridUserService missing from OpenSim.ini"); + throw new Exception("GridUser connector init error"); + } + + string serviceURI = gridConfig.GetString("GridUserServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[GRID USER CONNECTOR]: No Server URI named in section GridUserService"); + throw new Exception("GridUser connector init error"); + } + m_ServerURI = serviceURI; + base.Initialise(source, "GridUserService"); + } + + + #region IGridUserService + + + public GridUserInfo LoggedIn(string userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "loggedin"; + + sendData["UserID"] = userID; + + return Get(sendData); + + } + + public bool LoggedOut(string userID, UUID sessionID, UUID region, Vector3 position, Vector3 lookat) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "loggedout"; + + return Set(sendData, userID, region, position, lookat); + } + + public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "sethome"; + + return Set(sendData, userID, regionID, position, lookAt); + } + + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "setposition"; + + return Set(sendData, userID, regionID, position, lookAt); + } + + public GridUserInfo GetGridUserInfo(string userID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getgriduserinfo"; + + sendData["UserID"] = userID; + + return Get(sendData); + } + + #endregion + + protected bool Set(Dictionary sendData, string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + sendData["UserID"] = userID; + sendData["RegionID"] = regionID.ToString(); + sendData["Position"] = position.ToString(); + sendData["LookAt"] = lookAt.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/griduser"; + // m_log.DebugFormat("[GRID USER CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: SetPosition reply data does not contain result field"); + + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: SetPosition received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: Exception when contacting grid user server at {0}: {1}", uri, e.Message); + } + + return false; + } + + protected GridUserInfo Get(Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/griduser"; + // m_log.DebugFormat("[GRID USER CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + GridUserInfo guinfo = null; + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + guinfo = Create((Dictionary)replyData["result"]); + } + + return guinfo; + + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: Get received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: Exception when contacting grid user server at {0}: {1}", uri, e.Message); + } + + return null; + + } + + public GridUserInfo[] GetGridUserInfo(string[] userIDs) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getgriduserinfos"; + + sendData["AgentIDs"] = new List(userIDs); + + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/griduser"; + //m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: GetGridUserInfo received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[GRID USER CONNECTOR]: Exception when contacting grid user server at {0}: {1}", uri, e.Message); + } + + List rinfos = new List(); + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + if (replyData.ContainsKey("result") && + (replyData["result"].ToString() == "null" || replyData["result"].ToString() == "Failure")) + { + return new GridUserInfo[0]; + } + + Dictionary.ValueCollection pinfosList = replyData.Values; + //m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents returned {0} elements", pinfosList.Count); + foreach (object griduser in pinfosList) + { + if (griduser is Dictionary) + { + GridUserInfo pinfo = Create((Dictionary)griduser); + rinfos.Add(pinfo); + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: GetGridUserInfo received invalid response type {0}", + griduser.GetType()); + } + } + else + m_log.DebugFormat("[GRID USER CONNECTOR]: GetGridUserInfo received null response"); + + return rinfos.ToArray(); + } + + protected virtual GridUserInfo Create(Dictionary griduser) + { + return new GridUserInfo(griduser); + } + } +} diff --git a/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs new file mode 100644 index 0000000000..b1663ee2bf --- /dev/null +++ b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs @@ -0,0 +1,332 @@ +/* + * 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.Drawing; +using System.IO; +using System.Net; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenMetaverse; +using OpenMetaverse.Imaging; +using OpenMetaverse.StructuredData; +using Nwc.XmlRpc; +using log4net; + +using OpenSim.Services.Connectors.Simulation; + +namespace OpenSim.Services.Connectors.Hypergrid +{ + public class GatekeeperServiceConnector : SimulationServiceConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static UUID m_HGMapImage = new UUID("00000000-0000-1111-9999-000000000013"); + + private IAssetService m_AssetService; + + public GatekeeperServiceConnector() + : base() + { + } + + public GatekeeperServiceConnector(IAssetService assService) + { + m_AssetService = assService; + } + + protected override string AgentPath() + { + return "foreignagent/"; + } + + protected override string ObjectPath() + { + return "foreignobject/"; + } + + public bool LinkRegion(GridRegion info, out UUID regionID, out ulong realHandle, out string externalName, out string imageURL, out string reason) + { + regionID = UUID.Zero; + imageURL = string.Empty; + realHandle = 0; + externalName = string.Empty; + reason = string.Empty; + + Hashtable hash = new Hashtable(); + hash["region_name"] = info.RegionName; + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("link_region", paramList); + m_log.Debug("[GATEKEEPER SERVICE CONNECTOR]: Linking to " + info.ServerURI); + XmlRpcResponse response = null; + try + { + response = request.Send(info.ServerURI, 10000); + } + catch (Exception e) + { + m_log.Debug("[GATEKEEPER SERVICE CONNECTOR]: Exception " + e.Message); + reason = "Error contacting remote server"; + return false; + } + + if (response.IsFault) + { + reason = response.FaultString; + m_log.ErrorFormat("[GATEKEEPER SERVICE CONNECTOR]: remote call returned an error: {0}", response.FaultString); + return false; + } + + hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + bool success = false; + Boolean.TryParse((string)hash["result"], out success); + if (success) + { + UUID.TryParse((string)hash["uuid"], out regionID); + //m_log.Debug(">> HERE, uuid: " + regionID); + if ((string)hash["handle"] != null) + { + realHandle = Convert.ToUInt64((string)hash["handle"]); + //m_log.Debug(">> HERE, realHandle: " + realHandle); + } + if (hash["region_image"] != null) + { + imageURL = (string)hash["region_image"]; + //m_log.Debug(">> HERE, imageURL: " + imageURL); + } + if (hash["external_name"] != null) + { + externalName = (string)hash["external_name"]; + //m_log.Debug(">> HERE, externalName: " + externalName); + } + } + + } + catch (Exception e) + { + reason = "Error parsing return arguments"; + m_log.Error("[GATEKEEPER SERVICE CONNECTOR]: Got exception while parsing hyperlink response " + e.StackTrace); + return false; + } + + return true; + } + + public UUID GetMapImage(UUID regionID, string imageURL, string storagePath) + { + if (m_AssetService == null) + { + m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: No AssetService defined. Map tile not retrieved."); + return m_HGMapImage; + } + + UUID mapTile = m_HGMapImage; + string filename = string.Empty; + + try + { + WebClient c = new WebClient(); + string name = regionID.ToString(); + filename = Path.Combine(storagePath, name + ".jpg"); + m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: Map image at {0}, cached at {1}", imageURL, filename); + if (!File.Exists(filename)) + { + m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: downloading..."); + c.DownloadFile(imageURL, filename); + } + else + { + m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: using cached image"); + } + + byte[] imageData = null; + + using (Bitmap bitmap = new Bitmap(filename)) + { + //m_log.Debug("Size: " + m.PhysicalDimension.Height + "-" + m.PhysicalDimension.Width); + imageData = OpenJPEG.EncodeFromImage(bitmap, true); + } + + AssetBase ass = new AssetBase(UUID.Random(), "region " + name, (sbyte)AssetType.Texture, regionID.ToString()); + + // !!! for now + //info.RegionSettings.TerrainImageID = ass.FullID; + + ass.Data = imageData; + + mapTile = ass.FullID; + + // finally + m_AssetService.Store(ass); + + } + catch // LEGIT: Catching problems caused by OpenJPEG p/invoke + { + m_log.Info("[GATEKEEPER SERVICE CONNECTOR]: Failed getting/storing map image, because it is probably already in the cache"); + } + return mapTile; + } + + public GridRegion GetHyperlinkRegion(GridRegion gatekeeper, UUID regionID, UUID agentID, string agentHomeURI, out string message) + { + Hashtable hash = new Hashtable(); + hash["region_uuid"] = regionID.ToString(); + if (agentID != UUID.Zero) + { + hash["agent_id"] = agentID.ToString(); + if (agentHomeURI != null) + hash["agent_home_uri"] = agentHomeURI; + } + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("get_region", paramList); + m_log.Debug("[GATEKEEPER SERVICE CONNECTOR]: contacting " + gatekeeper.ServerURI); + XmlRpcResponse response = null; + try + { + response = request.Send(gatekeeper.ServerURI, 10000); + } + catch (Exception e) + { + message = "Error contacting grid."; + m_log.Debug("[GATEKEEPER SERVICE CONNECTOR]: Exception " + e.Message); + return null; + } + + if (response.IsFault) + { + message = "Error contacting grid."; + m_log.ErrorFormat("[GATEKEEPER SERVICE CONNECTOR]: remote call returned an error: {0}", response.FaultString); + return null; + } + + hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + bool success = false; + Boolean.TryParse((string)hash["result"], out success); + + if (hash["message"] != null) + message = (string)hash["message"]; + else if (success) + message = null; + else + message = "The teleport destination could not be found."; // probably the dest grid is old and doesn't send 'message', but the most common problem is that the region is unavailable + + if (success) + { + GridRegion region = new GridRegion(); + + UUID.TryParse((string)hash["uuid"], out region.RegionID); + //m_log.Debug(">> HERE, uuid: " + region.RegionID); + int n = 0; + if (hash["x"] != null) + { + Int32.TryParse((string)hash["x"], out n); + region.RegionLocX = n; + //m_log.Debug(">> HERE, x: " + region.RegionLocX); + } + if (hash["y"] != null) + { + Int32.TryParse((string)hash["y"], out n); + region.RegionLocY = n; + //m_log.Debug(">> HERE, y: " + region.RegionLocY); + } + if (hash["size_x"] != null) + { + Int32.TryParse((string)hash["size_x"], out n); + region.RegionSizeX = n; + //m_log.Debug(">> HERE, x: " + region.RegionLocX); + } + if (hash["size_y"] != null) + { + Int32.TryParse((string)hash["size_y"], out n); + region.RegionSizeY = n; + //m_log.Debug(">> HERE, y: " + region.RegionLocY); + } + if (hash["region_name"] != null) + { + region.RegionName = (string)hash["region_name"]; + //m_log.Debug(">> HERE, region_name: " + region.RegionName); + } + if (hash["hostname"] != null) + { + region.ExternalHostName = (string)hash["hostname"]; + //m_log.Debug(">> HERE, hostname: " + region.ExternalHostName); + } + if (hash["http_port"] != null) + { + uint p = 0; + UInt32.TryParse((string)hash["http_port"], out p); + region.HttpPort = p; + //m_log.Debug(">> HERE, http_port: " + region.HttpPort); + } + if (hash["internal_port"] != null) + { + int p = 0; + Int32.TryParse((string)hash["internal_port"], out p); + region.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), p); + //m_log.Debug(">> HERE, internal_port: " + region.InternalEndPoint); + } + + if (hash["server_uri"] != null) + { + region.ServerURI = (string)hash["server_uri"]; + //m_log.Debug(">> HERE, server_uri: " + region.ServerURI); + } + + // Successful return + return region; + } + + } + catch (Exception e) + { + message = "Error parsing response from grid."; + m_log.Error("[GATEKEEPER SERVICE CONNECTOR]: Got exception while parsing hyperlink response " + e.StackTrace); + return null; + } + + return null; + } + } +} diff --git a/OpenSim/Services/Connectors/Hypergrid/HGFriendsServicesConnector.cs b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServicesConnector.cs new file mode 100644 index 0000000000..622d4e1712 --- /dev/null +++ b/OpenSim/Services/Connectors/Hypergrid/HGFriendsServicesConnector.cs @@ -0,0 +1,312 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Friends; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors.Hypergrid +{ + public class HGFriendsServicesConnector : FriendsSimConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + private string m_ServiceKey = String.Empty; + private UUID m_SessionID; + + public HGFriendsServicesConnector() + { + } + + public HGFriendsServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public HGFriendsServicesConnector(string serverURI, UUID sessionID, string serviceKey) + { + m_ServerURI = serverURI.TrimEnd('/'); + m_ServiceKey = serviceKey; + m_SessionID = sessionID; + } + + protected override string ServicePath() + { + return "hgfriends"; + } + + #region IFriendsService + + public uint GetFriendPerms(UUID PrincipalID, UUID friendID) + { + Dictionary sendData = new Dictionary(); + + sendData["PRINCIPALID"] = PrincipalID.ToString(); + sendData["FRIENDID"] = friendID.ToString(); + sendData["METHOD"] = "getfriendperms"; + sendData["KEY"] = m_ServiceKey; + sendData["SESSIONID"] = m_SessionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/hgfriends"; + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("Value") && (replyData["Value"] != null)) + { + uint perms = 0; + uint.TryParse(replyData["Value"].ToString(), out perms); + return perms; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: GetFriendPerms {0} received null response", + PrincipalID); + + } + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + } + + return 0; + + } + + public bool NewFriendship(UUID PrincipalID, string Friend) + { + FriendInfo finfo = new FriendInfo(); + finfo.PrincipalID = PrincipalID; + finfo.Friend = Friend; + + Dictionary sendData = finfo.ToKeyValuePairs(); + + sendData["METHOD"] = "newfriendship"; + sendData["KEY"] = m_ServiceKey; + sendData["SESSIONID"] = m_SessionID.ToString(); + + string reply = string.Empty; + string uri = m_ServerURI + "/hgfriends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData)); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if ((replyData != null) && replyData.ContainsKey("Result") && (replyData["Result"] != null)) + { + bool success = false; + Boolean.TryParse(replyData["Result"].ToString(), out success); + return success; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: StoreFriend {0} {1} received null response", + PrincipalID, Friend); + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: StoreFriend received null reply"); + + return false; + + } + + public bool DeleteFriendship(UUID PrincipalID, UUID Friend, string secret) + { + FriendInfo finfo = new FriendInfo(); + finfo.PrincipalID = PrincipalID; + finfo.Friend = Friend.ToString(); + + Dictionary sendData = finfo.ToKeyValuePairs(); + + sendData["METHOD"] = "deletefriendship"; + sendData["SECRET"] = secret; + + string reply = string.Empty; + string uri = m_ServerURI + "/hgfriends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData)); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("RESULT")) + { + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field"); + + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply"); + + return false; + + } + + public bool ValidateFriendshipOffered(UUID fromID, UUID toID) + { + FriendInfo finfo = new FriendInfo(); + finfo.PrincipalID = fromID; + finfo.Friend = toID.ToString(); + + Dictionary sendData = finfo.ToKeyValuePairs(); + + sendData["METHOD"] = "validate_friendship_offered"; + + string reply = string.Empty; + string uri = m_ServerURI + "/hgfriends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData)); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return false; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("RESULT")) + { + if (replyData["RESULT"].ToString().ToLower() == "true") + return true; + else + return false; + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: reply data does not contain result field"); + + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: received empty reply"); + + return false; + + } + + public List StatusNotification(List friends, UUID userID, bool online) + { + Dictionary sendData = new Dictionary(); + List friendsOnline = new List(); + + sendData["METHOD"] = "statusnotification"; + sendData["userID"] = userID.ToString(); + sendData["online"] = online.ToString(); + int i = 0; + foreach (string s in friends) + { + sendData["friend_" + i.ToString()] = s; + i++; + } + + string reply = string.Empty; + string uri = m_ServerURI + "/hgfriends"; + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + ServerUtils.BuildQueryString(sendData), 15); + } + catch (Exception e) + { + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Exception when contacting friends server at {0}: {1}", uri, e.Message); + return friendsOnline; + } + + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + // Here is the actual response + foreach (string key in replyData.Keys) + { + if (key.StartsWith("friend_") && replyData[key] != null) + { + UUID uuid; + if (UUID.TryParse(replyData[key].ToString(), out uuid)) + friendsOnline.Add(uuid); + } + } + } + else + m_log.DebugFormat("[HGFRIENDS CONNECTOR]: Received empty reply from remote StatusNotify"); + + return friendsOnline; + + } + + #endregion + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/Hypergrid/HeloServicesConnector.cs b/OpenSim/Services/Connectors/Hypergrid/HeloServicesConnector.cs new file mode 100644 index 0000000000..b5e6d69405 --- /dev/null +++ b/OpenSim/Services/Connectors/Hypergrid/HeloServicesConnector.cs @@ -0,0 +1,113 @@ +/* + * 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 log4net; +using System; +using System.Net; +using System.Reflection; +using Nini.Config; + +namespace OpenSim.Services.Connectors +{ + public class HeloServicesConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public HeloServicesConnector() + { + } + + public HeloServicesConnector(string serverURI) + { + try + { + Uri uri; + + if (!serverURI.EndsWith("=")) + { + // Let's check if this is a valid URI, because it may not be + uri = new Uri(serverURI); + m_ServerURI = serverURI.TrimEnd('/') + "/helo/"; + } + else + { + // Simian sends malformed urls like this: + // http://valley.virtualportland.org/simtest/Grid/?id= + // + uri = new Uri(serverURI + "xxx"); + if (uri.Query == string.Empty) + m_ServerURI = serverURI.TrimEnd('/') + "/helo/"; + else + { + serverURI = serverURI + "xxx"; + m_ServerURI = serverURI.Replace(uri.Query, ""); + m_ServerURI = m_ServerURI.TrimEnd('/') + "/helo/"; + } + } + + } + catch (UriFormatException) + { + m_log.WarnFormat("[HELO SERVICE]: Malformed URL {0}", serverURI); + } + } + + public virtual string Helo() + { + if (String.IsNullOrEmpty(m_ServerURI)) + { + m_log.WarnFormat("[HELO SERVICE]: Unable to invoke HELO due to empty URL"); + return String.Empty; + } + + try + { + HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(m_ServerURI); + // Eventually we need to switch to HEAD + /* req.Method = "HEAD"; */ + + using (WebResponse response = req.GetResponse()) + { + if (response.Headers.Get("X-Handlers-Provided") == null) // just in case this ever returns a null + return string.Empty; + return response.Headers.Get("X-Handlers-Provided"); + } + } + catch (Exception e) + { + m_log.DebugFormat("[HELO SERVICE]: Unable to perform HELO request to {0}: {1}", m_ServerURI, e.Message); + } + + // fail + return string.Empty; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs new file mode 100644 index 0000000000..8abd046780 --- /dev/null +++ b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs @@ -0,0 +1,625 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Simulation; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using Nwc.XmlRpc; +using Nini.Config; + +namespace OpenSim.Services.Connectors.Hypergrid +{ + public class UserAgentServiceConnector : SimulationServiceConnector, IUserAgentService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURLHost; + private string m_ServerURL; + private GridRegion m_Gatekeeper; + + public UserAgentServiceConnector(string url) : this(url, true) + { + } + + public UserAgentServiceConnector(string url, bool dnsLookup) + { + m_ServerURL = m_ServerURLHost = url; + + if (dnsLookup) + { + // Doing this here, because XML-RPC or mono have some strong ideas about + // caching DNS translations. + try + { + Uri m_Uri = new Uri(m_ServerURL); + IPAddress ip = Util.GetHostFromDNS(m_Uri.Host); + m_ServerURL = m_ServerURL.Replace(m_Uri.Host, ip.ToString()); + if (!m_ServerURL.EndsWith("/")) + m_ServerURL += "/"; + } + catch (Exception e) + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: Malformed Uri {0}: {1}", url, e.Message); + } + } + + //m_log.DebugFormat("[USER AGENT CONNECTOR]: new connector to {0} ({1})", url, m_ServerURL); + } + + public UserAgentServiceConnector(IConfigSource config) + { + IConfig serviceConfig = config.Configs["UserAgentService"]; + if (serviceConfig == null) + { + m_log.Error("[USER AGENT CONNECTOR]: UserAgentService missing from ini"); + throw new Exception("UserAgent connector init error"); + } + + string serviceURI = serviceConfig.GetString("UserAgentServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[USER AGENT CONNECTOR]: No Server URI named in section UserAgentService"); + throw new Exception("UserAgent connector init error"); + } + + m_ServerURL = m_ServerURLHost = serviceURI; + if (!m_ServerURL.EndsWith("/")) + m_ServerURL += "/"; + + //m_log.DebugFormat("[USER AGENT CONNECTOR]: new connector to {0}", m_ServerURL); + } + + protected override string AgentPath() + { + return "homeagent/"; + } + + // The Login service calls this interface with fromLogin=true + // Sims call it with fromLogin=false + // Either way, this is verified by the handler + public bool LoginAgentToGrid(GridRegion source, AgentCircuitData aCircuit, GridRegion gatekeeper, GridRegion destination, bool fromLogin, out string reason) + { + reason = String.Empty; + + if (destination == null) + { + reason = "Destination is null"; + m_log.Debug("[USER AGENT CONNECTOR]: Given destination is null"); + return false; + } + + GridRegion home = new GridRegion(); + home.ServerURI = m_ServerURL; + home.RegionID = destination.RegionID; + home.RegionLocX = destination.RegionLocX; + home.RegionLocY = destination.RegionLocY; + + m_Gatekeeper = gatekeeper; + + Console.WriteLine(" >>> LoginAgentToGrid <<< " + home.ServerURI); + + uint flags = fromLogin ? (uint)TeleportFlags.ViaLogin : (uint)TeleportFlags.ViaHome; + return CreateAgent(source, home, aCircuit, flags, out reason); + } + + + // The simulators call this interface + public bool LoginAgentToGrid(GridRegion source, AgentCircuitData aCircuit, GridRegion gatekeeper, GridRegion destination, out string reason) + { + return LoginAgentToGrid(source, aCircuit, gatekeeper, destination, false, out reason); + } + + protected override void PackData(OSDMap args, GridRegion source, AgentCircuitData aCircuit, GridRegion destination, uint flags) + { + base.PackData(args, source, aCircuit, destination, flags); + args["gatekeeper_serveruri"] = OSD.FromString(m_Gatekeeper.ServerURI); + args["gatekeeper_host"] = OSD.FromString(m_Gatekeeper.ExternalHostName); + args["gatekeeper_port"] = OSD.FromString(m_Gatekeeper.HttpPort.ToString()); + args["destination_serveruri"] = OSD.FromString(destination.ServerURI); + } + + public void SetClientToken(UUID sessionID, string token) + { + // no-op + } + + private Hashtable CallServer(string methodName, Hashtable hash) + { + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest(methodName, paramList); + + // Send and get reply + XmlRpcResponse response = null; + try + { + response = request.Send(m_ServerURL, 10000); + } + catch (Exception e) + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: {0} call to {1} failed: {2}", methodName, m_ServerURLHost, e.Message); + throw; + } + + if (response.IsFault) + { + throw new Exception(string.Format("[USER AGENT CONNECTOR]: {0} call to {1} returned an error: {2}", methodName, m_ServerURLHost, response.FaultString)); + } + + hash = (Hashtable)response.Value; + + if (hash == null) + { + throw new Exception(string.Format("[USER AGENT CONNECTOR]: {0} call to {1} returned null", methodName, m_ServerURLHost)); + } + + return hash; + } + + public GridRegion GetHomeRegion(UUID userID, out Vector3 position, out Vector3 lookAt) + { + position = Vector3.UnitY; lookAt = Vector3.UnitY; + + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + + hash = CallServer("get_home_region", hash); + + bool success; + if (!Boolean.TryParse((string)hash["result"], out success) || !success) + return null; + + GridRegion region = new GridRegion(); + + UUID.TryParse((string)hash["uuid"], out region.RegionID); + //m_log.Debug(">> HERE, uuid: " + region.RegionID); + int n = 0; + if (hash["x"] != null) + { + Int32.TryParse((string)hash["x"], out n); + region.RegionLocX = n; + //m_log.Debug(">> HERE, x: " + region.RegionLocX); + } + if (hash["y"] != null) + { + Int32.TryParse((string)hash["y"], out n); + region.RegionLocY = n; + //m_log.Debug(">> HERE, y: " + region.RegionLocY); + } + if (hash["size_x"] != null) + { + Int32.TryParse((string)hash["size_x"], out n); + region.RegionSizeX = n; + //m_log.Debug(">> HERE, x: " + region.RegionLocX); + } + if (hash["size_y"] != null) + { + Int32.TryParse((string)hash["size_y"], out n); + region.RegionSizeY = n; + //m_log.Debug(">> HERE, y: " + region.RegionLocY); + } + if (hash["region_name"] != null) + { + region.RegionName = (string)hash["region_name"]; + //m_log.Debug(">> HERE, name: " + region.RegionName); + } + if (hash["hostname"] != null) + region.ExternalHostName = (string)hash["hostname"]; + if (hash["http_port"] != null) + { + uint p = 0; + UInt32.TryParse((string)hash["http_port"], out p); + region.HttpPort = p; + } + if (hash.ContainsKey("server_uri") && hash["server_uri"] != null) + region.ServerURI = (string)hash["server_uri"]; + + if (hash["internal_port"] != null) + { + int p = 0; + Int32.TryParse((string)hash["internal_port"], out p); + region.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), p); + } + if (hash["position"] != null) + Vector3.TryParse((string)hash["position"], out position); + if (hash["lookAt"] != null) + Vector3.TryParse((string)hash["lookAt"], out lookAt); + + // Successful return + return region; + } + + public bool IsAgentComingHome(UUID sessionID, string thisGridExternalName) + { + Hashtable hash = new Hashtable(); + hash["sessionID"] = sessionID.ToString(); + hash["externalName"] = thisGridExternalName; + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("agent_is_coming_home", paramList); + string reason = string.Empty; + return GetBoolResponse(request, out reason); + } + + public bool VerifyAgent(UUID sessionID, string token) + { + Hashtable hash = new Hashtable(); + hash["sessionID"] = sessionID.ToString(); + hash["token"] = token; + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("verify_agent", paramList); + string reason = string.Empty; + return GetBoolResponse(request, out reason); + } + + public bool VerifyClient(UUID sessionID, string token) + { + Hashtable hash = new Hashtable(); + hash["sessionID"] = sessionID.ToString(); + hash["token"] = token; + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("verify_client", paramList); + string reason = string.Empty; + return GetBoolResponse(request, out reason); + } + + public void LogoutAgent(UUID userID, UUID sessionID) + { + Hashtable hash = new Hashtable(); + hash["sessionID"] = sessionID.ToString(); + hash["userID"] = userID.ToString(); + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("logout_agent", paramList); + string reason = string.Empty; + GetBoolResponse(request, out reason); + } + + [Obsolete] + public List StatusNotification(List friends, UUID userID, bool online) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + hash["online"] = online.ToString(); + int i = 0; + foreach (string s in friends) + { + hash["friend_" + i.ToString()] = s; + i++; + } + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("status_notification", paramList); +// string reason = string.Empty; + + // Send and get reply + List friendsOnline = new List(); + XmlRpcResponse response = null; + try + { + response = request.Send(m_ServerURL, 6000); + } + catch + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: Unable to contact remote server {0} for StatusNotification", m_ServerURLHost); +// reason = "Exception: " + e.Message; + return friendsOnline; + } + + if (response.IsFault) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: remote call to {0} for StatusNotification returned an error: {1}", m_ServerURLHost, response.FaultString); +// reason = "XMLRPC Fault"; + return friendsOnline; + } + + hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + if (hash == null) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: GetOnlineFriends Got null response from {0}! THIS IS BAAAAD", m_ServerURLHost); +// reason = "Internal error 1"; + return friendsOnline; + } + + // Here is the actual response + foreach (object key in hash.Keys) + { + if (key is string && ((string)key).StartsWith("friend_") && hash[key] != null) + { + UUID uuid; + if (UUID.TryParse(hash[key].ToString(), out uuid)) + friendsOnline.Add(uuid); + } + } + + } + catch + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on GetOnlineFriends response."); +// reason = "Exception: " + e.Message; + } + + return friendsOnline; + } + + [Obsolete] + public List GetOnlineFriends(UUID userID, List friends) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + int i = 0; + foreach (string s in friends) + { + hash["friend_" + i.ToString()] = s; + i++; + } + + IList paramList = new ArrayList(); + paramList.Add(hash); + + XmlRpcRequest request = new XmlRpcRequest("get_online_friends", paramList); +// string reason = string.Empty; + + // Send and get reply + List online = new List(); + XmlRpcResponse response = null; + try + { + response = request.Send(m_ServerURL, 10000); + } + catch + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: Unable to contact remote server {0} for GetOnlineFriends", m_ServerURLHost); +// reason = "Exception: " + e.Message; + return online; + } + + if (response.IsFault) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: remote call to {0} for GetOnlineFriends returned an error: {1}", m_ServerURLHost, response.FaultString); +// reason = "XMLRPC Fault"; + return online; + } + + hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + if (hash == null) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: GetOnlineFriends Got null response from {0}! THIS IS BAAAAD", m_ServerURLHost); +// reason = "Internal error 1"; + return online; + } + + // Here is the actual response + foreach (object key in hash.Keys) + { + if (key is string && ((string)key).StartsWith("friend_") && hash[key] != null) + { + UUID uuid; + if (UUID.TryParse(hash[key].ToString(), out uuid)) + online.Add(uuid); + } + } + + } + catch + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on GetOnlineFriends response."); +// reason = "Exception: " + e.Message; + } + + return online; + } + + public Dictionary GetUserInfo (UUID userID) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + + hash = CallServer("get_user_info", hash); + + Dictionary info = new Dictionary(); + + foreach (object key in hash.Keys) + { + if (hash[key] != null) + { + info.Add(key.ToString(), hash[key]); + } + } + + return info; + } + + public Dictionary GetServerURLs(UUID userID) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + + hash = CallServer("get_server_urls", hash); + + Dictionary serverURLs = new Dictionary(); + foreach (object key in hash.Keys) + { + if (key is string && ((string)key).StartsWith("SRV_") && hash[key] != null) + { + string serverType = key.ToString().Substring(4); // remove "SRV_" + serverURLs.Add(serverType, hash[key].ToString()); + } + } + + return serverURLs; + } + + public string LocateUser(UUID userID) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + + hash = CallServer("locate_user", hash); + + string url = string.Empty; + + // Here's the actual response + if (hash.ContainsKey("URL")) + url = hash["URL"].ToString(); + + return url; + } + + public string GetUUI(UUID userID, UUID targetUserID) + { + Hashtable hash = new Hashtable(); + hash["userID"] = userID.ToString(); + hash["targetUserID"] = targetUserID.ToString(); + + hash = CallServer("get_uui", hash); + + string uui = string.Empty; + + // Here's the actual response + if (hash.ContainsKey("UUI")) + uui = hash["UUI"].ToString(); + + return uui; + } + + public UUID GetUUID(String first, String last) + { + Hashtable hash = new Hashtable(); + hash["first"] = first; + hash["last"] = last; + + hash = CallServer("get_uuid", hash); + + if (!hash.ContainsKey("UUID")) + { + throw new Exception(string.Format("[USER AGENT CONNECTOR]: get_uuid call to {0} didn't return a UUID", m_ServerURLHost)); + } + + UUID uuid; + if (!UUID.TryParse(hash["UUID"].ToString(), out uuid)) + { + throw new Exception(string.Format("[USER AGENT CONNECTOR]: get_uuid call to {0} returned an invalid UUID: {1}", m_ServerURLHost, hash["UUID"].ToString())); + } + + return uuid; + } + + private bool GetBoolResponse(XmlRpcRequest request, out string reason) + { + //m_log.Debug("[USER AGENT CONNECTOR]: GetBoolResponse from/to " + m_ServerURLHost); + XmlRpcResponse response = null; + try + { + response = request.Send(m_ServerURL, 10000); + } + catch (Exception e) + { + m_log.DebugFormat("[USER AGENT CONNECTOR]: Unable to contact remote server {0} for GetBoolResponse", m_ServerURLHost); + reason = "Exception: " + e.Message; + return false; + } + + if (response.IsFault) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: remote call to {0} for GetBoolResponse returned an error: {1}", m_ServerURLHost, response.FaultString); + reason = "XMLRPC Fault"; + return false; + } + + Hashtable hash = (Hashtable)response.Value; + //foreach (Object o in hash) + // m_log.Debug(">> " + ((DictionaryEntry)o).Key + ":" + ((DictionaryEntry)o).Value); + try + { + if (hash == null) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got null response from {0}! THIS IS BAAAAD", m_ServerURLHost); + reason = "Internal error 1"; + return false; + } + bool success = false; + reason = string.Empty; + if (hash.ContainsKey("result")) + Boolean.TryParse((string)hash["result"], out success); + else + { + reason = "Internal error 2"; + m_log.WarnFormat("[USER AGENT CONNECTOR]: response from {0} does not have expected key 'result'", m_ServerURLHost); + } + + return success; + } + catch (Exception e) + { + m_log.ErrorFormat("[USER AGENT CONNECTOR]: Got exception on GetBoolResponse response."); + if (hash.ContainsKey("result") && hash["result"] != null) + m_log.ErrorFormat("Reply was ", (string)hash["result"]); + reason = "Exception: " + e.Message; + return false; + } + + } + + } +} diff --git a/OpenSim/Services/Connectors/InstantMessage/InstantMessageServiceConnector.cs b/OpenSim/Services/Connectors/InstantMessage/InstantMessageServiceConnector.cs new file mode 100644 index 0000000000..e19c23dfdf --- /dev/null +++ b/OpenSim/Services/Connectors/InstantMessage/InstantMessageServiceConnector.cs @@ -0,0 +1,132 @@ +/* + * 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 OpenMetaverse; +using Nwc.XmlRpc; +using log4net; + +using OpenSim.Framework; + +namespace OpenSim.Services.Connectors.InstantMessage +{ + public class InstantMessageServiceConnector + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// This actually does the XMLRPC Request + /// + /// URL we pull the data out of to send the request to + /// The Instant Message + /// Bool if the message was successfully delivered at the other side. + public static bool SendInstantMessage(string url, GridInstantMessage im) + { + Hashtable xmlrpcdata = ConvertGridInstantMessageToXMLRPC(im); + xmlrpcdata["region_handle"] = 0; + + ArrayList SendParams = new ArrayList(); + SendParams.Add(xmlrpcdata); + XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); + try + { + + XmlRpcResponse GridResp = GridReq.Send(url, 10000); + + Hashtable responseData = (Hashtable)GridResp.Value; + + if (responseData.ContainsKey("success")) + { + if ((string)responseData["success"] == "TRUE") + { + //m_log.DebugFormat("[XXX] Success"); + return true; + } + else + { + //m_log.DebugFormat("[XXX] Fail"); + return false; + } + } + else + { + m_log.DebugFormat("[GRID INSTANT MESSAGE]: No response from {0}", url); + return false; + } + } + catch (WebException e) + { + m_log.ErrorFormat("[GRID INSTANT MESSAGE]: Error sending message to {0} the host didn't respond " + e.ToString(), url); + } + + return false; + } + + /// + /// Takes a GridInstantMessage and converts it into a Hashtable for XMLRPC + /// + /// The GridInstantMessage object + /// Hashtable containing the XMLRPC request + protected static Hashtable ConvertGridInstantMessageToXMLRPC(GridInstantMessage msg) + { + Hashtable gim = new Hashtable(); + gim["from_agent_id"] = msg.fromAgentID.ToString(); + // Kept for compatibility + gim["from_agent_session"] = UUID.Zero.ToString(); + gim["to_agent_id"] = msg.toAgentID.ToString(); + gim["im_session_id"] = msg.imSessionID.ToString(); + gim["timestamp"] = msg.timestamp.ToString(); + gim["from_agent_name"] = msg.fromAgentName; + gim["message"] = msg.message; + byte[] dialogdata = new byte[1]; dialogdata[0] = msg.dialog; + gim["dialog"] = Convert.ToBase64String(dialogdata, Base64FormattingOptions.None); + + if (msg.fromGroup) + gim["from_group"] = "TRUE"; + else + gim["from_group"] = "FALSE"; + byte[] offlinedata = new byte[1]; offlinedata[0] = msg.offline; + gim["offline"] = Convert.ToBase64String(offlinedata, Base64FormattingOptions.None); + gim["parent_estate_id"] = msg.ParentEstateID.ToString(); + gim["position_x"] = msg.Position.X.ToString(); + gim["position_y"] = msg.Position.Y.ToString(); + gim["position_z"] = msg.Position.Z.ToString(); + gim["region_id"] = msg.RegionID.ToString(); + gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket, Base64FormattingOptions.None); + gim["region_id"] = new UUID(msg.RegionID).ToString(); + + return gim; + } + + } +} diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs new file mode 100644 index 0000000000..b0615b8f3d --- /dev/null +++ b/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs @@ -0,0 +1,763 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Monitoring; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class XInventoryServicesConnector : BaseServiceConnector, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Number of requests made to the remote inventory service. + /// + public int RequestsMade { get; private set; } + + private string m_ServerURI = String.Empty; + + /// + /// Timeout for remote requests. + /// + /// + /// In this case, -1 is default timeout (100 seconds), not infinite. + /// + private int m_requestTimeoutSecs = -1; + + private const double CACHE_EXPIRATION_SECONDS = 20.0; + private static ExpiringCache m_ItemCache = new ExpiringCache(); + + public XInventoryServicesConnector() + { + } + + public XInventoryServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public XInventoryServicesConnector(IConfigSource source) + : base(source, "InventoryService") + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig config = source.Configs["InventoryService"]; + if (config == null) + { + m_log.Error("[INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); + throw new Exception("Inventory connector init error"); + } + + string serviceURI = config.GetString("InventoryServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[INVENTORY CONNECTOR]: No Server URI named in section InventoryService"); + throw new Exception("Inventory connector init error"); + } + m_ServerURI = serviceURI; + + m_requestTimeoutSecs = config.GetInt("RemoteRequestTimeout", m_requestTimeoutSecs); + + StatsManager.RegisterStat( + new Stat( + "RequestsMade", + "Requests made", + "Number of requests made to the remove inventory service", + "requests", + "inventory", + serviceURI, + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + s => s.Value = RequestsMade, + StatVerbosity.Debug)); + } + + private bool CheckReturn(Dictionary ret) + { + if (ret == null) + return false; + + if (ret.Count == 0) + return false; + + if (ret.ContainsKey("RESULT")) + { + if (ret["RESULT"] is string) + { + bool result; + + if (bool.TryParse((string)ret["RESULT"], out result)) + return result; + + return false; + } + } + + return true; + } + + public bool CreateUserInventory(UUID principalID) + { + Dictionary ret = MakeRequest("CREATEUSERINVENTORY", + new Dictionary { + { "PRINCIPAL", principalID.ToString() } + }); + + return CheckReturn(ret); + } + + public List GetInventorySkeleton(UUID principalID) + { + Dictionary ret = MakeRequest("GETINVENTORYSKELETON", + new Dictionary { + { "PRINCIPAL", principalID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + Dictionary folders = (Dictionary)ret["FOLDERS"]; + + List fldrs = new List(); + + try + { + foreach (Object o in folders.Values) + fldrs.Add(BuildFolder((Dictionary)o)); + } + catch (Exception e) + { + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception unwrapping folder list: ", e); + } + + return fldrs; + } + + public InventoryFolderBase GetRootFolder(UUID principalID) + { + Dictionary ret = MakeRequest("GETROOTFOLDER", + new Dictionary { + { "PRINCIPAL", principalID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + return BuildFolder((Dictionary)ret["folder"]); + } + + public InventoryFolderBase GetFolderForType(UUID principalID, FolderType type) + { + Dictionary ret = MakeRequest("GETFOLDERFORTYPE", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "TYPE", ((int)type).ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + return BuildFolder((Dictionary)ret["folder"]); + } + + public InventoryCollection GetFolderContent(UUID principalID, UUID folderID) + { + InventoryCollection inventory = new InventoryCollection(); + inventory.Folders = new List(); + inventory.Items = new List(); + inventory.OwnerID = principalID; + + try + { + Dictionary ret = MakeRequest("GETFOLDERCONTENT", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "FOLDER", folderID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + Dictionary folders = ret.ContainsKey("FOLDERS") ? + (Dictionary)ret["FOLDERS"] : null; + Dictionary items = ret.ContainsKey("ITEMS") ? + (Dictionary)ret["ITEMS"] : null; + + if (folders != null) + foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i + inventory.Folders.Add(BuildFolder((Dictionary)o)); + if (items != null) + foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i + inventory.Items.Add(BuildItem((Dictionary)o)); + } + catch (Exception e) + { + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Exception in GetFolderContent: {0}", e.Message); + } + + return inventory; + } + + public virtual InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderIDs) + { + InventoryCollection[] inventoryArr = new InventoryCollection[folderIDs.Length]; + // m_log.DebugFormat("[XXX]: In GetMultipleFoldersContent {0}", String.Join(",", folderIDs)); + try + { + Dictionary resultSet = MakeRequest("GETMULTIPLEFOLDERSCONTENT", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "FOLDERS", String.Join(",", folderIDs) }, + { "COUNT", folderIDs.Length.ToString() } + }); + + if (!CheckReturn(resultSet)) + return null; + + int i = 0; + foreach (KeyValuePair kvp in resultSet) + { + InventoryCollection inventory = new InventoryCollection(); + if (kvp.Key.StartsWith("F_")) + { + UUID fid = UUID.Zero; + if (UUID.TryParse(kvp.Key.Substring(2), out fid) && fid == folderIDs[i]) + { + inventory.Folders = new List(); + inventory.Items = new List(); + + Dictionary ret = (Dictionary)kvp.Value; + + if (ret.ContainsKey("FID")) + { + if (!UUID.TryParse(ret["FID"].ToString(), out inventory.FolderID)) + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Could not parse folder id {0}", ret["FID"].ToString()); + } + else + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: FID key not present in response"); + + inventory.Version = -1; + if (ret.ContainsKey("VERSION")) + Int32.TryParse(ret["VERSION"].ToString(), out inventory.Version); + if (ret.ContainsKey("OWNER")) + UUID.TryParse(ret["OWNER"].ToString(), out inventory.OwnerID); + + //m_log.DebugFormat("[XXX]: Received {0} ({1}) {2} {3}", inventory.FolderID, fid, inventory.Version, inventory.OwnerID); + + Dictionary folders = + (Dictionary)ret["FOLDERS"]; + Dictionary items = + (Dictionary)ret["ITEMS"]; + + foreach (Object o in folders.Values) // getting the values directly, we don't care about the keys folder_i + { + inventory.Folders.Add(BuildFolder((Dictionary)o)); + } + foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i + { + inventory.Items.Add(BuildItem((Dictionary)o)); + } + + inventoryArr[i] = inventory; + } + else + { + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Folder id does not match. Expected {0} got {1}", + folderIDs[i], fid); + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: {0} {1}", String.Join(",", folderIDs), String.Join(",", resultSet.Keys)); + } + + i += 1; + } + } + } + catch (Exception e) + { + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Exception in GetMultipleFoldersContent: {0}", e.Message); + } + + return inventoryArr; + } + + public List GetFolderItems(UUID principalID, UUID folderID) + { + Dictionary ret = MakeRequest("GETFOLDERITEMS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "FOLDER", folderID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + Dictionary items = (Dictionary)ret["ITEMS"]; + List fitems = new List(); + foreach (Object o in items.Values) // getting the values directly, we don't care about the keys item_i + fitems.Add(BuildItem((Dictionary)o)); + + return fitems; + } + + public bool AddFolder(InventoryFolderBase folder) + { + Dictionary ret = MakeRequest("ADDFOLDER", + new Dictionary { + { "ParentID", folder.ParentID.ToString() }, + { "Type", folder.Type.ToString() }, + { "Version", folder.Version.ToString() }, + { "Name", folder.Name.ToString() }, + { "Owner", folder.Owner.ToString() }, + { "ID", folder.ID.ToString() } + }); + + return CheckReturn(ret); + } + + public bool UpdateFolder(InventoryFolderBase folder) + { + Dictionary ret = MakeRequest("UPDATEFOLDER", + new Dictionary { + { "ParentID", folder.ParentID.ToString() }, + { "Type", folder.Type.ToString() }, + { "Version", folder.Version.ToString() }, + { "Name", folder.Name.ToString() }, + { "Owner", folder.Owner.ToString() }, + { "ID", folder.ID.ToString() } + }); + + return CheckReturn(ret); + } + + public bool MoveFolder(InventoryFolderBase folder) + { + Dictionary ret = MakeRequest("MOVEFOLDER", + new Dictionary { + { "ParentID", folder.ParentID.ToString() }, + { "ID", folder.ID.ToString() }, + { "PRINCIPAL", folder.Owner.ToString() } + }); + + return CheckReturn(ret); + } + + public bool DeleteFolders(UUID principalID, List folderIDs) + { + List slist = new List(); + + foreach (UUID f in folderIDs) + slist.Add(f.ToString()); + + Dictionary ret = MakeRequest("DELETEFOLDERS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "FOLDERS", slist } + }); + + return CheckReturn(ret); + } + + public bool PurgeFolder(InventoryFolderBase folder) + { + Dictionary ret = MakeRequest("PURGEFOLDER", + new Dictionary { + { "ID", folder.ID.ToString() } + }); + + return CheckReturn(ret); + } + + public bool AddItem(InventoryItemBase item) + { + if (item.Description == null) + item.Description = String.Empty; + if (item.CreatorData == null) + item.CreatorData = String.Empty; + if (item.CreatorId == null) + item.CreatorId = String.Empty; + Dictionary ret = MakeRequest("ADDITEM", + new Dictionary { + { "AssetID", item.AssetID.ToString() }, + { "AssetType", item.AssetType.ToString() }, + { "Name", item.Name.ToString() }, + { "Owner", item.Owner.ToString() }, + { "ID", item.ID.ToString() }, + { "InvType", item.InvType.ToString() }, + { "Folder", item.Folder.ToString() }, + { "CreatorId", item.CreatorId.ToString() }, + { "CreatorData", item.CreatorData.ToString() }, + { "Description", item.Description.ToString() }, + { "NextPermissions", item.NextPermissions.ToString() }, + { "CurrentPermissions", item.CurrentPermissions.ToString() }, + { "BasePermissions", item.BasePermissions.ToString() }, + { "EveryOnePermissions", item.EveryOnePermissions.ToString() }, + { "GroupPermissions", item.GroupPermissions.ToString() }, + { "GroupID", item.GroupID.ToString() }, + { "GroupOwned", item.GroupOwned.ToString() }, + { "SalePrice", item.SalePrice.ToString() }, + { "SaleType", item.SaleType.ToString() }, + { "Flags", item.Flags.ToString() }, + { "CreationDate", item.CreationDate.ToString() } + }); + + return CheckReturn(ret); + } + + public bool UpdateItem(InventoryItemBase item) + { + if (item.CreatorData == null) + item.CreatorData = String.Empty; + Dictionary ret = MakeRequest("UPDATEITEM", + new Dictionary { + { "AssetID", item.AssetID.ToString() }, + { "AssetType", item.AssetType.ToString() }, + { "Name", item.Name.ToString() }, + { "Owner", item.Owner.ToString() }, + { "ID", item.ID.ToString() }, + { "InvType", item.InvType.ToString() }, + { "Folder", item.Folder.ToString() }, + { "CreatorId", item.CreatorId.ToString() }, + { "CreatorData", item.CreatorData.ToString() }, + { "Description", item.Description.ToString() }, + { "NextPermissions", item.NextPermissions.ToString() }, + { "CurrentPermissions", item.CurrentPermissions.ToString() }, + { "BasePermissions", item.BasePermissions.ToString() }, + { "EveryOnePermissions", item.EveryOnePermissions.ToString() }, + { "GroupPermissions", item.GroupPermissions.ToString() }, + { "GroupID", item.GroupID.ToString() }, + { "GroupOwned", item.GroupOwned.ToString() }, + { "SalePrice", item.SalePrice.ToString() }, + { "SaleType", item.SaleType.ToString() }, + { "Flags", item.Flags.ToString() }, + { "CreationDate", item.CreationDate.ToString() } + }); + + return CheckReturn(ret); + } + + public bool MoveItems(UUID principalID, List items) + { + List idlist = new List(); + List destlist = new List(); + + foreach (InventoryItemBase item in items) + { + idlist.Add(item.ID.ToString()); + destlist.Add(item.Folder.ToString()); + } + + Dictionary ret = MakeRequest("MOVEITEMS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "IDLIST", idlist }, + { "DESTLIST", destlist } + }); + + return CheckReturn(ret); + } + + public bool DeleteItems(UUID principalID, List itemIDs) + { + List slist = new List(); + + foreach (UUID f in itemIDs) + slist.Add(f.ToString()); + + Dictionary ret = MakeRequest("DELETEITEMS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "ITEMS", slist } + }); + + return CheckReturn(ret); + } + + public InventoryItemBase GetItem(InventoryItemBase item) + { + InventoryItemBase retrieved = null; + if (m_ItemCache.TryGetValue(item.ID, out retrieved)) + return retrieved; + + try + { + Dictionary ret = MakeRequest("GETITEM", + new Dictionary { + { "ID", item.ID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + retrieved = BuildItem((Dictionary)ret["item"]); + } + catch (Exception e) + { + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception in GetItem: ", e); + } + + m_ItemCache.AddOrUpdate(item.ID, retrieved, CACHE_EXPIRATION_SECONDS); + + return retrieved; + } + + public virtual InventoryItemBase[] GetMultipleItems(UUID principalID, UUID[] itemIDs) + { + //m_log.DebugFormat("[XXX]: In GetMultipleItems {0}", String.Join(",", itemIDs)); + + InventoryItemBase[] itemArr = new InventoryItemBase[itemIDs.Length]; + // Try to get them from the cache + List pending = new List(); + InventoryItemBase item = null; + int i = 0; + foreach (UUID id in itemIDs) + { + if (m_ItemCache.TryGetValue(id, out item)) + itemArr[i++] = item; + else + pending.Add(id); + } + + if (pending.Count == 0) // we're done, everything was in the cache + return itemArr; + + try + { + Dictionary resultSet = MakeRequest("GETMULTIPLEITEMS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "ITEMS", String.Join(",", pending.ToArray()) }, + { "COUNT", pending.Count.ToString() } + }); + + if (!CheckReturn(resultSet)) + { + if (i == 0) + return null; + else + return itemArr; + } + + // carry over index i where we left above + foreach (KeyValuePair kvp in resultSet) + { + InventoryCollection inventory = new InventoryCollection(); + if (kvp.Key.StartsWith("item_")) + { + if (kvp.Value is Dictionary) + { + item = BuildItem((Dictionary)kvp.Value); + m_ItemCache.AddOrUpdate(item.ID, item, CACHE_EXPIRATION_SECONDS); + itemArr[i++] = item; + } + else + itemArr[i++] = null; + } + } + } + catch (Exception e) + { + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Exception in GetMultipleItems: {0}", e.Message); + } + + return itemArr; + } + + public InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + try + { + Dictionary ret = MakeRequest("GETFOLDER", + new Dictionary { + { "ID", folder.ID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + return BuildFolder((Dictionary)ret["folder"]); + } + catch (Exception e) + { + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception in GetFolder: ", e); + } + + return null; + } + + public List GetActiveGestures(UUID principalID) + { + Dictionary ret = MakeRequest("GETACTIVEGESTURES", + new Dictionary { + { "PRINCIPAL", principalID.ToString() } + }); + + if (!CheckReturn(ret)) + return null; + + List items = new List(); + + foreach (Object o in ((Dictionary)ret["ITEMS"]).Values) + items.Add(BuildItem((Dictionary)o)); + + return items; + } + + public int GetAssetPermissions(UUID principalID, UUID assetID) + { + Dictionary ret = MakeRequest("GETASSETPERMISSIONS", + new Dictionary { + { "PRINCIPAL", principalID.ToString() }, + { "ASSET", assetID.ToString() } + }); + + // We cannot use CheckReturn() here because valid values for RESULT are "false" (in the case of request failure) or an int + if (ret == null) + return 0; + + if (ret.ContainsKey("RESULT")) + { + if (ret["RESULT"] is string) + { + int intResult; + + if (int.TryParse ((string)ret["RESULT"], out intResult)) + return intResult; + } + } + + return 0; + } + + public bool HasInventoryForUser(UUID principalID) + { + return false; + } + + // Helpers + // + private Dictionary MakeRequest(string method, + Dictionary sendData) + { + // Add "METHOD" as the first key in the dictionary. This ensures that it will be + // visible even when using partial logging ("debug http all 5"). + Dictionary temp = sendData; + sendData = new Dictionary{ { "METHOD", method } }; + foreach (KeyValuePair kvp in temp) + sendData.Add(kvp.Key, kvp.Value); + + RequestsMade++; + + string reply + = SynchronousRestFormsRequester.MakeRequest( + "POST", m_ServerURI + "/xinventory", + ServerUtils.BuildQueryString(sendData), m_requestTimeoutSecs, m_Auth); + + Dictionary replyData = ServerUtils.ParseXmlResponse( + reply); + + return replyData; + } + + private InventoryFolderBase BuildFolder(Dictionary data) + { + InventoryFolderBase folder = new InventoryFolderBase(); + + try + { + folder.ParentID = new UUID(data["ParentID"].ToString()); + folder.Type = short.Parse(data["Type"].ToString()); + folder.Version = ushort.Parse(data["Version"].ToString()); + folder.Name = data["Name"].ToString(); + folder.Owner = new UUID(data["Owner"].ToString()); + folder.ID = new UUID(data["ID"].ToString()); + } + catch (Exception e) + { + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception building folder: ", e); + } + + return folder; + } + + private InventoryItemBase BuildItem(Dictionary data) + { + InventoryItemBase item = new InventoryItemBase(); + + try + { + item.AssetID = new UUID(data["AssetID"].ToString()); + item.AssetType = int.Parse(data["AssetType"].ToString()); + item.Name = data["Name"].ToString(); + item.Owner = new UUID(data["Owner"].ToString()); + item.ID = new UUID(data["ID"].ToString()); + item.InvType = int.Parse(data["InvType"].ToString()); + item.Folder = new UUID(data["Folder"].ToString()); + item.CreatorId = data["CreatorId"].ToString(); + if (data.ContainsKey("CreatorData")) + item.CreatorData = data["CreatorData"].ToString(); + else + item.CreatorData = String.Empty; + item.Description = data["Description"].ToString(); + item.NextPermissions = uint.Parse(data["NextPermissions"].ToString()); + item.CurrentPermissions = uint.Parse(data["CurrentPermissions"].ToString()); + item.BasePermissions = uint.Parse(data["BasePermissions"].ToString()); + item.EveryOnePermissions = uint.Parse(data["EveryOnePermissions"].ToString()); + item.GroupPermissions = uint.Parse(data["GroupPermissions"].ToString()); + item.GroupID = new UUID(data["GroupID"].ToString()); + item.GroupOwned = bool.Parse(data["GroupOwned"].ToString()); + item.SalePrice = int.Parse(data["SalePrice"].ToString()); + item.SaleType = byte.Parse(data["SaleType"].ToString()); + item.Flags = uint.Parse(data["Flags"].ToString()); + item.CreationDate = int.Parse(data["CreationDate"].ToString()); + } + catch (Exception e) + { + m_log.Error("[XINVENTORY CONNECTOR]: Exception building item: ", e); + } + + return item; + } + } +} diff --git a/OpenSim/Services/Connectors/Land/LandServicesConnector.cs b/OpenSim/Services/Connectors/Land/LandServicesConnector.cs new file mode 100644 index 0000000000..644331acf6 --- /dev/null +++ b/OpenSim/Services/Connectors/Land/LandServicesConnector.cs @@ -0,0 +1,133 @@ +/* + * 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 log4net; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using Nwc.XmlRpc; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Connectors +{ + public class LandServicesConnector : ILandService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected IGridService m_GridService = null; + + public LandServicesConnector() + { + } + + public LandServicesConnector(IGridService gridServices) + { + Initialise(gridServices); + } + + public virtual void Initialise(IGridService gridServices) + { + m_GridService = gridServices; + } + + public virtual LandData GetLandData(UUID scopeID, ulong regionHandle, uint x, uint y, out byte regionAccess) + { + LandData landData = null; + Hashtable hash = new Hashtable(); + hash["region_handle"] = regionHandle.ToString(); + hash["x"] = x.ToString(); + hash["y"] = y.ToString(); + + IList paramList = new ArrayList(); + paramList.Add(hash); + regionAccess = 42; // Default to adult. Better safe... + + try + { + uint xpos = 0, ypos = 0; + Util.RegionHandleToWorldLoc(regionHandle, out xpos, out ypos); + GridRegion info = m_GridService.GetRegionByPosition(scopeID, (int)xpos, (int)ypos); + if (info != null) // just to be sure + { + XmlRpcRequest request = new XmlRpcRequest("land_data", paramList); + XmlRpcResponse response = request.Send(info.ServerURI, 10000); + if (response.IsFault) + { + m_log.ErrorFormat("[LAND CONNECTOR]: remote call returned an error: {0}", response.FaultString); + } + else + { + hash = (Hashtable)response.Value; + try + { + landData = new LandData(); + landData.AABBMax = Vector3.Parse((string)hash["AABBMax"]); + landData.AABBMin = Vector3.Parse((string)hash["AABBMin"]); + landData.Area = Convert.ToInt32(hash["Area"]); + landData.AuctionID = Convert.ToUInt32(hash["AuctionID"]); + landData.Description = (string)hash["Description"]; + landData.Flags = Convert.ToUInt32(hash["Flags"]); + landData.GlobalID = new UUID((string)hash["GlobalID"]); + landData.Name = (string)hash["Name"]; + landData.OwnerID = new UUID((string)hash["OwnerID"]); + landData.SalePrice = Convert.ToInt32(hash["SalePrice"]); + landData.SnapshotID = new UUID((string)hash["SnapshotID"]); + landData.UserLocation = Vector3.Parse((string)hash["UserLocation"]); + if (hash["RegionAccess"] != null) + regionAccess = (byte)Convert.ToInt32((string)hash["RegionAccess"]); + m_log.DebugFormat("[LAND CONNECTOR]: Got land data for parcel {0}", landData.Name); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[LAND CONNECTOR]: Got exception while parsing land-data: {0} {1}", + e.Message, e.StackTrace); + } + } + } + else + m_log.WarnFormat("[LAND CONNECTOR]: Couldn't find region with handle {0}", regionHandle); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[LAND CONNECTOR]: Couldn't contact region {0}: {1} {2}", regionHandle, e.Message, e.StackTrace); + } + + return landData; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/MapImage/MapImageServicesConnector.cs b/OpenSim/Services/Connectors/MapImage/MapImageServicesConnector.cs new file mode 100644 index 0000000000..677825e098 --- /dev/null +++ b/OpenSim/Services/Connectors/MapImage/MapImageServicesConnector.cs @@ -0,0 +1,226 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; + +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Communications; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors +{ + public class MapImageServicesConnector : BaseServiceConnector, IMapImageService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public MapImageServicesConnector() + { + } + + public MapImageServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public MapImageServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig config = source.Configs["MapImageService"]; + if (config == null) + { + m_log.Error("[MAP IMAGE CONNECTOR]: MapImageService missing"); + throw new Exception("MapImage connector init error"); + } + + string serviceURI = config.GetString("MapImageServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[MAP IMAGE CONNECTOR]: No Server URI named in section MapImageService"); + throw new Exception("MapImage connector init error"); + } + m_ServerURI = serviceURI; + m_ServerURI = serviceURI.TrimEnd('/'); + base.Initialise(source, "MapImageService"); + } + + public bool RemoveMapTile(int x, int y, out string reason) + { + reason = string.Empty; + int tickstart = Util.EnvironmentTickCount(); + Dictionary sendData = new Dictionary(); + sendData["X"] = x.ToString(); + sendData["Y"] = y.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/removemap"; + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("Result") && (replyData["Result"].ToString().ToLower() == "success")) + { + return true; + } + else if (replyData.ContainsKey("Result") && (replyData["Result"].ToString().ToLower() == "failure")) + { + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: Delete failed: {0}", replyData["Message"].ToString()); + reason = replyData["Message"].ToString(); + return false; + } + else if (!replyData.ContainsKey("Result")) + { + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: reply data does not contain result field"); + } + else + { + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: unexpected result {0}", replyData["Result"].ToString()); + reason = "Unexpected result " + replyData["Result"].ToString(); + } + + } + else + { + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: Map post received null reply"); + } + } + catch (Exception e) + { + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: Exception when contacting map server at {0}: {1}", uri, e.Message); + } + finally + { + // This just dumps a warning for any operation that takes more than 100 ms + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: map tile deleted in {0}ms", tickdiff); + } + + return false; + } + + public bool AddMapTile(int x, int y, byte[] jpgData, out string reason) + { + reason = string.Empty; + int tickstart = Util.EnvironmentTickCount(); + Dictionary sendData = new Dictionary(); + sendData["X"] = x.ToString(); + sendData["Y"] = y.ToString(); + sendData["TYPE"] = "image/jpeg"; + sendData["DATA"] = Convert.ToBase64String(jpgData); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/map"; + + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("Result") && (replyData["Result"].ToString().ToLower() == "success")) + { + return true; + } + else if (replyData.ContainsKey("Result") && (replyData["Result"].ToString().ToLower() == "failure")) + { + reason = string.Format("Map post to {0} failed: {1}", uri, replyData["Message"].ToString()); + m_log.WarnFormat("[MAP IMAGE CONNECTOR]: {0}", reason); + + return false; + } + else if (!replyData.ContainsKey("Result")) + { + reason = string.Format("Reply data from {0} does not contain result field", uri); + m_log.WarnFormat("[MAP IMAGE CONNECTOR]: {0}", reason); + } + else + { + reason = string.Format("Unexpected result {0} from {1}" + replyData["Result"].ToString(), uri); + m_log.WarnFormat("[MAP IMAGE CONNECTOR]: {0}", reason); + } + } + else + { + reason = string.Format("Map post received null reply from {0}", uri); + m_log.WarnFormat("[MAP IMAGE CONNECTOR]: {0}", reason); + } + } + catch (Exception e) + { + reason = string.Format("Exception when posting to map server at {0}: {1}", uri, e.Message); + m_log.WarnFormat("[MAP IMAGE CONNECTOR]: {0}", reason); + } + finally + { + // This just dumps a warning for any operation that takes more than 100 ms + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + m_log.DebugFormat("[MAP IMAGE CONNECTOR]: map tile uploaded in {0}ms", tickdiff); + } + + return false; + + } + + public byte[] GetMapTile(string fileName, out string format) + { + format = string.Empty; + new Exception("GetMapTile method not Implemented"); + return null; + } + } +} diff --git a/OpenSim/Services/Connectors/Neighbour/NeighbourServicesConnector.cs b/OpenSim/Services/Connectors/Neighbour/NeighbourServicesConnector.cs new file mode 100644 index 0000000000..eecf096e2f --- /dev/null +++ b/OpenSim/Services/Connectors/Neighbour/NeighbourServicesConnector.cs @@ -0,0 +1,205 @@ +/* + * 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 log4net; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Reflection; +using System.Text; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Connectors +{ + public class NeighbourServicesConnector : INeighbourService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected IGridService m_GridService = null; + + public NeighbourServicesConnector() + { + } + + public NeighbourServicesConnector(IGridService gridServices) + { + Initialise(gridServices); + } + + public virtual void Initialise(IGridService gridServices) + { + m_GridService = gridServices; + } + + public virtual GridRegion HelloNeighbour(ulong regionHandle, RegionInfo thisRegion) + { + uint x = 0, y = 0; + Util.RegionHandleToWorldLoc(regionHandle, out x, out y); + GridRegion regInfo = m_GridService.GetRegionByPosition(thisRegion.ScopeID, (int)x, (int)y); + if ((regInfo != null) && + // Don't remote-call this instance; that's a startup hickup + !((regInfo.ExternalHostName == thisRegion.ExternalHostName) && (regInfo.HttpPort == thisRegion.HttpPort))) + { + if (!DoHelloNeighbourCall(regInfo, thisRegion)) + return null; + } + else + return null; + + return regInfo; + } + + public bool DoHelloNeighbourCall(GridRegion region, RegionInfo thisRegion) + { + string uri = region.ServerURI + "region/" + thisRegion.RegionID + "/"; +// m_log.Debug(" >>> DoHelloNeighbourCall <<< " + uri); + + WebRequest helloNeighbourRequest; + + try + { + helloNeighbourRequest = WebRequest.Create(uri); + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[NEIGHBOUR SERVICES CONNECTOR]: Unable to parse uri {0} to send HelloNeighbour from {1} to {2}. Exception {3} ", + uri, thisRegion.RegionName, region.RegionName, e.Message), e); + + return false; + } + + helloNeighbourRequest.Method = "POST"; + helloNeighbourRequest.ContentType = "application/json"; + helloNeighbourRequest.Timeout = 10000; + + // Fill it in + OSDMap args = null; + try + { + args = thisRegion.PackRegionInfoData(); + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[NEIGHBOUR SERVICES CONNECTOR]: PackRegionInfoData failed for HelloNeighbour from {0} to {1}. Exception {2} ", + thisRegion.RegionName, region.RegionName, e.Message), e); + + return false; + } + + // Add the regionhandle of the destination region + args["destination_handle"] = OSD.FromString(region.RegionHandle.ToString()); + + string strBuffer = ""; + byte[] buffer = new byte[1]; + + try + { + strBuffer = OSDParser.SerializeJsonString(args); + buffer = Util.UTF8NoBomEncoding.GetBytes(strBuffer); + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[NEIGHBOUR SERVICES CONNECTOR]: Exception thrown on serialization of HelloNeighbour from {0} to {1}. Exception {2} ", + thisRegion.RegionName, region.RegionName, e.Message), e); + + return false; + } + + Stream os = null; + try + { // send the Post + helloNeighbourRequest.ContentLength = buffer.Length; //Count bytes to send + os = helloNeighbourRequest.GetRequestStream(); + os.Write(buffer, 0, strBuffer.Length); //Send it + //m_log.InfoFormat("[REST COMMS]: Posted HelloNeighbour request to remote sim {0}", uri); + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[NEIGHBOUR SERVICES CONNECTOR]: Unable to send HelloNeighbour from {0} to {1} (uri {2}). Exception {3} ", + thisRegion.RegionName, region.RegionName, uri, e.Message), e); + + return false; + } + finally + { + if (os != null) + os.Dispose(); + } + + // Let's wait for the response + //m_log.Info("[REST COMMS]: Waiting for a reply after DoHelloNeighbourCall"); + + try + { + using (WebResponse webResponse = helloNeighbourRequest.GetResponse()) + { + if (webResponse == null) + { + m_log.DebugFormat( + "[NEIGHBOUR SERVICES CONNECTOR]: Null reply on DoHelloNeighbourCall post from {0} to {1}", + thisRegion.RegionName, region.RegionName); + } + + using (Stream s = webResponse.GetResponseStream()) + { + using (StreamReader sr = new StreamReader(s)) + { + //reply = sr.ReadToEnd().Trim(); + sr.ReadToEnd().Trim(); + //m_log.InfoFormat("[REST COMMS]: DoHelloNeighbourCall reply was {0} ", reply); + } + } + } + } + catch (Exception e) + { + m_log.Warn(string.Format( + "[NEIGHBOUR SERVICES CONNECTOR]: Exception on reply of DoHelloNeighbourCall from {0} back to {1}. Exception {2} ", + region.RegionName, thisRegion.RegionName, e.Message), e); + + return false; + } + + return true; + } + } +} diff --git a/OpenSim/Services/Connectors/Presence/PresenceServicesConnector.cs b/OpenSim/Services/Connectors/Presence/PresenceServicesConnector.cs new file mode 100644 index 0000000000..63f3f37047 --- /dev/null +++ b/OpenSim/Services/Connectors/Presence/PresenceServicesConnector.cs @@ -0,0 +1,388 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class PresenceServicesConnector : BaseServiceConnector, IPresenceService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public PresenceServicesConnector() + { + } + + public PresenceServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public PresenceServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["PresenceService"]; + if (gridConfig == null) + { + m_log.Error("[PRESENCE CONNECTOR]: PresenceService missing from OpenSim.ini"); + throw new Exception("Presence connector init error"); + } + + string serviceURI = gridConfig.GetString("PresenceServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[PRESENCE CONNECTOR]: No Server URI named in section PresenceService"); + throw new Exception("Presence connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "PresenceService"); + } + + + #region IPresenceService + + public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "login"; + + sendData["UserID"] = userID; + sendData["SessionID"] = sessionID.ToString(); + sendData["SecureSessionID"] = secureSessionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LoginAgent reply data does not contain result field"); + + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LoginAgent received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + + } + + public bool LogoutAgent(UUID sessionID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "logout"; + + sendData["SessionID"] = sessionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LogoutAgent reply data does not contain result field"); + + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LogoutAgent received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public bool LogoutRegionAgents(UUID regionID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "logoutregion"; + + sendData["RegionID"] = regionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LogoutRegionAgents reply data does not contain result field"); + + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: LogoutRegionAgents received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "report"; + + sendData["SessionID"] = sessionID.ToString(); + sendData["RegionID"] = regionID.ToString(); + + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: ReportAgent reply data does not contain result field"); + + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: ReportAgent received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + return false; + } + + public PresenceInfo GetAgent(UUID sessionID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getagent"; + + sendData["SessionID"] = sessionID.ToString(); + + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + // m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgent received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + return null; + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + PresenceInfo pinfo = null; + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + { + pinfo = new PresenceInfo((Dictionary)replyData["result"]); + } + } + + return pinfo; + } + + public PresenceInfo[] GetAgents(string[] userIDs) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getagents"; + + sendData["uuids"] = new List(userIDs); + + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/presence"; + //m_log.DebugFormat("[PRESENCE CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Exception when contacting presence server at {0}: {1}", uri, e.Message); + } + + List rinfos = new List(); + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + if (replyData.ContainsKey("result") && + (replyData["result"].ToString() == "null" || replyData["result"].ToString() == "Failure")) + { + return new PresenceInfo[0]; + } + + Dictionary.ValueCollection pinfosList = replyData.Values; + //m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents returned {0} elements", pinfosList.Count); + foreach (object presence in pinfosList) + { + if (presence is Dictionary) + { + PresenceInfo pinfo = new PresenceInfo((Dictionary)presence); + rinfos.Add(pinfo); + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents received invalid response type {0}", + presence.GetType()); + } + } + else + m_log.DebugFormat("[PRESENCE CONNECTOR]: GetAgents received null response"); + + return rinfos.ToArray(); + } + + + #endregion + + } +} diff --git a/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs b/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..2af2ec1c58 --- /dev/null +++ b/OpenSim/Services/Connectors/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.Connectors")] +[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("3ab0a9a1-3f45-4c07-a892-3848df8c0173")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs new file mode 100644 index 0000000000..cd4781deb0 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianActivityDetector.cs @@ -0,0 +1,100 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using log4net; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + public class SimianActivityDetector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IGridUserService m_GridUserService; + + public SimianActivityDetector(IGridUserService guService) + { + m_GridUserService = guService; + m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Started"); + } + + public void AddRegion(Scene scene) + { + // For now the only events we listen to are these + // But we could trigger the position update more often + scene.EventManager.OnMakeRootAgent += OnMakeRootAgent; + scene.EventManager.OnNewClient += OnNewClient; + scene.EventManager.OnAvatarEnteringNewParcel += OnEnteringNewParcel; + } + + public void RemoveRegion(Scene scene) + { + scene.EventManager.OnMakeRootAgent -= OnMakeRootAgent; + scene.EventManager.OnNewClient -= OnNewClient; + scene.EventManager.OnAvatarEnteringNewParcel -= OnEnteringNewParcel; + } + + public void OnMakeRootAgent(ScenePresence sp) + { + m_log.DebugFormat("[SIMIAN ACTIVITY DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); + Util.FireAndForget(delegate(object o) + { + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + }, null, "SimianActivityDetector.SetLastPositionOnMakeRootAgent"); + } + + public void OnNewClient(IClientAPI client) + { + client.OnConnectionClosed += OnConnectionClose; + } + + public void OnConnectionClose(IClientAPI client) + { + if (client.SceneAgent.IsChildAgent) + return; + +// m_log.DebugFormat("[ACTIVITY DETECTOR]: Detected client logout {0} in {1}", client.AgentId, client.Scene.RegionInfo.RegionName); + m_GridUserService.LoggedOut( + client.AgentId.ToString(), client.SessionId, client.Scene.RegionInfo.RegionID, + client.SceneAgent.AbsolutePosition, client.SceneAgent.Lookat); + } + + void OnEnteringNewParcel(ScenePresence sp, int localLandID, UUID regionID) + { + // Asynchronously update the position stored in the session table for this agent + Util.FireAndForget(delegate(object o) + { + m_GridUserService.SetLastPosition(sp.UUID.ToString(), sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + }, null, "SimianActivityDetector.SetLastPositionOnEnteringNewParcel"); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs new file mode 100644 index 0000000000..9ad4a7a56f --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs @@ -0,0 +1,685 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects to the SimianGrid asset service + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianAssetServiceConnector")] + public class SimianAssetServiceConnector : IAssetService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + private static string ZeroID = UUID.Zero.ToString(); + + private string m_serverUrl = String.Empty; + private IImprovedAssetCache m_cache; + private bool m_Enabled = false; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) + { + if (m_cache == null) + { + IImprovedAssetCache cache = scene.RequestModuleInterface(); + if (cache is ISharedRegionModule) + m_cache = cache; + } + } + public void PostInitialise() { } + public void Close() { } + + public SimianAssetServiceConnector() { } + public string Name { get { return "SimianAssetServiceConnector"; } } + public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAssetServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public SimianAssetServiceConnector(string url) + { + if (!url.EndsWith("/") && !url.EndsWith("=")) + url = url + '/'; + m_serverUrl = url; + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("AssetServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["AssetService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("AssetServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN ASSET CONNECTOR]: No AssetServerURI specified, disabling connector"); + else + m_Enabled = true; + } + +#region IAssetService + + public AssetBase Get(string id) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + // Cache fetch + if (m_cache != null) + { + AssetBase asset = m_cache.Get(id); + if (asset != null) + return asset; + } + + return SimianGetOperation(id); + } + + + public AssetBase GetCached(string id) + { + if (m_cache != null) + return m_cache.Get(id); + + return null; + } + + /// + /// Get an asset's metadata + /// + /// + /// + public AssetMetadata GetMetadata(string id) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + // Cache fetch + if (m_cache != null) + { + AssetBase asset = m_cache.Get(id); + if (asset != null) + return asset.Metadata; + } + + // return GetRemoteMetadata(id); + return SimianGetMetadataOperation(id); + } + + public byte[] GetData(string id) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + AssetBase asset = Get(id); + + if (asset != null) + return asset.Data; + + return null; + } + + /// + /// Get an asset asynchronously + /// + /// The asset id + /// Represents the requester. Passed back via the handler + /// The handler to call back once the asset has been retrieved + /// True if the id was parseable, false otherwise + public bool Get(string id, Object sender, AssetRetrieved handler) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + // Cache fetch + if (m_cache != null) + { + AssetBase asset = m_cache.Get(id); + if (asset != null) + { + handler(id, sender, asset); + return true; + } + } + + Util.FireAndForget( + delegate(object o) + { + AssetBase asset = SimianGetOperation(id); + handler(id, sender, asset); + }, null, "SimianAssetServiceConnector.GetFromService" + ); + + return true; + } + + public bool[] AssetsExist(string[] ids) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + bool[] exist = new bool[ids.Length]; + + for (int i = 0; i < ids.Length; i++) + { + AssetMetadata metadata = GetMetadata(ids[i]); + if (metadata != null) + exist[i] = true; + } + + return exist; + } + + /// + /// Creates a new asset + /// + /// Returns a random ID if none is passed into it + /// + /// + public string Store(AssetBase asset) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + bool storedInCache = false; + + // AssetID handling + if (String.IsNullOrEmpty(asset.ID) || asset.ID == ZeroID) + { + asset.FullID = UUID.Random(); + asset.ID = asset.FullID.ToString(); + } + + // Cache handling + if (m_cache != null) + { + m_cache.Cache(asset); + storedInCache = true; + } + + // Local asset handling + if (asset.Local) + { + if (!storedInCache) + { + m_log.Error("Cannot store local " + asset.Metadata.ContentType + " asset without an asset cache"); + asset.ID = null; + asset.FullID = UUID.Zero; + } + + return asset.ID; + } + + return SimianStoreOperation(asset); + } + + /// + /// Update an asset's content + /// + /// Attachments and bare scripts need this!! + /// + /// + /// + public bool UpdateContent(string id, byte[] data) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + AssetBase asset = Get(id); + + if (asset == null) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to fetch asset {0} for updating", id); + return false; + } + + asset.Data = data; + + string result = Store(asset); + return !String.IsNullOrEmpty(result); + } + + /// + /// Delete an asset + /// + /// + /// + public bool Delete(string id) + { + if (String.IsNullOrEmpty(m_serverUrl)) + { + m_log.Error("[SIMIAN ASSET CONNECTOR]: No AssetServerURI configured"); + throw new InvalidOperationException(); + } + + if (m_cache != null) + m_cache.Expire(id); + + return SimianDeleteOperation(id); + } + +#endregion IAssetService + +#region SimianOperations + /// + /// Invokes the xRemoveAsset operation on the simian server to delete an asset + /// + /// + /// + private bool SimianDeleteOperation(string id) + { + try + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "xRemoveAsset" }, + { "AssetID", id } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs); + if (! response["Success"].AsBoolean()) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset; {0}",response["Message"].AsString()); + return false; + } + + return true; + + } + catch (Exception ex) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to delete asset {0}; {1}", id, ex.Message); + } + + return false; + } + + /// + /// Invokes the xAddAsset operation on the simian server to create or update an asset + /// + /// + /// + private string SimianStoreOperation(AssetBase asset) + { + try + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "xAddAsset" }, + { "ContentType", asset.Metadata.ContentType }, + { "EncodedData", Convert.ToBase64String(asset.Data) }, + { "AssetID", asset.FullID.ToString() }, + { "CreatorID", asset.Metadata.CreatorID }, + { "Temporary", asset.Temporary ? "1" : "0" }, + { "Name", asset.Name } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs); + if (! response["Success"].AsBoolean()) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",response["Message"].AsString()); + return null; + } + + // asset.ID is always set before calling this function + return asset.ID; + + } + catch (Exception ex) + { + m_log.ErrorFormat("[SIMIAN ASSET CONNECTOR] failed to store asset; {0}",ex.Message); + } + + return null; + } + + /// + /// Invokes the xGetAsset operation on the simian server to get data associated with an asset + /// + /// + /// + private AssetBase SimianGetOperation(string id) + { + try + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "xGetAsset" }, + { "ID", id } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs); + if (! response["Success"].AsBoolean()) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset; {0}",response["Message"].AsString()); + return null; + } + + AssetBase asset = new AssetBase(); + + asset.ID = id; + asset.Name = String.Empty; + asset.Metadata.ContentType = response["ContentType"].AsString(); // this will also set the asset Type property + asset.CreatorID = response["CreatorID"].AsString(); + asset.Data = System.Convert.FromBase64String(response["EncodedData"].AsString()); + asset.Local = false; + asset.Temporary = response["Temporary"]; + + return asset; + } + catch (Exception ex) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: failed to retrieve asset {0}; {1}", id, ex.Message); + } + + return null; + } + + /// + /// Invokes the xGetAssetMetadata operation on the simian server to retrieve metadata for an asset + /// This operation is generally used to determine if an asset exists in the database + /// + /// + /// + private AssetMetadata SimianGetMetadataOperation(string id) + { + try + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "xGetAssetMetadata" }, + { "ID", id } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs); + if (! response["Success"].AsBoolean()) + { + // this is not really an error, this call is used to test existence + // m_log.DebugFormat("[SIMIAN ASSET CONNECTOR] Failed to get asset metadata; {0}",response["Message"].AsString()); + return null; + } + + AssetMetadata metadata = new AssetMetadata(); + metadata.ID = id; + metadata.ContentType = response["ContentType"].AsString(); + metadata.CreatorID = response["CreatorID"].AsString(); + metadata.Local = false; + metadata.Temporary = response["Temporary"]; + + string lastModifiedStr = response["Last-Modified"].AsString(); + if (! String.IsNullOrEmpty(lastModifiedStr)) + { + DateTime lastModified; + if (DateTime.TryParse(lastModifiedStr, out lastModified)) + metadata.CreationDate = lastModified; + } + + return metadata; + } + catch (Exception ex) + { + m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to get asset metadata; {0}", ex.Message); + } + + return null; + } +#endregion + + // private AssetMetadata GetRemoteMetadata(string id) + // { + // Uri url; + // AssetMetadata metadata = null; + + // // Determine if id is an absolute URL or a grid-relative UUID + // if (!Uri.TryCreate(id, UriKind.Absolute, out url)) + // url = new Uri(m_serverUrl + id); + + // try + // { + // HttpWebRequest request = UntrustedHttpWebRequest.Create(url); + // request.Method = "HEAD"; + + // using (WebResponse response = request.GetResponse()) + // { + // using (Stream responseStream = response.GetResponseStream()) + // { + // // Create the metadata object + // metadata = new AssetMetadata(); + // metadata.ContentType = response.ContentType; + // metadata.ID = id; + + // UUID uuid; + // if (UUID.TryParse(id, out uuid)) + // metadata.FullID = uuid; + + // string lastModifiedStr = response.Headers.Get("Last-Modified"); + // if (!String.IsNullOrEmpty(lastModifiedStr)) + // { + // DateTime lastModified; + // if (DateTime.TryParse(lastModifiedStr, out lastModified)) + // metadata.CreationDate = lastModified; + // } + // } + // } + // } + // catch (Exception ex) + // { + // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset HEAD from " + url + " failed: " + ex.Message); + // } + + // return metadata; + // } + + // private AssetBase GetRemote(string id) + // { + // AssetBase asset = null; + // Uri url; + + // // Determine if id is an absolute URL or a grid-relative UUID + // if (!Uri.TryCreate(id, UriKind.Absolute, out url)) + // url = new Uri(m_serverUrl + id); + + // try + // { + // HttpWebRequest request = UntrustedHttpWebRequest.Create(url); + + // using (WebResponse response = request.GetResponse()) + // { + // using (Stream responseStream = response.GetResponseStream()) + // { + // string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty; + + // // Create the asset object + // asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID); + + // UUID assetID; + // if (UUID.TryParse(id, out assetID)) + // asset.FullID = assetID; + + // // Grab the asset data from the response stream + // using (MemoryStream stream = new MemoryStream()) + // { + // responseStream.CopyStream(stream, Int32.MaxValue); + // asset.Data = stream.ToArray(); + // } + // } + // } + + // // Cache store + // if (m_cache != null && asset != null) + // m_cache.Cache(asset); + + // return asset; + // } + // catch (Exception ex) + // { + // m_log.Warn("[SIMIAN ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message); + // return null; + // } + // } + + // private string StoreRemote(AssetBase asset) + // { + // // Distinguish public and private assets + // bool isPublic = true; + // switch ((AssetType)asset.Type) + // { + // case AssetType.CallingCard: + // case AssetType.Gesture: + // case AssetType.LSLBytecode: + // case AssetType.LSLText: + // isPublic = false; + // break; + // } + + // string errorMessage = null; + + // // Build the remote storage request + // List postParameters = new List() + // { + // new MultipartForm.Parameter("AssetID", asset.FullID.ToString()), + // new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID), + // new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"), + // new MultipartForm.Parameter("Public", isPublic ? "1" : "0"), + // new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data) + // }; + + // // Make the remote storage request + // try + // { + // // Simian does not require the asset ID to be in the URL because it's in the post data. + // // By appending it to the URL also, we allow caching proxies (squid) to invalidate asset URLs + // HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl + asset.FullID.ToString()); + + // using (HttpWebResponse response = MultipartForm.Post(request, postParameters)) + // { + // using (Stream responseStream = response.GetResponseStream()) + // { + // string responseStr = null; + + // try + // { + // responseStr = responseStream.GetStreamString(); + // OSD responseOSD = OSDParser.Deserialize(responseStr); + // if (responseOSD.Type == OSDType.Map) + // { + // OSDMap responseMap = (OSDMap)responseOSD; + // if (responseMap["Success"].AsBoolean()) + // return asset.ID; + // else + // errorMessage = "Upload failed: " + responseMap["Message"].AsString(); + // } + // else + // { + // errorMessage = "Response format was invalid:\n" + responseStr; + // } + // } + // catch (Exception ex) + // { + // if (!String.IsNullOrEmpty(responseStr)) + // errorMessage = "Failed to parse the response:\n" + responseStr; + // else + // errorMessage = "Failed to retrieve the response: " + ex.Message; + // } + // } + // } + // } + // catch (WebException ex) + // { + // errorMessage = ex.Message; + // } + + // m_log.WarnFormat("[SIMIAN ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}", + // asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage); + + // return null; + // } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs new file mode 100644 index 0000000000..3bd11d99a3 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs @@ -0,0 +1,307 @@ +/* + * 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.Specialized; +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; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects authentication/authorization to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianAuthenticationServiceConnector")] + public class SimianAuthenticationServiceConnector : IAuthenticationService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private bool m_Enabled = false; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianAuthenticationServiceConnector() { } + public string Name { get { return "SimianAuthenticationServiceConnector"; } } + public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAuthenticationServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("AuthenticationServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["AuthenticationService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("AuthenticationServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + m_Enabled = true; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN AUTH CONNECTOR]: No AuthenticationServerURI specified, disabling connector"); + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetIdentities" }, + { "UserID", principalID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Identities"] is OSDArray) + { + bool md5hashFound = false; + + OSDArray identities = (OSDArray)response["Identities"]; + for (int i = 0; i < identities.Count; i++) + { + OSDMap identity = identities[i] as OSDMap; + if (identity != null) + { + if (identity["Type"].AsString() == "md5hash") + { + string authorizeResult; + if (CheckPassword(principalID, password, identity["Credential"].AsString(), out authorizeResult)) + return authorizeResult; + + md5hashFound = true; + break; + } + } + } + + if (!md5hashFound) + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Authentication failed for " + principalID + ", no md5hash identity found"); + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Failed to retrieve identities for " + principalID + ": " + + response["Message"].AsString()); + } + + return String.Empty; + } + + public bool Verify(UUID principalID, string token, int lifetime) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", token } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return true; + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Could not verify session for " + principalID + ": " + + response["Message"].AsString()); + } + + return false; + } + + public bool Release(UUID principalID, string token) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSession" }, + { "UserID", principalID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return true; + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Failed to remove session for " + principalID + ": " + + response["Message"].AsString()); + } + + return false; + } + + public bool SetPassword(UUID principalID, string passwd) + { + // Fetch the user name first + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", principalID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["User"] is OSDMap) + { + OSDMap userMap = (OSDMap)response["User"]; + string identifier = userMap["Name"].AsString(); + + if (!String.IsNullOrEmpty(identifier)) + { + // Add/update the md5hash identity + // TODO: Support salts when AddIdentity does + // TODO: Create an a1hash too for WebDAV logins + requestArgs = new NameValueCollection + { + { "RequestMethod", "AddIdentity" }, + { "Identifier", identifier }, + { "Credential", "$1$" + Utils.MD5String(passwd) }, + { "Type", "md5hash" }, + { "UserID", principalID.ToString() } + }; + + response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.WarnFormat("[SIMIAN AUTH CONNECTOR]: Failed to set password for {0} ({1})", identifier, principalID); + + return success; + } + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Failed to retrieve identities for " + principalID + ": " + + response["Message"].AsString()); + } + + return false; + } + + public AuthInfo GetAuthInfo(UUID principalID) + { + throw new NotImplementedException(); + } + + public bool SetAuthInfo(AuthInfo info) + { + throw new NotImplementedException(); + } + + private bool CheckPassword(UUID userID, string password, string simianGridCredential, out string authorizeResult) + { + if (simianGridCredential.Contains(":")) + { + // Salted version + int idx = simianGridCredential.IndexOf(':'); + string finalhash = simianGridCredential.Substring(0, idx); + string salt = simianGridCredential.Substring(idx + 1); + + if (finalhash == Utils.MD5String(password + ":" + salt)) + { + authorizeResult = Authorize(userID); + return true; + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Authentication failed for " + userID + + " using md5hash " + Utils.MD5String(password) + ":" + salt); + } + } + else + { + // Unsalted version + if (password == simianGridCredential || + "$1$" + password == simianGridCredential || + "$1$" + Utils.MD5String(password) == simianGridCredential || + Utils.MD5String(password) == simianGridCredential || + "$1$" + Utils.MD5String(password + ":") == simianGridCredential) + { + authorizeResult = Authorize(userID); + return true; + } + else + { + m_log.Warn("[SIMIAN AUTH CONNECTOR]: Authentication failed for " + userID + + " using md5hash $1$" + Utils.MD5String(password)); + } + } + + authorizeResult = null; + return false; + } + + private string Authorize(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddSession" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + return response["SessionID"].AsUUID().ToString(); + else + return String.Empty; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs new file mode 100644 index 0000000000..a3977407a4 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs @@ -0,0 +1,344 @@ +/* + * 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.Collections.Specialized; +// DEBUG ON +using System.Diagnostics; +// DEBUG OFF +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects avatar appearance data to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianAvatarServiceConnector")] + public class SimianAvatarServiceConnector : IAvatarService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); +// private static string ZeroID = UUID.Zero.ToString(); + + private string m_serverUrl = String.Empty; + private bool m_Enabled = false; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianAvatarServiceConnector() { } + public string Name { get { return "SimianAvatarServiceConnector"; } } + public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAvatarServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("AvatarServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["AvatarService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("AvatarServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + m_Enabled = true; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN AVATAR CONNECTOR]: No AvatarServerURI specified, disabling connector"); + } + + #region IAvatarService + + // + // Retrieves the LLPackedAppearance field from user data and unpacks + // it into an AvatarAppearance structure + // + // + public AvatarAppearance GetAppearance(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap map = null; + try { map = OSDParser.DeserializeJson(response["LLPackedAppearance"].AsString()) as OSDMap; } + catch { } + + if (map != null) + { + AvatarAppearance appearance = new AvatarAppearance(map); +// DEBUG ON + m_log.WarnFormat("[SIMIAN AVATAR CONNECTOR] retrieved appearance for {0}:\n{1}",userID,appearance.ToString()); +// DEBUG OFF + return appearance; + } + + m_log.WarnFormat("[SIMIAN AVATAR CONNECTOR]: Failed to decode appearance for {0}",userID); + return null; + } + + m_log.WarnFormat("[SIMIAN AVATAR CONNECTOR]: Failed to get appearance for {0}: {1}", + userID,response["Message"].AsString()); + return null; + } + + // + // + // + public bool SetAppearance(UUID userID, AvatarAppearance appearance) + { + OSDMap map = appearance.Pack(); + if (map == null) + { + m_log.WarnFormat("[SIMIAN AVATAR CONNECTOR]: Failed to encode appearance for {0}",userID); + return false; + } + + // m_log.DebugFormat("[SIMIAN AVATAR CONNECTOR] save appearance for {0}",userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LLPackedAppearance", OSDParser.SerializeJsonString(map) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (! success) + m_log.WarnFormat("[SIMIAN AVATAR CONNECTOR]: Failed to save appearance for {0}: {1}", + userID,response["Message"].AsString()); + + return success; + } + + // + // + // + public AvatarData GetAvatar(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap map = null; + try { map = OSDParser.DeserializeJson(response["LLAppearance"].AsString()) as OSDMap; } + catch { } + + if (map != null) + { + AvatarWearable[] wearables = new AvatarWearable[13]; + wearables[0] = new AvatarWearable(map["ShapeItem"].AsUUID(), map["ShapeAsset"].AsUUID()); + wearables[1] = new AvatarWearable(map["SkinItem"].AsUUID(), map["SkinAsset"].AsUUID()); + wearables[2] = new AvatarWearable(map["HairItem"].AsUUID(), map["HairAsset"].AsUUID()); + wearables[3] = new AvatarWearable(map["EyesItem"].AsUUID(), map["EyesAsset"].AsUUID()); + wearables[4] = new AvatarWearable(map["ShirtItem"].AsUUID(), map["ShirtAsset"].AsUUID()); + wearables[5] = new AvatarWearable(map["PantsItem"].AsUUID(), map["PantsAsset"].AsUUID()); + wearables[6] = new AvatarWearable(map["ShoesItem"].AsUUID(), map["ShoesAsset"].AsUUID()); + wearables[7] = new AvatarWearable(map["SocksItem"].AsUUID(), map["SocksAsset"].AsUUID()); + wearables[8] = new AvatarWearable(map["JacketItem"].AsUUID(), map["JacketAsset"].AsUUID()); + wearables[9] = new AvatarWearable(map["GlovesItem"].AsUUID(), map["GlovesAsset"].AsUUID()); + wearables[10] = new AvatarWearable(map["UndershirtItem"].AsUUID(), map["UndershirtAsset"].AsUUID()); + wearables[11] = new AvatarWearable(map["UnderpantsItem"].AsUUID(), map["UnderpantsAsset"].AsUUID()); + wearables[12] = new AvatarWearable(map["SkirtItem"].AsUUID(), map["SkirtAsset"].AsUUID()); + + AvatarAppearance appearance = new AvatarAppearance(); + appearance.Wearables = wearables; + appearance.AvatarHeight = (float)map["Height"].AsReal(); + + AvatarData avatar = new AvatarData(appearance); + + // Get attachments + map = null; + try { map = OSDParser.DeserializeJson(response["LLAttachments"].AsString()) as OSDMap; } + catch { } + + if (map != null) + { + foreach (KeyValuePair kvp in map) + avatar.Data[kvp.Key] = kvp.Value.AsString(); + } + + return avatar; + } + else + { + m_log.Warn("[SIMIAN AVATAR CONNECTOR]: Failed to get user appearance for " + userID + + ", LLAppearance is missing or invalid"); + return null; + } + } + else + { + m_log.Warn("[SIMIAN AVATAR CONNECTOR]: Failed to get user appearance for " + userID + ": " + + response["Message"].AsString()); + } + + return null; + } + + // + // + // + public bool SetAvatar(UUID userID, AvatarData avatar) + { + m_log.Debug("[SIMIAN AVATAR CONNECTOR]: SetAvatar called for " + userID); + + if (avatar.AvatarType == 1) // LLAvatar + { + AvatarAppearance appearance = avatar.ToAvatarAppearance(); + + OSDMap map = new OSDMap(); + + map["Height"] = OSD.FromReal(appearance.AvatarHeight); + + map["BodyItem"] = appearance.Wearables[AvatarWearable.BODY][0].ItemID.ToString(); + map["EyesItem"] = appearance.Wearables[AvatarWearable.EYES][0].ItemID.ToString(); + map["GlovesItem"] = appearance.Wearables[AvatarWearable.GLOVES][0].ItemID.ToString(); + map["HairItem"] = appearance.Wearables[AvatarWearable.HAIR][0].ItemID.ToString(); + map["JacketItem"] = appearance.Wearables[AvatarWearable.JACKET][0].ItemID.ToString(); + map["PantsItem"] = appearance.Wearables[AvatarWearable.PANTS][0].ItemID.ToString(); + map["ShirtItem"] = appearance.Wearables[AvatarWearable.SHIRT][0].ItemID.ToString(); + map["ShoesItem"] = appearance.Wearables[AvatarWearable.SHOES][0].ItemID.ToString(); + map["SkinItem"] = appearance.Wearables[AvatarWearable.SKIN][0].ItemID.ToString(); + map["SkirtItem"] = appearance.Wearables[AvatarWearable.SKIRT][0].ItemID.ToString(); + map["SocksItem"] = appearance.Wearables[AvatarWearable.SOCKS][0].ItemID.ToString(); + map["UnderPantsItem"] = appearance.Wearables[AvatarWearable.UNDERPANTS][0].ItemID.ToString(); + map["UnderShirtItem"] = appearance.Wearables[AvatarWearable.UNDERSHIRT][0].ItemID.ToString(); + map["BodyAsset"] = appearance.Wearables[AvatarWearable.BODY][0].AssetID.ToString(); + map["EyesAsset"] = appearance.Wearables[AvatarWearable.EYES][0].AssetID.ToString(); + map["GlovesAsset"] = appearance.Wearables[AvatarWearable.GLOVES][0].AssetID.ToString(); + map["HairAsset"] = appearance.Wearables[AvatarWearable.HAIR][0].AssetID.ToString(); + map["JacketAsset"] = appearance.Wearables[AvatarWearable.JACKET][0].AssetID.ToString(); + map["PantsAsset"] = appearance.Wearables[AvatarWearable.PANTS][0].AssetID.ToString(); + map["ShirtAsset"] = appearance.Wearables[AvatarWearable.SHIRT][0].AssetID.ToString(); + map["ShoesAsset"] = appearance.Wearables[AvatarWearable.SHOES][0].AssetID.ToString(); + map["SkinAsset"] = appearance.Wearables[AvatarWearable.SKIN][0].AssetID.ToString(); + map["SkirtAsset"] = appearance.Wearables[AvatarWearable.SKIRT][0].AssetID.ToString(); + map["SocksAsset"] = appearance.Wearables[AvatarWearable.SOCKS][0].AssetID.ToString(); + map["UnderPantsAsset"] = appearance.Wearables[AvatarWearable.UNDERPANTS][0].AssetID.ToString(); + map["UnderShirtAsset"] = appearance.Wearables[AvatarWearable.UNDERSHIRT][0].AssetID.ToString(); + + + OSDMap items = new OSDMap(); + foreach (KeyValuePair kvp in avatar.Data) + { + if (kvp.Key.StartsWith("_ap_")) + items.Add(kvp.Key, OSD.FromString(kvp.Value)); + } + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LLAppearance", OSDParser.SerializeJsonString(map) }, + { "LLAttachments", OSDParser.SerializeJsonString(items) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN AVATAR CONNECTOR]: Failed saving appearance for " + userID + ": " + response["Message"].AsString()); + + return success; + } + else + { + m_log.Error("[SIMIAN AVATAR CONNECTOR]: Can't save appearance for " + userID + ". Unhandled avatar type " + avatar.AvatarType); + return false; + } + } + + public bool ResetAvatar(UUID userID) + { + m_log.Error("[SIMIAN AVATAR CONNECTOR]: ResetAvatar called for " + userID + ", implement this"); + return false; + } + + public bool SetItems(UUID userID, string[] names, string[] values) + { + m_log.Error("[SIMIAN AVATAR CONNECTOR]: SetItems called for " + userID + " with " + names.Length + " names and " + values.Length + " values, implement this"); + return false; + } + + public bool RemoveItems(UUID userID, string[] names) + { + m_log.Error("[SIMIAN AVATAR CONNECTOR]: RemoveItems called for " + userID + " with " + names.Length + " names, implement this"); + return false; + } + + #endregion IAvatarService + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianExternalCapsModule.cs b/OpenSim/Services/Connectors/SimianGrid/SimianExternalCapsModule.cs new file mode 100644 index 0000000000..764e71fc31 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianExternalCapsModule.cs @@ -0,0 +1,180 @@ +/* + * 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 System.IO; +using System.Web; + +using log4net; +using Nini.Config; +using Mono.Addins; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using Caps = OpenSim.Framework.Capabilities.Caps; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianExternalCapsModule")] + public class SimianExternalCapsModule : INonSharedRegionModule, IExternalCapsModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_enabled = true; + private Scene m_scene; + private String m_simianURL; + +#region IRegionModule Members + + public string Name + { + get { return this.GetType().Name; } + } + + public void Initialise(IConfigSource config) + { + try + { + IConfig m_config; + + if ((m_config = config.Configs["SimianExternalCaps"]) != null) + { + m_enabled = m_config.GetBoolean("Enabled", m_enabled); + if ((m_config = config.Configs["SimianGrid"]) != null) + { + m_simianURL = m_config.GetString("SimianServiceURL"); + if (String.IsNullOrEmpty(m_simianURL)) + { + //m_log.DebugFormat("[SimianGrid] service URL is not defined"); + m_enabled = false; + return; + } + } + } + else + m_enabled = false; + } + catch (Exception e) + { + m_log.ErrorFormat("[SimianExternalCaps] initialization error: {0}",e.Message); + return; + } + } + + public void PostInitialise() { } + public void Close() { } + + public void AddRegion(Scene scene) + { + if (! m_enabled) + return; + + m_scene = scene; + m_scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + if (! m_enabled) + return; + + m_scene.EventManager.OnRegisterCaps -= RegisterCapsEventHandler; + m_scene.EventManager.OnDeregisterCaps -= DeregisterCapsEventHandler; + } + + public void RegionLoaded(Scene scene) + { + if (! m_enabled) + return; + + m_scene.EventManager.OnRegisterCaps += RegisterCapsEventHandler; + m_scene.EventManager.OnDeregisterCaps += DeregisterCapsEventHandler; + } + + public Type ReplaceableInterface + { + get { return null; } + } + +#endregion + +#region IExternalCapsModule + // Eg http://grid.sciencesim.com/GridPublic/%CAP%/%OP%/" + public bool RegisterExternalUserCapsHandler(UUID agentID, Caps caps, String capName, String urlSkel) + { + UUID cap = UUID.Random(); + + // Call to simian to register the cap we generated + // NameValueCollection requestArgs = new NameValueCollection + // { + // { "RequestMethod", "AddCapability" }, + // { "Resource", "user" }, + // { "Expiration", 0 }, + // { "OwnerID", agentID.ToString() }, + // { "CapabilityID", cap.ToString() } + // }; + + // OSDMap response = SimianGrid.PostToService(m_simianURL, requestArgs); + + Dictionary subs = new Dictionary(); + subs["%OP%"] = capName; + subs["%USR%"] = agentID.ToString(); + subs["%CAP%"] = cap.ToString(); + subs["%SIM%"] = m_scene.RegionInfo.RegionID.ToString(); + + caps.RegisterHandler(capName,ExpandSkeletonURL(urlSkel,subs)); + return true; + } + +#endregion + +#region EventHandlers + public void RegisterCapsEventHandler(UUID agentID, Caps caps) { } + public void DeregisterCapsEventHandler(UUID agentID, Caps caps) { } +#endregion + + private String ExpandSkeletonURL(String urlSkel, Dictionary subs) + { + String result = urlSkel; + + foreach (KeyValuePair kvp in subs) + { + result = result.Replace(kvp.Key,kvp.Value); + } + + return result; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs new file mode 100644 index 0000000000..9a8164c5bf --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs @@ -0,0 +1,236 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Stores and retrieves friend lists from the SimianGrid backend + /// + public class SimianFriendsServiceConnector : IFriendsService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + public SimianFriendsServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + IConfig gridConfig = source.Configs["FriendsService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("FriendsServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN FRIENDS CONNECTOR]: No FriendsServerURI specified, disabling connector"); + } + + #region IFriendsService + + public FriendInfo[] GetFriends(UUID principalID) + { + return GetFriends(principalID.ToString()); + } + + public FriendInfo[] GetFriends(string principalID) + { + if (String.IsNullOrEmpty(m_serverUrl)) + return new FriendInfo[0]; + + Dictionary friends = new Dictionary(); + + OSDArray friendsArray = GetFriended(principalID); + OSDArray friendedMeArray = GetFriendedBy(principalID); + + // Load the list of friends and their granted permissions + for (int i = 0; i < friendsArray.Count; i++) + { + OSDMap friendEntry = friendsArray[i] as OSDMap; + if (friendEntry != null) + { + UUID friendID = friendEntry["Key"].AsUUID(); + + FriendInfo friend = new FriendInfo(); + if (!UUID.TryParse(principalID, out friend.PrincipalID)) + { + string tmp = string.Empty; + if (!Util.ParseUniversalUserIdentifier(principalID, out friend.PrincipalID, out tmp, out tmp, out tmp, out tmp)) + // bad record. ignore this entry + continue; + } + + friend.Friend = friendID.ToString(); + friend.MyFlags = friendEntry["Value"].AsInteger(); + friend.TheirFlags = -1; + + friends[friendID] = friend; + } + } + + // Load the permissions those friends have granted to this user + for (int i = 0; i < friendedMeArray.Count; i++) + { + OSDMap friendedMeEntry = friendedMeArray[i] as OSDMap; + if (friendedMeEntry != null) + { + UUID friendID = friendedMeEntry["OwnerID"].AsUUID(); + + FriendInfo friend; + if (friends.TryGetValue(friendID, out friend)) + friend.TheirFlags = friendedMeEntry["Value"].AsInteger(); + } + } + + // Convert the dictionary of friends to an array and return it + FriendInfo[] array = new FriendInfo[friends.Count]; + int j = 0; + foreach (FriendInfo friend in friends.Values) + array[j++] = friend; + + return array; + } + + public bool StoreFriend(string principalID, string friend, int flags) + { + if (String.IsNullOrEmpty(m_serverUrl)) + return true; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddGeneric" }, + { "OwnerID", principalID.ToString() }, + { "Type", "Friend" }, + { "Key", friend }, + { "Value", flags.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Error("[SIMIAN FRIENDS CONNECTOR]: Failed to store friend " + friend + " for user " + principalID + ": " + response["Message"].AsString()); + + return success; + } + + public bool Delete(UUID principalID, string friend) + { + return Delete(principalID.ToString(), friend); + } + + public bool Delete(string principalID, string friend) + { + if (String.IsNullOrEmpty(m_serverUrl)) + return true; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveGeneric" }, + { "OwnerID", principalID.ToString() }, + { "Type", "Friend" }, + { "Key", friend } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Error("[SIMIAN FRIENDS CONNECTOR]: Failed to remove friend " + friend + " for user " + principalID + ": " + response["Message"].AsString()); + + return success; + } + + #endregion IFriendsService + + private OSDArray GetFriended(string ownerID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetGenerics" }, + { "OwnerID", ownerID.ToString() }, + { "Type", "Friend" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) + { + return (OSDArray)response["Entries"]; + } + else + { + m_log.Warn("[SIMIAN FRIENDS CONNECTOR]: Failed to retrieve friends for user " + ownerID + ": " + response["Message"].AsString()); + return new OSDArray(0); + } + } + + private OSDArray GetFriendedBy(string ownerID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetGenerics" }, + { "Key", ownerID.ToString() }, + { "Type", "Friend" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) + { + return (OSDArray)response["Entries"]; + } + else + { + m_log.Warn("[SIMIAN FRIENDS CONNECTOR]: Failed to retrieve reverse friends for user " + ownerID + ": " + response["Message"].AsString()); + return new OSDArray(0); + } + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs new file mode 100644 index 0000000000..a35d749b3c --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs @@ -0,0 +1,147 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +[assembly: Addin("SimianGrid", OpenSim.VersionInfo.VersionNumber)] +[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)] + +namespace OpenSim.Services.Connectors.SimianGrid +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianExternalCapsModule")] + public class SimianGrid : ISharedRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IConfig m_config = null; + + private String m_simianURL; + +#region IRegionModule Members + + public string Name + { + get { return this.GetType().Name; } + } + + public void Initialise(IConfigSource config) + { + try + { + m_config = config.Configs["SimianGrid"]; + + if (m_config != null) + { + m_simianURL = m_config.GetString("SimianServiceURL"); + if (String.IsNullOrEmpty(m_simianURL)) + { + // m_log.DebugFormat("[SimianGrid] service URL is not defined"); + return; + } + + InitialiseSimCap(); + SimulatorCapability = SimulatorCapability.Trim(); + m_log.InfoFormat("[SimianExternalCaps] using {0} as simulator capability",SimulatorCapability); + } + } + catch (Exception e) + { + m_log.ErrorFormat("[SimianExternalCaps] initialization error: {0}",e.Message); + return; + } + } + + public void PostInitialise() { } + public void Close() { } + public void AddRegion(Scene scene) { } + public void RemoveRegion(Scene scene) { } + public void RegionLoaded(Scene scene) { } + + public Type ReplaceableInterface + { + get { return null; } + } + + /// + /// Try a variety of methods for finding the simian simulator capability; first check the + /// configuration itself, then look for a file that contains the cap, then finally look + /// for an environment variable that contains it. + /// + private void InitialiseSimCap() + { + if (m_config.Contains("SimulatorCapability")) + { + SimulatorCapability = m_config.GetString("SimulatorCapability"); + return; + } + + if (m_config.Contains("SimulatorCapabilityFile")) + { + String filename = m_config.GetString("SimulatorCapabilityFile"); + if (System.IO.File.Exists(filename)) + { + SimulatorCapability = System.IO.File.ReadAllText(filename); + return; + } + } + + if (m_config.Contains("SimulatorCapabilityVariable")) + { + String envname = m_config.GetString("SimulatorCapabilityVariable"); + String envvalue = System.Environment.GetEnvironmentVariable(envname); + if (envvalue != null) + { + SimulatorCapability = envvalue; + return; + } + } + + m_log.WarnFormat("[SimianExternalCaps] no method specified for simulator capability"); + } + +#endregion + + public static String SimulatorCapability = UUID.Zero.ToString(); + public static OSDMap PostToService(string url, NameValueCollection data) + { + data["cap"] = SimulatorCapability; + return WebUtil.PostToService(url, data); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridMaptileModule.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridMaptileModule.cs new file mode 100644 index 0000000000..8375c9514c --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridMaptileModule.cs @@ -0,0 +1,266 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using System.Net; +using System.IO; +using System.Timers; +using System.Drawing; +using System.Drawing.Imaging; + +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +//namespace OpenSim.Region.OptionalModules.Simian +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// + /// + /// + + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianGridMaptile")] + public class SimianGridMaptile : ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private bool m_enabled = false; + private string m_serverUrl = String.Empty; + private Dictionary m_scenes = new Dictionary(); + + private int m_refreshtime = 0; + private int m_lastrefresh = 0; + private System.Timers.Timer m_refreshTimer = new System.Timers.Timer(); + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public string Name { get { return "SimianGridMaptile"; } } + public void RegionLoaded(Scene scene) { } + public void Close() { } + + /// + /// + /// + public void Initialise(IConfigSource source) + { + IConfig config = source.Configs["SimianGridMaptiles"]; + if (config == null) + return; + + if (! config.GetBoolean("Enabled", false)) + return; + + m_serverUrl = config.GetString("MaptileURL"); + if (String.IsNullOrEmpty(m_serverUrl)) + return; + + int refreshseconds = Convert.ToInt32(config.GetString("RefreshTime")); + if (refreshseconds <= 0) + return; + + m_refreshtime = refreshseconds * 1000; // convert from seconds to ms + m_log.InfoFormat("[SIMIAN MAPTILE] enabled with refresh timeout {0} and URL {1}", + m_refreshtime,m_serverUrl); + + m_enabled = true; + } + + /// + /// + /// + public void PostInitialise() + { + if (m_enabled) + { + m_refreshTimer.Enabled = true; + m_refreshTimer.AutoReset = true; + m_refreshTimer.Interval = 5 * 60 * 1000; // every 5 minutes + m_refreshTimer.Elapsed += new ElapsedEventHandler(HandleMaptileRefresh); + } + } + + + /// + /// + /// + public void AddRegion(Scene scene) + { + if (! m_enabled) + return; + + // Every shared region module has to maintain an indepedent list of + // currently running regions + lock (m_scenes) + m_scenes[scene.RegionInfo.RegionID] = scene; + } + + /// + /// + /// + public void RemoveRegion(Scene scene) + { + if (! m_enabled) + return; + + lock (m_scenes) + m_scenes.Remove(scene.RegionInfo.RegionID); + } + + #endregion ISharedRegionModule + + /// + /// + /// + private void HandleMaptileRefresh(object sender, EventArgs ea) + { + // this approach is a bit convoluted becase we want to wait for the + // first upload to happen on startup but after all the objects are + // loaded and initialized + if (m_lastrefresh > 0 && Util.EnvironmentTickCountSubtract(m_lastrefresh) < m_refreshtime) + return; + + m_log.DebugFormat("[SIMIAN MAPTILE] map refresh fired"); + lock (m_scenes) + { + foreach (IScene scene in m_scenes.Values) + { + try + { + UploadMapTile(scene); + } + catch (Exception ex) + { + m_log.WarnFormat("[SIMIAN MAPTILE] something bad happened {0}",ex.Message); + } + } + } + + m_lastrefresh = Util.EnvironmentTickCount(); + } + + /// + /// + /// + private void UploadMapTile(IScene scene) + { + m_log.DebugFormat("[SIMIAN MAPTILE]: upload maptile for {0}",scene.RegionInfo.RegionName); + + // Create a PNG map tile and upload it to the AddMapTile API + IMapImageGenerator tileGenerator = scene.RequestModuleInterface(); + if (tileGenerator == null) + { + m_log.Warn("[SIMIAN MAPTILE]: Cannot upload PNG map tile without an ImageGenerator"); + return; + } + + using (Bitmap mapTile = tileGenerator.CreateMapTile()) + { + if (mapTile != null) + { + // If the region/maptile is legacy sized, just upload the one tile like it has always been done + if (mapTile.Width == Constants.RegionSize && mapTile.Height == Constants.RegionSize) + { + ConvertAndUploadMaptile(mapTile, scene.RegionInfo.RegionLocX, scene.RegionInfo.RegionLocY); + } + else + { + // For larger regions (varregion) we must cut the region image into legacy sized + // pieces since that is how the maptile system works. + // Note the assumption that varregions are always a multiple of legacy size. + for (uint xx = 0; xx < mapTile.Width; xx += Constants.RegionSize) + { + for (uint yy = 0; yy < mapTile.Height; yy += Constants.RegionSize) + { + // Images are addressed from the upper left corner so have to do funny + // math to pick out the sub-tile since regions are numbered from + // the lower left. + Rectangle rect = new Rectangle( + (int)xx, + mapTile.Height - (int)yy - (int)Constants.RegionSize, + (int)Constants.RegionSize, (int)Constants.RegionSize); + + using (Bitmap subMapTile = mapTile.Clone(rect, mapTile.PixelFormat)) + { + uint locX = scene.RegionInfo.RegionLocX + (xx / Constants.RegionSize); + uint locY = scene.RegionInfo.RegionLocY + (yy / Constants.RegionSize); + + ConvertAndUploadMaptile(subMapTile, locX, locY); + } + } + } + } + } + else + { + m_log.WarnFormat("[SIMIAN MAPTILE] Tile image generation failed"); + } + } + + } + + /// + /// + /// + private void ConvertAndUploadMaptile(Image mapTile, uint locX, uint locY) + { + //m_log.DebugFormat("[SIMIAN MAPTILE]: upload maptile for location {0}, {1}", locX, locY); + + byte[] pngData = Utils.EmptyBytes; + using (MemoryStream stream = new MemoryStream()) + { + mapTile.Save(stream, ImageFormat.Png); + pngData = stream.ToArray(); + } + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "xAddMapTile" }, + { "X", locX.ToString() }, + { "Y", locY.ToString() }, + { "ContentType", "image/png" }, + { "EncodedData", System.Convert.ToBase64String(pngData) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl,requestArgs); + if (! response["Success"].AsBoolean()) + { + m_log.WarnFormat("[SIMIAN MAPTILE] failed to store map tile; {0}",response["Message"].AsString()); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs new file mode 100644 index 0000000000..b031f217f3 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs @@ -0,0 +1,484 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects region registration and neighbor lookups to the SimianGrid + /// backend + /// + public class SimianGridServiceConnector : IGridService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; +// private bool m_Enabled = false; + + public SimianGridServiceConnector() { } + public SimianGridServiceConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public SimianGridServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public void Initialise(IConfigSource source) + { + CommonInit(source); + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["GridService"]; + if (gridConfig == null) + { + m_log.Error("[SIMIAN GRID CONNECTOR]: GridService missing from OpenSim.ini"); + throw new Exception("Grid connector init error"); + } + + string serviceUrl = gridConfig.GetString("GridServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[SIMIAN GRID CONNECTOR]: No Server URI named in section GridService"); + throw new Exception("Grid connector init error"); + } + + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_ServerURI = serviceUrl; +// m_Enabled = true; + } + + #region IGridService + + public string RegisterRegion(UUID scopeID, GridRegion regionInfo) + { + Vector3d minPosition = new Vector3d(regionInfo.RegionLocX, regionInfo.RegionLocY, 0.0); + Vector3d maxPosition = minPosition + new Vector3d(regionInfo.RegionSizeX, regionInfo.RegionSizeY, Constants.RegionHeight); + + OSDMap extraData = new OSDMap + { + { "ServerURI", OSD.FromString(regionInfo.ServerURI) }, + { "InternalAddress", OSD.FromString(regionInfo.InternalEndPoint.Address.ToString()) }, + { "InternalPort", OSD.FromInteger(regionInfo.InternalEndPoint.Port) }, + { "ExternalAddress", OSD.FromString(regionInfo.ExternalEndPoint.Address.ToString()) }, + { "ExternalPort", OSD.FromInteger(regionInfo.ExternalEndPoint.Port) }, + { "MapTexture", OSD.FromUUID(regionInfo.TerrainImage) }, + { "Access", OSD.FromInteger(regionInfo.Access) }, + { "RegionSecret", OSD.FromString(regionInfo.RegionSecret) }, + { "EstateOwner", OSD.FromUUID(regionInfo.EstateOwner) }, + { "Token", OSD.FromString(regionInfo.Token) } + }; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddScene" }, + { "SceneID", regionInfo.RegionID.ToString() }, + { "Name", regionInfo.RegionName }, + { "MinPosition", minPosition.ToString() }, + { "MaxPosition", maxPosition.ToString() }, + { "Address", regionInfo.ServerURI }, + { "Enabled", "1" }, + { "ExtraData", OSDParser.SerializeJsonString(extraData) } + }; + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + return String.Empty; + else + return "Region registration for " + regionInfo.RegionName + " failed: " + response["Message"].AsString(); + } + + public bool DeregisterRegion(UUID regionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddScene" }, + { "SceneID", regionID.ToString() }, + { "Enabled", "0" } + }; + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN GRID CONNECTOR]: Region deregistration for " + regionID + " failed: " + response["Message"].AsString()); + + return success; + } + + public List GetNeighbours(UUID scopeID, UUID regionID) + { + GridRegion region = GetRegionByUUID(scopeID, regionID); + + int NEIGHBOR_RADIUS = Math.Max(region.RegionSizeX, region.RegionSizeY) / 2; + + if (region != null) + { + List regions = GetRegionRange(scopeID, + region.RegionLocX - NEIGHBOR_RADIUS, region.RegionLocX + region.RegionSizeX + NEIGHBOR_RADIUS, + region.RegionLocY - NEIGHBOR_RADIUS, region.RegionLocY + region.RegionSizeY + NEIGHBOR_RADIUS); + + for (int i = 0; i < regions.Count; i++) + { + if (regions[i].RegionID == regionID) + { + regions.RemoveAt(i); + break; + } + } + +// m_log.Debug("[SIMIAN GRID CONNECTOR]: Found " + regions.Count + " neighbors for region " + regionID); + return regions; + } + + return new List(0); + } + + public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "SceneID", regionID.ToString() } + }; + + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] request region with uuid {0}",regionID.ToString()); + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] uuid request successful {0}",response["Name"].AsString()); + return ResponseToGridRegion(response); + } + else + { + m_log.Warn("[SIMIAN GRID CONNECTOR]: Grid service did not find a match for region " + regionID); + return null; + } + } + + public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) + { + // Go one meter in from the requested x/y coords to avoid requesting a position + // that falls on the border of two sims + Vector3d position = new Vector3d(x + 1, y + 1, 0.0); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "Position", position.ToString() }, + { "Enabled", "1" } + }; + + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] request grid at {0}",position.ToString()); + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] position request successful {0}",response["Name"].AsString()); + return ResponseToGridRegion(response); + } + else + { + // m_log.InfoFormat("[SIMIAN GRID CONNECTOR]: Grid service did not find a match for region at {0},{1}", + // Util.WorldToRegionLoc(x), Util.WorldToRegionLoc(y)); + return null; + } + } + + public GridRegion GetRegionByName(UUID scopeID, string regionName) + { + List regions = GetRegionsByName(scopeID, regionName, 1); + + m_log.Debug("[SIMIAN GRID CONNECTOR]: Got " + regions.Count + " matches for region name " + regionName); + + if (regions.Count > 0) + return regions[0]; + + return null; + } + + public List GetRegionsByName(UUID scopeID, string name, int maxNumber) + { + List foundRegions = new List(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScenes" }, + { "NameQuery", name }, + { "Enabled", "1" } + }; + if (maxNumber > 0) + requestArgs["MaxNumber"] = maxNumber.ToString(); + + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] request regions with name {0}",name); + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] found regions with name {0}",name); + + OSDArray array = response["Scenes"] as OSDArray; + if (array != null) + { + for (int i = 0; i < array.Count; i++) + { + GridRegion region = ResponseToGridRegion(array[i] as OSDMap); + if (region != null) + foundRegions.Add(region); + } + } + } + + return foundRegions; + } + + public List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax) + { + List foundRegions = new List(); + + Vector3d minPosition = new Vector3d(xmin, ymin, 0.0); + Vector3d maxPosition = new Vector3d(xmax, ymax, Constants.RegionHeight); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScenes" }, + { "MinPosition", minPosition.ToString() }, + { "MaxPosition", maxPosition.ToString() }, + { "Enabled", "1" } + }; + + //m_log.DebugFormat("[SIMIAN GRID CONNECTOR] request regions by range {0} to {1}",minPosition.ToString(),maxPosition.ToString()); + + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDArray array = response["Scenes"] as OSDArray; + if (array != null) + { + for (int i = 0; i < array.Count; i++) + { + GridRegion region = ResponseToGridRegion(array[i] as OSDMap); + if (region != null) + foundRegions.Add(region); + } + } + } + + return foundRegions; + } + + public List GetDefaultRegions(UUID scopeID) + { + // TODO: Allow specifying the default grid location + const int DEFAULT_X = 1000 * 256; + const int DEFAULT_Y = 1000 * 256; + + GridRegion defRegion = GetNearestRegion(new Vector3d(DEFAULT_X, DEFAULT_Y, 0.0), true); + if (defRegion != null) + return new List(1) { defRegion }; + else + return new List(0); + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + // TODO: Allow specifying the default grid location + return GetDefaultRegions(scopeID); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + GridRegion defRegion = GetNearestRegion(new Vector3d(x, y, 0.0), true); + if (defRegion != null) + return new List(1) { defRegion }; + else + return new List(0); + } + + public List GetHyperlinks(UUID scopeID) + { + List foundRegions = new List(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScenes" }, + { "HyperGrid", "true" }, + { "Enabled", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + // m_log.DebugFormat("[SIMIAN GRID CONNECTOR] found regions with name {0}",name); + + OSDArray array = response["Scenes"] as OSDArray; + if (array != null) + { + for (int i = 0; i < array.Count; i++) + { + GridRegion region = ResponseToGridRegion(array[i] as OSDMap); + if (region != null) + foundRegions.Add(region); + } + } + } + + return foundRegions; + } + + public int GetRegionFlags(UUID scopeID, UUID regionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "SceneID", regionID.ToString() } + }; + + m_log.DebugFormat("[SIMIAN GRID CONNECTOR] request region flags for {0}",regionID.ToString()); + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap extraData = response["ExtraData"] as OSDMap; + int enabled = response["Enabled"].AsBoolean() ? (int)OpenSim.Framework.RegionFlags.RegionOnline : 0; + int hypergrid = extraData["HyperGrid"].AsBoolean() ? (int)OpenSim.Framework.RegionFlags.Hyperlink : 0; + int flags = enabled | hypergrid; + m_log.DebugFormat("[SGGC] enabled - {0} hg - {1} flags - {2}", enabled, hypergrid, flags); + return flags; + } + else + { + m_log.Warn("[SIMIAN GRID CONNECTOR]: Grid service did not find a match for region " + regionID + " during region flags check"); + return -1; + } + } + + public Dictionary GetExtraFeatures() + { + /// See SimulatorFeaturesModule - Need to get map, search and destination guide + Dictionary extraFeatures = new Dictionary(); + return extraFeatures; + } + + #endregion IGridService + + private GridRegion GetNearestRegion(Vector3d position, bool onlyEnabled) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "Position", position.ToString() }, + { "FindClosest", "1" } + }; + if (onlyEnabled) + requestArgs["Enabled"] = "1"; + + OSDMap response = SimianGrid.PostToService(m_ServerURI, requestArgs); + if (response["Success"].AsBoolean()) + { + return ResponseToGridRegion(response); + } + else + { + m_log.Warn("[SIMIAN GRID CONNECTOR]: Grid service did not find a match for region at " + position); + return null; + } + } + + private GridRegion ResponseToGridRegion(OSDMap response) + { + if (response == null) + return null; + + OSDMap extraData = response["ExtraData"] as OSDMap; + if (extraData == null) + return null; + + GridRegion region = new GridRegion(); + + region.RegionID = response["SceneID"].AsUUID(); + region.RegionName = response["Name"].AsString(); + + Vector3d minPosition = response["MinPosition"].AsVector3d(); + Vector3d maxPosition = response["MaxPosition"].AsVector3d(); + region.RegionLocX = (int)minPosition.X; + region.RegionLocY = (int)minPosition.Y; + + region.RegionSizeX = (int)maxPosition.X - (int)minPosition.X; + region.RegionSizeY = (int)maxPosition.Y - (int)minPosition.Y; + + if ( ! extraData["HyperGrid"] ) { + Uri httpAddress = response["Address"].AsUri(); + region.ExternalHostName = httpAddress.Host; + region.HttpPort = (uint)httpAddress.Port; + + IPAddress internalAddress; + IPAddress.TryParse(extraData["InternalAddress"].AsString(), out internalAddress); + if (internalAddress == null) + internalAddress = IPAddress.Any; + + region.InternalEndPoint = new IPEndPoint(internalAddress, extraData["InternalPort"].AsInteger()); + region.TerrainImage = extraData["MapTexture"].AsUUID(); + region.Access = (byte)extraData["Access"].AsInteger(); + region.RegionSecret = extraData["RegionSecret"].AsString(); + region.EstateOwner = extraData["EstateOwner"].AsUUID(); + region.Token = extraData["Token"].AsString(); + region.ServerURI = extraData["ServerURI"].AsString(); + } else { + region.ServerURI = response["Address"]; + } + + return region; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs new file mode 100644 index 0000000000..e793420e2e --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs @@ -0,0 +1,941 @@ +/* + * 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.Collections.Specialized; +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 PermissionMask = OpenSim.Framework.PermissionMask; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Permissions bitflags + /// + /* + [Flags] + public enum PermissionMask : uint + { + None = 0, + Transfer = 1 << 13, + Modify = 1 << 14, + Copy = 1 << 15, + Move = 1 << 19, + Damage = 1 << 20, + All = 0x7FFFFFFF + } + */ + + /// + /// Connects avatar inventories to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianInventoryServiceConnector")] + public class SimianInventoryServiceConnector : IInventoryService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private string m_userServerUrl = String.Empty; +// private object m_gestureSyncRoot = new object(); + private bool m_Enabled = false; + + private const double CACHE_EXPIRATION_SECONDS = 20.0; + private static ExpiringCache m_ItemCache; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianInventoryServiceConnector() { } + public string Name { get { return "SimianInventoryServiceConnector"; } } + public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianInventoryServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public SimianInventoryServiceConnector(string url) + { + if (!url.EndsWith("/") && !url.EndsWith("=")) + url = url + '/'; + m_serverUrl = url; + + if (m_ItemCache == null) + m_ItemCache = new ExpiringCache(); + + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("InventoryServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["InventoryService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("InventoryServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + + gridConfig = source.Configs["UserAccountService"]; + if (gridConfig != null) + { + serviceUrl = gridConfig.GetString("UserAccountServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + m_userServerUrl = serviceUrl; + m_Enabled = true; + if (m_ItemCache == null) + m_ItemCache = new ExpiringCache(); + } + } + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN INVENTORY CONNECTOR]: No InventoryServerURI specified, disabling connector"); + else if (String.IsNullOrEmpty(m_userServerUrl)) + m_log.Info("[SIMIAN INVENTORY CONNECTOR]: No UserAccountServerURI specified, disabling connector"); + } + + /// + /// Create the entire inventory for a given user + /// + /// + /// + public bool CreateUserInventory(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventory" }, + { "OwnerID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Inventory creation for " + userID + " failed: " + response["Message"].AsString()); + + return success; + } + + /// + /// Gets the skeleton of the inventory -- folders only + /// + /// + /// + public List GetInventorySkeleton(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", userID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "0" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + return GetFoldersFromResponse(items, userID, true); + } + else + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to retrieve inventory skeleton for " + userID + ": " + + response["Message"].AsString()); + return new List(0); + } + } + + /// + /// Retrieve the root inventory folder for the given user. + /// + /// + /// null if no root folder was found + public InventoryFolderBase GetRootFolder(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", userID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + List folders = GetFoldersFromResponse(items, userID, true); + + if (folders.Count > 0) + return folders[0]; + } + + return null; + } + + /// + /// Gets the user folder for the given folder-type + /// + /// + /// + /// + public InventoryFolderBase GetFolderForType(UUID userID, FolderType type) + { + string contentType = SLUtil.SLAssetTypeToContentType((int)type); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetFolderForType" }, + { "ContentType", contentType }, + { "OwnerID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Folder"] is OSDMap) + { + OSDMap folder = (OSDMap)response["Folder"]; + + return new InventoryFolderBase( + folder["ID"].AsUUID(), + folder["Name"].AsString(), + folder["OwnerID"].AsUUID(), + (short)SLUtil.ContentTypeToSLAssetType(folder["ContentType"].AsString()), + folder["ParentID"].AsUUID(), + (ushort)folder["Version"].AsInteger() + ); + } + else + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Default folder not found for content type " + contentType + ": " + response["Message"].AsString()); + return GetRootFolder(userID); + } + } + + /// + /// Get an item, given by its UUID + /// + /// + /// + public InventoryItemBase GetItem(InventoryItemBase item) + { + InventoryItemBase retrieved = null; + if (m_ItemCache.TryGetValue(item.ID, out retrieved)) + return retrieved; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", item.ID.ToString() }, + { "OwnerID", item.Owner.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + List items = GetItemsFromResponse((OSDArray)response["Items"]); + if (items.Count > 0) + { + // The requested item should be the first in this list, but loop through + // and sanity check just in case + for (int i = 0; i < items.Count; i++) + { + if (items[i].ID == item.ID) + { + retrieved = items[i]; + m_ItemCache.AddOrUpdate(item.ID, retrieved, CACHE_EXPIRATION_SECONDS); + return retrieved; + } + } + } + } + + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Item " + item.ID + " owned by " + item.Owner + " not found"); + return null; + } + + public InventoryItemBase[] GetMultipleItems(UUID principalID, UUID[] itemIDs) + { + InventoryItemBase[] result = new InventoryItemBase[itemIDs.Length]; + int i = 0; + InventoryItemBase item = new InventoryItemBase(); + item.Owner = principalID; + foreach (UUID id in itemIDs) + { + item.ID = id; + result[i++] = GetItem(item); + } + + return result; + } + + /// + /// Get a folder, given by its UUID + /// + /// + /// + public InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folder.ID.ToString() }, + { "OwnerID", folder.Owner.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + List folders = GetFoldersFromResponse(items, folder.ID, true); + + if (folders.Count > 0) + return folders[0]; + } + + return null; + } + + /// + /// Gets everything (folders and items) inside a folder + /// + /// + /// + /// + public InventoryCollection GetFolderContent(UUID userID, UUID folderID) + { + InventoryCollection inventory = new InventoryCollection(); + inventory.OwnerID = userID; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folderID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + + inventory.Folders = GetFoldersFromResponse(items, folderID, false); + inventory.Items = GetItemsFromResponse(items); + } + else + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error fetching folder " + folderID + " content for " + userID + ": " + + response["Message"].AsString()); + inventory.Folders = new List(0); + inventory.Items = new List(0); + } + + return inventory; + } + + public virtual InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderIDs) + { + InventoryCollection[] invColl = new InventoryCollection[folderIDs.Length]; + int i = 0; + foreach (UUID fid in folderIDs) + { + invColl[i++] = GetFolderContent(principalID, fid); + } + + return invColl; + } + + /// + /// Gets the items inside a folder + /// + /// + /// + /// + public List GetFolderItems(UUID userID, UUID folderID) + { + InventoryCollection inventory = new InventoryCollection(); + inventory.OwnerID = userID; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folderID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "0" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + return GetItemsFromResponse(items); + } + else + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error fetching folder " + folderID + " for " + userID + ": " + + response["Message"].AsString()); + return new List(0); + } + } + + /// + /// Add a new folder to the user's inventory + /// + /// + /// true if the folder was successfully added + public bool AddFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventoryFolder" }, + { "FolderID", folder.ID.ToString() }, + { "ParentID", folder.ParentID.ToString() }, + { "OwnerID", folder.Owner.ToString() }, + { "Name", folder.Name }, + { "ContentType", SLUtil.SLAssetTypeToContentType(folder.Type) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error creating folder " + folder.Name + " for " + folder.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Update a folder in the user's inventory + /// + /// + /// true if the folder was successfully updated + public bool UpdateFolder(InventoryFolderBase folder) + { + return AddFolder(folder); + } + + /// + /// Move an inventory folder to a new location + /// + /// A folder containing the details of the new location + /// true if the folder was successfully moved + public bool MoveFolder(InventoryFolderBase folder) + { + return AddFolder(folder); + } + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + //bool DeleteItem(InventoryItemBase item); + public bool DeleteFolders(UUID userID, List folderIDs) + { + return DeleteItems(userID, folderIDs); + } + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + public bool DeleteItems(UUID userID, List itemIDs) + { + // TODO: RemoveInventoryNode should be replaced with RemoveInventoryNodes + bool allSuccess = true; + + for (int i = 0; i < itemIDs.Count; i++) + { + UUID itemID = itemIDs[i]; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveInventoryNode" }, + { "OwnerID", userID.ToString() }, + { "ItemID", itemID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error removing item " + itemID + " for " + userID + ": " + + response["Message"].AsString()); + allSuccess = false; + } + } + + return allSuccess; + } + + /// + /// Purge an inventory folder of all its items and subfolders. + /// + /// + /// true if the folder was successfully purged + public bool PurgeFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "PurgeInventoryFolder" }, + { "OwnerID", folder.Owner.ToString() }, + { "FolderID", folder.ID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error purging folder " + folder.ID + " for " + folder.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Add a new item to the user's inventory + /// + /// + /// true if the item was successfully added + public bool AddItem(InventoryItemBase item) + { + // A folder of UUID.Zero means we need to find the most appropriate home for this item + if (item.Folder == UUID.Zero) + { + InventoryFolderBase folder = null; + if (Enum.IsDefined(typeof(FolderType), (sbyte)item.AssetType)) + folder = GetFolderForType(item.Owner, (FolderType)item.AssetType); + if (folder != null && folder.ID != UUID.Zero) + item.Folder = folder.ID; + else + item.Folder = item.Owner; // Root folder + } + + if ((AssetType)item.AssetType == AssetType.Gesture) + UpdateGesture(item.Owner, item.ID, item.Flags == 1); + + if (item.BasePermissions == 0) + m_log.WarnFormat("[SIMIAN INVENTORY CONNECTOR]: Adding inventory item {0} ({1}) with no base permissions", item.Name, item.ID); + + OSDMap permissions = new OSDMap + { + { "BaseMask", OSD.FromInteger(item.BasePermissions) }, + { "EveryoneMask", OSD.FromInteger(item.EveryOnePermissions) }, + { "GroupMask", OSD.FromInteger(item.GroupPermissions) }, + { "NextOwnerMask", OSD.FromInteger(item.NextPermissions) }, + { "OwnerMask", OSD.FromInteger(item.CurrentPermissions) } + }; + + OSDMap extraData = new OSDMap() + { + { "Flags", OSD.FromInteger(item.Flags) }, + { "GroupID", OSD.FromUUID(item.GroupID) }, + { "GroupOwned", OSD.FromBoolean(item.GroupOwned) }, + { "SalePrice", OSD.FromInteger(item.SalePrice) }, + { "SaleType", OSD.FromInteger(item.SaleType) }, + { "Permissions", permissions } + }; + + // Add different asset type only if it differs from inventory type + // (needed for links) + string invContentType = SLUtil.SLInvTypeToContentType(item.InvType); + string assetContentType = SLUtil.SLAssetTypeToContentType(item.AssetType); + + if (invContentType != assetContentType) + extraData["LinkedItemType"] = OSD.FromString(assetContentType); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventoryItem" }, + { "ItemID", item.ID.ToString() }, + { "AssetID", item.AssetID.ToString() }, + { "ParentID", item.Folder.ToString() }, + { "OwnerID", item.Owner.ToString() }, + { "Name", item.Name }, + { "Description", item.Description }, + { "CreatorID", item.CreatorId }, + { "CreatorData", item.CreatorData }, + { "ContentType", invContentType }, + { "ExtraData", OSDParser.SerializeJsonString(extraData) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Error creating item " + item.Name + " for " + item.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Update an item in the user's inventory + /// + /// + /// true if the item was successfully updated + public bool UpdateItem(InventoryItemBase item) + { + if (item.AssetID != UUID.Zero) + { + return AddItem(item); + } + else + { + // This is actually a folder update + InventoryFolderBase folder = new InventoryFolderBase(item.ID, item.Name, item.Owner, (short)item.AssetType, item.Folder, 0); + return UpdateFolder(folder); + } + } + + public bool MoveItems(UUID ownerID, List items) + { + bool success = true; + + while (items.Count > 0) + { + List currentItems = new List(); + UUID destFolderID = items[0].Folder; + + // Find all of the items being moved to the current destination folder + for (int i = 0; i < items.Count; i++) + { + InventoryItemBase item = items[i]; + if (item.Folder == destFolderID) + currentItems.Add(item); + } + + // Do the inventory move for the current items + success &= MoveItems(ownerID, items, destFolderID); + + // Remove the processed items from the list + for (int i = 0; i < currentItems.Count; i++) + items.Remove(currentItems[i]); + } + + return success; + } + + /// + /// Does the given user have an inventory structure? + /// + /// + /// + public bool HasInventoryForUser(UUID userID) + { + return GetRootFolder(userID) != null; + } + + /// + /// Get the active gestures of the agent. + /// + /// + /// + public List GetActiveGestures(UUID userID) + { + OSDArray items = FetchGestures(userID); + + string[] itemIDs = new string[items.Count]; + for (int i = 0; i < items.Count; i++) + itemIDs[i] = items[i].AsUUID().ToString(); + +// NameValueCollection requestArgs = new NameValueCollection +// { +// { "RequestMethod", "GetInventoryNodes" }, +// { "OwnerID", userID.ToString() }, +// { "Items", String.Join(",", itemIDs) } +// }; + + // FIXME: Implement this in SimianGrid + return new List(0); + } + + /// + /// Get the union of permissions of all inventory items + /// that hold the given assetID. + /// + /// + /// + /// The permissions or 0 if no such asset is found in + /// the user's inventory + public int GetAssetPermissions(UUID userID, UUID assetID) + { +// NameValueCollection requestArgs = new NameValueCollection +// { +// { "RequestMethod", "GetInventoryNodes" }, +// { "OwnerID", userID.ToString() }, +// { "AssetID", assetID.ToString() } +// }; + + // FIXME: Implement this in SimianGrid + return (int)PermissionMask.All; + } + + private List GetFoldersFromResponse(OSDArray items, UUID baseFolder, bool includeBaseFolder) + { + List invFolders = new List(items.Count); + + for (int i = 0; i < items.Count; i++) + { + OSDMap item = items[i] as OSDMap; + + if (item != null && item["Type"].AsString() == "Folder") + { + UUID folderID = item["ID"].AsUUID(); + + if (folderID == baseFolder && !includeBaseFolder) + continue; + + invFolders.Add(new InventoryFolderBase( + folderID, + item["Name"].AsString(), + item["OwnerID"].AsUUID(), + (short)SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()), + item["ParentID"].AsUUID(), + (ushort)item["Version"].AsInteger() + )); + } + } + +// m_log.Debug("[SIMIAN INVENTORY CONNECTOR]: Parsed " + invFolders.Count + " folders from SimianGrid response"); + return invFolders; + } + + private List GetItemsFromResponse(OSDArray items) + { + List invItems = new List(items.Count); + + for (int i = 0; i < items.Count; i++) + { + OSDMap item = items[i] as OSDMap; + + if (item != null && item["Type"].AsString() == "Item") + { + InventoryItemBase invItem = new InventoryItemBase(); + + invItem.AssetID = item["AssetID"].AsUUID(); + invItem.AssetType = SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()); + invItem.CreationDate = item["CreationDate"].AsInteger(); + invItem.CreatorId = item["CreatorID"].AsString(); + invItem.CreatorData = item["CreatorData"].AsString(); + invItem.Description = item["Description"].AsString(); + invItem.Folder = item["ParentID"].AsUUID(); + invItem.ID = item["ID"].AsUUID(); + invItem.InvType = SLUtil.ContentTypeToSLInvType(item["ContentType"].AsString()); + invItem.Name = item["Name"].AsString(); + invItem.Owner = item["OwnerID"].AsUUID(); + + OSDMap extraData = item["ExtraData"] as OSDMap; + if (extraData != null && extraData.Count > 0) + { + invItem.Flags = extraData["Flags"].AsUInteger(); + invItem.GroupID = extraData["GroupID"].AsUUID(); + invItem.GroupOwned = extraData["GroupOwned"].AsBoolean(); + invItem.SalePrice = extraData["SalePrice"].AsInteger(); + invItem.SaleType = (byte)extraData["SaleType"].AsInteger(); + + OSDMap perms = extraData["Permissions"] as OSDMap; + if (perms != null) + { + invItem.BasePermissions = perms["BaseMask"].AsUInteger(); + invItem.CurrentPermissions = perms["OwnerMask"].AsUInteger(); + invItem.EveryOnePermissions = perms["EveryoneMask"].AsUInteger(); + invItem.GroupPermissions = perms["GroupMask"].AsUInteger(); + invItem.NextPermissions = perms["NextOwnerMask"].AsUInteger(); + } + + if (extraData.ContainsKey("LinkedItemType")) + invItem.AssetType = SLUtil.ContentTypeToSLAssetType(extraData["LinkedItemType"].AsString()); + } + + if (invItem.BasePermissions == 0) + { + m_log.InfoFormat("[SIMIAN INVENTORY CONNECTOR]: Forcing item permissions to full for item {0} ({1})", + invItem.Name, invItem.ID); + invItem.BasePermissions = (uint)PermissionMask.All; + invItem.CurrentPermissions = (uint)PermissionMask.All; + invItem.EveryOnePermissions = (uint)PermissionMask.All; + invItem.GroupPermissions = (uint)PermissionMask.All; + invItem.NextPermissions = (uint)PermissionMask.All; + } + + invItems.Add(invItem); + } + } + +// m_log.Debug("[SIMIAN INVENTORY CONNECTOR]: Parsed " + invItems.Count + " items from SimianGrid response"); + return invItems; + } + + private bool MoveItems(UUID ownerID, List items, UUID destFolderID) + { + string[] itemIDs = new string[items.Count]; + for (int i = 0; i < items.Count; i++) + itemIDs[i] = items[i].ID.ToString(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "MoveInventoryNodes" }, + { "OwnerID", ownerID.ToString() }, + { "FolderID", destFolderID.ToString() }, + { "Items", String.Join(",", itemIDs) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to move " + items.Count + " items to " + + destFolderID + ": " + response["Message"].AsString()); + } + + return success; + } + + private void UpdateGesture(UUID userID, UUID itemID, bool enabled) + { + OSDArray gestures = FetchGestures(userID); + OSDArray newGestures = new OSDArray(); + + for (int i = 0; i < gestures.Count; i++) + { + UUID gesture = gestures[i].AsUUID(); + if (gesture != itemID) + newGestures.Add(OSD.FromUUID(gesture)); + } + + if (enabled) + newGestures.Add(OSD.FromUUID(itemID)); + + SaveGestures(userID, newGestures); + } + + private OSDArray FetchGestures(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_userServerUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap user = response["User"] as OSDMap; + if (user != null && response.ContainsKey("Gestures")) + { + OSD gestures = OSDParser.DeserializeJson(response["Gestures"].AsString()); + if (gestures != null && gestures is OSDArray) + return (OSDArray)gestures; + else + m_log.Error("[SIMIAN INVENTORY CONNECTOR]: Unrecognized active gestures data for " + userID); + } + } + else + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to fetch active gestures for " + userID + ": " + + response["Message"].AsString()); + } + + return new OSDArray(); + } + + private void SaveGestures(UUID userID, OSDArray gestures) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "Gestures", OSDParser.SerializeJsonString(gestures) } + }; + + OSDMap response = SimianGrid.PostToService(m_userServerUrl, requestArgs); + if (!response["Success"].AsBoolean()) + { + m_log.Warn("[SIMIAN INVENTORY CONNECTOR]: Failed to save active gestures for " + userID + ": " + + response["Message"].AsString()); + } + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs new file mode 100644 index 0000000000..211b775368 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -0,0 +1,459 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects avatar presence information (for tracking current location and + /// message routing) to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianPresenceServiceConnector")] + public class SimianPresenceServiceConnector : IPresenceService, IGridUserService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private SimianActivityDetector m_activityDetector; + private bool m_Enabled = false; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianPresenceServiceConnector() { } + public string Name { get { return "SimianPresenceServiceConnector"; } } + public void AddRegion(Scene scene) + { + if (m_Enabled) + { + scene.RegisterModuleInterface(this); + scene.RegisterModuleInterface(this); + + m_activityDetector.AddRegion(scene); + + LogoutRegionAgents(scene.RegionInfo.RegionID); + } + } + public void RemoveRegion(Scene scene) + { + if (m_Enabled) + { + scene.UnregisterModuleInterface(this); + scene.UnregisterModuleInterface(this); + + m_activityDetector.RemoveRegion(scene); + + LogoutRegionAgents(scene.RegionInfo.RegionID); + } + } + + #endregion ISharedRegionModule + + public SimianPresenceServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("PresenceServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["PresenceService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("PresenceServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + m_activityDetector = new SimianActivityDetector(this); + m_Enabled = true; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN PRESENCE CONNECTOR]: No PresenceServerURI specified, disabling connector"); + } + + #region IPresenceService + + public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) + { + m_log.ErrorFormat("[SIMIAN PRESENCE CONNECTOR]: Login requested, UserID={0}, SessionID={1}, SecureSessionID={2}", + userID, sessionID, secureSessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddSession" }, + { "UserID", userID.ToString() } + }; + + if (sessionID != UUID.Zero) + { + requestArgs["SessionID"] = sessionID.ToString(); + requestArgs["SecureSessionID"] = secureSessionID.ToString(); + } + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to login agent " + userID + ": " + response["Message"].AsString()); + + return success; + } + + public bool LogoutAgent(UUID sessionID) + { + // m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agent with sessionID " + sessionID + ": " + response["Message"].AsString()); + + return success; + } + + public bool LogoutRegionAgents(UUID regionID) + { + // m_log.InfoFormat("[SIMIAN PRESENCE CONNECTOR]: Logout requested for all agents in region " + regionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSessions" }, + { "SceneID", regionID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to logout agents from region " + regionID + ": " + response["Message"].AsString()); + + return success; + } + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + // Not needed for SimianGrid + return true; + } + + public PresenceInfo GetAgent(UUID sessionID) + { + OSDMap sessionResponse = GetSessionDataFromSessionID(sessionID); + if (sessionResponse == null) + { + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session {0}: {1}",sessionID.ToString(),sessionResponse["Message"].AsString()); + return null; + } + + UUID userID = sessionResponse["UserID"].AsUUID(); + OSDMap userResponse = GetUserData(userID); + if (userResponse == null) + { + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for {0}: {1}",userID.ToString(),userResponse["Message"].AsString()); + return null; + } + + return ResponseToPresenceInfo(sessionResponse); + } + + public PresenceInfo[] GetAgents(string[] userIDs) + { + List presences = new List(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSessions" }, + { "UserIDList", String.Join(",",userIDs) } + }; + + OSDMap sessionListResponse = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (! sessionListResponse["Success"].AsBoolean()) + { + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve sessions: {0}",sessionListResponse["Message"].AsString()); + return null; + } + + OSDArray sessionList = sessionListResponse["Sessions"] as OSDArray; + for (int i = 0; i < sessionList.Count; i++) + { + OSDMap sessionInfo = sessionList[i] as OSDMap; + presences.Add(ResponseToPresenceInfo(sessionInfo)); + } + + return presences.ToArray(); + } + + #endregion IPresenceService + + #region IGridUserService + + public GridUserInfo LoggedIn(string userID) + { + // Never implemented at the sim + return null; + } + + public bool LoggedOut(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + // m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Logging out user " + userID); + + // Remove the session to mark this user offline + if (!LogoutAgent(sessionID)) + return false; + + // Save our last position as user data + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LastLocation", SerializeLocation(regionID, lastPosition, lastLookAt) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString()); + + return success; + } + + public bool SetHome(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + // m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Setting home location for user " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "HomeLocation", SerializeLocation(regionID, position, lookAt) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to set home location for " + userID + ": " + response["Message"].AsString()); + + return success; + } + + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + return UpdateSession(sessionID, regionID, lastPosition, lastLookAt); + } + + public GridUserInfo GetGridUserInfo(string user) + { + // m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting session data for agent " + user); + + UUID userID = new UUID(user); + OSDMap userResponse = GetUserData(userID); + + if (userResponse == null) + { + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for {0}", userID); + } + + // Note that ResponseToGridUserInfo properly checks for and returns a null if passed a null. + return ResponseToGridUserInfo(userResponse); + + } + + #endregion + + #region Helpers + + private OSDMap GetUserData(UUID userID) + { + // m_log.DebugFormat("[SIMIAN PRESENCE CONNECTOR]: Requesting user data for " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["User"] is OSDMap) + return response; + + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve user data for {0}; {1}",userID.ToString(),response["Message"].AsString()); + return null; + } + + private OSDMap GetSessionDataFromSessionID(UUID sessionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + return response; + + m_log.WarnFormat("[SIMIAN PRESENCE CONNECTOR]: Failed to retrieve session data for {0}; {1}",sessionID.ToString(),response["Message"].AsString()); + return null; + } + + private bool UpdateSession(UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + // Save our current location as session data + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "UpdateSession" }, + { "SessionID", sessionID.ToString() }, + { "SceneID", regionID.ToString() }, + { "ScenePosition", lastPosition.ToString() }, + { "SceneLookAt", lastLookAt.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[SIMIAN PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString()); + + return success; + } + + private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse) + { + if (sessionResponse == null) + return null; + + PresenceInfo info = new PresenceInfo(); + + info.UserID = sessionResponse["UserID"].AsUUID().ToString(); + info.RegionID = sessionResponse["SceneID"].AsUUID(); + + return info; + } + + private GridUserInfo ResponseToGridUserInfo(OSDMap userResponse) + { + if (userResponse != null && userResponse["User"] is OSDMap) + { + GridUserInfo info = new GridUserInfo(); + + info.Online = true; + info.UserID = userResponse["UserID"].AsUUID().ToString(); + info.LastRegionID = userResponse["SceneID"].AsUUID(); + info.LastPosition = userResponse["ScenePosition"].AsVector3(); + info.LastLookAt = userResponse["SceneLookAt"].AsVector3(); + + OSDMap user = (OSDMap)userResponse["User"]; + + info.Login = user["LastLoginDate"].AsDate(); + info.Logout = user["LastLogoutDate"].AsDate(); + DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt); + + return info; + } + + return null; + } + + private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt) + { + return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}"; + } + + private bool DeserializeLocation(string location, out UUID regionID, out Vector3 position, out Vector3 lookAt) + { + OSDMap map = null; + + try { map = OSDParser.DeserializeJson(location) as OSDMap; } + catch { } + + if (map != null) + { + regionID = map["SceneID"].AsUUID(); + if (Vector3.TryParse(map["Position"].AsString(), out position) && + Vector3.TryParse(map["LookAt"].AsString(), out lookAt)) + { + return true; + } + } + + regionID = UUID.Zero; + position = Vector3.Zero; + lookAt = Vector3.Zero; + return false; + } + + public GridUserInfo[] GetGridUserInfo(string[] userIDs) + { + return new GridUserInfo[0]; + } + #endregion Helpers + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs new file mode 100644 index 0000000000..8fc766d9e0 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs @@ -0,0 +1,478 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Avatar profile flags + /// + [Flags] + public enum ProfileFlags : uint + { + AllowPublish = 1, + MaturePublish = 2, + Identified = 4, + Transacted = 8, + Online = 16 + } + + /// + /// Connects avatar profile and classified queries to the SimianGrid + /// backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianProfiles")] + public class SimianProfiles : INonSharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private bool m_Enabled = false; + + #region INonSharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void Close() { } + + public SimianProfiles() { } + public string Name { get { return "SimianProfiles"; } } + + public void AddRegion(Scene scene) + { + if (m_Enabled) + { + CheckEstateManager(scene); + scene.EventManager.OnClientConnect += ClientConnectHandler; + } + } + + public void RemoveRegion(Scene scene) + { + if (m_Enabled) + { + scene.EventManager.OnClientConnect -= ClientConnectHandler; + } + } + + #endregion INonSharedRegionModule + + public SimianProfiles(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + IConfig profileConfig = source.Configs["Profiles"]; + if (profileConfig == null) + return; + + if (profileConfig.GetString("Module", String.Empty) != Name) + return; + + m_log.DebugFormat("[SIMIAN PROFILES] module enabled"); + m_Enabled = true; + + IConfig gridConfig = source.Configs["UserAccountService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("UserAccountServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN PROFILES]: No UserAccountServerURI specified, disabling connector"); + } + + private void ClientConnectHandler(IClientCore clientCore) + { + if (clientCore is IClientAPI) + { + IClientAPI client = (IClientAPI)clientCore; + + // Classifieds + client.AddGenericPacketHandler("avatarclassifiedsrequest", AvatarClassifiedsRequestHandler); + client.OnClassifiedInfoRequest += ClassifiedInfoRequestHandler; + client.OnClassifiedInfoUpdate += ClassifiedInfoUpdateHandler; + client.OnClassifiedDelete += ClassifiedDeleteHandler; + + // Picks + client.AddGenericPacketHandler("avatarpicksrequest", HandleAvatarPicksRequest); + client.AddGenericPacketHandler("pickinforequest", HandlePickInfoRequest); + client.OnPickInfoUpdate += PickInfoUpdateHandler; + client.OnPickDelete += PickDeleteHandler; + + // Notes + client.AddGenericPacketHandler("avatarnotesrequest", HandleAvatarNotesRequest); + client.OnAvatarNotesUpdate += AvatarNotesUpdateHandler; + + // Profiles + client.OnRequestAvatarProperties += RequestAvatarPropertiesHandler; + + client.OnUpdateAvatarProperties += UpdateAvatarPropertiesHandler; + client.OnAvatarInterestUpdate += AvatarInterestUpdateHandler; + client.OnUserInfoRequest += UserInfoRequestHandler; + client.OnUpdateUserInfo += UpdateUserInfoHandler; + } + } + + #region Classifieds + + private void AvatarClassifiedsRequestHandler(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Query the generic key/value store for classifieds + client.SendAvatarClassifiedReply(targetAvatarID, new Dictionary(0)); + } + + private void ClassifiedInfoRequestHandler(UUID classifiedID, IClientAPI client) + { + // FIXME: Fetch this info + client.SendClassifiedInfoReply(classifiedID, UUID.Zero, 0, Utils.DateTimeToUnixTime(DateTime.UtcNow + TimeSpan.FromDays(1)), + 0, String.Empty, String.Empty, UUID.Zero, 0, UUID.Zero, String.Empty, Vector3.Zero, String.Empty, 0, 0); + } + + private void ClassifiedInfoUpdateHandler(UUID classifiedID, uint category, string name, string description, + UUID parcelID, uint parentEstate, UUID snapshotID, Vector3 globalPos, byte classifiedFlags, int price, + IClientAPI client) + { + // FIXME: Save this info + } + + private void ClassifiedDeleteHandler(UUID classifiedID, IClientAPI client) + { + // FIXME: Delete the specified classified ad + } + + #endregion Classifieds + + #region Picks + + private void HandleAvatarPicksRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch these + client.SendAvatarPicksReply(targetAvatarID, new Dictionary(0)); + } + + private void HandlePickInfoRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID avatarID; + UUID pickID; + if (args.Count < 2 || !UUID.TryParse(args[0], out avatarID) || !UUID.TryParse(args[1], out pickID)) + { + m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch this + client.SendPickInfoReply(pickID, avatarID, false, UUID.Zero, String.Empty, String.Empty, UUID.Zero, String.Empty, + String.Empty, String.Empty, Vector3.Zero, 0, false); + } + + private void PickInfoUpdateHandler(IClientAPI client, UUID pickID, UUID creatorID, bool topPick, string name, + string desc, UUID snapshotID, int sortOrder, bool enabled) + { + // FIXME: Save this + } + + private void PickDeleteHandler(IClientAPI client, UUID pickID) + { + // FIXME: Delete + } + + #endregion Picks + + #region Notes + + private void HandleAvatarNotesRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[SIMIAN PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch this + client.SendAvatarNotesReply(targetAvatarID, String.Empty); + } + + private void AvatarNotesUpdateHandler(IClientAPI client, UUID targetID, string notes) + { + // FIXME: Save this + } + + #endregion Notes + + #region Profiles + + private void RequestAvatarPropertiesHandler(IClientAPI client, UUID avatarID) + { + m_log.DebugFormat("[SIMIAN PROFILES]: Request avatar properties for {0}",avatarID); + + OSDMap user = FetchUserData(avatarID); + + ProfileFlags flags = ProfileFlags.AllowPublish | ProfileFlags.MaturePublish; + + if (user != null) + { + OSDMap about = null; + if (user.ContainsKey("LLAbout")) + { + try + { + about = OSDParser.DeserializeJson(user["LLAbout"].AsString()) as OSDMap; + } + catch + { + m_log.WarnFormat("[SIMIAN PROFILES]: Unable to decode LLAbout"); + } + } + + if (about == null) + about = new OSDMap(0); + + // Check if this user is a grid operator + byte[] charterMember; + if (user["AccessLevel"].AsInteger() >= 200) + charterMember = Utils.StringToBytes("Operator"); + else + charterMember = Utils.EmptyBytes; + + // Check if the user is online + if (client.Scene is Scene) + { + OpenSim.Services.Interfaces.PresenceInfo[] presences = ((Scene)client.Scene).PresenceService.GetAgents(new string[] { avatarID.ToString() }); + if (presences != null && presences.Length > 0) + flags |= ProfileFlags.Online; + } + + // Check if the user is identified + if (user["Identified"].AsBoolean()) + flags |= ProfileFlags.Identified; + + client.SendAvatarProperties(avatarID, about["About"].AsString(), user["CreationDate"].AsDate().ToString("M/d/yyyy", + System.Globalization.CultureInfo.InvariantCulture), charterMember, about["FLAbout"].AsString(), (uint)flags, + about["FLImage"].AsUUID(), about["Image"].AsUUID(), about["URL"].AsString(), user["Partner"].AsUUID()); + + OSDMap interests = null; + if (user.ContainsKey("LLInterests")) + { + try + { + interests = OSDParser.DeserializeJson(user["LLInterests"].AsString()) as OSDMap; + client.SendAvatarInterestsReply(avatarID, interests["WantMask"].AsUInteger(), interests["WantText"].AsString(), interests["SkillsMask"].AsUInteger(), interests["SkillsText"].AsString(), interests["Languages"].AsString()); + } + catch { } + } + + if (about == null) + about = new OSDMap(0); + } + else + { + m_log.Warn("[SIMIAN PROFILES]: Failed to fetch profile information for " + client.Name + ", returning default values"); + client.SendAvatarProperties(avatarID, String.Empty, "1/1/1970", Utils.EmptyBytes, + String.Empty, (uint)flags, UUID.Zero, UUID.Zero, String.Empty, UUID.Zero); + } + } + + private void UpdateAvatarPropertiesHandler(IClientAPI client, UserProfileData profileData) + { + OSDMap map = new OSDMap + { + { "About", OSD.FromString(profileData.AboutText) }, + { "Image", OSD.FromUUID(profileData.Image) }, + { "FLAbout", OSD.FromString(profileData.FirstLifeAboutText) }, + { "FLImage", OSD.FromUUID(profileData.FirstLifeImage) }, + { "URL", OSD.FromString(profileData.ProfileUrl) } + }; + + AddUserData(client.AgentId, "LLAbout", map); + } + + private void AvatarInterestUpdateHandler(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, + string skillstext, string languages) + { + OSDMap map = new OSDMap + { + { "WantMask", OSD.FromInteger(wantmask) }, + { "WantText", OSD.FromString(wanttext) }, + { "SkillsMask", OSD.FromInteger(skillsmask) }, + { "SkillsText", OSD.FromString(skillstext) }, + { "Languages", OSD.FromString(languages) } + }; + + AddUserData(client.AgentId, "LLInterests", map); + } + + private void UserInfoRequestHandler(IClientAPI client) + { + m_log.Error("[SIMIAN PROFILES]: UserInfoRequestHandler"); + + // Fetch this user's e-mail address + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", client.AgentId.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + string email = response["Email"].AsString(); + + if (!response["Success"].AsBoolean()) + m_log.Warn("[SIMIAN PROFILES]: GetUser failed during a user info request for " + client.Name); + + client.SendUserInfoReply(false, true, email); + } + + private void UpdateUserInfoHandler(bool imViaEmail, bool visible, IClientAPI client) + { + m_log.Info("[SIMIAN PROFILES]: Ignoring user info update from " + client.Name); + } + + #endregion Profiles + + /// + /// Sanity checks regions for a valid estate owner at startup + /// + private void CheckEstateManager(Scene scene) + { + EstateSettings estate = scene.RegionInfo.EstateSettings; + + if (estate.EstateOwner == UUID.Zero) + { + // Attempt to lookup the grid admin + UserAccount admin = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, UUID.Zero); + if (admin != null) + { + m_log.InfoFormat("[SIMIAN PROFILES]: Setting estate {0} (ID: {1}) owner to {2}", estate.EstateName, + estate.EstateID, admin.Name); + + estate.EstateOwner = admin.PrincipalID; + scene.EstateDataService.StoreEstateSettings(estate); + } + else + { + m_log.WarnFormat("[SIMIAN PROFILES]: Estate {0} (ID: {1}) does not have an owner", estate.EstateName, estate.EstateID); + } + } + } + + private bool AddUserData(UUID userID, string key, OSDMap value) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { key, OSDParser.SerializeJsonString(value) } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.WarnFormat("[SIMIAN PROFILES]: Failed to add user data with key {0} for {1}: {2}", key, userID, response["Message"].AsString()); + + return success; + } + + private OSDMap FetchUserData(UUID userID) + { + m_log.DebugFormat("[SIMIAN PROFILES]: Fetch information about {0}",userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["User"] is OSDMap) + { + return (OSDMap)response["User"]; + } + else + { + m_log.Error("[SIMIAN PROFILES]: Failed to fetch user data for " + userID + ": " + response["Message"].AsString()); + } + + return null; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs new file mode 100644 index 0000000000..698c4c0872 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs @@ -0,0 +1,337 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects user account data (creating new users, looking up existing + /// users) to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SimianUserAccountServiceConnector")] + public class SimianUserAccountServiceConnector : IUserAccountService, ISharedRegionModule + { + private const double CACHE_EXPIRATION_SECONDS = 120.0; + + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private ExpiringCache m_accountCache = new ExpiringCache(); + private bool m_Enabled; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianUserAccountServiceConnector() { } + public string Name { get { return "SimianUserAccountServiceConnector"; } } + public void AddRegion(Scene scene) { if (m_Enabled) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (m_Enabled) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianUserAccountServiceConnector(IConfigSource source) + { + CommonInit(source); + } + + public void Initialise(IConfigSource source) + { + IConfig moduleConfig = source.Configs["Modules"]; + if (moduleConfig != null) + { + string name = moduleConfig.GetString("UserAccountServices", ""); + if (name == Name) + CommonInit(source); + } + } + + private void CommonInit(IConfigSource source) + { + IConfig gridConfig = source.Configs["UserAccountService"]; + if (gridConfig != null) + { + string serviceUrl = gridConfig.GetString("UserAccountServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + { + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + m_serverUrl = serviceUrl; + m_Enabled = true; + } + } + + if (String.IsNullOrEmpty(m_serverUrl)) + m_log.Info("[SIMIAN ACCOUNT CONNECTOR]: No UserAccountServerURI specified, disabling connector"); + } + + public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "Name", firstName + ' ' + lastName } + }; + + return GetUser(requestArgs); + } + + public UserAccount GetUserAccount(UUID scopeID, string email) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "Email", email } + }; + + return GetUser(requestArgs); + } + + public UserAccount GetUserAccount(UUID scopeID, UUID userID) + { + // Cache check + UserAccount account; + if (m_accountCache.TryGetValue(userID, out account)) + return account; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + account = GetUser(requestArgs); + + if (account == null) + { + // Store null responses too, to avoid repeated lookups for missing accounts + m_accountCache.AddOrUpdate(userID, null, CACHE_EXPIRATION_SECONDS); + } + + return account; + } + + public List GetUserAccounts(UUID scopeID, string query) + { + List accounts = new List(); + +// m_log.DebugFormat("[SIMIAN ACCOUNT CONNECTOR]: Searching for user accounts with name query " + query); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUsers" }, + { "NameQuery", query } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDArray array = response["Users"] as OSDArray; + if (array != null && array.Count > 0) + { + for (int i = 0; i < array.Count; i++) + { + UserAccount account = ResponseToUserAccount(array[i] as OSDMap); + if (account != null) + accounts.Add(account); + } + } + else + { + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format"); + } + } + else + { + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to search for account data by name " + query); + } + + return accounts; + } + + public void InvalidateCache(UUID userID) + { + m_accountCache.Remove(userID); + } + + public bool StoreUserAccount(UserAccount data) + { +// m_log.InfoFormat("[SIMIAN ACCOUNT CONNECTOR]: Storing user account for " + data.Name); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUser" }, + { "UserID", data.PrincipalID.ToString() }, + { "Name", data.Name }, + { "Email", data.Email }, + { "AccessLevel", data.UserLevel.ToString() } + }; + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + + if (response["Success"].AsBoolean()) + { + m_log.InfoFormat("[SIMIAN ACCOUNT CONNECTOR]: Storing user account data for " + data.Name); + + requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", data.PrincipalID.ToString() }, + { "CreationDate", data.Created.ToString() }, + { "UserFlags", data.UserFlags.ToString() }, + { "UserTitle", data.UserTitle } + }; + + response = SimianGrid.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (success) + { + // Cache the user account info + m_accountCache.AddOrUpdate(data.PrincipalID, data, CACHE_EXPIRATION_SECONDS); + } + else + { + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to store user account data for " + data.Name + ": " + response["Message"].AsString()); + } + + return success; + } + else + { + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to store user account for " + data.Name + ": " + response["Message"].AsString()); + } + + return false; + } + + /// + /// Helper method for the various ways of retrieving a user account + /// + /// Service query parameters + /// A UserAccount object on success, null on failure + private UserAccount GetUser(NameValueCollection requestArgs) + { + string lookupValue = (requestArgs.Count > 1) ? requestArgs[1] : "(Unknown)"; +// m_log.DebugFormat("[SIMIAN ACCOUNT CONNECTOR]: Looking up user account with query: " + lookupValue); + + OSDMap response = SimianGrid.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap user = response["User"] as OSDMap; + if (user != null) + return ResponseToUserAccount(user); + else + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format"); + } + else + { + m_log.Warn("[SIMIAN ACCOUNT CONNECTOR]: Failed to lookup user account with query: " + lookupValue); + } + + return null; + } + + /// + /// Convert a User object in LLSD format to a UserAccount + /// + /// LLSD containing user account data + /// A UserAccount object on success, null on failure + private UserAccount ResponseToUserAccount(OSDMap response) + { + if (response == null) + return null; + + UserAccount account = new UserAccount(); + account.PrincipalID = response["UserID"].AsUUID(); + account.Created = response["CreationDate"].AsInteger(); + account.Email = response["Email"].AsString(); + account.ServiceURLs = new Dictionary(0); + account.UserFlags = response["UserFlags"].AsInteger(); + account.UserLevel = response["AccessLevel"].AsInteger(); + account.UserTitle = response["UserTitle"].AsString(); + account.LocalToGrid = true; + if (response.ContainsKey("LocalToGrid")) + account.LocalToGrid = (response["LocalToGrid"].AsString() == "true" ? true : false); + + GetFirstLastName(response["Name"].AsString(), out account.FirstName, out account.LastName); + + // Cache the user account info + m_accountCache.AddOrUpdate(account.PrincipalID, account, CACHE_EXPIRATION_SECONDS); + + return account; + } + + /// + /// Convert a name with a single space in it to a first and last name + /// + /// A full name such as "John Doe" + /// First name + /// Last name (surname) + private static void GetFirstLastName(string name, out string firstName, out string lastName) + { + if (String.IsNullOrEmpty(name)) + { + firstName = String.Empty; + lastName = String.Empty; + } + else + { + string[] names = name.Split(' '); + + if (names.Length == 2) + { + firstName = names[0]; + lastName = names[1]; + } + else + { + firstName = String.Empty; + lastName = name; + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs new file mode 100644 index 0000000000..1eedbefafa --- /dev/null +++ b/OpenSim/Services/Connectors/Simulation/SimulationServiceConnector.cs @@ -0,0 +1,482 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using System.Collections; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using log4net; +using Nini.Config; + +namespace OpenSim.Services.Connectors.Simulation +{ + public class SimulationServiceConnector : ISimulationService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // we use this dictionary to track the pending updateagent requests, maps URI --> position update + private Dictionary m_updateAgentQueue = new Dictionary(); + + //private GridRegion m_Region; + + public SimulationServiceConnector() + { + } + + public SimulationServiceConnector(IConfigSource config) + { + //m_Region = region; + } + + public IScene GetScene(UUID regionId) + { + return null; + } + + public ISimulationService GetInnerService() + { + return null; + } + + #region Agents + + protected virtual string AgentPath() + { + return "agent/"; + } + + protected virtual void PackData(OSDMap args, GridRegion source, AgentCircuitData aCircuit, GridRegion destination, uint flags) + { + if (source != null) + { + args["source_x"] = OSD.FromString(source.RegionLocX.ToString()); + args["source_y"] = OSD.FromString(source.RegionLocY.ToString()); + args["source_name"] = OSD.FromString(source.RegionName); + args["source_uuid"] = OSD.FromString(source.RegionID.ToString()); + if (!String.IsNullOrEmpty(source.RawServerURI)) + args["source_server_uri"] = OSD.FromString(source.RawServerURI); + } + + args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); + args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); + args["destination_name"] = OSD.FromString(destination.RegionName); + args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); + args["teleport_flags"] = OSD.FromString(flags.ToString()); + } + + public bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, out string reason) + { + string tmp = String.Empty; + return CreateAgent(source, destination, aCircuit, flags, out tmp, out reason); + } + + public bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, out string myipaddress, out string reason) + { + m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: Creating agent at {0}", destination.ServerURI); + reason = String.Empty; + myipaddress = String.Empty; + + if (destination == null) + { + m_log.Debug("[REMOTE SIMULATION CONNECTOR]: Given destination is null"); + return false; + } + + string uri = destination.ServerURI + AgentPath() + aCircuit.AgentID + "/"; + + try + { + OSDMap args = aCircuit.PackAgentCircuitData(); + PackData(args, source, aCircuit, destination, flags); + + OSDMap result = WebUtil.PostToServiceCompressed(uri, args, 30000); + bool success = result["success"].AsBoolean(); + if (success && result.ContainsKey("_Result")) + { + OSDMap data = (OSDMap)result["_Result"]; + + reason = data["reason"].AsString(); + success = data["success"].AsBoolean(); + myipaddress = data["your_ip"].AsString(); + return success; + } + + // Try the old version, uncompressed + result = WebUtil.PostToService(uri, args, 30000, false); + + if (result["Success"].AsBoolean()) + { + if (result.ContainsKey("_Result")) + { + OSDMap data = (OSDMap)result["_Result"]; + + reason = data["reason"].AsString(); + success = data["success"].AsBoolean(); + myipaddress = data["your_ip"].AsString(); + m_log.WarnFormat( + "[REMOTE SIMULATION CONNECTOR]: Remote simulator {0} did not accept compressed transfer, suggest updating it.", destination.RegionName); + return success; + } + } + + m_log.WarnFormat( + "[REMOTE SIMULATION CONNECTOR]: Failed to create agent {0} {1} at remote simulator {2}", + aCircuit.firstname, aCircuit.lastname, destination.RegionName); + reason = result["Message"] != null ? result["Message"].AsString() : "error"; + return false; + } + catch (Exception e) + { + m_log.Warn("[REMOTE SIMULATION CONNECTOR]: CreateAgent failed with exception: " + e.ToString()); + reason = e.Message; + } + + return false; + } + + /// + /// Send complete data about an agent in this region to a neighbor + /// + public bool UpdateAgent(GridRegion destination, AgentData data) + { + return UpdateAgent(destination, (IAgentData)data, 200000); // yes, 200 seconds + } + + private ExpiringCache _failedSims = new ExpiringCache(); + /// + /// Send updated position information about an agent in this region to a neighbor + /// This operation may be called very frequently if an avatar is moving about in + /// the region. + /// + public bool UpdateAgent(GridRegion destination, AgentPosition data) + { + bool v = true; + if (_failedSims.TryGetValue(destination.ServerURI, out v)) + return false; + + // The basic idea of this code is that the first thread that needs to + // send an update for a specific avatar becomes the worker for any subsequent + // requests until there are no more outstanding requests. Further, only send the most + // recent update; this *should* never be needed but some requests get + // slowed down and once that happens the problem with service end point + // limits kicks in and nothing proceeds + string uri = destination.ServerURI + AgentPath() + data.AgentID + "/"; + lock (m_updateAgentQueue) + { + if (m_updateAgentQueue.ContainsKey(uri)) + { + // Another thread is already handling + // updates for this simulator, just update + // the position and return, overwrites are + // not a problem since we only care about the + // last update anyway + m_updateAgentQueue[uri] = data; + return true; + } + + // Otherwise update the reference and start processing + m_updateAgentQueue[uri] = data; + } + + AgentPosition pos = null; + bool success = true; + while (success) + { + lock (m_updateAgentQueue) + { + // save the position + AgentPosition lastpos = pos; + + pos = m_updateAgentQueue[uri]; + + // this is true if no one put a new + // update in the map since the last + // one we processed, if thats the + // case then we are done + if (pos == lastpos) + { + m_updateAgentQueue.Remove(uri); + return true; + } + } + + success = UpdateAgent(destination, (IAgentData)pos, 10000); + } + // we get here iff success == false + // blacklist sim for 2 minutes + lock (m_updateAgentQueue) + { + _failedSims.AddOrUpdate(destination.ServerURI, true, 120); + m_updateAgentQueue.Remove(uri); + } + return false; + } + + /// + /// This is the worker function to send AgentData to a neighbor region + /// + private bool UpdateAgent(GridRegion destination, IAgentData cAgentData, int timeout) + { + // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: UpdateAgent in {0}", destination.ServerURI); + + // Eventually, we want to use a caps url instead of the agentID + string uri = destination.ServerURI + AgentPath() + cAgentData.AgentID + "/"; + + try + { + OSDMap args = cAgentData.Pack(); + + args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); + args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); + args["destination_name"] = OSD.FromString(destination.RegionName); + args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); + + OSDMap result = WebUtil.PutToServiceCompressed(uri, args, timeout); + if (result["Success"].AsBoolean()) + return true; + + result = WebUtil.PutToService(uri, args, timeout); + + return result["Success"].AsBoolean(); + } + catch (Exception e) + { + m_log.Warn("[REMOTE SIMULATION CONNECTOR]: UpdateAgent failed with exception: " + e.ToString()); + } + + return false; + } + + + public bool QueryAccess(GridRegion destination, UUID agentID, string agentHomeURI, bool viaTeleport, Vector3 position, string myversion, List featuresAvailable, out string version, out string reason) + { + reason = "Failed to contact destination"; + version = "Unknown"; + + // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: QueryAccess start, position={0}", position); + + IPEndPoint ext = destination.ExternalEndPoint; + if (ext == null) return false; + + // Eventually, we want to use a caps url instead of the agentID + string uri = destination.ServerURI + AgentPath() + agentID + "/" + destination.RegionID.ToString() + "/"; + + OSDMap request = new OSDMap(); + request.Add("viaTeleport", OSD.FromBoolean(viaTeleport)); + request.Add("position", OSD.FromString(position.ToString())); + request.Add("my_version", OSD.FromString(myversion)); + + OSDArray features = new OSDArray(); + foreach (UUID feature in featuresAvailable) + features.Add(OSD.FromString(feature.ToString())); + + request.Add("features", features); + + if (agentHomeURI != null) + request.Add("agent_home_uri", OSD.FromString(agentHomeURI)); + + try + { + OSDMap result = WebUtil.ServiceOSDRequest(uri, request, "QUERYACCESS", 30000, false, false); + bool success = result["success"].AsBoolean(); + if (result.ContainsKey("_Result")) + { + OSDMap data = (OSDMap)result["_Result"]; + + // FIXME: If there is a _Result map then it's the success key here that indicates the true success + // or failure, not the sibling result node. + success = data["success"]; + + reason = data["reason"].AsString(); + if (data["version"] != null && data["version"].AsString() != string.Empty) + version = data["version"].AsString(); + + m_log.DebugFormat( + "[REMOTE SIMULATION CONNECTOR]: QueryAccess to {0} returned {1}, reason {2}, version {3} ({4})", + uri, success, reason, version, data["version"].AsString()); + } + + if (!success) + { + // If we don't check this then OpenSimulator 0.7.3.1 and some period before will never see the + // actual failure message + if (!result.ContainsKey("_Result")) + { + if (result.ContainsKey("Message")) + { + string message = result["Message"].AsString(); + if (message == "Service request failed: [MethodNotAllowed] MethodNotAllowed") // Old style region + { + m_log.Info("[REMOTE SIMULATION CONNECTOR]: The above web util error was caused by a TP to a sim that doesn't support QUERYACCESS and can be ignored"); + return true; + } + + reason = result["Message"]; + } + else + { + reason = "Communications failure"; + } + } + + return false; + } + + + featuresAvailable.Clear(); + + if (result.ContainsKey("features")) + { + OSDArray array = (OSDArray)result["features"]; + + foreach (OSD o in array) + featuresAvailable.Add(new UUID(o.AsString())); + } + + return success; + } + catch (Exception e) + { + m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] QueryAcesss failed with exception; {0}",e.ToString()); + } + + return false; + } + + /// + /// + public bool ReleaseAgent(UUID origin, UUID id, string uri) + { + // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: ReleaseAgent start"); + + try + { + WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false); + } + catch (Exception e) + { + m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] ReleaseAgent failed with exception; {0}",e.ToString()); + } + + return true; + } + + /// + /// + public bool CloseAgent(GridRegion destination, UUID id, string auth_code) + { + string uri = destination.ServerURI + AgentPath() + id + "/" + destination.RegionID.ToString() + "/?auth=" + auth_code; + m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CloseAgent {0}", uri); + + try + { + WebUtil.ServiceOSDRequest(uri, null, "DELETE", 10000, false, false); + } + catch (Exception e) + { + m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CloseAgent failed with exception; {0}",e.ToString()); + } + + return true; + } + + #endregion Agents + + #region Objects + + protected virtual string ObjectPath() + { + return "object/"; + } + + /// + /// + /// + public bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog, bool isLocalCall) + { + // m_log.DebugFormat("[REMOTE SIMULATION CONNECTOR]: CreateObject start"); + + string uri = destination.ServerURI + ObjectPath() + sog.UUID + "/"; + + try + { + OSDMap args = new OSDMap(2); + + args["sog"] = OSD.FromString(sog.ToXml2()); + args["extra"] = OSD.FromString(sog.ExtraToXmlString()); + args["modified"] = OSD.FromBoolean(sog.HasGroupChanged); + args["new_position"] = newPosition.ToString(); + + string state = sog.GetStateSnapshot(); + if (state.Length > 0) + args["state"] = OSD.FromString(state); + + // Add the input general arguments + args["destination_x"] = OSD.FromString(destination.RegionLocX.ToString()); + args["destination_y"] = OSD.FromString(destination.RegionLocY.ToString()); + args["destination_name"] = OSD.FromString(destination.RegionName); + args["destination_uuid"] = OSD.FromString(destination.RegionID.ToString()); + + OSDMap result = WebUtil.PostToService(uri, args, 40000, false); + + if (result == null) + return false; + bool success = result["success"].AsBoolean(); + if (!success) + return false; + } + catch (Exception e) + { + m_log.WarnFormat("[REMOTE SIMULATION CONNECTOR] CreateObject failed with exception; {0}",e.ToString()); + return false; + } + + return true; + } + + /// + /// + /// + public bool CreateObject(GridRegion destination, UUID userID, UUID itemID) + { + // TODO, not that urgent + return false; + } + + #endregion Objects + } +} diff --git a/OpenSim/Services/Connectors/UserAccounts/UserAccountServicesConnector.cs b/OpenSim/Services/Connectors/UserAccounts/UserAccountServicesConnector.cs new file mode 100644 index 0000000000..560e6c4c13 --- /dev/null +++ b/OpenSim/Services/Connectors/UserAccounts/UserAccountServicesConnector.cs @@ -0,0 +1,328 @@ +/* + * 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 log4net; +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.ServiceAuth; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.Connectors +{ + public class UserAccountServicesConnector : BaseServiceConnector, IUserAccountService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_ServerURI = String.Empty; + + public UserAccountServicesConnector() + { + } + + public UserAccountServicesConnector(string serverURI) + { + m_ServerURI = serverURI.TrimEnd('/'); + } + + public UserAccountServicesConnector(IConfigSource source) + { + Initialise(source); + } + + public virtual void Initialise(IConfigSource source) + { + IConfig assetConfig = source.Configs["UserAccountService"]; + if (assetConfig == null) + { + m_log.Error("[ACCOUNT CONNECTOR]: UserAccountService missing from OpenSim.ini"); + throw new Exception("User account connector init error"); + } + + string serviceURI = assetConfig.GetString("UserAccountServerURI", + String.Empty); + + if (serviceURI == String.Empty) + { + m_log.Error("[ACCOUNT CONNECTOR]: No Server URI named in section UserAccountService"); + throw new Exception("User account connector init error"); + } + m_ServerURI = serviceURI; + + base.Initialise(source, "UserAccountService"); + } + + public virtual UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getaccount"; + + sendData["ScopeID"] = scopeID; + sendData["FirstName"] = firstName.ToString(); + sendData["LastName"] = lastName.ToString(); + + return SendAndGetReply(sendData); + } + + public virtual UserAccount GetUserAccount(UUID scopeID, string email) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getaccount"; + + sendData["ScopeID"] = scopeID; + sendData["Email"] = email; + + return SendAndGetReply(sendData); + } + + public virtual UserAccount GetUserAccount(UUID scopeID, UUID userID) + { + //m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetUserAccount {0}", userID); + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getaccount"; + + sendData["ScopeID"] = scopeID; + sendData["UserID"] = userID.ToString(); + + return SendAndGetReply(sendData); + } + + public List GetUserAccounts(UUID scopeID, string query) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "getaccounts"; + + sendData["ScopeID"] = scopeID.ToString(); + sendData["query"] = query; + + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/accounts"; + // m_log.DebugFormat("[ACCOUNTS CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[ACCOUNT CONNECTOR]: GetUserAccounts received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[ACCOUNT CONNECTOR]: Exception when contacting user accounts server at {0}: {1}", uri, e.Message); + } + + List accounts = new List(); + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData != null) + { + if (replyData.ContainsKey("result") && replyData["result"].ToString() == "null") + { + return accounts; + } + + Dictionary.ValueCollection accountList = replyData.Values; + //m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetAgents returned {0} elements", pinfosList.Count); + foreach (object acc in accountList) + { + if (acc is Dictionary) + { + UserAccount pinfo = new UserAccount((Dictionary)acc); + accounts.Add(pinfo); + } + else + m_log.DebugFormat("[ACCOUNT CONNECTOR]: GetUserAccounts received invalid response type {0}", + acc.GetType()); + } + } + else + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetUserAccounts received null response"); + + return accounts; + } + + public void InvalidateCache(UUID userID) + { + } + + public virtual bool StoreUserAccount(UserAccount data) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "setaccount"; + + Dictionary structData = data.ToKeyValuePairs(); + + foreach (KeyValuePair kvp in structData) + { + if (kvp.Value == null) + { + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: Null value for {0}", kvp.Key); + continue; + } + sendData[kvp.Key] = kvp.Value.ToString(); + } + + if (SendAndGetReply(sendData) != null) + return true; + else + return false; + } + + /// + /// Create user remotely. Note this this is not part of the IUserAccountsService + /// + /// + /// + /// + /// + /// + /// + public virtual UserAccount CreateUser(string first, string last, string password, string email, UUID scopeID) + { + Dictionary sendData = new Dictionary(); + //sendData["SCOPEID"] = scopeID.ToString(); + sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); + sendData["VERSIONMAX"] = ProtocolVersions.ClientProtocolVersionMax.ToString(); + sendData["METHOD"] = "createuser"; + + sendData["FirstName"] = first; + sendData["LastName"] = last; + sendData["Password"] = password; + if (!string.IsNullOrEmpty(email)) + sendData["Email"] = first; + sendData["ScopeID"] = scopeID.ToString(); + + return SendAndGetReply(sendData); + } + + private UserAccount SendAndGetReply(Dictionary sendData) + { + string reply = string.Empty; + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/accounts"; + // m_log.DebugFormat("[ACCOUNTS CONNECTOR]: queryString = {0}", reqString); + try + { + reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply == null || (reply != null && reply == string.Empty)) + { + m_log.DebugFormat("[ACCOUNT CONNECTOR]: GetUserAccount received null or empty reply"); + return null; + } + } + catch (Exception e) + { + m_log.DebugFormat("[ACCOUNT CONNECTOR]: Exception when contacting user accounts server at {0}: {1}", uri, e.Message); + } + + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + UserAccount account = null; + + if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) + { + if (replyData["result"] is Dictionary) + { + account = new UserAccount((Dictionary)replyData["result"]); + } + } + + return account; + + } + + private bool SendAndGetBoolReply(Dictionary sendData) + { + string reqString = ServerUtils.BuildQueryString(sendData); + string uri = m_ServerURI + "/accounts"; + //m_log.DebugFormat("[ACCOUNTS CONNECTOR]: queryString = {0}", reqString); + try + { + string reply = SynchronousRestFormsRequester.MakeRequest("POST", + uri, + reqString, + m_Auth); + if (reply != string.Empty) + { + //m_log.DebugFormat("[ACCOUNTS CONNECTOR]: reply = {0}", reply); + Dictionary replyData = ServerUtils.ParseXmlResponse(reply); + + if (replyData.ContainsKey("result")) + { + if (replyData["result"].ToString().ToLower() == "success") + return true; + else + return false; + } + else + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: Set or Create UserAccount reply data does not contain result field"); + + } + else + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: Set or Create UserAccount received empty reply"); + } + catch (Exception e) + { + m_log.DebugFormat("[ACCOUNT CONNECTOR]: Exception when contacting user accounts server at {0}: {1}", uri, e.Message); + } + + return false; + } + + } +} diff --git a/OpenSim/Services/EstateService/EstateDataService.cs b/OpenSim/Services/EstateService/EstateDataService.cs new file mode 100644 index 0000000000..f6a8654325 --- /dev/null +++ b/OpenSim/Services/EstateService/EstateDataService.cs @@ -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.Collections.Generic; +using OpenMetaverse; +using log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Data; +using OpenSim.Framework; + +namespace OpenSim.Services.EstateService +{ + public class EstateDataService : ServiceBase, IEstateDataService + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + protected IEstateDataStore m_database; + + public EstateDataService(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + + // Try reading the [DatabaseService] section, if it exists + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + dllName = dbConfig.GetString("StorageProvider", String.Empty); + connString = dbConfig.GetString("ConnectionString", String.Empty); + connString = dbConfig.GetString("EstateConnectionString", connString); + } + + // Try reading the [EstateDataStore] section, if it exists + IConfig estConfig = config.Configs["EstateDataStore"]; + if (estConfig != null) + { + dllName = estConfig.GetString("StorageProvider", dllName); + connString = estConfig.GetString("ConnectionString", connString); + } + + // We tried, but this doesn't exist. We can't proceed + if (dllName == String.Empty) + throw new Exception("No StorageProvider configured"); + + m_database = LoadPlugin(dllName, new Object[] { connString }); + if (m_database == null) + throw new Exception("Could not find a storage interface in the given module"); + } + + public EstateSettings LoadEstateSettings(UUID regionID, bool create) + { + return m_database.LoadEstateSettings(regionID, create); + } + + public EstateSettings LoadEstateSettings(int estateID) + { + return m_database.LoadEstateSettings(estateID); + } + + public EstateSettings CreateNewEstate() + { + return m_database.CreateNewEstate(); + } + + public List LoadEstateSettingsAll() + { + return m_database.LoadEstateSettingsAll(); + } + + public void StoreEstateSettings(EstateSettings es) + { + m_database.StoreEstateSettings(es); + } + + public List GetEstates(string search) + { + return m_database.GetEstates(search); + } + + public List GetEstatesAll() + { + return m_database.GetEstatesAll(); + } + + public List GetEstatesByOwner(UUID ownerID) + { + return m_database.GetEstatesByOwner(ownerID); + } + + public bool LinkRegion(UUID regionID, int estateID) + { + return m_database.LinkRegion(regionID, estateID); + } + + public List GetRegions(int estateID) + { + return m_database.GetRegions(estateID); + } + + public bool DeleteEstate(int estateID) + { + return m_database.DeleteEstate(estateID); + } + } +} diff --git a/OpenSim/Services/FSAssetService/FSAssetService.cs b/OpenSim/Services/FSAssetService/FSAssetService.cs new file mode 100644 index 0000000000..1bab687fe8 --- /dev/null +++ b/OpenSim/Services/FSAssetService/FSAssetService.cs @@ -0,0 +1,723 @@ +/* + * 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.Diagnostics; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Threading; +using System.Reflection; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Framework.Serialization.External; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using Nini.Config; +using log4net; +using OpenMetaverse; +using System.Security.Cryptography; + +namespace OpenSim.Services.FSAssetService +{ + public class FSAssetConnector : ServiceBase, IAssetService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + static System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + static SHA256CryptoServiceProvider SHA256 = new SHA256CryptoServiceProvider(); + + static byte[] ToCString(string s) + { + byte[] ret = enc.GetBytes(s); + Array.Resize(ref ret, ret.Length + 1); + ret[ret.Length - 1] = 0; + + return ret; + } + + protected IAssetLoader m_AssetLoader = null; + protected IFSAssetDataPlugin m_DataConnector = null; + protected IAssetService m_FallbackService; + protected Thread m_WriterThread; + protected Thread m_StatsThread; + protected string m_SpoolDirectory; + protected object m_readLock = new object(); + protected object m_statsLock = new object(); + protected int m_readCount = 0; + protected int m_readTicks = 0; + protected int m_missingAssets = 0; + protected int m_missingAssetsFS = 0; + protected string m_FSBase; + + private static bool m_Initialized; + private bool m_MainInstance; + + public FSAssetConnector(IConfigSource config) + : this(config, "AssetService") + { + } + + public FSAssetConnector(IConfigSource config, string configName) : base(config) + { + if (!m_Initialized) + { + m_Initialized = true; + m_MainInstance = true; + + MainConsole.Instance.Commands.AddCommand("fs", false, + "show assets", "show assets", "Show asset stats", + HandleShowAssets); + MainConsole.Instance.Commands.AddCommand("fs", false, + "show digest", "show digest ", "Show asset digest", + HandleShowDigest); + MainConsole.Instance.Commands.AddCommand("fs", false, + "delete asset", "delete asset ", + "Delete asset from database", + HandleDeleteAsset); + MainConsole.Instance.Commands.AddCommand("fs", false, + "import", "import [ ]", + "Import legacy assets", + HandleImportAssets); + MainConsole.Instance.Commands.AddCommand("fs", false, + "force import", "force import
[ ]", + "Import legacy assets, overwriting current content", + HandleImportAssets); + } + + IConfig assetConfig = config.Configs[configName]; + + if (assetConfig == null) + throw new Exception("No AssetService configuration"); + + // Get Database Connector from Asset Config (If present) + string dllName = assetConfig.GetString("StorageProvider", string.Empty); + string m_ConnectionString = assetConfig.GetString("ConnectionString", string.Empty); + string m_Realm = assetConfig.GetString("Realm", "fsassets"); + + int SkipAccessTimeDays = assetConfig.GetInt("DaysBetweenAccessTimeUpdates", 0); + + // If not found above, fallback to Database defaults + IConfig dbConfig = config.Configs["DatabaseService"]; + + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + + if (m_ConnectionString == String.Empty) + m_ConnectionString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // No databse connection found in either config + if (dllName.Equals(String.Empty)) + throw new Exception("No StorageProvider configured"); + + if (m_ConnectionString.Equals(String.Empty)) + throw new Exception("Missing database connection string"); + + // Create Storage Provider + m_DataConnector = LoadPlugin(dllName); + + if (m_DataConnector == null) + throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName)); + + // Initialize DB And perform any migrations required + m_DataConnector.Initialise(m_ConnectionString, m_Realm, SkipAccessTimeDays); + + // Setup Fallback Service + string str = assetConfig.GetString("FallbackService", string.Empty); + + if (str != string.Empty) + { + object[] args = new object[] { config }; + m_FallbackService = LoadPlugin(str, args); + if (m_FallbackService != null) + { + m_log.Info("[FSASSETS]: Fallback service loaded"); + } + else + { + m_log.Error("[FSASSETS]: Failed to load fallback service"); + } + } + + // Setup directory structure including temp directory + m_SpoolDirectory = assetConfig.GetString("SpoolDirectory", "/tmp"); + + string spoolTmp = Path.Combine(m_SpoolDirectory, "spool"); + + Directory.CreateDirectory(spoolTmp); + + m_FSBase = assetConfig.GetString("BaseDirectory", String.Empty); + if (m_FSBase == String.Empty) + { + m_log.ErrorFormat("[FSASSETS]: BaseDirectory not specified"); + throw new Exception("Configuration error"); + } + + if (m_MainInstance) + { + string loader = assetConfig.GetString("DefaultAssetLoader", string.Empty); + if (loader != string.Empty) + { + m_AssetLoader = LoadPlugin(loader); + string loaderArgs = assetConfig.GetString("AssetLoaderArgs", string.Empty); + m_log.InfoFormat("[FSASSETS]: Loading default asset set from {0}", loaderArgs); + m_AssetLoader.ForEachDefaultXmlAsset(loaderArgs, + delegate(AssetBase a) + { + Store(a, false); + }); + } + + m_WriterThread = new Thread(Writer); + m_WriterThread.Start(); + m_StatsThread = new Thread(Stats); + m_StatsThread.Start(); + } + + m_log.Info("[FSASSETS]: FS asset service enabled"); + } + + private void Stats() + { + while (true) + { + Thread.Sleep(60000); + + lock (m_statsLock) + { + if (m_readCount > 0) + { + double avg = (double)m_readTicks / (double)m_readCount; +// if (avg > 10000) +// Environment.Exit(0); + m_log.InfoFormat("[FSASSETS]: Read stats: {0} files, {1} ticks, avg {2:F2}, missing {3}, FS {4}", m_readCount, m_readTicks, (double)m_readTicks / (double)m_readCount, m_missingAssets, m_missingAssetsFS); + } + m_readCount = 0; + m_readTicks = 0; + m_missingAssets = 0; + m_missingAssetsFS = 0; + } + } + } + + private void Writer() + { + m_log.Info("[FSASSETS]: Writer started"); + + while (true) + { + string[] files = Directory.GetFiles(m_SpoolDirectory); + + if (files.Length > 0) + { + int tickCount = Environment.TickCount; + for (int i = 0 ; i < files.Length ; i++) + { + string hash = Path.GetFileNameWithoutExtension(files[i]); + string s = HashToFile(hash); + string diskFile = Path.Combine(m_FSBase, s); + + Directory.CreateDirectory(Path.GetDirectoryName(diskFile)); + try + { + byte[] data = File.ReadAllBytes(files[i]); + + using (GZipStream gz = new GZipStream(new FileStream(diskFile + ".gz", FileMode.Create), CompressionMode.Compress)) + { + gz.Write(data, 0, data.Length); + gz.Close(); + } + File.Delete(files[i]); + + //File.Move(files[i], diskFile); + } + catch(System.IO.IOException e) + { + if (e.Message.StartsWith("Win32 IO returned ERROR_ALREADY_EXISTS")) + File.Delete(files[i]); + else + throw; + } + } + int totalTicks = System.Environment.TickCount - tickCount; + if (totalTicks > 0) // Wrap? + { + m_log.InfoFormat("[FSASSETS]: Write cycle complete, {0} files, {1} ticks, avg {2:F2}", files.Length, totalTicks, (double)totalTicks / (double)files.Length); + } + } + + Thread.Sleep(1000); + } + } + + string GetSHA256Hash(byte[] data) + { + byte[] hash = SHA256.ComputeHash(data); + + return BitConverter.ToString(hash).Replace("-", String.Empty); + } + + public string HashToPath(string hash) + { + if (hash == null || hash.Length < 10) + return "junkyard"; + + return Path.Combine(hash.Substring(0, 3), + Path.Combine(hash.Substring(3, 3))); + /* + * The below is what core would normally use. + * This is modified to work in OSGrid, as seen + * above, because the SRAS data is structured + * that way. + */ + /* + return Path.Combine(hash.Substring(0, 2), + Path.Combine(hash.Substring(2, 2), + Path.Combine(hash.Substring(4, 2), + hash.Substring(6, 4)))); + */ + } + + private bool AssetExists(string hash) + { + string s = HashToFile(hash); + string diskFile = Path.Combine(m_FSBase, s); + + if (File.Exists(diskFile + ".gz") || File.Exists(diskFile)) + return true; + + return false; + } + + public virtual bool[] AssetsExist(string[] ids) + { + UUID[] uuid = Array.ConvertAll(ids, id => UUID.Parse(id)); + return m_DataConnector.AssetsExist(uuid); + } + + public string HashToFile(string hash) + { + return Path.Combine(HashToPath(hash), hash); + } + + public virtual AssetBase Get(string id) + { + string hash; + + return Get(id, out hash); + } + + private AssetBase Get(string id, out string sha) + { + string hash = string.Empty; + + int startTime = System.Environment.TickCount; + AssetMetadata metadata; + + lock (m_readLock) + { + metadata = m_DataConnector.Get(id, out hash); + } + + sha = hash; + + if (metadata == null) + { + AssetBase asset = null; + if (m_FallbackService != null) + { + asset = m_FallbackService.Get(id); + if (asset != null) + { + asset.Metadata.ContentType = + SLUtil.SLAssetTypeToContentType((int)asset.Type); + sha = GetSHA256Hash(asset.Data); + m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id); + Store(asset); + } + } + if (asset == null) + { + // m_log.InfoFormat("[FSASSETS]: Asset {0} not found", id); + m_missingAssets++; + } + return asset; + } + AssetBase newAsset = new AssetBase(); + newAsset.Metadata = metadata; + try + { + newAsset.Data = GetFsData(hash); + if (newAsset.Data.Length == 0) + { + AssetBase asset = null; + if (m_FallbackService != null) + { + asset = m_FallbackService.Get(id); + if (asset != null) + { + asset.Metadata.ContentType = + SLUtil.SLAssetTypeToContentType((int)asset.Type); + sha = GetSHA256Hash(asset.Data); + m_log.InfoFormat("[FSASSETS]: Added asset {0} from fallback to local store", id); + Store(asset); + } + } + if (asset == null) + m_missingAssetsFS++; + // m_log.InfoFormat("[FSASSETS]: Asset {0}, hash {1} not found in FS", id, hash); + else + { + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + return asset; + } + } + + lock (m_statsLock) + { + m_readTicks += Environment.TickCount - startTime; + m_readCount++; + } + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + if (newAsset.Type == (int)AssetType.Object && newAsset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(newAsset.Data)); + newAsset.Data = Utils.StringToBytes(xml); + } + + return newAsset; + } + catch (Exception exception) + { + m_log.Error(exception.ToString()); + Thread.Sleep(5000); + Environment.Exit(1); + return null; + } + } + + public virtual AssetMetadata GetMetadata(string id) + { + string hash; + return m_DataConnector.Get(id, out hash); + } + + public virtual byte[] GetData(string id) + { + string hash; + if (m_DataConnector.Get(id, out hash) == null) + return null; + + return GetFsData(hash); + } + + public bool Get(string id, Object sender, AssetRetrieved handler) + { + AssetBase asset = Get(id); + + handler(id, sender, asset); + + return true; + } + + public byte[] GetFsData(string hash) + { + string spoolFile = Path.Combine(m_SpoolDirectory, hash + ".asset"); + + if (File.Exists(spoolFile)) + { + try + { + byte[] content = File.ReadAllBytes(spoolFile); + + return content; + } + catch + { + } + } + + string file = HashToFile(hash); + string diskFile = Path.Combine(m_FSBase, file); + + if (File.Exists(diskFile + ".gz")) + { + try + { + using (GZipStream gz = new GZipStream(new FileStream(diskFile + ".gz", FileMode.Open, FileAccess.Read), CompressionMode.Decompress)) + { + using (MemoryStream ms = new MemoryStream()) + { + byte[] data = new byte[32768]; + int bytesRead; + + do + { + bytesRead = gz.Read(data, 0, 32768); + if (bytesRead > 0) + ms.Write(data, 0, bytesRead); + } while (bytesRead > 0); + + return ms.ToArray(); + } + } + } + catch (Exception) + { + return new Byte[0]; + } + } + else if (File.Exists(diskFile)) + { + try + { + byte[] content = File.ReadAllBytes(diskFile); + + return content; + } + catch + { + } + } + return new Byte[0]; + + } + + public virtual string Store(AssetBase asset) + { + return Store(asset, false); + } + + private string Store(AssetBase asset, bool force) + { + int tickCount = Environment.TickCount; + string hash = GetSHA256Hash(asset.Data); + + if (!AssetExists(hash)) + { + string tempFile = Path.Combine(Path.Combine(m_SpoolDirectory, "spool"), hash + ".asset"); + string finalFile = Path.Combine(m_SpoolDirectory, hash + ".asset"); + + if (!File.Exists(finalFile)) + { + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before storing on this server + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + + FileStream fs = File.Create(tempFile); + + fs.Write(asset.Data, 0, asset.Data.Length); + + fs.Close(); + + File.Move(tempFile, finalFile); + } + } + + if (asset.ID == string.Empty) + { + if (asset.FullID == UUID.Zero) + { + asset.FullID = UUID.Random(); + } + asset.ID = asset.FullID.ToString(); + } + else if (asset.FullID == UUID.Zero) + { + UUID uuid = UUID.Zero; + if (UUID.TryParse(asset.ID, out uuid)) + { + asset.FullID = uuid; + } + else + { + asset.FullID = UUID.Random(); + } + } + + if (!m_DataConnector.Store(asset.Metadata, hash)) + { + return UUID.Zero.ToString(); + } + else + { + return asset.ID; + } + } + + public bool UpdateContent(string id, byte[] data) + { + return false; + +// string oldhash; +// AssetMetadata meta = m_DataConnector.Get(id, out oldhash); +// +// if (meta == null) +// return false; +// +// AssetBase asset = new AssetBase(); +// asset.Metadata = meta; +// asset.Data = data; +// +// Store(asset); +// +// return true; + } + + public virtual bool Delete(string id) + { + m_DataConnector.Delete(id); + + return true; + } + + private void HandleShowAssets(string module, string[] args) + { + int num = m_DataConnector.Count(); + MainConsole.Instance.Output(string.Format("Total asset count: {0}", num)); + } + + private void HandleShowDigest(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: show digest "); + return; + } + + string hash; + AssetBase asset = Get(args[2], out hash); + + if (asset == null || asset.Data.Length == 0) + { + MainConsole.Instance.Output("Asset not found"); + return; + } + + int i; + + MainConsole.Instance.Output(String.Format("Name: {0}", asset.Name)); + MainConsole.Instance.Output(String.Format("Description: {0}", asset.Description)); + MainConsole.Instance.Output(String.Format("Type: {0}", asset.Type)); + MainConsole.Instance.Output(String.Format("Content-type: {0}", asset.Metadata.ContentType)); + MainConsole.Instance.Output(String.Format("Flags: {0}", asset.Metadata.Flags.ToString())); + MainConsole.Instance.Output(String.Format("FS file: {0}", HashToFile(hash))); + + for (i = 0 ; i < 5 ; i++) + { + int off = i * 16; + if (asset.Data.Length <= off) + break; + int len = 16; + if (asset.Data.Length < off + len) + len = asset.Data.Length - off; + + byte[] line = new byte[len]; + Array.Copy(asset.Data, off, line, 0, len); + + string text = BitConverter.ToString(line); + MainConsole.Instance.Output(String.Format("{0:x4}: {1}", off, text)); + } + } + + private void HandleDeleteAsset(string module, string[] args) + { + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: delete asset "); + return; + } + + AssetBase asset = Get(args[2]); + + if (asset == null || asset.Data.Length == 0) + { + MainConsole.Instance.Output("Asset not found"); + return; + } + + m_DataConnector.Delete(args[2]); + + MainConsole.Instance.Output("Asset deleted"); + } + + private void HandleImportAssets(string module, string[] args) + { + bool force = false; + if (args[0] == "force") + { + force = true; + List list = new List(args); + list.RemoveAt(0); + args = list.ToArray(); + } + if (args.Length < 3) + { + MainConsole.Instance.Output("Syntax: import
[ ]"); + } + else + { + string conn = args[1]; + string table = args[2]; + int start = 0; + int count = -1; + if (args.Length > 3) + { + start = Convert.ToInt32(args[3]); + } + if (args.Length > 4) + { + count = Convert.ToInt32(args[4]); + } + m_DataConnector.Import(conn, table, start, count, force, new FSStoreDelegate(Store)); + } + } + + public AssetBase GetCached(string id) + { + return Get(id); + } + } +} diff --git a/OpenSim/Services/FreeswitchService/FreeswitchService.cs b/OpenSim/Services/FreeswitchService/FreeswitchService.cs new file mode 100644 index 0000000000..201e72f511 --- /dev/null +++ b/OpenSim/Services/FreeswitchService/FreeswitchService.cs @@ -0,0 +1,407 @@ +/* + * 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.Text; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using System.Collections; + +namespace OpenSim.Services.FreeswitchService +{ + public class FreeswitchService : FreeswitchServiceBase, IFreeswitchService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public FreeswitchService(IConfigSource config) : base(config) + { + // Perform initilialization here + } + + public Hashtable HandleDialplanRequest(Hashtable request) + { + m_log.DebugFormat("[FreeSwitchVoice]: HandleDialplanRequest called with {0}",request.ToString()); + + Hashtable response = new Hashtable(); + +// foreach (DictionaryEntry item in request) +// { +//// m_log.InfoFormat("[FreeSwitchDirectory]: requestBody item {0} {1}",item.Key, item.Value); +// } + + string requestcontext = (string) request["Hunt-Context"]; + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + + if (m_freeSwitchContext != String.Empty && m_freeSwitchContext != requestcontext) + { + m_log.Debug("[FreeSwitchDirectory]: returning empty as it's for another context"); + response["str_response_string"] = ""; + } + else + { + response["str_response_string"] = String.Format(@" + +
+ " + + +/* + + + + + + */ + + @" + + + + + + + + + + + + + + + + + + + + +
+
", m_freeSwitchContext, m_freeSwitchRealm); + } + + return response; + } + + public Hashtable HandleDirectoryRequest(Hashtable request) + { + Hashtable response = new Hashtable(); + string domain = (string) request["domain"]; + if (domain != m_freeSwitchRealm) + { + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = ""; + } + else + { +// m_log.DebugFormat("[FreeSwitchDirectory]: HandleDirectoryRequest called with {0}",request.ToString()); + + // information in the request we might be interested in + + // Request 1 sip_auth for users account + + //Event-Calling-Function=sofia_reg_parse_auth + //Event-Calling-Line-Number=1494 + //action=sip_auth + //sip_user_agent=Vivox-SDK-2.1.3010.6151-Mac%20(Feb-11-2009/16%3A42%3A41) + //sip_auth_username=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) + //sip_auth_realm=9.20.151.43 + //sip_contact_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D (==) + //sip_contact_host=192.168.0.3 // this shouldnt really be a local IP, investigate STUN servers + //sip_to_user=xhZuXKmRpECyr2AARJYyGgg%3D%3D + //sip_to_host=9.20.151.43 + //sip_auth_method=REGISTER + //user=xhZuXKmRpECyr2AARJYyGgg%3D%3D + //domain=9.20.151.43 + //ip=9.167.220.137 // this is the correct IP rather than sip_contact_host above when through a vpn or NAT setup + +// foreach (DictionaryEntry item in request) +// m_log.DebugFormat("[FreeSwitchDirectory]: requestBody item {0} {1}", item.Key, item.Value); + + string eventCallingFunction = (string) request["Event-Calling-Function"]; + if (eventCallingFunction == null) + { + eventCallingFunction = "sofia_reg_parse_auth"; + } + + if (eventCallingFunction.Length == 0) + { + eventCallingFunction = "sofia_reg_parse_auth"; + } + + if (eventCallingFunction == "sofia_reg_parse_auth") + { + string sipAuthMethod = (string)request["sip_auth_method"]; + + if (sipAuthMethod == "REGISTER") + { + response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (sipAuthMethod == "INVITE") + { + response = HandleInvite(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else + { + m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown sip_auth_method {0}",sipAuthMethod); + response["int_response_code"] = 404; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + } + else if (eventCallingFunction == "switch_xml_locate_user") + { + response = HandleLocateUser(m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "user_data_function") // gets called when an avatar to avatar call is made + { + response = HandleLocateUser(m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "user_outgoing_channel") + { + response = HandleRegister(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "config_sofia") // happens once on freeswitch startup + { + response = HandleConfigSofia(m_freeSwitchContext, m_freeSwitchRealm, request); + } + else if (eventCallingFunction == "switch_load_network_lists") + { + //response = HandleLoadNetworkLists(request); + response["int_response_code"] = 404; + response["keepalive"] = false; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + else + { + m_log.ErrorFormat("[FreeSwitchVoice]: HandleDirectoryRequest unknown Event-Calling-Function {0}",eventCallingFunction); + response["int_response_code"] = 404; + response["keepalive"] = false; + response["content_type"] = "text/xml"; + response["str_response_string"] = ""; + } + } + return response; + } + + private Hashtable HandleRegister(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory]: HandleRegister called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string password = "1234"; + string domain = (string) request["domain"]; + string user = (string) request["user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user, password, Context); + + return response; + } + + private Hashtable HandleInvite(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory]: HandleInvite called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string password = "1234"; + string domain = (string) request["domain"]; + string user = (string) request["user"]; + string sipRequestUser = (string) request["sip_request_user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + ""+ + "\r\n" + + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user, password,sipRequestUser, Context); + + return response; + } + + private Hashtable HandleLocateUser(String Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory]: HandleLocateUser called"); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string domain = (string) request["domain"]; + string user = (string) request["user"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n"+ + "\r\n"+ + ""+ + "\r\n"+ + "\r\n" + + "\r\n" + + "
\r\n" + + "
\r\n", + domain , user); + + return response; + } + + private Hashtable HandleConfigSofia(string Context, string Realm, Hashtable request) + { + m_log.Info("[FreeSwitchDirectory]: HandleConfigSofia called."); + + // TODO the password we return needs to match that sent in the request, this is hard coded for now + string domain = (string) request["domain"]; + + Hashtable response = new Hashtable(); + response["content_type"] = "text/xml"; + response["keepalive"] = false; + response["int_response_code"] = 200; + response["str_response_string"] = String.Format( + "\r\n" + + "\r\n" + + "
\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n" + + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n"+ + ""+ + "\r\n" + + "\r\n"+ + "\r\n"+ + "\r\n"+ + "\r\n" + + "
\r\n" + + "
\r\n", + domain, Context); + + return response; + } + + public string GetJsonConfig() + { + OSDMap map = new OSDMap(9); + + map.Add("Realm", m_freeSwitchRealm); + map.Add("SIPProxy", m_freeSwitchSIPProxy); + map.Add("AttemptUseSTUN", m_freeSwitchAttemptUseSTUN); + map.Add("EchoServer", m_freeSwitchEchoServer); + map.Add("EchoPort", m_freeSwitchEchoPort); + map.Add("DefaultWellKnownIP", m_freeSwitchDefaultWellKnownIP); + map.Add("DefaultTimeout", m_freeSwitchDefaultTimeout); + map.Add("Context", m_freeSwitchContext); + map.Add("APIPrefix", m_freeSwitchAPIPrefix); + + return OSDParser.SerializeJsonString(map); + } + } +} diff --git a/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs new file mode 100644 index 0000000000..25c18b6a23 --- /dev/null +++ b/OpenSim/Services/FreeswitchService/FreeswitchServiceBase.cs @@ -0,0 +1,85 @@ +/* + * 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.Services.Interfaces; +using OpenSim.Services.Base; +using log4net; + +namespace OpenSim.Services.FreeswitchService +{ + public class FreeswitchServiceBase : ServiceBase + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected string m_freeSwitchRealm; + protected string m_freeSwitchSIPProxy; + protected bool m_freeSwitchAttemptUseSTUN = false; + protected string m_freeSwitchEchoServer; + protected int m_freeSwitchEchoPort = 50505; + protected string m_freeSwitchDefaultWellKnownIP; + protected int m_freeSwitchDefaultTimeout = 5000; + protected string m_freeSwitchContext = "default"; + protected string m_freeSwitchServerUser = "freeswitch"; + protected string m_freeSwitchServerPass = "password"; + protected readonly string m_freeSwitchAPIPrefix = "/fsapi"; + + protected bool m_Enabled = false; + + public FreeswitchServiceBase(IConfigSource config) : base(config) + { + // + // Try reading the [FreeswitchService] section first, if it exists + // + IConfig freeswitchConfig = config.Configs["FreeswitchService"]; + if (freeswitchConfig != null) + { + m_freeSwitchDefaultWellKnownIP = freeswitchConfig.GetString("ServerAddress", String.Empty); + if (m_freeSwitchDefaultWellKnownIP == String.Empty) + { + m_log.Error("[FREESWITCH]: No ServerAddress given, cannot start service."); + return; + } + + m_freeSwitchRealm = freeswitchConfig.GetString("Realm", m_freeSwitchDefaultWellKnownIP); + m_freeSwitchSIPProxy = freeswitchConfig.GetString("SIPProxy", m_freeSwitchDefaultWellKnownIP + ":5060"); + m_freeSwitchEchoServer = freeswitchConfig.GetString("EchoServer", m_freeSwitchDefaultWellKnownIP); + m_freeSwitchEchoPort = freeswitchConfig.GetInt("EchoPort", m_freeSwitchEchoPort); + m_freeSwitchAttemptUseSTUN = freeswitchConfig.GetBoolean("AttemptSTUN", false); // This may not work + m_freeSwitchDefaultTimeout = freeswitchConfig.GetInt("DefaultTimeout", m_freeSwitchDefaultTimeout); + m_freeSwitchContext = freeswitchConfig.GetString("Context", m_freeSwitchContext); + m_freeSwitchServerUser = freeswitchConfig.GetString("UserName", m_freeSwitchServerUser); + m_freeSwitchServerPass = freeswitchConfig.GetString("Password", m_freeSwitchServerPass); + + m_Enabled = true; + } + } + } +} diff --git a/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs b/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ca16f08325 --- /dev/null +++ b/OpenSim/Services/FreeswitchService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.FreeswitchService")] +[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("af7d2401-cfd9-4ba5-8d6c-8af629984123")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/Friends/FriendsService.cs b/OpenSim/Services/Friends/FriendsService.cs new file mode 100644 index 0000000000..e2033ac93d --- /dev/null +++ b/OpenSim/Services/Friends/FriendsService.cs @@ -0,0 +1,115 @@ +/* + * 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; +using OpenSim.Framework; +using System; +using System.Collections.Generic; +using OpenSim.Services.Interfaces; +using OpenSim.Data; +using Nini.Config; +using log4net; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +namespace OpenSim.Services.Friends +{ + public class FriendsService : FriendsServiceBase, IFriendsService + { + public FriendsService(IConfigSource config) : base(config) + { + } + + public virtual FriendInfo[] GetFriends(UUID PrincipalID) + { + FriendsData[] data = m_Database.GetFriends(PrincipalID); + List info = new List(); + + foreach (FriendsData d in data) + { + FriendInfo i = new FriendInfo(); + + i.PrincipalID = new UUID(d.PrincipalID); + i.Friend = d.Friend; + i.MyFlags = Convert.ToInt32(d.Data["Flags"]); + i.TheirFlags = Convert.ToInt32(d.Data["TheirFlags"]); + + info.Add(i); + } + + return info.ToArray(); + } + + public virtual FriendInfo[] GetFriends(string PrincipalID) + { + FriendsData[] data = m_Database.GetFriends(PrincipalID); + List info = new List(); + + foreach (FriendsData d in data) + { + FriendInfo i = new FriendInfo(); + + if (!UUID.TryParse(d.PrincipalID, out i.PrincipalID)) + { + string tmp = string.Empty; + if (!Util.ParseUniversalUserIdentifier(d.PrincipalID, out i.PrincipalID, out tmp, out tmp, out tmp, out tmp)) + // bad record. ignore this entry + continue; + } + i.Friend = d.Friend; + i.MyFlags = Convert.ToInt32(d.Data["Flags"]); + i.TheirFlags = Convert.ToInt32(d.Data["TheirFlags"]); + + info.Add(i); + } + + return info.ToArray(); + } + + public virtual bool StoreFriend(string PrincipalID, string Friend, int flags) + { + FriendsData d = new FriendsData(); + + d.PrincipalID = PrincipalID; + d.Friend = Friend; + d.Data = new Dictionary(); + d.Data["Flags"] = flags.ToString(); + + return m_Database.Store(d); + } + + public bool Delete(string principalID, string friend) + { + return m_Database.Delete(principalID, friend); + } + + public virtual bool Delete(UUID PrincipalID, string Friend) + { + return m_Database.Delete(PrincipalID, Friend); + } + + } +} diff --git a/OpenSim/Services/Friends/FriendsServiceBase.cs b/OpenSim/Services/Friends/FriendsServiceBase.cs new file mode 100644 index 0000000000..6ab0bff0a8 --- /dev/null +++ b/OpenSim/Services/Friends/FriendsServiceBase.cs @@ -0,0 +1,89 @@ +/* + * 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 log4net; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Services.Friends +{ + public class FriendsServiceBase : ServiceBase + { + protected IFriendsData m_Database = null; + + public FriendsServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + + // + // Try reading the [FriendsService] section first, if it exists + // + IConfig friendsConfig = config.Configs["FriendsService"]; + if (friendsConfig != null) + { + dllName = friendsConfig.GetString("StorageProvider", dllName); + connString = friendsConfig.GetString("ConnectionString", connString); + } + + // + // 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); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (String.Empty.Equals(dllName)) + throw new Exception("No StorageProvider configured"); + + string realm = "Friends"; + if (friendsConfig != null) + realm = friendsConfig.GetString("Realm", realm); + + m_Database = LoadPlugin(dllName, new Object[] { connString, realm }); + if (m_Database == null) + { + throw new Exception( + string.Format( + "Could not find a storage interface {0} in the given StorageProvider {1}", "IFriendsData", dllName)); + } + } + } +} diff --git a/OpenSim/Services/Friends/Properties/AssemblyInfo.cs b/OpenSim/Services/Friends/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f258f946f9 --- /dev/null +++ b/OpenSim/Services/Friends/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.FriendsService")] +[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("a265d071-e152-42cc-9674-3ddd053977f5")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs new file mode 100644 index 0000000000..8807397caa --- /dev/null +++ b/OpenSim/Services/GridService/GridService.cs @@ -0,0 +1,1007 @@ +/* + * 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.Net; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Data; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenMetaverse; + +namespace OpenSim.Services.GridService +{ + public class GridService : GridServiceBase, IGridService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + private string LogHeader = "[GRID SERVICE]"; + + private bool m_DeleteOnUnregister = true; + private static GridService m_RootInstance = null; + protected IConfigSource m_config; + protected static HypergridLinker m_HypergridLinker; + + protected IAuthenticationService m_AuthenticationService = null; + protected bool m_AllowDuplicateNames = false; + protected bool m_AllowHypergridMapSearch = false; + + protected bool m_SuppressVarregionOverlapCheckOnRegistration = false; + + private static Dictionary m_ExtraFeatures = new Dictionary(); + + public GridService(IConfigSource config) + : base(config) + { + m_log.DebugFormat("[GRID SERVICE]: Starting..."); + + m_config = config; + IConfig gridConfig = config.Configs["GridService"]; + + bool suppressConsoleCommands = false; + + if (gridConfig != null) + { + m_DeleteOnUnregister = gridConfig.GetBoolean("DeleteOnUnregister", true); + + string authService = gridConfig.GetString("AuthenticationService", String.Empty); + + if (authService != String.Empty) + { + Object[] args = new Object[] { config }; + m_AuthenticationService = ServerUtils.LoadPlugin(authService, args); + } + m_AllowDuplicateNames = gridConfig.GetBoolean("AllowDuplicateNames", m_AllowDuplicateNames); + m_AllowHypergridMapSearch = gridConfig.GetBoolean("AllowHypergridMapSearch", m_AllowHypergridMapSearch); + + m_SuppressVarregionOverlapCheckOnRegistration = gridConfig.GetBoolean("SuppressVarregionOverlapCheckOnRegistration", m_SuppressVarregionOverlapCheckOnRegistration); + + // This service is also used locally by a simulator running in grid mode. This switches prevents + // inappropriate console commands from being registered + suppressConsoleCommands = gridConfig.GetBoolean("SuppressConsoleCommands", suppressConsoleCommands); + } + + if (m_RootInstance == null) + { + m_RootInstance = this; + + if (!suppressConsoleCommands && MainConsole.Instance != null) + { + MainConsole.Instance.Commands.AddCommand("Regions", true, + "deregister region id", + "deregister region id +", + "Deregister a region manually.", + String.Empty, + HandleDeregisterRegion); + + MainConsole.Instance.Commands.AddCommand("Regions", true, + "show regions", + "show regions", + "Show details on all regions", + String.Empty, + HandleShowRegions); + + MainConsole.Instance.Commands.AddCommand("Regions", true, + "show region name", + "show region name ", + "Show details on a region", + String.Empty, + HandleShowRegion); + + MainConsole.Instance.Commands.AddCommand("Regions", true, + "show region at", + "show region at ", + "Show details on a region at the given co-ordinate.", + "For example, show region at 1000 1000", + HandleShowRegionAt); + + MainConsole.Instance.Commands.AddCommand("General", true, + "show grid size", + "show grid size", + "Show the current grid size (excluding hyperlink references)", + String.Empty, + HandleShowGridSize); + + MainConsole.Instance.Commands.AddCommand("Regions", true, + "set region flags", + "set region flags ", + "Set database flags for region", + String.Empty, + HandleSetFlags); + } + + if (!suppressConsoleCommands) + SetExtraServiceURLs(config); + + m_HypergridLinker = new HypergridLinker(m_config, this, m_Database); + } + } + + private void SetExtraServiceURLs(IConfigSource config) + { + IConfig loginConfig = config.Configs["LoginService"]; + IConfig gridConfig = config.Configs["GridService"]; + + if (loginConfig == null || gridConfig == null) + return; + + string configVal; + + configVal = loginConfig.GetString("SearchURL", string.Empty); + if (!string.IsNullOrEmpty(configVal)) + m_ExtraFeatures["search-server-url"] = configVal; + + configVal = loginConfig.GetString("MapTileURL", string.Empty); + if (!string.IsNullOrEmpty(configVal)) + { + // This URL must end with '/', the viewer doesn't check + configVal = configVal.Trim(); + if (!configVal.EndsWith("/")) + configVal = configVal + "/"; + m_ExtraFeatures["map-server-url"] = configVal; + } + + configVal = loginConfig.GetString("DestinationGuide", string.Empty); + if (!string.IsNullOrEmpty(configVal)) + m_ExtraFeatures["destination-guide-url"] = configVal; + + configVal = Util.GetConfigVarFromSections( + config, "GatekeeperURI", new string[] { "Startup", "Hypergrid" }, String.Empty); + if (!string.IsNullOrEmpty(configVal)) + m_ExtraFeatures["GridURL"] = configVal; + + configVal = Util.GetConfigVarFromSections( + config, "GridName", new string[] { "Const", "Hypergrid" }, String.Empty); + if (string.IsNullOrEmpty(configVal)) + configVal = Util.GetConfigVarFromSections( + config, "gridname", new string[] { "GridInfo" }, String.Empty); + if (!string.IsNullOrEmpty(configVal)) + m_ExtraFeatures["GridName"] = configVal; + + m_ExtraFeatures["ExportSupported"] = gridConfig.GetString("ExportSupported", "true"); + } + + #region IGridService + + public string RegisterRegion(UUID scopeID, GridRegion regionInfos) + { + IConfig gridConfig = m_config.Configs["GridService"]; + + if (regionInfos.RegionID == UUID.Zero) + return "Invalid RegionID - cannot be zero UUID"; + + String reason = "Region overlaps another region"; + RegionData region = FindAnyConflictingRegion(regionInfos, scopeID, out reason); + // If there is a conflicting region, if it has the same ID and same coordinates + // then it is a region re-registering (permissions and ownership checked later). + if ((region != null) + && ( (region.coordX != regionInfos.RegionCoordX) + || (region.coordY != regionInfos.RegionCoordY) + || (region.RegionID != regionInfos.RegionID) ) + ) + { + // If not same ID and same coordinates, this new region has conflicts and can't be registered. + m_log.WarnFormat("{0} Register region conflict in scope {1}. {2}", LogHeader, scopeID, reason); + return reason; + } + + if (region != null) + { + // There is a preexisting record + // + // Get it's flags + // + OpenSim.Framework.RegionFlags rflags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(region.Data["flags"]); + + // Is this a reservation? + // + if ((rflags & OpenSim.Framework.RegionFlags.Reservation) != 0) + { + // Regions reserved for the null key cannot be taken. + if ((string)region.Data["PrincipalID"] == UUID.Zero.ToString()) + return "Region location is reserved"; + + // Treat it as an auth request + // + // NOTE: Fudging the flags value here, so these flags + // should not be used elsewhere. Don't optimize + // this with the later retrieval of the same flags! + rflags |= OpenSim.Framework.RegionFlags.Authenticate; + } + + if ((rflags & OpenSim.Framework.RegionFlags.Authenticate) != 0) + { + // Can we authenticate at all? + // + if (m_AuthenticationService == null) + return "No authentication possible"; + + if (!m_AuthenticationService.Verify(new UUID(region.Data["PrincipalID"].ToString()), regionInfos.Token, 30)) + return "Bad authentication"; + } + } + + // If we get here, the destination is clear. Now for the real check. + + if (!m_AllowDuplicateNames) + { + List dupe = m_Database.Get(Util.EscapeForLike(regionInfos.RegionName), scopeID); + if (dupe != null && dupe.Count > 0) + { + foreach (RegionData d in dupe) + { + if (d.RegionID != regionInfos.RegionID) + { + m_log.WarnFormat("[GRID SERVICE]: Region tried to register using a duplicate name. New region: {0} ({1}), existing region: {2} ({3}).", + regionInfos.RegionName, regionInfos.RegionID, d.RegionName, d.RegionID); + return "Duplicate region name"; + } + } + } + } + + // If there is an old record for us, delete it if it is elsewhere. + region = m_Database.Get(regionInfos.RegionID, scopeID); + if ((region != null) && (region.RegionID == regionInfos.RegionID) && + ((region.posX != regionInfos.RegionLocX) || (region.posY != regionInfos.RegionLocY))) + { + if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.NoMove) != 0) + return "Can't move this region"; + + if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.LockedOut) != 0) + return "Region locked out"; + + // Region reregistering in other coordinates. Delete the old entry + m_log.DebugFormat("[GRID SERVICE]: Region {0} ({1}) was previously registered at {2}-{3}. Deleting old entry.", + regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionLocX, regionInfos.RegionLocY); + + try + { + m_Database.Delete(regionInfos.RegionID); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e); + } + } + + // Everything is ok, let's register + RegionData rdata = RegionInfo2RegionData(regionInfos); + rdata.ScopeID = scopeID; + + if (region != null) + { + int oldFlags = Convert.ToInt32(region.Data["flags"]); + + oldFlags &= ~(int)OpenSim.Framework.RegionFlags.Reservation; + + rdata.Data["flags"] = oldFlags.ToString(); // Preserve flags + } + else + { + rdata.Data["flags"] = "0"; + if ((gridConfig != null) && rdata.RegionName != string.Empty) + { + int newFlags = 0; + string regionName = rdata.RegionName.Trim().Replace(' ', '_'); + newFlags = ParseFlags(newFlags, gridConfig.GetString("DefaultRegionFlags", String.Empty)); + newFlags = ParseFlags(newFlags, gridConfig.GetString("Region_" + regionName, String.Empty)); + newFlags = ParseFlags(newFlags, gridConfig.GetString("Region_" + rdata.RegionID.ToString(), String.Empty)); + rdata.Data["flags"] = newFlags.ToString(); + } + } + + int flags = Convert.ToInt32(rdata.Data["flags"]); + flags |= (int)OpenSim.Framework.RegionFlags.RegionOnline; + rdata.Data["flags"] = flags.ToString(); + + try + { + rdata.Data["last_seen"] = Util.UnixTimeSinceEpoch(); + m_Database.Store(rdata); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e); + } + + m_log.DebugFormat + ("[GRID SERVICE]: Region {0} ({1}, {2}x{3}) registered at {4},{5} with flags {6}", + regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionSizeX, regionInfos.RegionSizeY, + regionInfos.RegionCoordX, regionInfos.RegionCoordY, + (OpenSim.Framework.RegionFlags)flags); + + return String.Empty; + } + + /// + /// Search the region map for regions conflicting with this region. + /// The region to be added is passed and we look for any existing regions that are + /// in the requested location, that are large varregions that overlap this region, or + /// are previously defined regions that would lie under this new region. + /// + /// Information on region requested to be added to the world map + /// Grid id for region + /// The reason the returned region conflicts with passed region + /// + private RegionData FindAnyConflictingRegion(GridRegion regionInfos, UUID scopeID, out string reason) + { + reason = "Reregistration"; + // First see if there is an existing region right where this region is trying to go + // (We keep this result so it can be returned if suppressing errors) + RegionData noErrorRegion = m_Database.Get(regionInfos.RegionLocX, regionInfos.RegionLocY, scopeID); + RegionData region = noErrorRegion; + if (region != null + && region.RegionID == regionInfos.RegionID + && region.sizeX == regionInfos.RegionSizeX + && region.sizeY == regionInfos.RegionSizeY) + { + // If this seems to be exactly the same region, return this as it could be + // a re-registration (permissions checked by calling routine). + m_log.DebugFormat("{0} FindAnyConflictingRegion: re-register of {1}", + LogHeader, RegionString(regionInfos)); + return region; + } + + // No region exactly there or we're resizing an existing region. + // Fetch regions that could be varregions overlapping requested location. + int xmin = regionInfos.RegionLocX - (int)Constants.MaximumRegionSize + 10; + int xmax = regionInfos.RegionLocX; + int ymin = regionInfos.RegionLocY - (int)Constants.MaximumRegionSize + 10; + int ymax = regionInfos.RegionLocY; + List rdatas = m_Database.Get(xmin, ymin, xmax, ymax, scopeID); + foreach (RegionData rdata in rdatas) + { + // m_log.DebugFormat("{0} FindAnyConflictingRegion: find existing. Checking {1}", LogHeader, RegionString(rdata) ); + if ( (rdata.posX + rdata.sizeX > regionInfos.RegionLocX) + && (rdata.posY + rdata.sizeY > regionInfos.RegionLocY) ) + { + region = rdata; + m_log.WarnFormat("{0} FindAnyConflictingRegion: conflict of {1} by existing varregion {2}", + LogHeader, RegionString(regionInfos), RegionString(region)); + reason = String.Format("Region location is overlapped by existing varregion {0}", + RegionString(region)); + + if (m_SuppressVarregionOverlapCheckOnRegistration) + region = noErrorRegion; + return region; + } + } + + // There isn't a region that overlaps this potential region. + // See if this potential region overlaps an existing region. + // First, a shortcut of not looking for overlap if new region is legacy region sized + // and connot overlap anything. + if (regionInfos.RegionSizeX != Constants.RegionSize + || regionInfos.RegionSizeY != Constants.RegionSize) + { + // trim range looked for so we don't pick up neighbor regions just off the edges + xmin = regionInfos.RegionLocX; + xmax = regionInfos.RegionLocX + regionInfos.RegionSizeX - 10; + ymin = regionInfos.RegionLocY; + ymax = regionInfos.RegionLocY + regionInfos.RegionSizeY - 10; + rdatas = m_Database.Get(xmin, ymin, xmax, ymax, scopeID); + + // If the region is being resized, the found region could be ourself. + foreach (RegionData rdata in rdatas) + { + // m_log.DebugFormat("{0} FindAnyConflictingRegion: see if overlap. Checking {1}", LogHeader, RegionString(rdata) ); + if (region == null || region.RegionID != regionInfos.RegionID) + { + region = rdata; + m_log.WarnFormat("{0} FindAnyConflictingRegion: conflict of varregion {1} overlaps existing region {2}", + LogHeader, RegionString(regionInfos), RegionString(region)); + reason = String.Format("Region {0} would overlap existing region {1}", + RegionString(regionInfos), RegionString(region)); + + if (m_SuppressVarregionOverlapCheckOnRegistration) + region = noErrorRegion; + return region; + } + } + } + + // If we get here, region is either null (nothing found here) or + // is the non-conflicting region found at the location being requested. + return region; + } + + // String describing name and region location of passed region + private String RegionString(RegionData reg) + { + return String.Format("{0}/{1} at <{2},{3}>", + reg.RegionName, reg.RegionID, reg.coordX, reg.coordY); + } + + // String describing name and region location of passed region + private String RegionString(GridRegion reg) + { + return String.Format("{0}/{1} at <{2},{3}>", + reg.RegionName, reg.RegionID, reg.RegionCoordX, reg.RegionCoordY); + } + + public bool DeregisterRegion(UUID regionID) + { + RegionData region = m_Database.Get(regionID, UUID.Zero); + if (region == null) + return false; + + m_log.DebugFormat( + "[GRID SERVICE]: Deregistering region {0} ({1}) at {2}-{3}", + region.RegionName, region.RegionID, region.coordX, region.coordY); + + int flags = Convert.ToInt32(region.Data["flags"]); + + if (!m_DeleteOnUnregister || (flags & (int)OpenSim.Framework.RegionFlags.Persistent) != 0) + { + flags &= ~(int)OpenSim.Framework.RegionFlags.RegionOnline; + region.Data["flags"] = flags.ToString(); + region.Data["last_seen"] = Util.UnixTimeSinceEpoch(); + try + { + m_Database.Store(region); + } + catch (Exception e) + { + m_log.DebugFormat("[GRID SERVICE]: Database exception: {0}", e); + } + + return true; + + } + + return m_Database.Delete(regionID); + } + + public List GetNeighbours(UUID scopeID, UUID regionID) + { + List rinfos = new List(); + RegionData region = m_Database.Get(regionID, scopeID); + + if (region != null) + { + // Not really? Maybe? + // The adjacent regions are presumed to be the same size as the current region + List rdatas = m_Database.Get( + region.posX - region.sizeX - 1, region.posY - region.sizeY - 1, + region.posX + region.sizeX + 1, region.posY + region.sizeY + 1, scopeID); + + foreach (RegionData rdata in rdatas) + { + if (rdata.RegionID != regionID) + { + int flags = Convert.ToInt32(rdata.Data["flags"]); + if ((flags & (int)Framework.RegionFlags.Hyperlink) == 0) // no hyperlinks as neighbours + rinfos.Add(RegionData2RegionInfo(rdata)); + } + } + + // string rNames = ""; + // foreach (GridRegion gr in rinfos) + // rNames += gr.RegionName + ","; + // m_log.DebugFormat("{0} region {1} has {2} neighbours ({3})", + // LogHeader, region.RegionName, rinfos.Count, rNames); + } + else + { + m_log.WarnFormat( + "[GRID SERVICE]: GetNeighbours() called for scope {0}, region {1} but no such region found", + scopeID, regionID); + } + + return rinfos; + } + + public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID) + { + RegionData rdata = m_Database.Get(regionID, scopeID); + if (rdata != null) + return RegionData2RegionInfo(rdata); + + return null; + } + + // Get a region given its base coordinates. + // NOTE: this is NOT 'get a region by some point in the region'. The coordinate MUST + // be the base coordinate of the region. + // The snapping is technically unnecessary but is harmless because regions are always + // multiples of the legacy region size (256). + public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) + { + uint regionX = Util.WorldToRegionLoc((uint)x); + uint regionY = Util.WorldToRegionLoc((uint)y); + int snapX = (int)Util.RegionToWorldLoc(regionX); + int snapY = (int)Util.RegionToWorldLoc(regionY); + + RegionData rdata = m_Database.Get(snapX, snapY, scopeID); + if (rdata != null) + { + m_log.DebugFormat("{0} GetRegionByPosition. Found region {1} in database. Pos=<{2},{3}>", + LogHeader, rdata.RegionName, regionX, regionY); + return RegionData2RegionInfo(rdata); + } + else + { + m_log.DebugFormat("{0} GetRegionByPosition. Did not find region in database. Pos=<{1},{2}>", + LogHeader, regionX, regionY); + return null; + } + } + + public GridRegion GetRegionByName(UUID scopeID, string name) + { + List rdatas = m_Database.Get(Util.EscapeForLike(name), scopeID); + if ((rdatas != null) && (rdatas.Count > 0)) + return RegionData2RegionInfo(rdatas[0]); // get the first + + if (m_AllowHypergridMapSearch) + { + GridRegion r = GetHypergridRegionByName(scopeID, name); + if (r != null) + return r; + } + + return null; + } + + public List GetRegionsByName(UUID scopeID, string name, int maxNumber) + { +// m_log.DebugFormat("[GRID SERVICE]: GetRegionsByName {0}", name); + + List rdatas = m_Database.Get(Util.EscapeForLike(name) + "%", scopeID); + + int count = 0; + List rinfos = new List(); + + if (rdatas != null) + { +// m_log.DebugFormat("[GRID SERVICE]: Found {0} regions", rdatas.Count); + foreach (RegionData rdata in rdatas) + { + if (count++ < maxNumber) + rinfos.Add(RegionData2RegionInfo(rdata)); + } + } + + if (m_AllowHypergridMapSearch && (rdatas == null || (rdatas != null && rdatas.Count == 0))) + { + GridRegion r = GetHypergridRegionByName(scopeID, name); + if (r != null) + rinfos.Add(r); + } + + return rinfos; + } + + /// + /// Get a hypergrid region. + /// + /// + /// + /// null if no hypergrid region could be found. + protected GridRegion GetHypergridRegionByName(UUID scopeID, string name) + { + if (name.Contains(".")) + return m_HypergridLinker.LinkRegion(scopeID, name); + else + return null; + } + + public List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax) + { + int xminSnap = (int)(xmin / Constants.RegionSize) * (int)Constants.RegionSize; + int xmaxSnap = (int)(xmax / Constants.RegionSize) * (int)Constants.RegionSize; + int yminSnap = (int)(ymin / Constants.RegionSize) * (int)Constants.RegionSize; + int ymaxSnap = (int)(ymax / Constants.RegionSize) * (int)Constants.RegionSize; + + List rdatas = m_Database.Get(xminSnap, yminSnap, xmaxSnap, ymaxSnap, scopeID); + List rinfos = new List(); + foreach (RegionData rdata in rdatas) + rinfos.Add(RegionData2RegionInfo(rdata)); + + return rinfos; + } + + #endregion + + #region Data structure conversions + + public RegionData RegionInfo2RegionData(GridRegion rinfo) + { + RegionData rdata = new RegionData(); + rdata.posX = (int)rinfo.RegionLocX; + rdata.posY = (int)rinfo.RegionLocY; + rdata.sizeX = rinfo.RegionSizeX; + rdata.sizeY = rinfo.RegionSizeY; + rdata.RegionID = rinfo.RegionID; + rdata.RegionName = rinfo.RegionName; + rdata.Data = rinfo.ToKeyValuePairs(); + rdata.Data["regionHandle"] = Utils.UIntsToLong((uint)rdata.posX, (uint)rdata.posY); + rdata.Data["owner_uuid"] = rinfo.EstateOwner.ToString(); + return rdata; + } + + public GridRegion RegionData2RegionInfo(RegionData rdata) + { + GridRegion rinfo = new GridRegion(rdata.Data); + rinfo.RegionLocX = rdata.posX; + rinfo.RegionLocY = rdata.posY; + rinfo.RegionSizeX = rdata.sizeX; + rinfo.RegionSizeY = rdata.sizeY; + rinfo.RegionID = rdata.RegionID; + rinfo.RegionName = rdata.RegionName; + rinfo.ScopeID = rdata.ScopeID; + + return rinfo; + } + + #endregion + + public List GetDefaultRegions(UUID scopeID) + { + List ret = new List(); + + List regions = m_Database.GetDefaultRegions(scopeID); + + foreach (RegionData r in regions) + { + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) + ret.Add(RegionData2RegionInfo(r)); + } + + m_log.DebugFormat("[GRID SERVICE]: GetDefaultRegions returning {0} regions", ret.Count); + return ret; + } + + public List GetDefaultHypergridRegions(UUID scopeID) + { + List ret = new List(); + + List regions = m_Database.GetDefaultHypergridRegions(scopeID); + + foreach (RegionData r in regions) + { + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) + ret.Add(RegionData2RegionInfo(r)); + } + + int hgDefaultRegionsFoundOnline = regions.Count; + + // For now, hypergrid default regions will always be given precedence but we will also return simple default + // regions in case no specific hypergrid regions are specified. + ret.AddRange(GetDefaultRegions(scopeID)); + + int normalDefaultRegionsFoundOnline = ret.Count - hgDefaultRegionsFoundOnline; + + m_log.DebugFormat( + "[GRID SERVICE]: GetDefaultHypergridRegions returning {0} hypergrid default and {1} normal default regions", + hgDefaultRegionsFoundOnline, normalDefaultRegionsFoundOnline); + + return ret; + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + List ret = new List(); + + List regions = m_Database.GetFallbackRegions(scopeID, x, y); + + foreach (RegionData r in regions) + { + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) + ret.Add(RegionData2RegionInfo(r)); + } + + m_log.DebugFormat("[GRID SERVICE]: Fallback returned {0} regions", ret.Count); + return ret; + } + + public List GetHyperlinks(UUID scopeID) + { + List ret = new List(); + + List regions = m_Database.GetHyperlinks(scopeID); + + foreach (RegionData r in regions) + { + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) + ret.Add(RegionData2RegionInfo(r)); + } + + m_log.DebugFormat("[GRID SERVICE]: Hyperlinks returned {0} regions", ret.Count); + return ret; + } + + public int GetRegionFlags(UUID scopeID, UUID regionID) + { + RegionData region = m_Database.Get(regionID, scopeID); + + if (region != null) + { + int flags = Convert.ToInt32(region.Data["flags"]); + //m_log.DebugFormat("[GRID SERVICE]: Request for flags of {0}: {1}", regionID, flags); + return flags; + } + else + return -1; + } + + private void HandleDeregisterRegion(string module, string[] cmd) + { + if (cmd.Length < 4) + { + MainConsole.Instance.Output("Usage: degregister region id +"); + return; + } + + for (int i = 3; i < cmd.Length; i++) + { + string rawRegionUuid = cmd[i]; + UUID regionUuid; + + if (!UUID.TryParse(rawRegionUuid, out regionUuid)) + { + MainConsole.Instance.OutputFormat("{0} is not a valid region uuid", rawRegionUuid); + return; + } + + GridRegion region = GetRegionByUUID(UUID.Zero, regionUuid); + + if (region == null) + { + MainConsole.Instance.OutputFormat("No region with UUID {0}", regionUuid); + return; + } + + if (DeregisterRegion(regionUuid)) + { + MainConsole.Instance.OutputFormat("Deregistered {0} {1}", region.RegionName, regionUuid); + } + else + { + // I don't think this can ever occur if we know that the region exists. + MainConsole.Instance.OutputFormat("Error deregistering {0} {1}", region.RegionName, regionUuid); + } + } + } + + private void HandleShowRegions(string module, string[] cmd) + { + if (cmd.Length != 2) + { + MainConsole.Instance.Output("Syntax: show regions"); + return; + } + + List regions = m_Database.Get(int.MinValue, int.MinValue, int.MaxValue, int.MaxValue, UUID.Zero); + + OutputRegionsToConsoleSummary(regions); + } + + private void HandleShowGridSize(string module, string[] cmd) + { + List regions = m_Database.Get(int.MinValue, int.MinValue, int.MaxValue, int.MaxValue, UUID.Zero); + + double size = 0; + + foreach (RegionData region in regions) + { + int flags = Convert.ToInt32(region.Data["flags"]); + + if ((flags & (int)Framework.RegionFlags.Hyperlink) == 0) + size += region.sizeX * region.sizeY; + } + + MainConsole.Instance.Output("This is a very rough approximation."); + MainConsole.Instance.Output("Although it will not count regions that are actually links to others over the Hypergrid, "); + MainConsole.Instance.Output("it will count regions that are inactive but were not deregistered from the grid service"); + MainConsole.Instance.Output("(e.g. simulator crashed rather than shutting down cleanly).\n"); + + MainConsole.Instance.OutputFormat("Grid size: {0} km squared.", size / 1000000); + } + + private void HandleShowRegion(string module, string[] cmd) + { + if (cmd.Length != 4) + { + MainConsole.Instance.Output("Syntax: show region name "); + return; + } + + string regionName = cmd[3]; + + List regions = m_Database.Get(Util.EscapeForLike(regionName), UUID.Zero); + if (regions == null || regions.Count < 1) + { + MainConsole.Instance.Output("No region with name {0} found", regionName); + return; + } + + OutputRegionsToConsole(regions); + } + + private void HandleShowRegionAt(string module, string[] cmd) + { + if (cmd.Length != 5) + { + MainConsole.Instance.Output("Syntax: show region at "); + return; + } + + uint x, y; + if (!uint.TryParse(cmd[3], out x)) + { + MainConsole.Instance.Output("x-coord must be an integer"); + return; + } + + if (!uint.TryParse(cmd[4], out y)) + { + MainConsole.Instance.Output("y-coord must be an integer"); + return; + } + + RegionData region = m_Database.Get((int)Util.RegionToWorldLoc(x), (int)Util.RegionToWorldLoc(y), UUID.Zero); + if (region == null) + { + MainConsole.Instance.OutputFormat("No region found at {0},{1}", x, y); + return; + } + + OutputRegionToConsole(region); + } + + private void OutputRegionToConsole(RegionData r) + { + OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]); + + ConsoleDisplayList dispList = new ConsoleDisplayList(); + dispList.AddRow("Region Name", r.RegionName); + dispList.AddRow("Region ID", r.RegionID); + dispList.AddRow("Position", string.Format("{0},{1}", r.coordX, r.coordY)); + dispList.AddRow("Size", string.Format("{0}x{1}", r.sizeX, r.sizeY)); + dispList.AddRow("URI", r.Data["serverURI"]); + dispList.AddRow("Owner ID", r.Data["owner_uuid"]); + dispList.AddRow("Flags", flags); + + MainConsole.Instance.Output(dispList.ToString()); + } + + private void OutputRegionsToConsole(List regions) + { + foreach (RegionData r in regions) + OutputRegionToConsole(r); + } + + private void OutputRegionsToConsoleSummary(List regions) + { + ConsoleDisplayTable dispTable = new ConsoleDisplayTable(); + dispTable.AddColumn("Name", 44); + dispTable.AddColumn("ID", 36); + dispTable.AddColumn("Position", 11); + dispTable.AddColumn("Size", 11); + dispTable.AddColumn("Flags", 60); + + foreach (RegionData r in regions) + { + OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]); + dispTable.AddRow( + r.RegionName, + r.RegionID.ToString(), + string.Format("{0},{1}", r.coordX, r.coordY), + string.Format("{0}x{1}", r.sizeX, r.sizeY), + flags.ToString()); + } + + MainConsole.Instance.Output(dispTable.ToString()); + } + + private int ParseFlags(int prev, string flags) + { + OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)prev; + + string[] parts = flags.Split(new char[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries); + + foreach (string p in parts) + { + int val; + + try + { + if (p.StartsWith("+")) + { + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1)); + f |= (OpenSim.Framework.RegionFlags)val; + } + else if (p.StartsWith("-")) + { + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1)); + f &= ~(OpenSim.Framework.RegionFlags)val; + } + else + { + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p); + f |= (OpenSim.Framework.RegionFlags)val; + } + } + catch (Exception) + { + MainConsole.Instance.Output("Error in flag specification: " + p); + } + } + + return (int)f; + } + + private void HandleSetFlags(string module, string[] cmd) + { + if (cmd.Length < 5) + { + MainConsole.Instance.Output("Syntax: set region flags "); + return; + } + + List regions = m_Database.Get(Util.EscapeForLike(cmd[3]), UUID.Zero); + if (regions == null || regions.Count < 1) + { + MainConsole.Instance.Output("Region not found"); + return; + } + + foreach (RegionData r in regions) + { + int flags = Convert.ToInt32(r.Data["flags"]); + flags = ParseFlags(flags, cmd[4]); + r.Data["flags"] = flags.ToString(); + OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)flags; + + MainConsole.Instance.Output(String.Format("Set region {0} to {1}", r.RegionName, f)); + m_Database.Store(r); + } + } + + /// + /// Gets the grid extra service URls we wish for the region to send in OpenSimExtras to dynamically refresh + /// parameters in the viewer used to access services like map, search and destination guides. + /// see "SimulatorFeaturesModule" + /// + /// + /// The grid extra service URls. + /// + public Dictionary GetExtraFeatures() + { + return m_ExtraFeatures; + } + } +} diff --git a/OpenSim/Services/GridService/GridServiceBase.cs b/OpenSim/Services/GridService/GridServiceBase.cs new file mode 100644 index 0000000000..444f79b40f --- /dev/null +++ b/OpenSim/Services/GridService/GridServiceBase.cs @@ -0,0 +1,84 @@ +/* + * 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.Services.GridService +{ + public class GridServiceBase : ServiceBase + { + protected IRegionData m_Database = null; + + public GridServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "regions"; + + // + // 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); + } + + // + // [GridService] section overrides [DatabaseService], if it exists + // + IConfig gridConfig = config.Configs["GridService"]; + if (gridConfig != null) + { + dllName = gridConfig.GetString("StorageProvider", dllName); + connString = gridConfig.GetString("ConnectionString", connString); + realm = gridConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + + } + } +} diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs new file mode 100644 index 0000000000..9d016fc3d4 --- /dev/null +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -0,0 +1,799 @@ +/* + * 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.Net; +using System.Reflection; +using System.Xml; + +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Data; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.Hypergrid; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenMetaverse; + +namespace OpenSim.Services.GridService +{ + public class HypergridLinker : IHypergridLinker + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private static uint m_autoMappingX = 0; + private static uint m_autoMappingY = 0; + private static bool m_enableAutoMapping = false; + + protected IRegionData m_Database; + protected GridService m_GridService; + protected IAssetService m_AssetService; + protected GatekeeperServiceConnector m_GatekeeperConnector; + + protected UUID m_ScopeID = UUID.Zero; +// protected bool m_Check4096 = true; + protected string m_MapTileDirectory = string.Empty; + protected string m_ThisGatekeeper = string.Empty; + protected Uri m_ThisGatekeeperURI = null; + + protected GridRegion m_DefaultRegion; + protected GridRegion DefaultRegion + { + get + { + if (m_DefaultRegion == null) + { + List defs = m_GridService.GetDefaultHypergridRegions(m_ScopeID); + if (defs != null && defs.Count > 0) + m_DefaultRegion = defs[0]; + else + { + // Get any region + defs = m_GridService.GetRegionsByName(m_ScopeID, "", 1); + if (defs != null && defs.Count > 0) + m_DefaultRegion = defs[0]; + else + { + // This shouldn't happen + m_DefaultRegion = new GridRegion(1000, 1000); + m_log.Error("[HYPERGRID LINKER]: Something is wrong with this grid. It has no regions?"); + } + } + } + return m_DefaultRegion; + } + } + + public HypergridLinker(IConfigSource config, GridService gridService, IRegionData db) + { + IConfig gridConfig = config.Configs["GridService"]; + if (gridConfig == null) + return; + + if (!gridConfig.GetBoolean("HypergridLinker", false)) + return; + + m_Database = db; + m_GridService = gridService; + m_log.DebugFormat("[HYPERGRID LINKER]: Starting with db {0}", db.GetType()); + + string assetService = gridConfig.GetString("AssetService", string.Empty); + + Object[] args = new Object[] { config }; + + if (assetService != string.Empty) + m_AssetService = ServerUtils.LoadPlugin(assetService, args); + + string scope = gridConfig.GetString("ScopeID", string.Empty); + if (scope != string.Empty) + UUID.TryParse(scope, out m_ScopeID); + +// m_Check4096 = gridConfig.GetBoolean("Check4096", true); + + m_MapTileDirectory = gridConfig.GetString("MapTileDirectory", "maptiles"); + + m_ThisGatekeeper = Util.GetConfigVarFromSections(config, "GatekeeperURI", + new string[] { "Startup", "Hypergrid", "GridService" }, String.Empty); + // Legacy. Remove soon! + m_ThisGatekeeper = gridConfig.GetString("Gatekeeper", m_ThisGatekeeper); + try + { + m_ThisGatekeeperURI = new Uri(m_ThisGatekeeper); + } + catch + { + m_log.WarnFormat("[HYPERGRID LINKER]: Malformed URL in [GridService], variable Gatekeeper = {0}", m_ThisGatekeeper); + } + + m_GatekeeperConnector = new GatekeeperServiceConnector(m_AssetService); + + m_log.Debug("[HYPERGRID LINKER]: Loaded all services..."); + + if (!string.IsNullOrEmpty(m_MapTileDirectory)) + { + try + { + Directory.CreateDirectory(m_MapTileDirectory); + } + catch (Exception e) + { + m_log.WarnFormat("[HYPERGRID LINKER]: Could not create map tile storage directory {0}: {1}", m_MapTileDirectory, e); + m_MapTileDirectory = string.Empty; + } + } + + if (MainConsole.Instance != null) + { + MainConsole.Instance.Commands.AddCommand("Hypergrid", false, "link-region", + "link-region []", + "Link a HyperGrid Region. Examples for : http://grid.net:8002/ or http://example.org/path/foo.php", RunCommand); + MainConsole.Instance.Commands.AddCommand("Hypergrid", false, "link-region", + "link-region []", + "Link a hypergrid region (deprecated)", RunCommand); + MainConsole.Instance.Commands.AddCommand("Hypergrid", false, "unlink-region", + "unlink-region ", + "Unlink a hypergrid region", RunCommand); + MainConsole.Instance.Commands.AddCommand("Hypergrid", false, "link-mapping", "link-mapping [ ]", + "Set local coordinate to map HG regions to", RunCommand); + MainConsole.Instance.Commands.AddCommand("Hypergrid", false, "show hyperlinks", "show hyperlinks", + "List the HG regions", HandleShow); + } + } + + + #region Link Region + + // from map search + public GridRegion LinkRegion(UUID scopeID, string regionDescriptor) + { + string reason = string.Empty; + uint xloc = Util.RegionToWorldLoc((uint)random.Next(0, Int16.MaxValue)); + return TryLinkRegionToCoords(scopeID, regionDescriptor, (int)xloc, 0, out reason); + } + + private static Random random = new Random(); + + // From the command line link-region (obsolete) and the map + private GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, out string reason) + { + return TryLinkRegionToCoords(scopeID, mapName, xloc, yloc, UUID.Zero, out reason); + } + + public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason) + { + reason = string.Empty; + GridRegion regInfo = null; + + mapName = mapName.Trim(); + + if (!mapName.StartsWith("http")) + { + // Formats: grid.example.com:8002:region name + // grid.example.com:region name + // grid.example.com:8002 + // grid.example.com + + string host; + uint port = 80; + string regionName = ""; + + string[] parts = mapName.Split(new char[] { ':' }); + + if (parts.Length == 0) + { + reason = "Wrong format for link-region"; + return null; + } + + host = parts[0]; + + if (parts.Length >= 2) + { + // If it's a number then assume it's a port. Otherwise, it's a region name. + if (!UInt32.TryParse(parts[1], out port)) + regionName = parts[1]; + } + + // always take the last one + if (parts.Length >= 3) + { + regionName = parts[2]; + } + + + bool success = TryCreateLink(scopeID, xloc, yloc, regionName, port, host, ownerID, out regInfo, out reason); + if (success) + { + regInfo.RegionName = mapName; + return regInfo; + } + } + else + { + // Formats: http://grid.example.com region name + // http://grid.example.com "region name" + // http://grid.example.com + + string serverURI; + string regionName = ""; + + string[] parts = mapName.Split(new char[] { ' ' }); + + if (parts.Length == 0) + { + reason = "Wrong format for link-region"; + return null; + } + + serverURI = parts[0]; + + if (parts.Length >= 2) + { + regionName = mapName.Substring(serverURI.Length); + regionName = regionName.Trim(new char[] { '"', ' ' }); + } + + if (TryCreateLink(scopeID, xloc, yloc, regionName, 0, null, serverURI, ownerID, out regInfo, out reason)) + { + regInfo.RegionName = mapName; + return regInfo; + } + } + + return null; + } + + private bool TryCreateLink(UUID scopeID, int xloc, int yloc, string remoteRegionName, uint externalPort, string externalHostName, UUID ownerID, out GridRegion regInfo, out string reason) + { + return TryCreateLink(scopeID, xloc, yloc, remoteRegionName, externalPort, externalHostName, null, ownerID, out regInfo, out reason); + } + + private bool TryCreateLink(UUID scopeID, int xloc, int yloc, string remoteRegionName, uint externalPort, string externalHostName, string serverURI, UUID ownerID, out GridRegion regInfo, out string reason) + { + lock (this) + { + return TryCreateLinkImpl(scopeID, xloc, yloc, remoteRegionName, externalPort, externalHostName, serverURI, ownerID, out regInfo, out reason); + } + } + + private bool TryCreateLinkImpl(UUID scopeID, int xloc, int yloc, string remoteRegionName, uint externalPort, string externalHostName, string serverURI, UUID ownerID, out GridRegion regInfo, out string reason) + { + m_log.InfoFormat("[HYPERGRID LINKER]: Link to {0} {1}, in <{2},{3}>", + ((serverURI == null) ? (externalHostName + ":" + externalPort) : serverURI), + remoteRegionName, Util.WorldToRegionLoc((uint)xloc), Util.WorldToRegionLoc((uint)yloc)); + + reason = string.Empty; + Uri uri = null; + + regInfo = new GridRegion(); + if (externalPort > 0) + regInfo.HttpPort = externalPort; + else + regInfo.HttpPort = 80; + if (externalHostName != null) + regInfo.ExternalHostName = externalHostName; + else + regInfo.ExternalHostName = "0.0.0.0"; + if (serverURI != null) + { + regInfo.ServerURI = serverURI; + try + { + uri = new Uri(serverURI); + regInfo.ExternalHostName = uri.Host; + regInfo.HttpPort = (uint)uri.Port; + } + catch {} + } + + if (remoteRegionName != string.Empty) + regInfo.RegionName = remoteRegionName; + + regInfo.RegionLocX = xloc; + regInfo.RegionLocY = yloc; + regInfo.ScopeID = scopeID; + regInfo.EstateOwner = ownerID; + + // Make sure we're not hyperlinking to regions on this grid! + if (m_ThisGatekeeperURI != null) + { + if (regInfo.ExternalHostName == m_ThisGatekeeperURI.Host && regInfo.HttpPort == m_ThisGatekeeperURI.Port) + { + m_log.InfoFormat("[HYPERGRID LINKER]: Cannot hyperlink to regions on the same grid"); + reason = "Cannot hyperlink to regions on the same grid"; + return false; + } + } + else + m_log.WarnFormat("[HYPERGRID LINKER]: Please set this grid's Gatekeeper's address in [GridService]!"); + + // Check for free coordinates + GridRegion region = m_GridService.GetRegionByPosition(regInfo.ScopeID, regInfo.RegionLocX, regInfo.RegionLocY); + if (region != null) + { + m_log.WarnFormat("[HYPERGRID LINKER]: Coordinates <{0},{1}> are already occupied by region {2} with uuid {3}", + Util.WorldToRegionLoc((uint)regInfo.RegionLocX), Util.WorldToRegionLoc((uint)regInfo.RegionLocY), + region.RegionName, region.RegionID); + reason = "Coordinates are already in use"; + return false; + } + + try + { + regInfo.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), (int)0); + } + catch (Exception e) + { + m_log.Warn("[HYPERGRID LINKER]: Wrong format for link-region: " + e.Message); + reason = "Internal error"; + return false; + } + + // Finally, link it + ulong handle = 0; + UUID regionID = UUID.Zero; + string externalName = string.Empty; + string imageURL = string.Empty; + if (!m_GatekeeperConnector.LinkRegion(regInfo, out regionID, out handle, out externalName, out imageURL, out reason)) + return false; + + if (regionID == UUID.Zero) + { + m_log.Warn("[HYPERGRID LINKER]: Unable to link region"); + reason = "Remote region could not be found"; + return false; + } + + region = m_GridService.GetRegionByUUID(scopeID, regionID); + if (region != null) + { + m_log.DebugFormat("[HYPERGRID LINKER]: Region already exists in coordinates <{0},{1}>", + Util.WorldToRegionLoc((uint)region.RegionLocX), Util.WorldToRegionLoc((uint)region.RegionLocY)); + regInfo = region; + return true; + } + + // We are now performing this check for each individual teleport in the EntityTransferModule instead. This + // allows us to give better feedback when teleports fail because of the distance reason (which can't be + // done here) and it also hypergrid teleports that are within range (possibly because the source grid + // itself has regions that are very far apart). +// uint x, y; +// if (m_Check4096 && !Check4096(handle, out x, out y)) +// { +// //RemoveHyperlinkRegion(regInfo.RegionID); +// reason = "Region is too far (" + x + ", " + y + ")"; +// m_log.Info("[HYPERGRID LINKER]: Unable to link, region is too far (" + x + ", " + y + ")"); +// //return false; +// } + + regInfo.RegionID = regionID; + + if (externalName == string.Empty) + regInfo.RegionName = regInfo.ServerURI; + else + regInfo.RegionName = externalName; + + m_log.DebugFormat("[HYPERGRID LINKER]: naming linked region {0}, handle {1}", regInfo.RegionName, handle.ToString()); + + // Get the map image + regInfo.TerrainImage = GetMapImage(regionID, imageURL); + + // Store the origin's coordinates somewhere + regInfo.RegionSecret = handle.ToString(); + + AddHyperlinkRegion(regInfo, handle); + m_log.InfoFormat("[HYPERGRID LINKER]: Successfully linked to region {0} at <{1},{2}> with image {3}", + regInfo.RegionName, Util.WorldToRegionLoc((uint)regInfo.RegionLocX), Util.WorldToRegionLoc((uint)regInfo.RegionLocY), regInfo.TerrainImage); + return true; + } + + public bool TryUnlinkRegion(string mapName) + { + m_log.DebugFormat("[HYPERGRID LINKER]: Request to unlink {0}", mapName); + GridRegion regInfo = null; + + List regions = m_Database.Get(Util.EscapeForLike(mapName), m_ScopeID); + if (regions != null && regions.Count > 0) + { + OpenSim.Framework.RegionFlags rflags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(regions[0].Data["flags"]); + if ((rflags & OpenSim.Framework.RegionFlags.Hyperlink) != 0) + { + regInfo = new GridRegion(); + regInfo.RegionID = regions[0].RegionID; + regInfo.ScopeID = m_ScopeID; + } + } + + if (regInfo != null) + { + RemoveHyperlinkRegion(regInfo.RegionID); + return true; + } + else + { + m_log.InfoFormat("[HYPERGRID LINKER]: Region {0} not found", mapName); + return false; + } + } + +// Not currently used +// /// +// /// Cope with this viewer limitation. +// /// +// /// +// /// +// public bool Check4096(ulong realHandle, out uint x, out uint y) +// { +// uint ux = 0, uy = 0; +// Utils.LongToUInts(realHandle, out ux, out uy); +// x = Util.WorldToRegionLoc(ux); +// y = Util.WorldToRegionLoc(uy); +// +// const uint limit = Util.RegionToWorldLoc(4096 - 1); +// uint xmin = ux - limit; +// uint xmax = ux + limit; +// uint ymin = uy - limit; +// uint ymax = uy + limit; +// // World map boundary checks +// if (xmin < 0 || xmin > ux) +// xmin = 0; +// if (xmax > int.MaxValue || xmax < ux) +// xmax = int.MaxValue; +// if (ymin < 0 || ymin > uy) +// ymin = 0; +// if (ymax > int.MaxValue || ymax < uy) +// ymax = int.MaxValue; +// +// // Check for any regions that are within the possible teleport range to the linked region +// List regions = m_GridService.GetRegionRange(m_ScopeID, (int)xmin, (int)xmax, (int)ymin, (int)ymax); +// if (regions.Count == 0) +// { +// return false; +// } +// else +// { +// // Check for regions which are not linked regions +// List hyperlinks = m_GridService.GetHyperlinks(m_ScopeID); +// IEnumerable availableRegions = regions.Except(hyperlinks); +// if (availableRegions.Count() == 0) +// return false; +// } +// +// return true; +// } + + private void AddHyperlinkRegion(GridRegion regionInfo, ulong regionHandle) + { + RegionData rdata = m_GridService.RegionInfo2RegionData(regionInfo); + int flags = (int)OpenSim.Framework.RegionFlags.Hyperlink + (int)OpenSim.Framework.RegionFlags.NoDirectLogin + (int)OpenSim.Framework.RegionFlags.RegionOnline; + rdata.Data["flags"] = flags.ToString(); + + m_Database.Store(rdata); + } + + private void RemoveHyperlinkRegion(UUID regionID) + { + m_Database.Delete(regionID); + } + + public UUID GetMapImage(UUID regionID, string imageURL) + { + return m_GatekeeperConnector.GetMapImage(regionID, imageURL, m_MapTileDirectory); + } + #endregion + + + #region Console Commands + + public void HandleShow(string module, string[] cmd) + { + if (cmd.Length != 2) + { + MainConsole.Instance.Output("Syntax: show hyperlinks"); + return; + } + List regions = m_Database.GetHyperlinks(UUID.Zero); + if (regions == null || regions.Count < 1) + { + MainConsole.Instance.Output("No hyperlinks"); + return; + } + + MainConsole.Instance.Output("Region Name"); + MainConsole.Instance.Output("Location Region UUID"); + MainConsole.Instance.Output(new string('-', 72)); + foreach (RegionData r in regions) + { + MainConsole.Instance.Output( + String.Format("{0}\n{2,-32} {1}\n", + r.RegionName, r.RegionID, + String.Format("{0},{1} ({2},{3})", r.posX, r.posY, + Util.WorldToRegionLoc((uint)r.posX), Util.WorldToRegionLoc((uint)r.posY) + ) + ) + ); + } + return; + } + + public void RunCommand(string module, string[] cmdparams) + { + List args = new List(cmdparams); + if (args.Count < 1) + return; + + string command = args[0]; + args.RemoveAt(0); + + cmdparams = args.ToArray(); + + RunHGCommand(command, cmdparams); + + } + + private void RunLinkRegionCommand(string[] cmdparams) + { + int xloc, yloc; + string serverURI; + string remoteName = null; + xloc = (int)Util.RegionToWorldLoc((uint)Convert.ToInt32(cmdparams[0])); + yloc = (int)Util.RegionToWorldLoc((uint)Convert.ToInt32(cmdparams[1])); + serverURI = cmdparams[2]; + if (cmdparams.Length > 3) + remoteName = string.Join(" ", cmdparams, 3, cmdparams.Length - 3); + string reason = string.Empty; + GridRegion regInfo; + if (TryCreateLink(UUID.Zero, xloc, yloc, remoteName, 0, null, serverURI, UUID.Zero, out regInfo, out reason)) + MainConsole.Instance.Output("Hyperlink established"); + else + MainConsole.Instance.Output("Failed to link region: " + reason); + } + + private void RunHGCommand(string command, string[] cmdparams) + { + if (command.Equals("link-mapping")) + { + if (cmdparams.Length == 2) + { + try + { + m_autoMappingX = Convert.ToUInt32(cmdparams[0]); + m_autoMappingY = Convert.ToUInt32(cmdparams[1]); + m_enableAutoMapping = true; + } + catch (Exception) + { + m_autoMappingX = 0; + m_autoMappingY = 0; + m_enableAutoMapping = false; + } + } + } + else if (command.Equals("link-region")) + { + if (cmdparams.Length < 3) + { + if ((cmdparams.Length == 1) || (cmdparams.Length == 2)) + { + LoadXmlLinkFile(cmdparams); + } + else + { + LinkRegionCmdUsage(); + } + return; + } + + //this should be the prefererred way of setting up hg links now + if (cmdparams[2].StartsWith("http")) + { + RunLinkRegionCommand(cmdparams); + } + else if (cmdparams[2].Contains(":")) + { + // New format + string[] parts = cmdparams[2].Split(':'); + if (parts.Length > 2) + { + // Insert remote region name + ArrayList parameters = new ArrayList(cmdparams); + parameters.Insert(3, parts[2]); + cmdparams = (string[])parameters.ToArray(typeof(string)); + } + cmdparams[2] = "http://" + parts[0] + ':' + parts[1]; + + RunLinkRegionCommand(cmdparams); + } + else + { + // old format + GridRegion regInfo; + uint xloc, yloc; + uint externalPort; + string externalHostName; + try + { + xloc = Convert.ToUInt32(cmdparams[0]); + yloc = Convert.ToUInt32(cmdparams[1]); + externalPort = Convert.ToUInt32(cmdparams[3]); + externalHostName = cmdparams[2]; + //internalPort = Convert.ToUInt32(cmdparams[4]); + //remotingPort = Convert.ToUInt32(cmdparams[5]); + } + catch (Exception e) + { + MainConsole.Instance.Output("[HGrid] Wrong format for link-region command: " + e.Message); + LinkRegionCmdUsage(); + return; + } + + // Convert cell coordinates given by the user to meters + xloc = Util.RegionToWorldLoc(xloc); + yloc = Util.RegionToWorldLoc(yloc); + string reason = string.Empty; + if (TryCreateLink(UUID.Zero, (int)xloc, (int)yloc, + string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) + { + // What is this? The GridRegion instance will be discarded anyway, + // which effectively ignores any local name given with the command. + //if (cmdparams.Length >= 5) + //{ + // regInfo.RegionName = ""; + // for (int i = 4; i < cmdparams.Length; i++) + // regInfo.RegionName += cmdparams[i] + " "; + //} + } + } + return; + } + else if (command.Equals("unlink-region")) + { + if (cmdparams.Length < 1) + { + UnlinkRegionCmdUsage(); + return; + } + string region = string.Join(" ", cmdparams); + if (TryUnlinkRegion(region)) + MainConsole.Instance.Output("Successfully unlinked " + region); + else + MainConsole.Instance.Output("Unable to unlink " + region + ", region not found."); + } + } + + private void LoadXmlLinkFile(string[] cmdparams) + { + //use http://www.hgurl.com/hypergrid.xml for test + try + { + XmlReader r = XmlReader.Create(cmdparams[0]); + XmlConfigSource cs = new XmlConfigSource(r); + string[] excludeSections = null; + + if (cmdparams.Length == 2) + { + if (cmdparams[1].ToLower().StartsWith("excludelist:")) + { + string excludeString = cmdparams[1].ToLower(); + excludeString = excludeString.Remove(0, 12); + char[] splitter = { ';' }; + + excludeSections = excludeString.Split(splitter); + } + } + + for (int i = 0; i < cs.Configs.Count; i++) + { + bool skip = false; + if ((excludeSections != null) && (excludeSections.Length > 0)) + { + for (int n = 0; n < excludeSections.Length; n++) + { + if (excludeSections[n] == cs.Configs[i].Name.ToLower()) + { + skip = true; + break; + } + } + } + if (!skip) + { + ReadLinkFromConfig(cs.Configs[i]); + } + } + } + catch (Exception e) + { + m_log.Error(e.ToString()); + } + } + + + private void ReadLinkFromConfig(IConfig config) + { + GridRegion regInfo; + uint xloc, yloc; + uint externalPort; + string externalHostName; + uint realXLoc, realYLoc; + + xloc = Convert.ToUInt32(config.GetString("xloc", "0")); + yloc = Convert.ToUInt32(config.GetString("yloc", "0")); + externalPort = Convert.ToUInt32(config.GetString("externalPort", "0")); + externalHostName = config.GetString("externalHostName", ""); + realXLoc = Convert.ToUInt32(config.GetString("real-xloc", "0")); + realYLoc = Convert.ToUInt32(config.GetString("real-yloc", "0")); + + if (m_enableAutoMapping) + { + xloc = (xloc % 100) + m_autoMappingX; + yloc = (yloc % 100) + m_autoMappingY; + } + + if (((realXLoc == 0) && (realYLoc == 0)) || + (((realXLoc - xloc < 3896) || (xloc - realXLoc < 3896)) && + ((realYLoc - yloc < 3896) || (yloc - realYLoc < 3896)))) + { + xloc = Util.RegionToWorldLoc(xloc); + yloc = Util.RegionToWorldLoc(yloc); + string reason = string.Empty; + if (TryCreateLink(UUID.Zero, (int)xloc, (int)yloc, + string.Empty, externalPort, externalHostName, UUID.Zero, out regInfo, out reason)) + { + regInfo.RegionName = config.GetString("localName", ""); + } + else + MainConsole.Instance.Output("Unable to link " + externalHostName + ": " + reason); + } + } + + + private void LinkRegionCmdUsage() + { + MainConsole.Instance.Output("Usage: link-region []"); + MainConsole.Instance.Output("Usage (deprecated): link-region :[:]"); + MainConsole.Instance.Output("Usage (deprecated): link-region []"); + MainConsole.Instance.Output("Usage: link-region []"); + } + + private void UnlinkRegionCmdUsage() + { + MainConsole.Instance.Output("Usage: unlink-region "); + } + + #endregion + + } +} diff --git a/OpenSim/Services/GridService/Properties/AssemblyInfo.cs b/OpenSim/Services/GridService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ebe3c44ec6 --- /dev/null +++ b/OpenSim/Services/GridService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.GridService")] +[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("96526d7b-4943-4b8e-9f0f-5908af621090")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/HypergridService/GatekeeperService.cs b/OpenSim/Services/HypergridService/GatekeeperService.cs new file mode 100644 index 0000000000..44b26d5ac2 --- /dev/null +++ b/OpenSim/Services/HypergridService/GatekeeperService.cs @@ -0,0 +1,559 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text.RegularExpressions; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using OpenSim.Services.Connectors.Hypergrid; + +using OpenMetaverse; + +using Nini.Config; +using log4net; + +namespace OpenSim.Services.HypergridService +{ + public class GatekeeperService : IGatekeeperService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private static bool m_Initialized = false; + + private static IGridService m_GridService; + private static IPresenceService m_PresenceService; + private static IUserAccountService m_UserAccountService; + private static IUserAgentService m_UserAgentService; + private static ISimulationService m_SimulationService; + private static IGridUserService m_GridUserService; + private static IBansService m_BansService; + + private static string m_AllowedClients = string.Empty; + private static string m_DeniedClients = string.Empty; + private static bool m_ForeignAgentsAllowed = true; + private static List m_ForeignsAllowedExceptions = new List(); + private static List m_ForeignsDisallowedExceptions = new List(); + + private static UUID m_ScopeID; + private static bool m_AllowTeleportsToAnyRegion; + private static string m_ExternalName; + private static Uri m_Uri; + private static GridRegion m_DefaultGatewayRegion; + + public GatekeeperService(IConfigSource config, ISimulationService simService) + { + if (!m_Initialized) + { + m_Initialized = true; + + IConfig serverConfig = config.Configs["GatekeeperService"]; + if (serverConfig == null) + throw new Exception(String.Format("No section GatekeeperService in config file")); + + string accountService = serverConfig.GetString("UserAccountService", String.Empty); + string homeUsersService = serverConfig.GetString("UserAgentService", string.Empty); + string gridService = serverConfig.GetString("GridService", String.Empty); + string presenceService = serverConfig.GetString("PresenceService", String.Empty); + string simulationService = serverConfig.GetString("SimulationService", String.Empty); + string gridUserService = serverConfig.GetString("GridUserService", String.Empty); + string bansService = serverConfig.GetString("BansService", String.Empty); + + // These are mandatory, the others aren't + if (gridService == string.Empty || presenceService == string.Empty) + throw new Exception("Incomplete specifications, Gatekeeper Service cannot function."); + + string scope = serverConfig.GetString("ScopeID", UUID.Zero.ToString()); + UUID.TryParse(scope, out m_ScopeID); + //m_WelcomeMessage = serverConfig.GetString("WelcomeMessage", "Welcome to OpenSim!"); + m_AllowTeleportsToAnyRegion = serverConfig.GetBoolean("AllowTeleportsToAnyRegion", true); + m_ExternalName = Util.GetConfigVarFromSections(config, "GatekeeperURI", + new string[] { "Startup", "Hypergrid", "GatekeeperService" }, String.Empty); + m_ExternalName = serverConfig.GetString("ExternalName", m_ExternalName); + if (m_ExternalName != string.Empty && !m_ExternalName.EndsWith("/")) + m_ExternalName = m_ExternalName + "/"; + + try + { + m_Uri = new Uri(m_ExternalName); + } + catch + { + m_log.WarnFormat("[GATEKEEPER SERVICE]: Malformed gatekeeper address {0}", m_ExternalName); + } + + Object[] args = new Object[] { config }; + m_GridService = ServerUtils.LoadPlugin(gridService, args); + m_PresenceService = ServerUtils.LoadPlugin(presenceService, args); + + if (accountService != string.Empty) + m_UserAccountService = ServerUtils.LoadPlugin(accountService, args); + if (homeUsersService != string.Empty) + m_UserAgentService = ServerUtils.LoadPlugin(homeUsersService, args); + if (gridUserService != string.Empty) + m_GridUserService = ServerUtils.LoadPlugin(gridUserService, args); + if (bansService != string.Empty) + m_BansService = ServerUtils.LoadPlugin(bansService, args); + + if (simService != null) + m_SimulationService = simService; + else if (simulationService != string.Empty) + m_SimulationService = ServerUtils.LoadPlugin(simulationService, args); + + m_AllowedClients = serverConfig.GetString("AllowedClients", string.Empty); + m_DeniedClients = serverConfig.GetString("DeniedClients", string.Empty); + m_ForeignAgentsAllowed = serverConfig.GetBoolean("ForeignAgentsAllowed", true); + + LoadDomainExceptionsFromConfig(serverConfig, "AllowExcept", m_ForeignsAllowedExceptions); + LoadDomainExceptionsFromConfig(serverConfig, "DisallowExcept", m_ForeignsDisallowedExceptions); + + if (m_GridService == null || m_PresenceService == null || m_SimulationService == null) + throw new Exception("Unable to load a required plugin, Gatekeeper Service cannot function."); + + m_log.Debug("[GATEKEEPER SERVICE]: Starting..."); + } + } + + public GatekeeperService(IConfigSource config) + : this(config, null) + { + } + + protected void LoadDomainExceptionsFromConfig(IConfig config, string variable, List exceptions) + { + string value = config.GetString(variable, string.Empty); + string[] parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string s in parts) + exceptions.Add(s.Trim()); + } + + public bool LinkRegion(string regionName, out UUID regionID, out ulong regionHandle, out string externalName, out string imageURL, out string reason) + { + regionID = UUID.Zero; + regionHandle = 0; + externalName = m_ExternalName + ((regionName != string.Empty) ? " " + regionName : ""); + imageURL = string.Empty; + reason = string.Empty; + GridRegion region = null; + + m_log.DebugFormat("[GATEKEEPER SERVICE]: Request to link to {0}", (regionName == string.Empty)? "default region" : regionName); + if (!m_AllowTeleportsToAnyRegion || regionName == string.Empty) + { + List defs = m_GridService.GetDefaultHypergridRegions(m_ScopeID); + if (defs != null && defs.Count > 0) + { + region = defs[0]; + m_DefaultGatewayRegion = region; + } + else + { + reason = "Grid setup problem. Try specifying a particular region here."; + m_log.DebugFormat("[GATEKEEPER SERVICE]: Unable to send information. Please specify a default region for this grid!"); + return false; + } + } + else + { + region = m_GridService.GetRegionByName(m_ScopeID, regionName); + if (region == null) + { + reason = "Region not found"; + return false; + } + } + + regionID = region.RegionID; + regionHandle = region.RegionHandle; + + string regionimage = "regionImage" + regionID.ToString(); + regionimage = regionimage.Replace("-", ""); + imageURL = region.ServerURI + "index.php?method=" + regionimage; + + return true; + } + + public GridRegion GetHyperlinkRegion(UUID regionID, UUID agentID, string agentHomeURI, out string message) + { + message = null; + + if (!m_AllowTeleportsToAnyRegion) + { + // Don't even check the given regionID + m_log.DebugFormat( + "[GATEKEEPER SERVICE]: Returning gateway region {0} {1} @ {2} to user {3}{4} as teleporting to arbitrary regions is not allowed.", + m_DefaultGatewayRegion.RegionName, + m_DefaultGatewayRegion.RegionID, + m_DefaultGatewayRegion.ServerURI, + agentID, + agentHomeURI == null ? "" : " @ " + agentHomeURI); + + message = "Teleporting to the default region."; + return m_DefaultGatewayRegion; + } + + GridRegion region = m_GridService.GetRegionByUUID(m_ScopeID, regionID); + + if (region == null) + { + m_log.DebugFormat( + "[GATEKEEPER SERVICE]: Could not find region with ID {0} as requested by user {1}{2}. Returning null.", + regionID, agentID, (agentHomeURI == null) ? "" : " @ " + agentHomeURI); + + message = "The teleport destination could not be found."; + return null; + } + + m_log.DebugFormat( + "[GATEKEEPER SERVICE]: Returning region {0} {1} @ {2} to user {3}{4}.", + region.RegionName, + region.RegionID, + region.ServerURI, + agentID, + agentHomeURI == null ? "" : " @ " + agentHomeURI); + + return region; + } + + #region Login Agent + public bool LoginAgent(GridRegion source, AgentCircuitData aCircuit, GridRegion destination, out string reason) + { + reason = string.Empty; + + string authURL = string.Empty; + if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) + authURL = aCircuit.ServiceURLs["HomeURI"].ToString(); + + m_log.InfoFormat("[GATEKEEPER SERVICE]: Login request for {0} {1} @ {2} ({3}) at {4} using viewer {5}, channel {6}, IP {7}, Mac {8}, Id0 {9}, Teleport Flags: {10}. From region {11}", + aCircuit.firstname, aCircuit.lastname, authURL, aCircuit.AgentID, destination.RegionID, + aCircuit.Viewer, aCircuit.Channel, aCircuit.IPAddress, aCircuit.Mac, aCircuit.Id0, (TeleportFlags)aCircuit.teleportFlags, + (source == null) ? "Unknown" : string.Format("{0} ({1}){2}", source.RegionName, source.RegionID, (source.RawServerURI == null) ? "" : " @ " + source.ServerURI)); + + string curViewer = Util.GetViewerName(aCircuit); + + // + // Check client + // + if (m_AllowedClients != string.Empty) + { + Regex arx = new Regex(m_AllowedClients); + Match am = arx.Match(curViewer); + + if (!am.Success) + { + m_log.InfoFormat("[GATEKEEPER SERVICE]: Login failed, reason: client {0} is not allowed", curViewer); + return false; + } + } + + if (m_DeniedClients != string.Empty) + { + Regex drx = new Regex(m_DeniedClients); + Match dm = drx.Match(curViewer); + + if (dm.Success) + { + m_log.InfoFormat("[GATEKEEPER SERVICE]: Login failed, reason: client {0} is denied", curViewer); + return false; + } + } + + // + // Authenticate the user + // + if (!Authenticate(aCircuit)) + { + reason = "Unable to verify identity"; + m_log.InfoFormat("[GATEKEEPER SERVICE]: Unable to verify identity of agent {0} {1}. Refusing service.", aCircuit.firstname, aCircuit.lastname); + return false; + } + m_log.DebugFormat("[GATEKEEPER SERVICE]: Identity verified for {0} {1} @ {2}", aCircuit.firstname, aCircuit.lastname, authURL); + + // + // Check for impersonations + // + UserAccount account = null; + if (m_UserAccountService != null) + { + // Check to see if we have a local user with that UUID + account = m_UserAccountService.GetUserAccount(m_ScopeID, aCircuit.AgentID); + if (account != null) + { + // Make sure this is the user coming home, and not a foreign user with same UUID as a local user + if (m_UserAgentService != null) + { + if (!m_UserAgentService.IsAgentComingHome(aCircuit.SessionID, m_ExternalName)) + { + // Can't do, sorry + reason = "Unauthorized"; + m_log.InfoFormat("[GATEKEEPER SERVICE]: Foreign agent {0} {1} has same ID as local user. Refusing service.", + aCircuit.firstname, aCircuit.lastname); + return false; + + } + } + } + } + + // + // Foreign agents allowed? Exceptions? + // + if (account == null) + { + bool allowed = m_ForeignAgentsAllowed; + + if (m_ForeignAgentsAllowed && IsException(aCircuit, m_ForeignsAllowedExceptions)) + allowed = false; + + if (!m_ForeignAgentsAllowed && IsException(aCircuit, m_ForeignsDisallowedExceptions)) + allowed = true; + + if (!allowed) + { + reason = "Destination does not allow visitors from your world"; + m_log.InfoFormat("[GATEKEEPER SERVICE]: Foreign agents are not permitted {0} {1} @ {2}. Refusing service.", + aCircuit.firstname, aCircuit.lastname, aCircuit.ServiceURLs["HomeURI"]); + return false; + } + } + + // + // Is the user banned? + // This uses a Ban service that's more powerful than the configs + // + string uui = (account != null ? aCircuit.AgentID.ToString() : Util.ProduceUserUniversalIdentifier(aCircuit)); + if (m_BansService != null && m_BansService.IsBanned(uui, aCircuit.IPAddress, aCircuit.Id0, authURL)) + { + reason = "You are banned from this world"; + m_log.InfoFormat("[GATEKEEPER SERVICE]: Login failed, reason: user {0} is banned", uui); + return false; + } + + m_log.DebugFormat("[GATEKEEPER SERVICE]: User {0} is ok", aCircuit.Name); + + bool isFirstLogin = false; + // + // Login the presence, if it's not there yet (by the login service) + // + PresenceInfo presence = m_PresenceService.GetAgent(aCircuit.SessionID); + if (presence != null) // it has been placed there by the login service + isFirstLogin = true; + + else + { + if (!m_PresenceService.LoginAgent(aCircuit.AgentID.ToString(), aCircuit.SessionID, aCircuit.SecureSessionID)) + { + reason = "Unable to login presence"; + m_log.InfoFormat("[GATEKEEPER SERVICE]: Presence login failed for foreign agent {0} {1}. Refusing service.", + aCircuit.firstname, aCircuit.lastname); + return false; + } + + m_log.DebugFormat("[GATEKEEPER SERVICE]: Login presence {0} is ok", aCircuit.Name); + + // Also login foreigners with GridUser service + if (m_GridUserService != null && account == null) + { + string userId = aCircuit.AgentID.ToString(); + string first = aCircuit.firstname, last = aCircuit.lastname; + if (last.StartsWith("@")) + { + string[] parts = aCircuit.firstname.Split('.'); + if (parts.Length >= 2) + { + first = parts[0]; + last = parts[1]; + } + } + + userId += ";" + aCircuit.ServiceURLs["HomeURI"] + ";" + first + " " + last; + m_GridUserService.LoggedIn(userId); + } + } + + // + // Get the region + // + destination = m_GridService.GetRegionByUUID(m_ScopeID, destination.RegionID); + if (destination == null) + { + reason = "Destination region not found"; + return false; + } + + m_log.DebugFormat( + "[GATEKEEPER SERVICE]: Destination {0} is ok for {1}", destination.RegionName, aCircuit.Name); + + // + // Adjust the visible name + // + if (account != null) + { + aCircuit.firstname = account.FirstName; + aCircuit.lastname = account.LastName; + } + if (account == null) + { + if (!aCircuit.lastname.StartsWith("@")) + aCircuit.firstname = aCircuit.firstname + "." + aCircuit.lastname; + try + { + Uri uri = new Uri(aCircuit.ServiceURLs["HomeURI"].ToString()); + aCircuit.lastname = "@" + uri.Authority; + } + catch + { + m_log.WarnFormat("[GATEKEEPER SERVICE]: Malformed HomeURI (this should never happen): {0}", aCircuit.ServiceURLs["HomeURI"]); + aCircuit.lastname = "@" + aCircuit.ServiceURLs["HomeURI"].ToString(); + } + } + + // + // Finally launch the agent at the destination + // + Constants.TeleportFlags loginFlag = isFirstLogin ? Constants.TeleportFlags.ViaLogin : Constants.TeleportFlags.ViaHGLogin; + + // Preserve our TeleportFlags we have gathered so-far + loginFlag |= (Constants.TeleportFlags) aCircuit.teleportFlags; + + m_log.DebugFormat("[GATEKEEPER SERVICE]: Launching {0}, Teleport Flags: {1}", aCircuit.Name, loginFlag); + + string version; + + if (!m_SimulationService.QueryAccess( + destination, aCircuit.AgentID, aCircuit.ServiceURLs["HomeURI"].ToString(), + true, aCircuit.startpos, "SIMULATION/0.3", new List(), out version, out reason)) + return false; + + return m_SimulationService.CreateAgent(source, destination, aCircuit, (uint)loginFlag, out reason); + } + + protected bool Authenticate(AgentCircuitData aCircuit) + { + if (!CheckAddress(aCircuit.ServiceSessionID)) + return false; + + if (string.IsNullOrEmpty(aCircuit.IPAddress)) + { + m_log.DebugFormat("[GATEKEEPER SERVICE]: Agent did not provide a client IP address."); + return false; + } + + string userURL = string.Empty; + if (aCircuit.ServiceURLs.ContainsKey("HomeURI")) + userURL = aCircuit.ServiceURLs["HomeURI"].ToString(); + + if (userURL == string.Empty) + { + m_log.DebugFormat("[GATEKEEPER SERVICE]: Agent did not provide an authentication server URL"); + return false; + } + + if (userURL == m_ExternalName) + { + return m_UserAgentService.VerifyAgent(aCircuit.SessionID, aCircuit.ServiceSessionID); + } + else + { + IUserAgentService userAgentService = new UserAgentServiceConnector(userURL); + + try + { + return userAgentService.VerifyAgent(aCircuit.SessionID, aCircuit.ServiceSessionID); + } + catch + { + m_log.DebugFormat("[GATEKEEPER SERVICE]: Unable to contact authentication service at {0}", userURL); + return false; + } + } + } + + // Check that the service token was generated for *this* grid. + // If it wasn't then that's a fake agent. + protected bool CheckAddress(string serviceToken) + { + string[] parts = serviceToken.Split(new char[] { ';' }); + if (parts.Length < 2) + return false; + + char[] trailing_slash = new char[] { '/' }; + string addressee = parts[0].TrimEnd(trailing_slash); + string externalname = m_ExternalName.TrimEnd(trailing_slash); + m_log.DebugFormat("[GATEKEEPER SERVICE]: Verifying {0} against {1}", addressee, externalname); + + Uri uri; + try + { + uri = new Uri(addressee); + } + catch + { + m_log.DebugFormat("[GATEKEEPER SERVICE]: Visitor provided malformed service address {0}", addressee); + return false; + } + + return string.Equals(uri.GetLeftPart(UriPartial.Authority), m_Uri.GetLeftPart(UriPartial.Authority), StringComparison.OrdinalIgnoreCase) ; + } + + #endregion + + + #region Misc + + private bool IsException(AgentCircuitData aCircuit, List exceptions) + { + bool exception = false; + if (exceptions.Count > 0) // we have exceptions + { + // Retrieve the visitor's origin + string userURL = aCircuit.ServiceURLs["HomeURI"].ToString(); + if (!userURL.EndsWith("/")) + userURL += "/"; + + if (exceptions.Find(delegate(string s) + { + if (!s.EndsWith("/")) + s += "/"; + return s == userURL; + }) != null) + exception = true; + } + + return exception; + } + + #endregion + } +} diff --git a/OpenSim/Services/HypergridService/HGAssetService.cs b/OpenSim/Services/HypergridService/HGAssetService.cs new file mode 100644 index 0000000000..b83fb1ee50 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGAssetService.cs @@ -0,0 +1,193 @@ +/* + * 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 Nini.Config; +using log4net; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Serialization.External; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.AssetService; + +namespace OpenSim.Services.HypergridService +{ + /// + /// Hypergrid asset service. It serves the IAssetService interface, + /// but implements it in ways that are appropriate for inter-grid + /// asset exchanges. + /// + public class HGAssetService : OpenSim.Services.AssetService.AssetService, IAssetService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_HomeURL; + private IUserAccountService m_UserAccountService; + + private UserAccountCache m_Cache; + + private AssetPermissions m_AssetPerms; + + public HGAssetService(IConfigSource config, string configName) : base(config, configName) + { + m_log.Debug("[HGAsset Service]: Starting"); + IConfig assetConfig = config.Configs[configName]; + if (assetConfig == null) + throw new Exception("No HGAssetService configuration"); + + string userAccountsDll = assetConfig.GetString("UserAccountsService", string.Empty); + if (userAccountsDll == string.Empty) + throw new Exception("Please specify UserAccountsService in HGAssetService configuration"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(userAccountsDll, args); + if (m_UserAccountService == null) + throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll)); + + m_HomeURL = Util.GetConfigVarFromSections(config, "HomeURI", + new string[] { "Startup", "Hypergrid", configName }, string.Empty); + if (m_HomeURL == string.Empty) + throw new Exception("[HGAssetService] No HomeURI specified"); + + m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); + + // Permissions + m_AssetPerms = new AssetPermissions(assetConfig); + + } + + #region IAssetService overrides + public override AssetBase Get(string id) + { + AssetBase asset = base.Get(id); + + if (asset == null) + return null; + + if (!m_AssetPerms.AllowedExport(asset.Type)) + return null; + + if (asset.Metadata.Type == (sbyte)AssetType.Object) + asset.Data = AdjustIdentifiers(asset.Data); + + AdjustIdentifiers(asset.Metadata); + + return asset; + } + + public override AssetMetadata GetMetadata(string id) + { + AssetMetadata meta = base.GetMetadata(id); + + if (meta == null) + return null; + + AdjustIdentifiers(meta); + + return meta; + } + + public override byte[] GetData(string id) + { + AssetBase asset = Get(id); + + if (asset == null) + return null; + + if (!m_AssetPerms.AllowedExport(asset.Type)) + return null; + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + + return asset.Data; + } + + //public virtual bool Get(string id, Object sender, AssetRetrieved handler) + + public override string Store(AssetBase asset) + { + if (!m_AssetPerms.AllowedImport(asset.Type)) + return string.Empty; + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before storing on this server + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + + return base.Store(asset); + } + + public override bool Delete(string id) + { + // NOGO + return false; + } + + #endregion + + protected void AdjustIdentifiers(AssetMetadata meta) + { + if (meta == null || m_Cache == null) + return; + + UserAccount creator = m_Cache.GetUser(meta.CreatorID); + if (creator != null) + meta.CreatorID = meta.CreatorID + ";" + m_HomeURL + "/" + creator.FirstName + " " + creator.LastName; + } + + // Only for Object + protected byte[] AdjustIdentifiers(byte[] data) + { + string xml = Utils.BytesToString(data); + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + xml = ExternalRepresentationUtils.SanitizeXml(xml); + + return Utils.StringToBytes(ExternalRepresentationUtils.RewriteSOP(xml, "HGAssetService", m_HomeURL, m_Cache, UUID.Zero)); + } + + } + +} diff --git a/OpenSim/Services/HypergridService/HGFSAssetService.cs b/OpenSim/Services/HypergridService/HGFSAssetService.cs new file mode 100644 index 0000000000..54e8ccba42 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGFSAssetService.cs @@ -0,0 +1,189 @@ +/* + * 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 log4net; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Serialization.External; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.FSAssetService; + +namespace OpenSim.Services.HypergridService +{ + /// + /// Hypergrid asset service. It serves the IAssetService interface, + /// but implements it in ways that are appropriate for inter-grid + /// asset exchanges. This version is for FSAssets. + /// + public class HGFSAssetService : FSAssetConnector, IAssetService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_HomeURL; + private IUserAccountService m_UserAccountService; + + private UserAccountCache m_Cache; + + private AssetPermissions m_AssetPerms; + + public HGFSAssetService(IConfigSource config, string configName) : base(config, "AssetService") + { + m_log.Debug("[HGAsset Service]: Starting in FSAsset mode"); + IConfig assetConfig = config.Configs[configName]; + if (assetConfig == null) + throw new Exception("No HGAssetService configuration"); + + string userAccountsDll = assetConfig.GetString("UserAccountsService", string.Empty); + if (userAccountsDll == string.Empty) + throw new Exception("Please specify UserAccountsService in HGAssetService configuration"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(userAccountsDll, args); + if (m_UserAccountService == null) + throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll)); + + m_HomeURL = Util.GetConfigVarFromSections(config, "HomeURI", + new string[] { "Startup", "Hypergrid", configName }, string.Empty); + if (m_HomeURL == string.Empty) + throw new Exception("[HGAssetService] No HomeURI specified"); + + m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); + + // Permissions + m_AssetPerms = new AssetPermissions(assetConfig); + } + + #region IAssetService overrides + public override AssetBase Get(string id) + { + AssetBase asset = base.Get(id); + + if (asset == null) + return null; + + if (!m_AssetPerms.AllowedExport(asset.Type)) + return null; + + if (asset.Metadata.Type == (sbyte)AssetType.Object) + asset.Data = AdjustIdentifiers(asset.Data); + + AdjustIdentifiers(asset.Metadata); + + return asset; + } + + public override AssetMetadata GetMetadata(string id) + { + AssetMetadata meta = base.GetMetadata(id); + + if (meta == null) + return null; + + AdjustIdentifiers(meta); + + return meta; + } + + public override byte[] GetData(string id) + { + AssetBase asset = Get(id); + + if (asset == null) + return null; + + if (!m_AssetPerms.AllowedExport(asset.Type)) + return null; + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + + return asset.Data; + } + + //public virtual bool Get(string id, Object sender, AssetRetrieved handler) + + public override string Store(AssetBase asset) + { + if (!m_AssetPerms.AllowedImport(asset.Type)) + return string.Empty; + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before storing on this server + if (asset.Type == (int)AssetType.Object && asset.Data != null) + { + string xml = ExternalRepresentationUtils.SanitizeXml(Utils.BytesToString(asset.Data)); + asset.Data = Utils.StringToBytes(xml); + } + + return base.Store(asset); + } + + public override bool Delete(string id) + { + // NOGO + return false; + } + + #endregion + + protected void AdjustIdentifiers(AssetMetadata meta) + { + if (meta == null || m_Cache == null) + return; + + UserAccount creator = m_Cache.GetUser(meta.CreatorID); + if (creator != null) + meta.CreatorID = meta.CreatorID + ";" + m_HomeURL + "/" + creator.FirstName + " " + creator.LastName; + } + + // Only for Object + protected byte[] AdjustIdentifiers(byte[] data) + { + string xml = Utils.BytesToString(data); + + // Deal with bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + // Fix bad assets before sending them elsewhere + xml = ExternalRepresentationUtils.SanitizeXml(xml); + + return Utils.StringToBytes(ExternalRepresentationUtils.RewriteSOP(xml, "HGAssetService", m_HomeURL, m_Cache, UUID.Zero)); + } + + } + +} diff --git a/OpenSim/Services/HypergridService/HGFriendsService.cs b/OpenSim/Services/HypergridService/HGFriendsService.cs new file mode 100644 index 0000000000..6e35a88712 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGFriendsService.cs @@ -0,0 +1,409 @@ +/* + * 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.Net; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Services.Connectors.Friends; +using OpenSim.Services.Connectors.Hypergrid; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.Services.HypergridService +{ + /// + /// W2W social networking + /// + public class HGFriendsService : IHGFriendsService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + static bool m_Initialized = false; + + protected static IGridUserService m_GridUserService; + protected static IGridService m_GridService; + protected static IGatekeeperService m_GatekeeperService; + protected static IFriendsService m_FriendsService; + protected static IPresenceService m_PresenceService; + protected static IUserAccountService m_UserAccountService; + protected static IFriendsSimConnector m_FriendsLocalSimConnector; // standalone, points to HGFriendsModule + protected static FriendsSimConnector m_FriendsSimConnector; // grid + + private static string m_ConfigName = "HGFriendsService"; + + public HGFriendsService(IConfigSource config, String configName, IFriendsSimConnector localSimConn) + { + if (m_FriendsLocalSimConnector == null) + m_FriendsLocalSimConnector = localSimConn; + + if (!m_Initialized) + { + m_Initialized = true; + + if (configName != String.Empty) + m_ConfigName = configName; + + Object[] args = new Object[] { config }; + + IConfig serverConfig = config.Configs[m_ConfigName]; + if (serverConfig == null) + throw new Exception(String.Format("No section {0} in config file", m_ConfigName)); + + string theService = serverConfig.GetString("FriendsService", string.Empty); + if (theService == String.Empty) + throw new Exception("No FriendsService in config file " + m_ConfigName); + m_FriendsService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("UserAccountService", string.Empty); + if (theService == String.Empty) + throw new Exception("No UserAccountService in " + m_ConfigName); + m_UserAccountService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("GridService", string.Empty); + if (theService == String.Empty) + throw new Exception("No GridService in " + m_ConfigName); + m_GridService = ServerUtils.LoadPlugin(theService, args); + + theService = serverConfig.GetString("PresenceService", string.Empty); + if (theService == String.Empty) + throw new Exception("No PresenceService in " + m_ConfigName); + m_PresenceService = ServerUtils.LoadPlugin(theService, args); + + m_FriendsSimConnector = new FriendsSimConnector(); + + m_log.DebugFormat("[HGFRIENDS SERVICE]: Starting..."); + + } + } + + #region IHGFriendsService + + public int GetFriendPerms(UUID userID, UUID friendID) + { + FriendInfo[] friendsInfo = m_FriendsService.GetFriends(userID); + foreach (FriendInfo finfo in friendsInfo) + { + if (finfo.Friend.StartsWith(friendID.ToString())) + return finfo.TheirFlags; + } + return -1; + } + + public bool NewFriendship(FriendInfo friend, bool verified) + { + UUID friendID; + string tmp = string.Empty, url = String.Empty, first = String.Empty, last = String.Empty; + if (!Util.ParseUniversalUserIdentifier(friend.Friend, out friendID, out url, out first, out last, out tmp)) + return false; + + m_log.DebugFormat("[HGFRIENDS SERVICE]: New friendship {0} {1} ({2})", friend.PrincipalID, friend.Friend, verified); + + // Does the friendship already exist? + FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); + foreach (FriendInfo finfo in finfos) + { + if (finfo.Friend.StartsWith(friendID.ToString())) + return false; + } + // Verified user session. But the user needs to confirm friendship when he gets home + if (verified) + return m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 0); + + // Does the reverted friendship exist? meaning that this user initiated the request + finfos = m_FriendsService.GetFriends(friendID); + bool userInitiatedOffer = false; + foreach (FriendInfo finfo in finfos) + { + if (friend.Friend.StartsWith(finfo.PrincipalID.ToString()) && finfo.Friend.StartsWith(friend.PrincipalID.ToString()) && finfo.TheirFlags == -1) + { + userInitiatedOffer = true; + // Let's delete the existing friendship relations that was stored + m_FriendsService.Delete(friendID, finfo.Friend); + break; + } + } + + if (userInitiatedOffer) + { + m_FriendsService.StoreFriend(friend.PrincipalID.ToString(), friend.Friend, 1); + m_FriendsService.StoreFriend(friend.Friend, friend.PrincipalID.ToString(), 1); + // notify the user + ForwardToSim("ApproveFriendshipRequest", friendID, Util.UniversalName(first, last, url), "", friend.PrincipalID, ""); + return true; + } + return false; + } + + public bool DeleteFriendship(FriendInfo friend, string secret) + { + FriendInfo[] finfos = m_FriendsService.GetFriends(friend.PrincipalID); + foreach (FriendInfo finfo in finfos) + { + // We check the secret here. Or if the friendship request was initiated here, and was declined + if (finfo.Friend.StartsWith(friend.Friend) && finfo.Friend.EndsWith(secret)) + { + m_log.DebugFormat("[HGFRIENDS SERVICE]: Delete friendship {0} {1}", friend.PrincipalID, friend.Friend); + m_FriendsService.Delete(friend.PrincipalID, finfo.Friend); + m_FriendsService.Delete(finfo.Friend, friend.PrincipalID.ToString()); + + return true; + } + } + + return false; + } + + public bool FriendshipOffered(UUID fromID, string fromName, UUID toID, string message) + { + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, toID); + if (account == null) + return false; + + // OK, we have that user here. + // So let's send back the call, but start a thread to continue + // with the verification and the actual action. + + Util.FireAndForget( + o => ProcessFriendshipOffered(fromID, fromName, toID, message), null, "HGFriendsService.ProcessFriendshipOffered"); + + return true; + } + + public bool ValidateFriendshipOffered(UUID fromID, UUID toID) + { + FriendInfo[] finfos = m_FriendsService.GetFriends(toID.ToString()); + foreach (FriendInfo fi in finfos) + { + if (fi.Friend.StartsWith(fromID.ToString()) && fi.TheirFlags == -1) + return true; + } + return false; + } + + public List StatusNotification(List friends, UUID foreignUserID, bool online) + { + if (m_FriendsService == null || m_PresenceService == null) + { + m_log.WarnFormat("[HGFRIENDS SERVICE]: Unable to perform status notifications because friends or presence services are missing"); + return new List(); + } + + // Let's unblock the caller right now, and take it from here async + + List localFriendsOnline = new List(); + + m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: foreign user {0} wants to notify {1} local friends of {2} status", + foreignUserID, friends.Count, (online ? "online" : "offline")); + + // First, let's double check that the reported friends are, indeed, friends of that user + // And let's check that the secret matches + List usersToBeNotified = new List(); + foreach (string uui in friends) + { + UUID localUserID; + string secret = string.Empty, tmp = string.Empty; + if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) + { + FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); + foreach (FriendInfo finfo in friendInfos) + { + if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret)) + { + // great! + usersToBeNotified.Add(localUserID.ToString()); + } + } + } + } + + // Now, let's send the notifications + //m_log.DebugFormat("[HGFRIENDS SERVICE]: Status notification: user has {0} local friends", usersToBeNotified.Count); + + // First, let's send notifications to local users who are online in the home grid + PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); + if (friendSessions != null && friendSessions.Length > 0) + { + PresenceInfo friendSession = null; + foreach (PresenceInfo pinfo in friendSessions) + if (pinfo.RegionID != UUID.Zero) // let's guard against traveling agents + { + friendSession = pinfo; + break; + } + + if (friendSession != null) + { + ForwardStatusNotificationToSim(friendSession.RegionID, foreignUserID, friendSession.UserID, online); + usersToBeNotified.Remove(friendSession.UserID.ToString()); + UUID id; + if (UUID.TryParse(friendSession.UserID, out id)) + localFriendsOnline.Add(id); + + } + } + +// // Lastly, let's notify the rest who may be online somewhere else +// foreach (string user in usersToBeNotified) +// { +// UUID id = new UUID(user); +// //m_UserAgentService.LocateUser(id); +// //etc... +// //if (m_TravelingAgents.ContainsKey(id) && m_TravelingAgents[id].GridExternalName != m_GridName) +// //{ +// // string url = m_TravelingAgents[id].GridExternalName; +// // // forward +// //} +// //m_log.WarnFormat("[HGFRIENDS SERVICE]: User {0} is visiting another grid. HG Status notifications still not implemented.", user); +// } + + // and finally, let's send the online friends + if (online) + { + return localFriendsOnline; + } + else + return new List(); + } + + #endregion IHGFriendsService + + #region Aux + + private void ProcessFriendshipOffered(UUID fromID, String fromName, UUID toID, String message) + { + // Great, it's a genuine request. Let's proceed. + // But now we need to confirm that the requester is who he says he is + // before we act on the friendship request. + + if (!fromName.Contains("@")) + return; + + string[] parts = fromName.Split(new char[] {'@'}); + if (parts.Length != 2) + return; + + string uriStr = "http://" + parts[1]; + try + { + new Uri(uriStr); + } + catch (UriFormatException) + { + return; + } + + UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uriStr); + Dictionary servers = uasConn.GetServerURLs(fromID); + if (!servers.ContainsKey("FriendsServerURI")) + return; + + HGFriendsServicesConnector friendsConn = new HGFriendsServicesConnector(servers["FriendsServerURI"].ToString()); + if (!friendsConn.ValidateFriendshipOffered(fromID, toID)) + { + m_log.WarnFormat("[HGFRIENDS SERVICE]: Friendship request from {0} to {1} is invalid. Impersonations?", fromID, toID); + return; + } + + string fromUUI = Util.UniversalIdentifier(fromID, parts[0], "@" + parts[1], uriStr); + // OK, we're good! + ForwardToSim("FriendshipOffered", fromID, fromName, fromUUI, toID, message); + } + + private bool ForwardToSim(string op, UUID fromID, string name, String fromUUI, UUID toID, string message) + { + PresenceInfo session = null; + GridRegion region = null; + PresenceInfo[] sessions = m_PresenceService.GetAgents(new string[] { toID.ToString() }); + if (sessions != null && sessions.Length > 0) + session = sessions[0]; + if (session != null) + region = m_GridService.GetRegionByUUID(UUID.Zero, session.RegionID); + + switch (op) + { + case "FriendshipOffered": + // Let's store backwards + string secret = UUID.Random().ToString().Substring(0, 8); + m_FriendsService.StoreFriend(toID.ToString(), fromUUI + ";" + secret, 0); + if (m_FriendsLocalSimConnector != null) // standalone + { + GridInstantMessage im = new GridInstantMessage(null, fromID, name, toID, + (byte)InstantMessageDialog.FriendshipOffered, message, false, Vector3.Zero); + // !! HACK + im.imSessionID = im.fromAgentID; + return m_FriendsLocalSimConnector.LocalFriendshipOffered(toID, im); + } + else if (region != null) // grid + return m_FriendsSimConnector.FriendshipOffered(region, fromID, toID, message, name); + break; + case "ApproveFriendshipRequest": + if (m_FriendsLocalSimConnector != null) // standalone + return m_FriendsLocalSimConnector.LocalFriendshipApproved(fromID, name, toID); + else if (region != null) //grid + return m_FriendsSimConnector.FriendshipApproved(region, fromID, name, toID); + break; + } + + return false; + } + + protected void ForwardStatusNotificationToSim(UUID regionID, UUID foreignUserID, string user, bool online) + { + UUID userID; + if (UUID.TryParse(user, out userID)) + { + if (m_FriendsLocalSimConnector != null) + { + m_log.DebugFormat("[HGFRIENDS SERVICE]: Local Notify, user {0} is {1}", foreignUserID, (online ? "online" : "offline")); + m_FriendsLocalSimConnector.StatusNotify(foreignUserID, userID, online); + } + else + { + GridRegion region = m_GridService.GetRegionByUUID(UUID.Zero /* !!! */, regionID); + if (region != null) + { + m_log.DebugFormat("[HGFRIENDS SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); + m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online); + } + } + } + } + + #endregion Aux + } +} diff --git a/OpenSim/Services/HypergridService/HGInstantMessageService.cs b/OpenSim/Services/HypergridService/HGInstantMessageService.cs new file mode 100644 index 0000000000..32ca09a871 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGInstantMessageService.cs @@ -0,0 +1,376 @@ +/* + * 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.Net; +using System.Reflection; + +using OpenSim.Framework; +using OpenSim.Services.Connectors.Friends; +using OpenSim.Services.Connectors.Hypergrid; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.InstantMessage; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.Services.HypergridService +{ + /// + /// Inter-grid IM + /// + public class HGInstantMessageService : IInstantMessage + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private const double CACHE_EXPIRATION_SECONDS = 120000.0; // 33 hours + + static bool m_Initialized = false; + + protected static IGridService m_GridService; + protected static IPresenceService m_PresenceService; + protected static IUserAgentService m_UserAgentService; + protected static IOfflineIMService m_OfflineIMService; + + protected static IInstantMessageSimConnector m_IMSimConnector; + + protected static Dictionary m_UserLocationMap = new Dictionary(); + private static ExpiringCache m_RegionCache; + + private static bool m_ForwardOfflineGroupMessages; + private static bool m_InGatekeeper; + + public HGInstantMessageService(IConfigSource config) + : this(config, null) + { + } + + public HGInstantMessageService(IConfigSource config, IInstantMessageSimConnector imConnector) + { + if (imConnector != null) + m_IMSimConnector = imConnector; + + if (!m_Initialized) + { + m_Initialized = true; + + IConfig serverConfig = config.Configs["HGInstantMessageService"]; + if (serverConfig == null) + throw new Exception(String.Format("No section HGInstantMessageService in config file")); + + string gridService = serverConfig.GetString("GridService", String.Empty); + string presenceService = serverConfig.GetString("PresenceService", String.Empty); + string userAgentService = serverConfig.GetString("UserAgentService", String.Empty); + m_InGatekeeper = serverConfig.GetBoolean("InGatekeeper", false); + m_log.DebugFormat("[HG IM SERVICE]: Starting... InRobust? {0}", m_InGatekeeper); + + if (gridService == string.Empty || presenceService == string.Empty) + throw new Exception(String.Format("Incomplete specifications, InstantMessage Service cannot function.")); + + Object[] args = new Object[] { config }; + m_GridService = ServerUtils.LoadPlugin(gridService, args); + m_PresenceService = ServerUtils.LoadPlugin(presenceService, args); + try + { + m_UserAgentService = ServerUtils.LoadPlugin(userAgentService, args); + } + catch + { + m_log.WarnFormat("[HG IM SERVICE]: Unable to create User Agent Service. Missing config var in [HGInstantMessageService]?"); + } + + m_RegionCache = new ExpiringCache(); + + IConfig cnf = config.Configs["Messaging"]; + if (cnf == null) + { + return; + } + + m_ForwardOfflineGroupMessages = cnf.GetBoolean("ForwardOfflineGroupMessages", false); + + if (m_InGatekeeper) + { + string offlineIMService = cnf.GetString("OfflineIMService", string.Empty); + if (offlineIMService != string.Empty) + m_OfflineIMService = ServerUtils.LoadPlugin(offlineIMService, args); + } + } + } + + public bool IncomingInstantMessage(GridInstantMessage im) + { +// m_log.DebugFormat("[HG IM SERVICE]: Received message from {0} to {1}", im.fromAgentID, im.toAgentID); +// UUID toAgentID = new UUID(im.toAgentID); + + bool success = false; + if (m_IMSimConnector != null) + { + //m_log.DebugFormat("[XXX] SendIMToRegion local im connector"); + success = m_IMSimConnector.SendInstantMessage(im); + } + else + { + success = TrySendInstantMessage(im, "", true, false); + } + + if (!success && m_InGatekeeper) // we do this only in the Gatekeeper IM service + UndeliveredMessage(im); + + return success; + } + + public bool OutgoingInstantMessage(GridInstantMessage im, string url, bool foreigner) + { +// m_log.DebugFormat("[HG IM SERVICE]: Sending message from {0} to {1}@{2}", im.fromAgentID, im.toAgentID, url); + if (url != string.Empty) + return TrySendInstantMessage(im, url, true, foreigner); + else + { + PresenceInfo upd = new PresenceInfo(); + upd.RegionID = UUID.Zero; + return TrySendInstantMessage(im, upd, true, foreigner); + } + + } + + protected bool TrySendInstantMessage(GridInstantMessage im, object previousLocation, bool firstTime, bool foreigner) + { + UUID toAgentID = new UUID(im.toAgentID); + + PresenceInfo upd = null; + string url = string.Empty; + + bool lookupAgent = false; + + lock (m_UserLocationMap) + { + if (m_UserLocationMap.ContainsKey(toAgentID)) + { + object o = m_UserLocationMap[toAgentID]; + if (o is PresenceInfo) + upd = (PresenceInfo)o; + else if (o is string) + url = (string)o; + + // We need to compare the current location with the previous + // or the recursive loop will never end because it will never try to lookup the agent again + if (!firstTime) + { + lookupAgent = true; + upd = null; + } + } + else + { + lookupAgent = true; + } + } + + //m_log.DebugFormat("[XXX] Neeed lookup ? {0}", (lookupAgent ? "yes" : "no")); + + // Are we needing to look-up an agent? + if (lookupAgent) + { + // Non-cached user agent lookup. + PresenceInfo[] presences = m_PresenceService.GetAgents(new string[] { toAgentID.ToString() }); + if (presences != null && presences.Length > 0) + { + foreach (PresenceInfo p in presences) + { + if (p.RegionID != UUID.Zero) + { + //m_log.DebugFormat("[XXX]: Found presence in {0}", p.RegionID); + upd = p; + break; + } + } + } + + if (upd == null && !foreigner) + { + // Let's check with the UAS if the user is elsewhere + m_log.DebugFormat("[HG IM SERVICE]: User is not present. Checking location with User Agent service"); + try + { + url = m_UserAgentService.LocateUser(toAgentID); + } + catch (Exception e) + { + m_log.Warn("[HG IM SERVICE]: LocateUser call failed ", e); + url = string.Empty; + } + } + + // check if we've tried this before.. + // This is one way to end the recursive loop + // + if (!firstTime && ((previousLocation is PresenceInfo && upd != null && upd.RegionID == ((PresenceInfo)previousLocation).RegionID) || + (previousLocation is string && upd == null && previousLocation.Equals(url)))) + { + // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); + m_log.DebugFormat("[HG IM SERVICE]: Fail 2 {0} {1}", previousLocation, url); + + return false; + } + } + + if (upd != null) + { + // ok, the user is around somewhere. Let's send back the reply with "success" + // even though the IM may still fail. Just don't keep the caller waiting for + // the entire time we're trying to deliver the IM + return SendIMToRegion(upd, im, toAgentID, foreigner); + } + else if (url != string.Empty) + { + // ok, the user is around somewhere. Let's send back the reply with "success" + // even though the IM may still fail. Just don't keep the caller waiting for + // the entire time we're trying to deliver the IM + return ForwardIMToGrid(url, im, toAgentID, foreigner); + } + else if (firstTime && previousLocation is string && (string)previousLocation != string.Empty) + { + return ForwardIMToGrid((string)previousLocation, im, toAgentID, foreigner); + } + else + m_log.DebugFormat("[HG IM SERVICE]: Unable to locate user {0}", toAgentID); + return false; + } + + bool SendIMToRegion(PresenceInfo upd, GridInstantMessage im, UUID toAgentID, bool foreigner) + { + bool imresult = false; + GridRegion reginfo = null; + if (!m_RegionCache.TryGetValue(upd.RegionID, out reginfo)) + { + reginfo = m_GridService.GetRegionByUUID(UUID.Zero /*!!!*/, upd.RegionID); + if (reginfo != null) + m_RegionCache.AddOrUpdate(upd.RegionID, reginfo, CACHE_EXPIRATION_SECONDS); + } + + if (reginfo != null) + { + imresult = InstantMessageServiceConnector.SendInstantMessage(reginfo.ServerURI, im); + } + else + { + m_log.DebugFormat("[HG IM SERVICE]: Failed to deliver message to {0}", reginfo.ServerURI); + return false; + } + + if (imresult) + { + // IM delivery successful, so store the Agent's location in our local cache. + lock (m_UserLocationMap) + { + if (m_UserLocationMap.ContainsKey(toAgentID)) + { + m_UserLocationMap[toAgentID] = upd; + } + else + { + m_UserLocationMap.Add(toAgentID, upd); + } + } + return true; + } + else + { + // try again, but lookup user this time. + // Warning, this must call the Async version + // of this method or we'll be making thousands of threads + // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync + // The version that spawns the thread is SendGridInstantMessageViaXMLRPC + + // This is recursive!!!!! + return TrySendInstantMessage(im, upd, false, foreigner); + } + } + + bool ForwardIMToGrid(string url, GridInstantMessage im, UUID toAgentID, bool foreigner) + { + if (InstantMessageServiceConnector.SendInstantMessage(url, im)) + { + // IM delivery successful, so store the Agent's location in our local cache. + lock (m_UserLocationMap) + { + if (m_UserLocationMap.ContainsKey(toAgentID)) + { + m_UserLocationMap[toAgentID] = url; + } + else + { + m_UserLocationMap.Add(toAgentID, url); + } + } + + return true; + } + else + { + // try again, but lookup user this time. + + // This is recursive!!!!! + return TrySendInstantMessage(im, url, false, foreigner); + } + } + + private bool UndeliveredMessage(GridInstantMessage im) + { + if (m_OfflineIMService == null) + return false; + + 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 false; + } + + if (!m_ForwardOfflineGroupMessages) + { + if (im.dialog == (byte)InstantMessageDialog.GroupNotice || + im.dialog == (byte)InstantMessageDialog.GroupInvitation) + return false; + } + +// m_log.DebugFormat("[HG IM SERVICE]: Message saved"); + string reason = string.Empty; + return m_OfflineIMService.StoreMessage(im, out reason); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/HypergridService/HGInventoryService.cs b/OpenSim/Services/HypergridService/HGInventoryService.cs new file mode 100644 index 0000000000..9158b417a0 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGInventoryService.cs @@ -0,0 +1,321 @@ +/* + * 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 log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.InventoryService; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Server.Base; + +namespace OpenSim.Services.HypergridService +{ + /// + /// Hypergrid inventory service. It serves the IInventoryService interface, + /// but implements it in ways that are appropriate for inter-grid + /// inventory exchanges. Specifically, it does not performs deletions + /// and it responds to GetRootFolder requests with the ID of the + /// Suitcase folder, not the actual "My Inventory" folder. + /// + public class HGInventoryService : XInventoryService, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_HomeURL; + private IUserAccountService m_UserAccountService; + + private UserAccountCache m_Cache; + + public HGInventoryService(IConfigSource config, string configName) + : base(config, configName) + { + m_log.Debug("[HGInventory Service]: Starting"); + if (configName != string.Empty) + m_ConfigName = configName; + + // + // Try reading the [InventoryService] section, if it exists + // + IConfig invConfig = config.Configs[m_ConfigName]; + if (invConfig != null) + { + // realm = authConfig.GetString("Realm", realm); + string userAccountsDll = invConfig.GetString("UserAccountsService", string.Empty); + if (userAccountsDll == string.Empty) + throw new Exception("Please specify UserAccountsService in HGInventoryService configuration"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(userAccountsDll, args); + if (m_UserAccountService == null) + throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll)); + + m_HomeURL = Util.GetConfigVarFromSections(config, "HomeURI", + new string[] { "Startup", "Hypergrid", m_ConfigName }, String.Empty); + + m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); + } + + m_log.Debug("[HG INVENTORY SERVICE]: Starting..."); + } + + public override bool CreateUserInventory(UUID principalID) + { + // NOGO + return false; + } + + + public override List GetInventorySkeleton(UUID principalID) + { + // NOGO for this inventory service + return new List(); + } + + public override InventoryFolderBase GetRootFolder(UUID principalID) + { + //m_log.DebugFormat("[HG INVENTORY SERVICE]: GetRootFolder for {0}", principalID); + // Warp! Root folder for travelers + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "folderName"}, + new string[] { principalID.ToString(), "My Suitcase" }); + + if (folders.Length > 0) + return ConvertToOpenSim(folders[0]); + + // make one + XInventoryFolder suitcase = CreateFolder(principalID, UUID.Zero, (int)FolderType.Suitcase, "My Suitcase"); + return ConvertToOpenSim(suitcase); + } + + //private bool CreateSystemFolders(UUID principalID, XInventoryFolder suitcase) + //{ + + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Animation, "Animations"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Bodypart, "Body Parts"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.CallingCard, "Calling Cards"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Clothing, "Clothing"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Gesture, "Gestures"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Landmark, "Landmarks"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.LostAndFoundFolder, "Lost And Found"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Notecard, "Notecards"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Object, "Objects"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.SnapshotFolder, "Photo Album"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.LSLText, "Scripts"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Sound, "Sounds"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.Texture, "Textures"); + // CreateFolder(principalID, suitcase.folderID, (int)AssetType.TrashFolder, "Trash"); + + // return true; + //} + + + public override InventoryFolderBase GetFolderForType(UUID principalID, FolderType type) + { + //m_log.DebugFormat("[HG INVENTORY SERVICE]: GetFolderForType for {0} {0}", principalID, type); + return GetRootFolder(principalID); + } + + // + // Use the inherited methods + // + //public InventoryCollection GetFolderContent(UUID principalID, UUID folderID) + //{ + //} + + // NOGO + // + public override InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderID) + { + return new InventoryCollection[0]; + } + + //public List GetFolderItems(UUID principalID, UUID folderID) + //{ + //} + + //public override bool AddFolder(InventoryFolderBase folder) + //{ + // // Check if it's under the Suitcase folder + // List skel = base.GetInventorySkeleton(folder.Owner); + // InventoryFolderBase suitcase = GetRootFolder(folder.Owner); + // List suitDescendents = GetDescendents(skel, suitcase.ID); + + // foreach (InventoryFolderBase f in suitDescendents) + // if (folder.ParentID == f.ID) + // { + // XInventoryFolder xFolder = ConvertFromOpenSim(folder); + // return m_Database.StoreFolder(xFolder); + // } + // return false; + //} + + private List GetDescendents(List lst, UUID root) + { + List direct = lst.FindAll(delegate(InventoryFolderBase f) { return f.ParentID == root; }); + if (direct == null) + return new List(); + + List indirect = new List(); + foreach (InventoryFolderBase f in direct) + indirect.AddRange(GetDescendents(lst, f.ID)); + + direct.AddRange(indirect); + return direct; + } + + // Use inherited method + //public bool UpdateFolder(InventoryFolderBase folder) + //{ + //} + + //public override bool MoveFolder(InventoryFolderBase folder) + //{ + // XInventoryFolder[] x = m_Database.GetFolders( + // new string[] { "folderID" }, + // new string[] { folder.ID.ToString() }); + + // if (x.Length == 0) + // return false; + + // // Check if it's under the Suitcase folder + // List skel = base.GetInventorySkeleton(folder.Owner); + // InventoryFolderBase suitcase = GetRootFolder(folder.Owner); + // List suitDescendents = GetDescendents(skel, suitcase.ID); + + // foreach (InventoryFolderBase f in suitDescendents) + // if (folder.ParentID == f.ID) + // { + // x[0].parentFolderID = folder.ParentID; + // return m_Database.StoreFolder(x[0]); + // } + + // return false; + //} + + public override bool DeleteFolders(UUID principalID, List folderIDs) + { + // NOGO + return false; + } + + public override bool PurgeFolder(InventoryFolderBase folder) + { + // NOGO + return false; + } + + // Unfortunately we need to use the inherited method because of how DeRez works. + // The viewer sends the folderID hard-wired in the derez message + //public override bool AddItem(InventoryItemBase item) + //{ + // // Check if it's under the Suitcase folder + // List skel = base.GetInventorySkeleton(item.Owner); + // InventoryFolderBase suitcase = GetRootFolder(item.Owner); + // List suitDescendents = GetDescendents(skel, suitcase.ID); + + // foreach (InventoryFolderBase f in suitDescendents) + // if (item.Folder == f.ID) + // return m_Database.StoreItem(ConvertFromOpenSim(item)); + + // return false; + //} + + //public override bool UpdateItem(InventoryItemBase item) + //{ + // // Check if it's under the Suitcase folder + // List skel = base.GetInventorySkeleton(item.Owner); + // InventoryFolderBase suitcase = GetRootFolder(item.Owner); + // List suitDescendents = GetDescendents(skel, suitcase.ID); + + // foreach (InventoryFolderBase f in suitDescendents) + // if (item.Folder == f.ID) + // return m_Database.StoreItem(ConvertFromOpenSim(item)); + + // return false; + //} + + //public override bool MoveItems(UUID principalID, List items) + //{ + // // Principal is b0rked. *sigh* + // // + // // Let's assume they all have the same principal + // // Check if it's under the Suitcase folder + // List skel = base.GetInventorySkeleton(items[0].Owner); + // InventoryFolderBase suitcase = GetRootFolder(items[0].Owner); + // List suitDescendents = GetDescendents(skel, suitcase.ID); + + // foreach (InventoryItemBase i in items) + // { + // foreach (InventoryFolderBase f in suitDescendents) + // if (i.Folder == f.ID) + // m_Database.MoveItem(i.ID.ToString(), i.Folder.ToString()); + // } + + // return true; + //} + + // Let these pass. Use inherited methods. + //public bool DeleteItems(UUID principalID, List itemIDs) + //{ + //} + + public override InventoryItemBase GetItem(InventoryItemBase item) + { + InventoryItemBase it = base.GetItem(item); + if (it != null) + { + UserAccount user = m_Cache.GetUser(it.CreatorId); + + // Adjust the creator data + if (user != null && it != null && string.IsNullOrEmpty(it.CreatorData)) + it.CreatorData = m_HomeURL + ";" + user.FirstName + " " + user.LastName; + } + return it; + } + + //public InventoryFolderBase GetFolder(InventoryFolderBase folder) + //{ + //} + + //public List GetActiveGestures(UUID principalID) + //{ + //} + + //public int GetAssetPermissions(UUID principalID, UUID assetID) + //{ + //} + + } +} diff --git a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs new file mode 100644 index 0000000000..40eb6d4c84 --- /dev/null +++ b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs @@ -0,0 +1,652 @@ +/* + * 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 OpenMetaverse; +using log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.InventoryService; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Server.Base; + +namespace OpenSim.Services.HypergridService +{ + /// + /// Hypergrid inventory service. It serves the IInventoryService interface, + /// but implements it in ways that are appropriate for inter-grid + /// inventory exchanges. Specifically, it does not performs deletions + /// and it responds to GetRootFolder requests with the ID of the + /// Suitcase folder, not the actual "My Inventory" folder. + /// + public class HGSuitcaseInventoryService : XInventoryService, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + +// private string m_HomeURL; + private IUserAccountService m_UserAccountService; + private IAvatarService m_AvatarService; + +// private UserAccountCache m_Cache; + + private ExpiringCache> m_SuitcaseTrees = new ExpiringCache>(); + private ExpiringCache m_Appearances = new ExpiringCache(); + + public HGSuitcaseInventoryService(IConfigSource config, string configName) + : base(config, configName) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: Starting with config name {0}", configName); + if (configName != string.Empty) + m_ConfigName = configName; + + if (m_Database == null) + m_log.ErrorFormat("[HG SUITCASE INVENTORY SERVICE]: m_Database is null!"); + + // + // Try reading the [InventoryService] section, if it exists + // + IConfig invConfig = config.Configs[m_ConfigName]; + if (invConfig != null) + { + string userAccountsDll = invConfig.GetString("UserAccountsService", string.Empty); + if (userAccountsDll == string.Empty) + throw new Exception("Please specify UserAccountsService in HGInventoryService configuration"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(userAccountsDll, args); + if (m_UserAccountService == null) + throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll)); + + string avatarDll = invConfig.GetString("AvatarService", string.Empty); + if (avatarDll == string.Empty) + throw new Exception("Please specify AvatarService in HGInventoryService configuration"); + + m_AvatarService = ServerUtils.LoadPlugin(avatarDll, args); + if (m_AvatarService == null) + throw new Exception(String.Format("Unable to create m_AvatarService from {0}", avatarDll)); + +// m_HomeURL = Util.GetConfigVarFromSections(config, "HomeURI", +// new string[] { "Startup", "Hypergrid", m_ConfigName }, String.Empty); + +// m_Cache = UserAccountCache.CreateUserAccountCache(m_UserAccountService); + } + + m_log.Debug("[HG SUITCASE INVENTORY SERVICE]: Starting..."); + } + + public override bool CreateUserInventory(UUID principalID) + { + // NOGO + return false; + } + + public override List GetInventorySkeleton(UUID principalID) + { + XInventoryFolder suitcase = GetSuitcaseXFolder(principalID); + + if (suitcase == null) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Found no suitcase folder for user {0} when looking for inventory skeleton", principalID); + return null; + } + + List tree = GetFolderTree(principalID, suitcase.folderID); + if (tree.Count == 0) + return null; + + List folders = new List(); + foreach (XInventoryFolder x in tree) + { + folders.Add(ConvertToOpenSim(x)); + } + + SetAsNormalFolder(suitcase); + folders.Add(ConvertToOpenSim(suitcase)); + + return folders; + } + + public override InventoryFolderBase GetRootFolder(UUID principalID) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetRootFolder for {0}", principalID); + + // Let's find out the local root folder + XInventoryFolder root = GetRootXFolder(principalID); + + if (root == null) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Unable to retrieve local root folder for user {0}", principalID); + return null; + } + + // Warp! Root folder for travelers is the suitcase folder + XInventoryFolder suitcase = GetSuitcaseXFolder(principalID); + + if (suitcase == null) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: User {0} does not have a Suitcase folder. Creating it...", principalID); + // Create the My Suitcase folder under the user's root folder. + // In the DB we tag it as type 100, but we use type 8 (Folder) outside, as this affects the sort order. + suitcase = CreateFolder(principalID, root.folderID, (int)FolderType.Suitcase, InventoryFolderBase.SUITCASE_FOLDER_NAME); + if (suitcase == null) + { + m_log.ErrorFormat("[HG SUITCASE INVENTORY SERVICE]: Unable to create suitcase folder"); + return null; + } + + CreateSystemFolders(principalID, suitcase.folderID); + } + + SetAsNormalFolder(suitcase); + + return ConvertToOpenSim(suitcase); + } + + protected void CreateSystemFolders(UUID principalID, UUID rootID) + { + m_log.Debug("[HG SUITCASE INVENTORY SERVICE]: Creating System folders under Suitcase..."); + XInventoryFolder[] sysFolders = GetSystemFolders(principalID, rootID); + + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Animation) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Animation, "Animations"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.BodyPart) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.BodyPart, "Body Parts"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.CallingCard) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.CallingCard, "Calling Cards"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Clothing) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Clothing, "Clothing"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.CurrentOutfit) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.CurrentOutfit, "Current Outfit"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Favorites) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Favorites, "Favorites"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Gesture) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Gesture, "Gestures"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Landmark) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Landmark, "Landmarks"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.LostAndFound) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.LostAndFound, "Lost And Found"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Notecard) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Notecard, "Notecards"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Object) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Object, "Objects"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Snapshot) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Snapshot, "Photo Album"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.LSLText) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.LSLText, "Scripts"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Sound) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Sound, "Sounds"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Texture) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Texture, "Textures"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Trash) return true; return false; })) + CreateFolder(principalID, rootID, (int)FolderType.Trash, "Trash"); + } + + public override InventoryFolderBase GetFolderForType(UUID principalID, FolderType type) + { + //m_log.DebugFormat("[HG INVENTORY SERVICE]: GetFolderForType for {0} {0}", principalID, type); + XInventoryFolder suitcase = GetSuitcaseXFolder(principalID); + + if (suitcase == null) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Found no suitcase folder for user {0} when looking for child type folder {1}", principalID, type); + return null; + } + + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "type", "parentFolderID" }, + new string[] { principalID.ToString(), ((int)type).ToString(), suitcase.folderID.ToString() }); + + if (folders.Length == 0) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Found no folder for type {0} for user {1}", type, principalID); + return null; + } + + m_log.DebugFormat( + "[HG SUITCASE INVENTORY SERVICE]: Found folder {0} {1} for type {2} for user {3}", + folders[0].folderName, folders[0].folderID, type, principalID); + + return ConvertToOpenSim(folders[0]); + } + + public override InventoryCollection GetFolderContent(UUID principalID, UUID folderID) + { + InventoryCollection coll = null; + + if (!IsWithinSuitcaseTree(principalID, folderID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetFolderContent: folder {0} (user {1}) is not within Suitcase tree", folderID, principalID); + return new InventoryCollection(); + } + + coll = base.GetFolderContent(principalID, folderID); + + if (coll == null) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: Something wrong with user {0}'s suitcase folder", principalID); + coll = new InventoryCollection(); + } + return coll; + } + + public override List GetFolderItems(UUID principalID, UUID folderID) + { + // Let's do a bit of sanity checking, more than the base service does + // make sure the given folder exists under the suitcase tree of this user + if (!IsWithinSuitcaseTree(principalID, folderID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetFolderItems: folder {0} (user {1}) is not within Suitcase tree", folderID, principalID); + return new List(); + } + + return base.GetFolderItems(principalID, folderID); + } + + public override bool AddFolder(InventoryFolderBase folder) + { + //m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: AddFolder {0} {1}", folder.Name, folder.ParentID); + // Let's do a bit of sanity checking, more than the base service does + // make sure the given folder's parent folder exists under the suitcase tree of this user + + if (!IsWithinSuitcaseTree(folder.Owner, folder.ParentID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: AddFolder: folder {0} (user {1}) is not within Suitcase tree", folder.ParentID, folder.Owner); + return false; + } + + // OK, it's legit + if (base.AddFolder(folder)) + { + List tree; + if (m_SuitcaseTrees.TryGetValue(folder.Owner, out tree)) + tree.Add(ConvertFromOpenSim(folder)); + + return true; + } + + return false; + } + + public override bool UpdateFolder(InventoryFolderBase folder) + { + //m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: Update folder {0}, version {1}", folder.ID, folder.Version); + if (!IsWithinSuitcaseTree(folder.Owner, folder.ID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: UpdateFolder: folder {0}/{1} (user {2}) is not within Suitcase tree", folder.Name, folder.ID, folder.Owner); + return false; + } + + // For all others + return base.UpdateFolder(folder); + } + + public override bool MoveFolder(InventoryFolderBase folder) + { + if (!IsWithinSuitcaseTree(folder.Owner, folder.ID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: MoveFolder: folder {0} (user {1}) is not within Suitcase tree", folder.ID, folder.Owner); + return false; + } + + if (!IsWithinSuitcaseTree(folder.Owner, folder.ParentID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: MoveFolder: folder {0} (user {1}) is not within Suitcase tree", folder.ParentID, folder.Owner); + return false; + } + + return base.MoveFolder(folder); + } + + public override bool DeleteFolders(UUID principalID, List folderIDs) + { + // NOGO + return false; + } + + public override bool PurgeFolder(InventoryFolderBase folder) + { + // NOGO + return false; + } + + public override bool AddItem(InventoryItemBase item) + { + // Let's do a bit of sanity checking, more than the base service does + // make sure the given folder's parent folder exists under the suitcase tree of this user + if (!IsWithinSuitcaseTree(item.Owner, item.Folder)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: AddItem: folder {0} (user {1}) is not within Suitcase tree", item.Folder, item.Owner); + return false; + } + + // OK, it's legit + return base.AddItem(item); + + } + + public override bool UpdateItem(InventoryItemBase item) + { + if (!IsWithinSuitcaseTree(item.Owner, item.Folder)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: UpdateItem: folder {0} (user {1}) is not within Suitcase tree", item.Folder, item.Owner); + return false; + } + + return base.UpdateItem(item); + } + + public override bool MoveItems(UUID principalID, List items) + { + // Principal is b0rked. *sigh* + + // Check the items' destination folders + foreach (InventoryItemBase item in items) + { + if (!IsWithinSuitcaseTree(item.Owner, item.Folder)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: MoveItems: folder {0} (user {1}) is not within Suitcase tree", item.Folder, item.Owner); + return false; + } + } + + // Check the items' current folders + foreach (InventoryItemBase item in items) + { + InventoryItemBase originalItem = base.GetItem(item); + if (!IsWithinSuitcaseTree(originalItem.Owner, originalItem.Folder)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: MoveItems: folder {0} (user {1}) is not within Suitcase tree", item.Folder, item.Owner); + return false; + } + } + + return base.MoveItems(principalID, items); + } + + public override bool DeleteItems(UUID principalID, List itemIDs) + { + return false; + } + + public new InventoryItemBase GetItem(InventoryItemBase item) + { + InventoryItemBase it = base.GetItem(item); + if (it == null) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: Unable to retrieve item {0} ({1}) in folder {2}", + item.Name, item.ID, item.Folder); + return null; + } + + if (!IsWithinSuitcaseTree(it.Owner, it.Folder) && !IsPartOfAppearance(it.Owner, it.ID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetItem: item {0}/{1} (folder {2}) (user {3}) is not within Suitcase tree or Appearance", + it.Name, it.ID, it.Folder, it.Owner); + return null; + } + + // UserAccount user = m_Cache.GetUser(it.CreatorId); + + // // Adjust the creator data + // if (user != null && it != null && (it.CreatorData == null || it.CreatorData == string.Empty)) + // it.CreatorData = m_HomeURL + ";" + user.FirstName + " " + user.LastName; + //} + + return it; + } + + public new InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + InventoryFolderBase f = base.GetFolder(folder); + + if (f != null) + { + if (!IsWithinSuitcaseTree(f.Owner, f.ID)) + { + m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: GetFolder: folder {0}/{1} (user {2}) is not within Suitcase tree", + f.Name, f.ID, f.Owner); + return null; + } + } + + return f; + } + + //public List GetActiveGestures(UUID principalID) + //{ + //} + + //public int GetAssetPermissions(UUID principalID, UUID assetID) + //{ + //} + + #region Auxiliary functions + private XInventoryFolder GetXFolder(UUID userID, UUID folderID) + { + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "folderID" }, + new string[] { userID.ToString(), folderID.ToString() }); + + if (folders.Length == 0) + return null; + + return folders[0]; + } + + private XInventoryFolder GetRootXFolder(UUID principalID) + { + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "folderName", "type" }, + new string[] { principalID.ToString(), InventoryFolderBase.ROOT_FOLDER_NAME, ((int)FolderType.Root).ToString() }); + + if (folders != null && folders.Length > 0) + return folders[0]; + + // OK, so the RootFolder type didn't work. Let's look for any type with parent UUID.Zero. + folders = m_Database.GetFolders( + new string[] { "agentID", "folderName", "parentFolderID" }, + new string[] { principalID.ToString(), InventoryFolderBase.ROOT_FOLDER_NAME, UUID.Zero.ToString() }); + + if (folders != null && folders.Length > 0) + return folders[0]; + + return null; + } + + private XInventoryFolder GetCurrentOutfitXFolder(UUID userID) + { + XInventoryFolder root = GetRootXFolder(userID); + if (root == null) + return null; + + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "type", "parentFolderID" }, + new string[] { userID.ToString(), ((int)FolderType.CurrentOutfit).ToString(), root.folderID.ToString() }); + + if (folders.Length == 0) + return null; + + return folders[0]; + } + + private XInventoryFolder GetSuitcaseXFolder(UUID principalID) + { + // Warp! Root folder for travelers + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "type" }, + new string[] { principalID.ToString(), ((int)FolderType.Suitcase).ToString() }); + + if (folders != null && folders.Length > 0) + return folders[0]; + + // check to see if we have the old Suitcase folder + folders = m_Database.GetFolders( + new string[] { "agentID", "folderName", "parentFolderID" }, + new string[] { principalID.ToString(), InventoryFolderBase.SUITCASE_FOLDER_NAME, UUID.Zero.ToString() }); + if (folders != null && folders.Length > 0) + { + // Move it to under the root folder + XInventoryFolder root = GetRootXFolder(principalID); + folders[0].parentFolderID = root.folderID; + folders[0].type = (int)FolderType.Suitcase; + m_Database.StoreFolder(folders[0]); + return folders[0]; + } + + return null; + } + + private void SetAsNormalFolder(XInventoryFolder suitcase) + { + //suitcase.type = InventoryItemBase.SUITCASE_FOLDER_FAKE_TYPE; + } + + private List GetFolderTree(UUID principalID, UUID folder) + { + List t; + if (m_SuitcaseTrees.TryGetValue(principalID, out t)) + return t; + + // Get the tree of the suitcase folder + t = GetFolderTreeRecursive(folder); + m_SuitcaseTrees.AddOrUpdate(principalID, t, 5*60); // 5 minutes + return t; + } + + private List GetFolderTreeRecursive(UUID root) + { + List tree = new List(); + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "parentFolderID" }, + new string[] { root.ToString() }); + + if (folders == null || folders.Length == 0) + { + return tree; // empty tree + } + else + { + foreach (XInventoryFolder f in folders) + { + tree.Add(f); + tree.AddRange(GetFolderTreeRecursive(f.folderID)); + } + return tree; + } + + } + + /// + /// Return true if the folderID is a subfolder of the Suitcase or the suitcase folder itself + /// + /// + /// + /// + /// + private bool IsWithinSuitcaseTree(UUID principalID, UUID folderID) + { + XInventoryFolder suitcase = GetSuitcaseXFolder(principalID); + + if (suitcase == null) + { + m_log.WarnFormat("[HG SUITCASE INVENTORY SERVICE]: User {0} does not have a Suitcase folder", principalID); + return false; + } + + List tree = new List(); + tree.Add(suitcase); // Warp! the tree is the real root folder plus the children of the suitcase folder + tree.AddRange(GetFolderTree(principalID, suitcase.folderID)); + + // Also add the Current Outfit folder to the list of available folders + XInventoryFolder folder = GetCurrentOutfitXFolder(principalID); + if (folder != null) + tree.Add(folder); + + XInventoryFolder f = tree.Find(delegate(XInventoryFolder fl) + { + return (fl.folderID == folderID); + }); + + return (f != null); + } + #endregion + + #region Avatar Appearance + + private AvatarAppearance GetAppearance(UUID principalID) + { + AvatarAppearance a = null; + if (m_Appearances.TryGetValue(principalID, out a)) + return a; + + a = m_AvatarService.GetAppearance(principalID); + m_Appearances.AddOrUpdate(principalID, a, 5 * 60); // 5minutes + return a; + } + + private bool IsPartOfAppearance(UUID principalID, UUID itemID) + { + AvatarAppearance a = GetAppearance(principalID); + + if (a == null) + return false; + + // Check wearables (body parts and clothes) + for (int i = 0; i < a.Wearables.Length; i++) + { + for (int j = 0; j < a.Wearables[i].Count; j++) + { + if (a.Wearables[i][j].ItemID == itemID) + { + //m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: item {0} is a wearable", itemID); + return true; + } + } + } + + // Check attachments + if (a.GetAttachmentForItem(itemID) != null) + { + //m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: item {0} is an attachment", itemID); + return true; + } + + return false; + } + + #endregion + + } + +} diff --git a/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs b/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..a565729ccf --- /dev/null +++ b/OpenSim/Services/HypergridService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.HypergridService")] +[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("8584f3c1-26dd-4d95-86f4-cd8f0110a18f")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/HypergridService/UserAccountCache.cs b/OpenSim/Services/HypergridService/UserAccountCache.cs new file mode 100644 index 0000000000..fa7dd0be47 --- /dev/null +++ b/OpenSim/Services/HypergridService/UserAccountCache.cs @@ -0,0 +1,111 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +using log4net; +using OpenMetaverse; + +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.HypergridService +{ + public class UserAccountCache : IUserAccountService + { + private const double CACHE_EXPIRATION_SECONDS = 120000.0; // 33 hours! + +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private ExpiringCache m_UUIDCache; + + private IUserAccountService m_UserAccountService; + + private static UserAccountCache m_Singleton; + + public static UserAccountCache CreateUserAccountCache(IUserAccountService u) + { + if (m_Singleton == null) + m_Singleton = new UserAccountCache(u); + + return m_Singleton; + } + + private UserAccountCache(IUserAccountService u) + { + m_UUIDCache = new ExpiringCache(); + m_UserAccountService = u; + } + + public void Cache(UUID userID, UserAccount account) + { + // Cache even null accounts + m_UUIDCache.AddOrUpdate(userID, account, CACHE_EXPIRATION_SECONDS); + + //m_log.DebugFormat("[USER CACHE]: cached user {0}", userID); + } + + public UserAccount Get(UUID userID, out bool inCache) + { + UserAccount account = null; + inCache = false; + if (m_UUIDCache.TryGetValue(userID, out account)) + { + //m_log.DebugFormat("[USER CACHE]: Account {0} {1} found in cache", account.FirstName, account.LastName); + inCache = true; + return account; + } + + return null; + } + + public UserAccount GetUser(string id) + { + UUID uuid = UUID.Zero; + UUID.TryParse(id, out uuid); + bool inCache = false; + UserAccount account = Get(uuid, out inCache); + if (!inCache) + { + account = m_UserAccountService.GetUserAccount(UUID.Zero, uuid); + Cache(uuid, account); + } + + return account; + } + + #region IUserAccountService + public UserAccount GetUserAccount(UUID scopeID, UUID userID) + { + return GetUser(userID.ToString()); + } + + public UserAccount GetUserAccount(UUID scopeID, string FirstName, string LastName) + { + return null; + } + + public UserAccount GetUserAccount(UUID scopeID, string Email) + { + return null; + } + + public List GetUserAccounts(UUID scopeID, string query) + { + return null; + } + + public void InvalidateCache(UUID userID) + { + m_UUIDCache.Remove(userID); + } + + public bool StoreUserAccount(UserAccount data) + { + return false; + } + #endregion + + } + +} diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs new file mode 100644 index 0000000000..c65122a4fb --- /dev/null +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -0,0 +1,746 @@ +/* + * 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.Net; +using System.Reflection; + +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Connectors.Friends; +using OpenSim.Services.Connectors.Hypergrid; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Server.Base; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +using OpenMetaverse; +using log4net; +using Nini.Config; + +namespace OpenSim.Services.HypergridService +{ + /// + /// This service is for HG1.5 only, to make up for the fact that clients don't + /// keep any private information in themselves, and that their 'home service' + /// needs to do it for them. + /// Once we have better clients, this shouldn't be needed. + /// + public class UserAgentService : UserAgentServiceBase, IUserAgentService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + // This will need to go into a DB table + //static Dictionary m_Database = new Dictionary(); + + static bool m_Initialized = false; + + protected static IGridUserService m_GridUserService; + protected static IGridService m_GridService; + protected static GatekeeperServiceConnector m_GatekeeperConnector; + protected static IGatekeeperService m_GatekeeperService; + protected static IFriendsService m_FriendsService; + protected static IPresenceService m_PresenceService; + protected static IUserAccountService m_UserAccountService; + protected static IFriendsSimConnector m_FriendsLocalSimConnector; // standalone, points to HGFriendsModule + protected static FriendsSimConnector m_FriendsSimConnector; // grid + + protected static string m_GridName; + + protected static int m_LevelOutsideContacts; + protected static bool m_ShowDetails; + + protected static bool m_BypassClientVerification; + + private static Dictionary m_ForeignTripsAllowed = new Dictionary(); + private static Dictionary> m_TripsAllowedExceptions = new Dictionary>(); + private static Dictionary> m_TripsDisallowedExceptions = new Dictionary>(); + + public UserAgentService(IConfigSource config) : this(config, null) + { + } + + public UserAgentService(IConfigSource config, IFriendsSimConnector friendsConnector) + : base(config) + { + // Let's set this always, because we don't know the sequence + // of instantiations + if (friendsConnector != null) + m_FriendsLocalSimConnector = friendsConnector; + + if (!m_Initialized) + { + m_Initialized = true; + + m_log.DebugFormat("[HOME USERS SECURITY]: Starting..."); + + m_FriendsSimConnector = new FriendsSimConnector(); + + IConfig serverConfig = config.Configs["UserAgentService"]; + if (serverConfig == null) + throw new Exception(String.Format("No section UserAgentService in config file")); + + string gridService = serverConfig.GetString("GridService", String.Empty); + string gridUserService = serverConfig.GetString("GridUserService", String.Empty); + string gatekeeperService = serverConfig.GetString("GatekeeperService", String.Empty); + string friendsService = serverConfig.GetString("FriendsService", String.Empty); + string presenceService = serverConfig.GetString("PresenceService", String.Empty); + string userAccountService = serverConfig.GetString("UserAccountService", String.Empty); + + m_BypassClientVerification = serverConfig.GetBoolean("BypassClientVerification", false); + + if (gridService == string.Empty || gridUserService == string.Empty || gatekeeperService == string.Empty) + throw new Exception(String.Format("Incomplete specifications, UserAgent Service cannot function.")); + + Object[] args = new Object[] { config }; + m_GridService = ServerUtils.LoadPlugin(gridService, args); + m_GridUserService = ServerUtils.LoadPlugin(gridUserService, args); + m_GatekeeperConnector = new GatekeeperServiceConnector(); + m_GatekeeperService = ServerUtils.LoadPlugin(gatekeeperService, args); + m_FriendsService = ServerUtils.LoadPlugin(friendsService, args); + m_PresenceService = ServerUtils.LoadPlugin(presenceService, args); + m_UserAccountService = ServerUtils.LoadPlugin(userAccountService, args); + + m_LevelOutsideContacts = serverConfig.GetInt("LevelOutsideContacts", 0); + m_ShowDetails = serverConfig.GetBoolean("ShowUserDetailsInHGProfile", true); + + LoadTripPermissionsFromConfig(serverConfig, "ForeignTripsAllowed"); + LoadDomainExceptionsFromConfig(serverConfig, "AllowExcept", m_TripsAllowedExceptions); + LoadDomainExceptionsFromConfig(serverConfig, "DisallowExcept", m_TripsDisallowedExceptions); + + m_GridName = Util.GetConfigVarFromSections(config, "GatekeeperURI", + new string[] { "Startup", "Hypergrid", "UserAgentService" }, String.Empty); + if (string.IsNullOrEmpty(m_GridName)) // Legacy. Remove soon. + { + m_GridName = serverConfig.GetString("ExternalName", string.Empty); + if (m_GridName == string.Empty) + { + serverConfig = config.Configs["GatekeeperService"]; + m_GridName = serverConfig.GetString("ExternalName", string.Empty); + } + } + + if (!m_GridName.EndsWith("/")) + m_GridName = m_GridName + "/"; + + // Finally some cleanup + m_Database.DeleteOld(); + + } + } + + protected void LoadTripPermissionsFromConfig(IConfig config, string variable) + { + foreach (string keyName in config.GetKeys()) + { + if (keyName.StartsWith(variable + "_Level_")) + { + int level = 0; + if (Int32.TryParse(keyName.Replace(variable + "_Level_", ""), out level)) + m_ForeignTripsAllowed.Add(level, config.GetBoolean(keyName, true)); + } + } + } + + protected void LoadDomainExceptionsFromConfig(IConfig config, string variable, Dictionary> exceptions) + { + foreach (string keyName in config.GetKeys()) + { + if (keyName.StartsWith(variable + "_Level_")) + { + int level = 0; + if (Int32.TryParse(keyName.Replace(variable + "_Level_", ""), out level) && !exceptions.ContainsKey(level)) + { + exceptions.Add(level, new List()); + string value = config.GetString(keyName, string.Empty); + string[] parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + + foreach (string s in parts) + exceptions[level].Add(s.Trim()); + } + } + } + } + + + public GridRegion GetHomeRegion(UUID userID, out Vector3 position, out Vector3 lookAt) + { + position = new Vector3(128, 128, 0); lookAt = Vector3.UnitY; + + m_log.DebugFormat("[USER AGENT SERVICE]: Request to get home region of user {0}", userID); + + GridRegion home = null; + GridUserInfo uinfo = m_GridUserService.GetGridUserInfo(userID.ToString()); + if (uinfo != null) + { + if (uinfo.HomeRegionID != UUID.Zero) + { + home = m_GridService.GetRegionByUUID(UUID.Zero, uinfo.HomeRegionID); + position = uinfo.HomePosition; + lookAt = uinfo.HomeLookAt; + } + if (home == null) + { + List defs = m_GridService.GetDefaultRegions(UUID.Zero); + if (defs != null && defs.Count > 0) + home = defs[0]; + } + } + + return home; + } + + public bool LoginAgentToGrid(GridRegion source, AgentCircuitData agentCircuit, GridRegion gatekeeper, GridRegion finalDestination, bool fromLogin, out string reason) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Request to login user {0} {1} (@{2}) to grid {3}", + agentCircuit.firstname, agentCircuit.lastname, (fromLogin ? agentCircuit.IPAddress : "stored IP"), gatekeeper.ServerURI); + + string gridName = gatekeeper.ServerURI; + + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, agentCircuit.AgentID); + if (account == null) + { + m_log.WarnFormat("[USER AGENT SERVICE]: Someone attempted to lauch a foreign user from here {0} {1}", agentCircuit.firstname, agentCircuit.lastname); + reason = "Forbidden to launch your agents from here"; + return false; + } + + // Is this user allowed to go there? + if (m_GridName != gridName) + { + if (m_ForeignTripsAllowed.ContainsKey(account.UserLevel)) + { + bool allowed = m_ForeignTripsAllowed[account.UserLevel]; + + if (m_ForeignTripsAllowed[account.UserLevel] && IsException(gridName, account.UserLevel, m_TripsAllowedExceptions)) + allowed = false; + + if (!m_ForeignTripsAllowed[account.UserLevel] && IsException(gridName, account.UserLevel, m_TripsDisallowedExceptions)) + allowed = true; + + if (!allowed) + { + reason = "Your world does not allow you to visit the destination"; + m_log.InfoFormat("[USER AGENT SERVICE]: Agents not permitted to visit {0}. Refusing service.", gridName); + return false; + } + } + } + + + // Take the IP address + port of the gatekeeper (reg) plus the info of finalDestination + GridRegion region = new GridRegion(gatekeeper); + region.ServerURI = gatekeeper.ServerURI; + region.ExternalHostName = finalDestination.ExternalHostName; + region.InternalEndPoint = finalDestination.InternalEndPoint; + region.RegionName = finalDestination.RegionName; + region.RegionID = finalDestination.RegionID; + region.RegionLocX = finalDestination.RegionLocX; + region.RegionLocY = finalDestination.RegionLocY; + + // Generate a new service session + agentCircuit.ServiceSessionID = region.ServerURI + ";" + UUID.Random(); + TravelingAgentInfo old = null; + TravelingAgentInfo travel = CreateTravelInfo(agentCircuit, region, fromLogin, out old); + + bool success = false; + string myExternalIP = string.Empty; + + m_log.DebugFormat("[USER AGENT SERVICE]: this grid: {0}, desired grid: {1}, desired region: {2}", m_GridName, gridName, region.RegionID); + + if (m_GridName == gridName) + { + success = m_GatekeeperService.LoginAgent(source, agentCircuit, finalDestination, out reason); + } + else + { + success = m_GatekeeperConnector.CreateAgent(source, region, agentCircuit, (uint)Constants.TeleportFlags.ViaLogin, out myExternalIP, out reason); + } + + if (!success) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Unable to login user {0} {1} to grid {2}, reason: {3}", + agentCircuit.firstname, agentCircuit.lastname, region.ServerURI, reason); + + if (old != null) + StoreTravelInfo(old); + else + m_Database.Delete(agentCircuit.SessionID); + + return false; + } + + // Everything is ok + + // Update the perceived IP Address of our grid + m_log.DebugFormat("[USER AGENT SERVICE]: Gatekeeper sees me as {0}", myExternalIP); + travel.MyIpAddress = myExternalIP; + + StoreTravelInfo(travel); + + return true; + } + + public bool LoginAgentToGrid(GridRegion source, AgentCircuitData agentCircuit, GridRegion gatekeeper, GridRegion finalDestination, out string reason) + { + reason = string.Empty; + return LoginAgentToGrid(source, agentCircuit, gatekeeper, finalDestination, false, out reason); + } + + TravelingAgentInfo CreateTravelInfo(AgentCircuitData agentCircuit, GridRegion region, bool fromLogin, out TravelingAgentInfo existing) + { + HGTravelingData hgt = m_Database.Get(agentCircuit.SessionID); + existing = null; + + if (hgt != null) + { + // Very important! Override whatever this agent comes with. + // UserAgentService always sets the IP for every new agent + // with the original IP address. + existing = new TravelingAgentInfo(hgt); + agentCircuit.IPAddress = existing.ClientIPAddress; + } + + TravelingAgentInfo travel = new TravelingAgentInfo(existing); + travel.SessionID = agentCircuit.SessionID; + travel.UserID = agentCircuit.AgentID; + travel.GridExternalName = region.ServerURI; + travel.ServiceToken = agentCircuit.ServiceSessionID; + + if (fromLogin) + travel.ClientIPAddress = agentCircuit.IPAddress; + + StoreTravelInfo(travel); + + return travel; + } + + public void LogoutAgent(UUID userID, UUID sessionID) + { + m_log.DebugFormat("[USER AGENT SERVICE]: User {0} logged out", userID); + + m_Database.Delete(sessionID); + + GridUserInfo guinfo = m_GridUserService.GetGridUserInfo(userID.ToString()); + if (guinfo != null) + m_GridUserService.LoggedOut(userID.ToString(), sessionID, guinfo.LastRegionID, guinfo.LastPosition, guinfo.LastLookAt); + } + + // We need to prevent foreign users with the same UUID as a local user + public bool IsAgentComingHome(UUID sessionID, string thisGridExternalName) + { + HGTravelingData hgt = m_Database.Get(sessionID); + if (hgt == null) + return false; + + TravelingAgentInfo travel = new TravelingAgentInfo(hgt); + + return travel.GridExternalName.ToLower() == thisGridExternalName.ToLower(); + } + + public bool VerifyClient(UUID sessionID, string reportedIP) + { + if (m_BypassClientVerification) + return true; + + m_log.DebugFormat("[USER AGENT SERVICE]: Verifying Client session {0} with reported IP {1}.", + sessionID, reportedIP); + + HGTravelingData hgt = m_Database.Get(sessionID); + if (hgt == null) + return false; + + TravelingAgentInfo travel = new TravelingAgentInfo(hgt); + + bool result = travel.ClientIPAddress == reportedIP || travel.MyIpAddress == reportedIP; // NATed + + m_log.DebugFormat("[USER AGENT SERVICE]: Comparing {0} with login IP {1} and MyIP {1}; result is {3}", + reportedIP, travel.ClientIPAddress, travel.MyIpAddress, result); + + return result; + } + + public bool VerifyAgent(UUID sessionID, string token) + { + HGTravelingData hgt = m_Database.Get(sessionID); + if (hgt == null) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Token verification for session {0}: no such session", sessionID); + return false; + } + + TravelingAgentInfo travel = new TravelingAgentInfo(hgt); + m_log.DebugFormat("[USER AGENT SERVICE]: Verifying agent token {0} against {1}", token, travel.ServiceToken); + return travel.ServiceToken == token; + } + + [Obsolete] + public List StatusNotification(List friends, UUID foreignUserID, bool online) + { + if (m_FriendsService == null || m_PresenceService == null) + { + m_log.WarnFormat("[USER AGENT SERVICE]: Unable to perform status notifications because friends or presence services are missing"); + return new List(); + } + + List localFriendsOnline = new List(); + + m_log.DebugFormat("[USER AGENT SERVICE]: Status notification: foreign user {0} wants to notify {1} local friends", foreignUserID, friends.Count); + + // First, let's double check that the reported friends are, indeed, friends of that user + // And let's check that the secret matches + List usersToBeNotified = new List(); + foreach (string uui in friends) + { + UUID localUserID; + string secret = string.Empty, tmp = string.Empty; + if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) + { + FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); + foreach (FriendInfo finfo in friendInfos) + { + if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret)) + { + // great! + usersToBeNotified.Add(localUserID.ToString()); + } + } + } + } + + // Now, let's send the notifications + m_log.DebugFormat("[USER AGENT SERVICE]: Status notification: user has {0} local friends", usersToBeNotified.Count); + + // First, let's send notifications to local users who are online in the home grid + PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); + if (friendSessions != null && friendSessions.Length > 0) + { + PresenceInfo friendSession = null; + foreach (PresenceInfo pinfo in friendSessions) + if (pinfo.RegionID != UUID.Zero) // let's guard against traveling agents + { + friendSession = pinfo; + break; + } + + if (friendSession != null) + { + ForwardStatusNotificationToSim(friendSession.RegionID, foreignUserID, friendSession.UserID, online); + usersToBeNotified.Remove(friendSession.UserID.ToString()); + UUID id; + if (UUID.TryParse(friendSession.UserID, out id)) + localFriendsOnline.Add(id); + + } + } + + //// Lastly, let's notify the rest who may be online somewhere else + //foreach (string user in usersToBeNotified) + //{ + // UUID id = new UUID(user); + // if (m_Database.ContainsKey(id) && m_Database[id].GridExternalName != m_GridName) + // { + // string url = m_Database[id].GridExternalName; + // // forward + // m_log.WarnFormat("[USER AGENT SERVICE]: User {0} is visiting {1}. HG Status notifications still not implemented.", user, url); + // } + //} + + // and finally, let's send the online friends + if (online) + { + return localFriendsOnline; + } + else + return new List(); + } + + [Obsolete] + protected void ForwardStatusNotificationToSim(UUID regionID, UUID foreignUserID, string user, bool online) + { + UUID userID; + if (UUID.TryParse(user, out userID)) + { + if (m_FriendsLocalSimConnector != null) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Local Notify, user {0} is {1}", foreignUserID, (online ? "online" : "offline")); + m_FriendsLocalSimConnector.StatusNotify(foreignUserID, userID, online); + } + else + { + GridRegion region = m_GridService.GetRegionByUUID(UUID.Zero /* !!! */, regionID); + if (region != null) + { + m_log.DebugFormat("[USER AGENT SERVICE]: Remote Notify to region {0}, user {1} is {2}", region.RegionName, foreignUserID, (online ? "online" : "offline")); + m_FriendsSimConnector.StatusNotify(region, foreignUserID, userID.ToString(), online); + } + } + } + } + + public List GetOnlineFriends(UUID foreignUserID, List friends) + { + List online = new List(); + + if (m_FriendsService == null || m_PresenceService == null) + { + m_log.WarnFormat("[USER AGENT SERVICE]: Unable to get online friends because friends or presence services are missing"); + return online; + } + + m_log.DebugFormat("[USER AGENT SERVICE]: Foreign user {0} wants to know status of {1} local friends", foreignUserID, friends.Count); + + // First, let's double check that the reported friends are, indeed, friends of that user + // And let's check that the secret matches and the rights + List usersToBeNotified = new List(); + foreach (string uui in friends) + { + UUID localUserID; + string secret = string.Empty, tmp = string.Empty; + if (Util.ParseUniversalUserIdentifier(uui, out localUserID, out tmp, out tmp, out tmp, out secret)) + { + FriendInfo[] friendInfos = m_FriendsService.GetFriends(localUserID); + foreach (FriendInfo finfo in friendInfos) + { + if (finfo.Friend.StartsWith(foreignUserID.ToString()) && finfo.Friend.EndsWith(secret) && + (finfo.TheirFlags & (int)FriendRights.CanSeeOnline) != 0 && (finfo.TheirFlags != -1)) + { + // great! + usersToBeNotified.Add(localUserID.ToString()); + } + } + } + } + + // Now, let's find out their status + m_log.DebugFormat("[USER AGENT SERVICE]: GetOnlineFriends: user has {0} local friends with status rights", usersToBeNotified.Count); + + // First, let's send notifications to local users who are online in the home grid + PresenceInfo[] friendSessions = m_PresenceService.GetAgents(usersToBeNotified.ToArray()); + if (friendSessions != null && friendSessions.Length > 0) + { + foreach (PresenceInfo pi in friendSessions) + { + UUID presenceID; + if (UUID.TryParse(pi.UserID, out presenceID)) + online.Add(presenceID); + } + } + + return online; + } + + public Dictionary GetUserInfo(UUID userID) + { + Dictionary info = new Dictionary(); + + if (m_UserAccountService == null) + { + m_log.WarnFormat("[USER AGENT SERVICE]: Unable to get user flags because user account service is missing"); + info["result"] = "fail"; + info["message"] = "UserAccountService is missing!"; + return info; + } + + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero /*!!!*/, userID); + + if (account != null) + { + info.Add("user_firstname", account.FirstName); + info.Add("user_lastname", account.LastName); + info.Add("result", "success"); + + if (m_ShowDetails) + { + info.Add("user_flags", account.UserFlags); + info.Add("user_created", account.Created); + info.Add("user_title", account.UserTitle); + } + else + { + info.Add("user_flags", 0); + info.Add("user_created", 0); + info.Add("user_title", string.Empty); + } + } + + return info; + } + + public Dictionary GetServerURLs(UUID userID) + { + if (m_UserAccountService == null) + { + m_log.WarnFormat("[USER AGENT SERVICE]: Unable to get server URLs because user account service is missing"); + return new Dictionary(); + } + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero /*!!!*/, userID); + if (account != null) + return account.ServiceURLs; + + return new Dictionary(); + } + + public string LocateUser(UUID userID) + { + HGTravelingData[] hgts = m_Database.GetSessions(userID); + if (hgts == null) + return string.Empty; + + foreach (HGTravelingData t in hgts) + if (t.Data.ContainsKey("GridExternalName") && !m_GridName.Equals(t.Data["GridExternalName"])) + return t.Data["GridExternalName"]; + + return string.Empty; + } + + public string GetUUI(UUID userID, UUID targetUserID) + { + // Let's see if it's a local user + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, targetUserID); + if (account != null) + return targetUserID.ToString() + ";" + m_GridName + ";" + account.FirstName + " " + account.LastName ; + + // Let's try the list of friends + FriendInfo[] friends = m_FriendsService.GetFriends(userID); + if (friends != null && friends.Length > 0) + { + foreach (FriendInfo f in friends) + if (f.Friend.StartsWith(targetUserID.ToString())) + { + // Let's remove the secret + UUID id; string tmp = string.Empty, secret = string.Empty; + if (Util.ParseUniversalUserIdentifier(f.Friend, out id, out tmp, out tmp, out tmp, out secret)) + return f.Friend.Replace(secret, "0"); + } + } + + return string.Empty; + } + + public UUID GetUUID(String first, String last) + { + // Let's see if it's a local user + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, first, last); + if (account != null) + { + // check user level + if (account.UserLevel < m_LevelOutsideContacts) + return UUID.Zero; + else + return account.PrincipalID; + } + else + return UUID.Zero; + } + + #region Misc + + private bool IsException(string dest, int level, Dictionary> exceptions) + { + if (!exceptions.ContainsKey(level)) + return false; + + bool exception = false; + if (exceptions[level].Count > 0) // we have exceptions + { + string destination = dest; + if (!destination.EndsWith("/")) + destination += "/"; + + if (exceptions[level].Find(delegate(string s) + { + if (!s.EndsWith("/")) + s += "/"; + return s == destination; + }) != null) + exception = true; + } + + return exception; + } + + private void StoreTravelInfo(TravelingAgentInfo travel) + { + if (travel == null) + return; + + HGTravelingData hgt = new HGTravelingData(); + hgt.SessionID = travel.SessionID; + hgt.UserID = travel.UserID; + hgt.Data = new Dictionary(); + hgt.Data["GridExternalName"] = travel.GridExternalName; + hgt.Data["ServiceToken"] = travel.ServiceToken; + hgt.Data["ClientIPAddress"] = travel.ClientIPAddress; + hgt.Data["MyIPAddress"] = travel.MyIpAddress; + + m_Database.Store(hgt); + } + #endregion + + } + + class TravelingAgentInfo + { + public UUID SessionID; + public UUID UserID; + public string GridExternalName = string.Empty; + public string ServiceToken = string.Empty; + public string ClientIPAddress = string.Empty; // as seen from this user agent service + public string MyIpAddress = string.Empty; // the user agent service's external IP, as seen from the next gatekeeper + + public TravelingAgentInfo(HGTravelingData t) + { + if (t.Data != null) + { + SessionID = new UUID(t.SessionID); + UserID = new UUID(t.UserID); + GridExternalName = t.Data["GridExternalName"]; + ServiceToken = t.Data["ServiceToken"]; + ClientIPAddress = t.Data["ClientIPAddress"]; + MyIpAddress = t.Data["MyIPAddress"]; + } + } + + public TravelingAgentInfo(TravelingAgentInfo old) + { + if (old != null) + { + SessionID = old.SessionID; + UserID = old.UserID; + GridExternalName = old.GridExternalName; + ServiceToken = old.ServiceToken; + ClientIPAddress = old.ClientIPAddress; + MyIpAddress = old.MyIpAddress; + } + } + } + +} diff --git a/OpenSim/Services/HypergridService/UserAgentServiceBase.cs b/OpenSim/Services/HypergridService/UserAgentServiceBase.cs new file mode 100644 index 0000000000..a00e5a6a08 --- /dev/null +++ b/OpenSim/Services/HypergridService/UserAgentServiceBase.cs @@ -0,0 +1,84 @@ +/* + * 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.Services.HypergridService +{ + public class UserAgentServiceBase : ServiceBase + { + protected IHGTravelingData m_Database = null; + + public UserAgentServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "hg_traveling_data"; + + // + // 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); + } + + // + // [UserAgentService] section overrides [DatabaseService], if it exists + // + IConfig gridConfig = config.Configs["UserAgentService"]; + if (gridConfig != null) + { + dllName = gridConfig.GetString("StorageProvider", dllName); + connString = gridConfig.GetString("ConnectionString", connString); + realm = gridConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + + } + } +} diff --git a/OpenSim/Services/Interfaces/IAgentPreferencesService.cs b/OpenSim/Services/Interfaces/IAgentPreferencesService.cs new file mode 100644 index 0000000000..3b4fda20b0 --- /dev/null +++ b/OpenSim/Services/Interfaces/IAgentPreferencesService.cs @@ -0,0 +1,115 @@ +/* + * 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; + +namespace OpenSim.Services.Interfaces +{ + public class AgentPrefs + { + public AgentPrefs(UUID principalID) + { + PrincipalID = principalID; + } + + public AgentPrefs(Dictionary kvp) + { + if (kvp.ContainsKey("PrincipalID")) + UUID.TryParse(kvp["PrincipalID"], out PrincipalID); + if (kvp.ContainsKey("AccessPrefs")) + AccessPrefs = kvp["AccessPrefs"]; + if (kvp.ContainsKey("HoverHeight")) + HoverHeight = double.Parse(kvp["HoverHeight"]); + if (kvp.ContainsKey("Language")) + Language = kvp["Language"]; + if (kvp.ContainsKey("LanguageIsPublic")) + LanguageIsPublic = bool.Parse(kvp["LanguageIsPublic"]); + if (kvp.ContainsKey("PermEveryone")) + PermEveryone = int.Parse(kvp["PermEveryone"]); + if (kvp.ContainsKey("PermGroup")) + PermGroup = int.Parse(kvp["PermGroup"]); + if (kvp.ContainsKey("PermNextOwner")) + PermNextOwner = int.Parse(kvp["PermNextOwner"]); + } + + public AgentPrefs(Dictionary kvp) + { + if (kvp.ContainsKey("PrincipalID")) + UUID.TryParse(kvp["PrincipalID"].ToString(), out PrincipalID); + if (kvp.ContainsKey("AccessPrefs")) + AccessPrefs = kvp["AccessPrefs"].ToString(); + if (kvp.ContainsKey("HoverHeight")) + HoverHeight = double.Parse(kvp["HoverHeight"].ToString()); + if (kvp.ContainsKey("Language")) + Language = kvp["Language"].ToString(); + if (kvp.ContainsKey("LanguageIsPublic")) + LanguageIsPublic = bool.Parse(kvp["LanguageIsPublic"].ToString()); + if (kvp.ContainsKey("PermEveryone")) + PermEveryone = int.Parse(kvp["PermEveryone"].ToString()); + if (kvp.ContainsKey("PermGroup")) + PermGroup = int.Parse(kvp["PermGroup"].ToString()); + if (kvp.ContainsKey("PermNextOwner")) + PermNextOwner = int.Parse(kvp["PermNextOwner"].ToString()); + } + + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["PrincipalID"] = PrincipalID.ToString(); + result["AccessPrefs"] = AccessPrefs.ToString(); + result["HoverHeight"] = HoverHeight.ToString(); + result["Language"] = Language.ToString(); + result["LanguageIsPublic"] = LanguageIsPublic.ToString(); + result["PermEveryone"] = PermEveryone.ToString(); + result["PermGroup"] = PermGroup.ToString(); + result["PermNextOwner"] = PermNextOwner.ToString(); + return result; + } + + public UUID PrincipalID = UUID.Zero; + public string AccessPrefs = "M"; + //public int GodLevel; // *TODO: Implement GodLevel (Unused by the viewer, afaict - 6/11/2015) + public double HoverHeight = 0.0; + public string Language = "en-us"; + public bool LanguageIsPublic = true; + // DefaultObjectPermMasks + public int PermEveryone = 0; + public int PermGroup = 0; + public int PermNextOwner = 532480; + } + + public interface IAgentPreferencesService + { + AgentPrefs GetAgentPreferences(UUID principalID); + bool StoreAgentPreferences(AgentPrefs data); + + string GetLang(UUID principalID); + } +} + diff --git a/OpenSim/Services/Interfaces/IAssetService.cs b/OpenSim/Services/Interfaces/IAssetService.cs new file mode 100644 index 0000000000..28c3315b1e --- /dev/null +++ b/OpenSim/Services/Interfaces/IAssetService.cs @@ -0,0 +1,114 @@ +/* + * 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 OpenSim.Framework; + +namespace OpenSim.Services.Interfaces +{ + public delegate void AssetRetrieved(string id, Object sender, AssetBase asset); + + public interface IAssetService + { + /// + /// Get an asset synchronously. + /// + /// + /// + AssetBase Get(string id); + + /// + /// Get an asset's metadata + /// + /// + /// + AssetMetadata GetMetadata(string id); + + /// + /// Get an asset's data, ignoring the metadata. + /// + /// + /// null if there is no such asset + byte[] GetData(string id); + + /// + /// Synchronously fetches an asset from the local cache only. + /// + /// Asset ID + /// The fetched asset, or null if it did not exist in the local cache + AssetBase GetCached(string id); + + /// + /// Get an asset synchronously or asynchronously (depending on whether + /// it is locally cached) and fire a callback with the fetched asset + /// + /// The asset id + /// Represents the requester. Passed back via the handler + /// + /// The handler to call back once the asset has been retrieved. This will be called back with a null AssetBase + /// if the asset could not be found for some reason (e.g. if it does not exist, if a remote asset service + /// was not contactable, if it is not in the database, etc.). + /// + /// True if the id was parseable, false otherwise + bool Get(string id, Object sender, AssetRetrieved handler); + + /// + /// Check if assets exist in the database. + /// + /// The assets' IDs + /// For each asset: true if it exists, false otherwise + bool[] AssetsExist(string[] ids); + + /// + /// Creates a new asset + /// + /// + /// Returns a random ID if none is passed via the asset argument. + /// + /// + /// The Asset ID, or string.Empty if an error occurred + string Store(AssetBase asset); + + /// + /// Update an asset's content + /// + /// + /// Attachments and bare scripts need this!! + /// + /// + /// + /// + bool UpdateContent(string id, byte[] data); + + /// + /// Delete an asset + /// + /// + /// + bool Delete(string id); + } +} diff --git a/OpenSim/Services/Interfaces/IAuthenticationService.cs b/OpenSim/Services/Interfaces/IAuthenticationService.cs new file mode 100644 index 0000000000..cee8bc09c7 --- /dev/null +++ b/OpenSim/Services/Interfaces/IAuthenticationService.cs @@ -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.Generic; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public class AuthInfo + { + public UUID PrincipalID { get; set; } + public string AccountType { get; set; } + public string PasswordHash { get; set; } + public string PasswordSalt { get; set; } + public string WebLoginKey { get; set; } + + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["PrincipalID"] = PrincipalID; + result["AccountType"] = AccountType; + result["PasswordHash"] = PasswordHash; + result["PasswordSalt"] = PasswordSalt; + result["WebLoginKey"] = WebLoginKey; + + return result; + } + } + + // Generic Authentication service used for identifying + // and authenticating principals. + // Principals may be clients acting on users' behalf, + // or any other components that need + // verifiable identification. + // + public interface IAuthenticationService + { + ////////////////////////////////////////////////////// + // Authentication + // + // These methods will return a token, which can be used to access + // various services. + // + string Authenticate(UUID principalID, string password, int lifetime); + + ////////////////////////////////////////////////////// + // Verification + // + // Allows to verify the authenticity of a token + // + // Tokens expire after 30 minutes and can be refreshed by + // re-verifying. + // + bool Verify(UUID principalID, string token, int lifetime); + + ////////////////////////////////////////////////////// + // Teardown + // + // A token can be returned before the timeout. This + // invalidates it and it can not subsequently be used + // or refreshed. + // + bool Release(UUID principalID, string token); + + ////////////////////////////////////////////////////// + // SetPassword for a principal + // + // This method exists for the service, but may or may not + // be served remotely. That is, the authentication + // handlers may not include one handler for this, + // because it's a bit risky. Such handlers require + // authentication/authorization. + // + bool SetPassword(UUID principalID, string passwd); + + AuthInfo GetAuthInfo(UUID principalID); + + bool SetAuthInfo(AuthInfo info); + + ////////////////////////////////////////////////////// + // Grid + // + // We no longer need a shared secret between grid + // servers. Anything a server requests from another + // server is either done on behalf of a user, in which + // case there is a token, or on behalf of a region, + // which has a session. So, no more keys. + // If sniffing on the local lan is an issue, admins + // need to take approriate action (IPSec is recommended) + // to secure inter-server traffic. + + ////////////////////////////////////////////////////// + // NOTE + // + // Session IDs are not handled here. After obtaining + // a token, the session ID regions use can be + // obtained from the presence service. + } +} diff --git a/OpenSim/Services/Interfaces/IAuthorizationService.cs b/OpenSim/Services/Interfaces/IAuthorizationService.cs new file mode 100644 index 0000000000..e5c68f62fa --- /dev/null +++ b/OpenSim/Services/Interfaces/IAuthorizationService.cs @@ -0,0 +1,148 @@ +/* + * 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 OpenSim.Framework; + +namespace OpenSim.Services.Interfaces +{ + // Generic Authorization service used for authorizing principals in a particular region + + public interface IAuthorizationService + { + /// + /// Check whether the user should be given access to the region. + /// + /// + /// We also supply user first name and last name for situations where the user does not have an account + /// on the region (e.g. they're a visitor via Hypergrid). + /// + /// + /// /param> + /// + /// + /// + /// + bool IsAuthorizedForRegion( + string userID, string firstName, string lastName, string regionID, out string message); + } + + public class AuthorizationRequest + { + private string m_userID; + private string m_firstname; + private string m_surname; + private string m_email; + private string m_regionName; + private string m_regionID; + + public AuthorizationRequest() + { + } + + public AuthorizationRequest(string ID, string RegionID) + { + m_userID = ID; + m_regionID = RegionID; + } + + public AuthorizationRequest( + string ID, string FirstName, string SurName, string Email, string RegionName, string RegionID) + { + m_userID = ID; + m_firstname = FirstName; + m_surname = SurName; + m_email = Email; + m_regionName = RegionName; + m_regionID = RegionID; + } + + public string ID + { + get { return m_userID; } + set { m_userID = value; } + } + + public string FirstName + { + get { return m_firstname; } + set { m_firstname = value; } + } + + public string SurName + { + get { return m_surname; } + set { m_surname = value; } + } + + public string Email + { + get { return m_email; } + set { m_email = value; } + } + + public string RegionName + { + get { return m_regionName; } + set { m_regionName = value; } + } + + public string RegionID + { + get { return m_regionID; } + set { m_regionID = value; } + } + } + + public class AuthorizationResponse + { + private bool m_isAuthorized; + private string m_message; + + public AuthorizationResponse() + { + } + + public AuthorizationResponse(bool isAuthorized, string message) + { + m_isAuthorized = isAuthorized; + m_message = message; + } + + public bool IsAuthorized + { + get { return m_isAuthorized; } + set { m_isAuthorized = value; } + } + + public string Message + { + get { return m_message; } + set { m_message = value; } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/IAvatarService.cs b/OpenSim/Services/Interfaces/IAvatarService.cs new file mode 100644 index 0000000000..6ca0b15874 --- /dev/null +++ b/OpenSim/Services/Interfaces/IAvatarService.cs @@ -0,0 +1,356 @@ +/* + * 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 OpenSim.Framework; + +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface IAvatarService + { + /// + /// Called by the login service + /// + /// + /// + AvatarAppearance GetAppearance(UUID userID); + + /// + /// Called by everyone who can change the avatar data (so, regions) + /// + /// + /// + /// + bool SetAppearance(UUID userID, AvatarAppearance appearance); + + /// + /// Called by the login service + /// + /// + /// + AvatarData GetAvatar(UUID userID); + + /// + /// Called by everyone who can change the avatar data (so, regions) + /// + /// + /// + /// + bool SetAvatar(UUID userID, AvatarData avatar); + + /// + /// Not sure if it's needed + /// + /// + /// + bool ResetAvatar(UUID userID); + + /// + /// These methods raison d'etre: + /// No need to send the entire avatar data (SetAvatar) for changing attachments + /// + /// + /// + /// + bool SetItems(UUID userID, string[] names, string[] values); + bool RemoveItems(UUID userID, string[] names); + } + + /// + /// Each region/client that uses avatars will have a data structure + /// of this type representing the avatars. + /// + public class AvatarData + { + // This pretty much determines which name/value pairs will be + // present below. The name/value pair describe a part of + // the avatar. For SL avatars, these would be "shape", "texture1", + // etc. For other avatars, they might be "mesh", "skin", etc. + // The value portion is a URL that is expected to resolve to an + // asset of the type required by the handler for that field. + // It is required that regions can access these URLs. Allowing + // direct access by a viewer is not required, and, if provided, + // may be read-only. A "naked" UUID can be used to refer to an + // asset int he current region's asset service, which is not + // portable, but allows legacy appearance to continue to + // function. Closed, LL-based grids will never need URLs here. + + public int AvatarType; + public Dictionary Data; + + public AvatarData() + { + } + + public AvatarData(Dictionary kvp) + { + Data = new Dictionary(); + + if (kvp.ContainsKey("AvatarType")) + Int32.TryParse(kvp["AvatarType"].ToString(), out AvatarType); + + foreach (KeyValuePair _kvp in kvp) + { + if (_kvp.Value != null) + Data[_kvp.Key] = _kvp.Value.ToString(); + } + } + + /// + /// + /// + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + + result["AvatarType"] = AvatarType.ToString(); + foreach (KeyValuePair _kvp in Data) + { + if (_kvp.Value != null) + result[_kvp.Key] = _kvp.Value; + } + return result; + } + + public AvatarData(AvatarAppearance appearance) + { + AvatarType = 1; // SL avatars + Data = new Dictionary(); + + Data["Serial"] = appearance.Serial.ToString(); + // Wearables + Data["AvatarHeight"] = appearance.AvatarHeight.ToString(); + + for (int i = 0 ; i < AvatarWearable.MAX_WEARABLES ; i++) + { + for (int j = 0 ; j < appearance.Wearables[i].Count ; j++) + { + string fieldName = String.Format("Wearable {0}:{1}", i, j); + Data[fieldName] = String.Format("{0}:{1}", + appearance.Wearables[i][j].ItemID.ToString(), + appearance.Wearables[i][j].AssetID.ToString()); + } + } + + // Visual Params + string[] vps = new string[AvatarAppearance.VISUALPARAM_COUNT]; + byte[] binary = appearance.VisualParams; + + for (int i = 0 ; i < AvatarAppearance.VISUALPARAM_COUNT ; i++) + { + vps[i] = binary[i].ToString(); + } + + Data["VisualParams"] = String.Join(",", vps); + + // Attachments + List attachments = appearance.GetAttachments(); + Dictionary> atts = new Dictionary>(); + foreach (AvatarAttachment attach in attachments) + { + if (attach.ItemID != UUID.Zero) + { + if (!atts.ContainsKey(attach.AttachPoint)) + atts[attach.AttachPoint] = new List(); + atts[attach.AttachPoint].Add(attach.ItemID.ToString()); + } + } + foreach (KeyValuePair> kvp in atts) + Data["_ap_" + kvp.Key] = string.Join(",", kvp.Value.ToArray()); + } + + public AvatarAppearance ToAvatarAppearance() + { + AvatarAppearance appearance = new AvatarAppearance(); + + if (Data.Count == 0) + return appearance; + + appearance.ClearWearables(); + try + { + if (Data.ContainsKey("Serial")) + appearance.Serial = Int32.Parse(Data["Serial"]); + + if (Data.ContainsKey("AvatarHeight")) + appearance.AvatarHeight = float.Parse(Data["AvatarHeight"]); + + // Legacy Wearables + if (Data.ContainsKey("BodyItem")) + appearance.Wearables[AvatarWearable.BODY].Wear( + UUID.Parse(Data["BodyItem"]), + UUID.Parse(Data["BodyAsset"])); + + if (Data.ContainsKey("SkinItem")) + appearance.Wearables[AvatarWearable.SKIN].Wear( + UUID.Parse(Data["SkinItem"]), + UUID.Parse(Data["SkinAsset"])); + + if (Data.ContainsKey("HairItem")) + appearance.Wearables[AvatarWearable.HAIR].Wear( + UUID.Parse(Data["HairItem"]), + UUID.Parse(Data["HairAsset"])); + + if (Data.ContainsKey("EyesItem")) + appearance.Wearables[AvatarWearable.EYES].Wear( + UUID.Parse(Data["EyesItem"]), + UUID.Parse(Data["EyesAsset"])); + + if (Data.ContainsKey("ShirtItem")) + appearance.Wearables[AvatarWearable.SHIRT].Wear( + UUID.Parse(Data["ShirtItem"]), + UUID.Parse(Data["ShirtAsset"])); + + if (Data.ContainsKey("PantsItem")) + appearance.Wearables[AvatarWearable.PANTS].Wear( + UUID.Parse(Data["PantsItem"]), + UUID.Parse(Data["PantsAsset"])); + + if (Data.ContainsKey("ShoesItem")) + appearance.Wearables[AvatarWearable.SHOES].Wear( + UUID.Parse(Data["ShoesItem"]), + UUID.Parse(Data["ShoesAsset"])); + + if (Data.ContainsKey("SocksItem")) + appearance.Wearables[AvatarWearable.SOCKS].Wear( + UUID.Parse(Data["SocksItem"]), + UUID.Parse(Data["SocksAsset"])); + + if (Data.ContainsKey("JacketItem")) + appearance.Wearables[AvatarWearable.JACKET].Wear( + UUID.Parse(Data["JacketItem"]), + UUID.Parse(Data["JacketAsset"])); + + if (Data.ContainsKey("GlovesItem")) + appearance.Wearables[AvatarWearable.GLOVES].Wear( + UUID.Parse(Data["GlovesItem"]), + UUID.Parse(Data["GlovesAsset"])); + + if (Data.ContainsKey("UnderShirtItem")) + appearance.Wearables[AvatarWearable.UNDERSHIRT].Wear( + UUID.Parse(Data["UnderShirtItem"]), + UUID.Parse(Data["UnderShirtAsset"])); + + if (Data.ContainsKey("UnderPantsItem")) + appearance.Wearables[AvatarWearable.UNDERPANTS].Wear( + UUID.Parse(Data["UnderPantsItem"]), + UUID.Parse(Data["UnderPantsAsset"])); + + if (Data.ContainsKey("SkirtItem")) + appearance.Wearables[AvatarWearable.SKIRT].Wear( + UUID.Parse(Data["SkirtItem"]), + UUID.Parse(Data["SkirtAsset"])); + + if (Data.ContainsKey("VisualParams")) + { + string[] vps = Data["VisualParams"].Split(new char[] {','}); + byte[] binary = new byte[AvatarAppearance.VISUALPARAM_COUNT]; + + for (int i = 0 ; i < vps.Length && i < binary.Length ; i++) + binary[i] = (byte)Convert.ToInt32(vps[i]); + + appearance.VisualParams = binary; + } + + // New style wearables + foreach (KeyValuePair _kvp in Data) + { + if (_kvp.Key.StartsWith("Wearable ")) + { + string wearIndex = _kvp.Key.Substring(9); + string[] wearIndices = wearIndex.Split(new char[] {':'}); + int index = Convert.ToInt32(wearIndices[0]); + + string[] ids = _kvp.Value.Split(new char[] {':'}); + UUID itemID = new UUID(ids[0]); + UUID assetID = new UUID(ids[1]); + + appearance.Wearables[index].Add(itemID, assetID); + } + } + + // Attachments + Dictionary attchs = new Dictionary(); + foreach (KeyValuePair _kvp in Data) + if (_kvp.Key.StartsWith("_ap_")) + attchs[_kvp.Key] = _kvp.Value; + + foreach (KeyValuePair _kvp in attchs) + { + string pointStr = _kvp.Key.Substring(4); + int point = 0; + if (!Int32.TryParse(pointStr, out point)) + continue; + + List idList = new List(_kvp.Value.Split(new char[] {','})); + + appearance.SetAttachment(point, UUID.Zero, UUID.Zero); + foreach (string id in idList) + { + UUID uuid = UUID.Zero; + UUID.TryParse(id, out uuid); + + appearance.SetAttachment(point | 0x80, uuid, UUID.Zero); + } + } + + if (appearance.Wearables[AvatarWearable.BODY].Count == 0) + appearance.Wearables[AvatarWearable.BODY].Wear( + AvatarWearable.DefaultWearables[ + AvatarWearable.BODY][0]); + + if (appearance.Wearables[AvatarWearable.SKIN].Count == 0) + appearance.Wearables[AvatarWearable.SKIN].Wear( + AvatarWearable.DefaultWearables[ + AvatarWearable.SKIN][0]); + + if (appearance.Wearables[AvatarWearable.HAIR].Count == 0) + appearance.Wearables[AvatarWearable.HAIR].Wear( + AvatarWearable.DefaultWearables[ + AvatarWearable.HAIR][0]); + + if (appearance.Wearables[AvatarWearable.EYES].Count == 0) + appearance.Wearables[AvatarWearable.EYES].Wear( + AvatarWearable.DefaultWearables[ + AvatarWearable.EYES][0]); + } + catch + { + // We really should report something here, returning null + // will at least break the wrapper + return null; + } + + return appearance; + } + } +} diff --git a/OpenSim/Services/Interfaces/IBakedTextureService.cs b/OpenSim/Services/Interfaces/IBakedTextureService.cs new file mode 100644 index 0000000000..69df4a0ec5 --- /dev/null +++ b/OpenSim/Services/Interfaces/IBakedTextureService.cs @@ -0,0 +1,38 @@ +/* + * 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 Nini.Config; + +namespace OpenSim.Services.Interfaces +{ + public interface IBakedTextureService + { + string Get(string id); + void Store(string id, string data); + } +} diff --git a/OpenSim/Services/Interfaces/IBansService.cs b/OpenSim/Services/Interfaces/IBansService.cs new file mode 100644 index 0000000000..8fd3521aac --- /dev/null +++ b/OpenSim/Services/Interfaces/IBansService.cs @@ -0,0 +1,48 @@ +/* + * 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.Services.Interfaces +{ + public interface IBansService + { + /// + /// Are any of the given arguments banned from the grid? + /// + /// + /// + /// + /// + /// + bool IsBanned(string userID, string ip, string id0, string origin); + } + +} diff --git a/OpenSim/Services/Interfaces/IBasicProfileService.cs b/OpenSim/Services/Interfaces/IBasicProfileService.cs new file mode 100644 index 0000000000..ecf3d33008 --- /dev/null +++ b/OpenSim/Services/Interfaces/IBasicProfileService.cs @@ -0,0 +1,37 @@ +/* + * 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 OpenSim.Framework; +using System.Collections.Generic; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface IBasicProfileService + { + } +} diff --git a/OpenSim/Services/Interfaces/IEstateDataService.cs b/OpenSim/Services/Interfaces/IEstateDataService.cs new file mode 100644 index 0000000000..719563dfac --- /dev/null +++ b/OpenSim/Services/Interfaces/IEstateDataService.cs @@ -0,0 +1,115 @@ +/* + * 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.Services.Interfaces +{ + public interface IEstateDataService + { + /// + /// Load estate settings for a region. + /// + /// + /// If true, then an estate is created if one is not found. + /// + EstateSettings LoadEstateSettings(UUID regionID, bool create); + + /// + /// Load estate settings for an estate ID. + /// + /// + /// + EstateSettings LoadEstateSettings(int estateID); + + /// + /// Create a new estate. + /// + /// + /// A + /// + EstateSettings CreateNewEstate(); + + /// + /// Load/Get all estate settings. + /// + /// An empty list if no estates were found. + List LoadEstateSettingsAll(); + + /// + /// Store estate settings. + /// + /// + /// This is also called by EstateSettings.Save() + /// + void StoreEstateSettings(EstateSettings es); + + /// + /// Get estate IDs. + /// + /// Name of estate to search for. This is the exact name, no parttern matching is done. + /// + List GetEstates(string search); + + /// + /// Get the IDs of all estates owned by the given user. + /// + /// An empty list if no estates were found. + List GetEstatesByOwner(UUID ownerID); + + /// + /// Get the IDs of all estates. + /// + /// An empty list if no estates were found. + List GetEstatesAll(); + + /// + /// Link a region to an estate. + /// + /// + /// + /// true if the link succeeded, false otherwise + bool LinkRegion(UUID regionID, int estateID); + + /// + /// Get the UUIDs of all the regions in an estate. + /// + /// + /// + List GetRegions(int estateID); + + /// + /// Delete an estate + /// + /// + /// true if the delete succeeded, false otherwise + bool DeleteEstate(int estateID); + } +} \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/IFreeswitchService.cs b/OpenSim/Services/Interfaces/IFreeswitchService.cs new file mode 100644 index 0000000000..e7941d5406 --- /dev/null +++ b/OpenSim/Services/Interfaces/IFreeswitchService.cs @@ -0,0 +1,40 @@ +/* + * 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 OpenSim.Framework; +using System.Collections; + +namespace OpenSim.Services.Interfaces +{ + public interface IFreeswitchService + { + Hashtable HandleDirectoryRequest(Hashtable requestBody); + Hashtable HandleDialplanRequest(Hashtable requestBody); + string GetJsonConfig(); + } +} diff --git a/OpenSim/Services/Interfaces/IFriendsService.cs b/OpenSim/Services/Interfaces/IFriendsService.cs new file mode 100644 index 0000000000..d0d3b1002d --- /dev/null +++ b/OpenSim/Services/Interfaces/IFriendsService.cs @@ -0,0 +1,90 @@ +/* + * 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.Framework; +using System.Collections.Generic; + +namespace OpenSim.Services.Interfaces +{ + public class FriendInfo + { + public UUID PrincipalID; + public string Friend; + + /// + /// The permissions that this user has granted to the friend. + /// + public int MyFlags; + + /// + /// The permissions that the friend has granted to this user. + /// + public int TheirFlags; + + public FriendInfo() + { + } + + public FriendInfo(Dictionary kvp) + { + PrincipalID = UUID.Zero; + if (kvp.ContainsKey("PrincipalID") && kvp["PrincipalID"] != null) + UUID.TryParse(kvp["PrincipalID"].ToString(), out PrincipalID); + Friend = string.Empty; + if (kvp.ContainsKey("Friend") && kvp["Friend"] != null) + Friend = kvp["Friend"].ToString(); + MyFlags = (int)FriendRights.None; + if (kvp.ContainsKey("MyFlags") && kvp["MyFlags"] != null) + Int32.TryParse(kvp["MyFlags"].ToString(), out MyFlags); + TheirFlags = 0; + if (kvp.ContainsKey("TheirFlags") && kvp["TheirFlags"] != null) + Int32.TryParse(kvp["TheirFlags"].ToString(), out TheirFlags); + } + + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["PrincipalID"] = PrincipalID.ToString(); + result["Friend"] = Friend; + result["MyFlags"] = MyFlags.ToString(); + result["TheirFlags"] = TheirFlags.ToString(); + + return result; + } + } + + public interface IFriendsService + { + FriendInfo[] GetFriends(UUID PrincipalID); + FriendInfo[] GetFriends(string PrincipalID); + bool StoreFriend(string PrincipalID, string Friend, int flags); + bool Delete(UUID PrincipalID, string Friend); + bool Delete(string PrincipalID, string Friend); + } +} diff --git a/OpenSim/Services/Interfaces/IGridService.cs b/OpenSim/Services/Interfaces/IGridService.cs new file mode 100644 index 0000000000..f5f1f75413 --- /dev/null +++ b/OpenSim/Services/Interfaces/IGridService.cs @@ -0,0 +1,529 @@ +/* + * 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.Net.Sockets; +using System.Reflection; + +using OpenSim.Framework; +using OpenMetaverse; + +using log4net; + +namespace OpenSim.Services.Interfaces +{ + public interface IGridService + { + /// + /// Register a region with the grid service. + /// + /// + /// + /// Thrown if region registration failed + string RegisterRegion(UUID scopeID, GridRegion regionInfos); + + /// + /// Deregister a region with the grid service. + /// + /// + /// + /// Thrown if region deregistration failed + bool DeregisterRegion(UUID regionID); + + /// + /// Get information about the regions neighbouring the given co-ordinates (in meters). + /// + /// + /// + /// + List GetNeighbours(UUID scopeID, UUID regionID); + + GridRegion GetRegionByUUID(UUID scopeID, UUID regionID); + + /// + /// Get the region at the given position (in meters) + /// + /// + /// + /// + /// + GridRegion GetRegionByPosition(UUID scopeID, int x, int y); + + /// + /// Get information about a region which exactly matches the name given. + /// + /// + /// + /// Returns the region information if the name matched. Null otherwise. + GridRegion GetRegionByName(UUID scopeID, string regionName); + + /// + /// Get information about regions starting with the provided name. + /// + /// + /// The name to match against. + /// + /// + /// The maximum number of results to return. + /// + /// + /// A list of s of regions with matching name. If the + /// grid-server couldn't be contacted or returned an error, return null. + /// + List GetRegionsByName(UUID scopeID, string name, int maxNumber); + + List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax); + + List GetDefaultRegions(UUID scopeID); + List GetDefaultHypergridRegions(UUID scopeID); + List GetFallbackRegions(UUID scopeID, int x, int y); + List GetHyperlinks(UUID scopeID); + + /// + /// Get internal OpenSimulator region flags. + /// + /// + /// See OpenSimulator.Framework.RegionFlags. These are not returned in the GridRegion structure - + /// they currently need to be requested separately. Possibly this should change to avoid multiple service calls + /// in some situations. + /// + /// + /// The region flags. + /// + /// + /// + int GetRegionFlags(UUID scopeID, UUID regionID); + + Dictionary GetExtraFeatures(); + } + + public interface IHypergridLinker + { + GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason); + bool TryUnlinkRegion(string mapName); + } + + public class GridRegion + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + +#pragma warning disable 414 + private static readonly string LogHeader = "[GRID REGION]"; +#pragma warning restore 414 + + /// + /// The port by which http communication occurs with the region + /// + public uint HttpPort { get; set; } + + /// + /// A well-formed URI for the host region server (namely "http://" + ExternalHostName) + /// + public string ServerURI + { + get { + if (!String.IsNullOrEmpty(m_serverURI)) { + return m_serverURI; + } else { + if (HttpPort == 0) + return "http://" + m_externalHostName + "/"; + else + return "http://" + m_externalHostName + ":" + HttpPort + "/"; + } + } + set { + if (value.EndsWith("/")) { + m_serverURI = value; + } else { + m_serverURI = value + '/'; + } + } + } + protected string m_serverURI; + + /// + /// Provides direct access to the 'm_serverURI' field, without returning a generated URL if m_serverURI is missing. + /// + public string RawServerURI + { + get { return m_serverURI; } + set { m_serverURI = value; } + } + + + public string RegionName + { + get { return m_regionName; } + set { m_regionName = value; } + } + protected string m_regionName = String.Empty; + + /// + /// Region flags. + /// + /// + /// If not set (chiefly if a robust service is running code pre OpenSim 0.8.1) then this will be null and + /// should be ignored. If you require flags information please use the separate IGridService.GetRegionFlags() call + /// XXX: This field is currently ignored when used in RegisterRegion, but could potentially be + /// used to set flags at this point. + /// + public OpenSim.Framework.RegionFlags? RegionFlags { get; set; } + + protected string m_externalHostName; + + protected IPEndPoint m_internalEndPoint; + + /// + /// The co-ordinate of this region in region units. + /// + public int RegionCoordX { get { return (int)Util.WorldToRegionLoc((uint)RegionLocX); } } + + /// + /// The co-ordinate of this region in region units + /// + public int RegionCoordY { get { return (int)Util.WorldToRegionLoc((uint)RegionLocY); } } + + /// + /// The location of this region in meters. + /// DANGER DANGER! Note that this name means something different in RegionInfo. + /// + public int RegionLocX + { + get { return m_regionLocX; } + set { m_regionLocX = value; } + } + protected int m_regionLocX; + + public int RegionSizeX { get; set; } + public int RegionSizeY { get; set; } + + /// + /// The location of this region in meters. + /// DANGER DANGER! Note that this name means something different in RegionInfo. + /// + public int RegionLocY + { + get { return m_regionLocY; } + set { m_regionLocY = value; } + } + protected int m_regionLocY; + + protected UUID m_estateOwner; + + public UUID EstateOwner + { + get { return m_estateOwner; } + set { m_estateOwner = value; } + } + + public UUID RegionID = UUID.Zero; + public UUID ScopeID = UUID.Zero; + + public UUID TerrainImage = UUID.Zero; + public UUID ParcelImage = UUID.Zero; + public byte Access; + public int Maturity; + public string RegionSecret = string.Empty; + public string Token = string.Empty; + + public GridRegion() + { + RegionSizeX = (int)Constants.RegionSize; + RegionSizeY = (int)Constants.RegionSize; + m_serverURI = string.Empty; + } + + /* + public GridRegion(int regionLocX, int regionLocY, IPEndPoint internalEndPoint, string externalUri) + { + m_regionLocX = regionLocX; + m_regionLocY = regionLocY; + RegionSizeX = (int)Constants.RegionSize; + RegionSizeY = (int)Constants.RegionSize; + + m_internalEndPoint = internalEndPoint; + m_externalHostName = externalUri; + } + + public GridRegion(int regionLocX, int regionLocY, string externalUri, uint port) + { + m_regionLocX = regionLocX; + m_regionLocY = regionLocY; + RegionSizeX = (int)Constants.RegionSize; + RegionSizeY = (int)Constants.RegionSize; + + m_externalHostName = externalUri; + + m_internalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), (int)port); + } + */ + + public GridRegion(uint xcell, uint ycell) + { + m_regionLocX = (int)Util.RegionToWorldLoc(xcell); + m_regionLocY = (int)Util.RegionToWorldLoc(ycell); + RegionSizeX = (int)Constants.RegionSize; + RegionSizeY = (int)Constants.RegionSize; + } + + public GridRegion(RegionInfo ConvertFrom) + { + m_regionName = ConvertFrom.RegionName; + m_regionLocX = (int)(ConvertFrom.WorldLocX); + m_regionLocY = (int)(ConvertFrom.WorldLocY); + RegionSizeX = (int)ConvertFrom.RegionSizeX; + RegionSizeY = (int)ConvertFrom.RegionSizeY; + m_internalEndPoint = ConvertFrom.InternalEndPoint; + m_externalHostName = ConvertFrom.ExternalHostName; + HttpPort = ConvertFrom.HttpPort; + RegionID = ConvertFrom.RegionID; + ServerURI = ConvertFrom.ServerURI; + TerrainImage = ConvertFrom.RegionSettings.TerrainImageID; + ParcelImage = ConvertFrom.RegionSettings.ParcelImageID; + Access = ConvertFrom.AccessLevel; + Maturity = ConvertFrom.RegionSettings.Maturity; + RegionSecret = ConvertFrom.regionSecret; + EstateOwner = ConvertFrom.EstateSettings.EstateOwner; + } + + public GridRegion(GridRegion ConvertFrom) + { + m_regionName = ConvertFrom.RegionName; + RegionFlags = ConvertFrom.RegionFlags; + m_regionLocX = ConvertFrom.RegionLocX; + m_regionLocY = ConvertFrom.RegionLocY; + RegionSizeX = ConvertFrom.RegionSizeX; + RegionSizeY = ConvertFrom.RegionSizeY; + m_internalEndPoint = ConvertFrom.InternalEndPoint; + m_externalHostName = ConvertFrom.ExternalHostName; + HttpPort = ConvertFrom.HttpPort; + RegionID = ConvertFrom.RegionID; + ServerURI = ConvertFrom.ServerURI; + TerrainImage = ConvertFrom.TerrainImage; + ParcelImage = ConvertFrom.ParcelImage; + Access = ConvertFrom.Access; + Maturity = ConvertFrom.Maturity; + RegionSecret = ConvertFrom.RegionSecret; + EstateOwner = ConvertFrom.EstateOwner; + } + + public GridRegion(Dictionary kvp) + { + if (kvp.ContainsKey("uuid")) + RegionID = new UUID((string)kvp["uuid"]); + + if (kvp.ContainsKey("locX")) + RegionLocX = Convert.ToInt32((string)kvp["locX"]); + + if (kvp.ContainsKey("locY")) + RegionLocY = Convert.ToInt32((string)kvp["locY"]); + + if (kvp.ContainsKey("sizeX")) + RegionSizeX = Convert.ToInt32((string)kvp["sizeX"]); + else + RegionSizeX = (int)Constants.RegionSize; + + if (kvp.ContainsKey("sizeY")) + RegionSizeY = Convert.ToInt32((string)kvp["sizeY"]); + else + RegionSizeX = (int)Constants.RegionSize; + + if (kvp.ContainsKey("regionName")) + RegionName = (string)kvp["regionName"]; + + if (kvp.ContainsKey("flags") && kvp["flags"] != null) + RegionFlags = (OpenSim.Framework.RegionFlags?)Convert.ToInt32((string)kvp["flags"]); + + if (kvp.ContainsKey("serverIP")) + { + //int port = 0; + //Int32.TryParse((string)kvp["serverPort"], out port); + //IPEndPoint ep = new IPEndPoint(IPAddress.Parse((string)kvp["serverIP"]), port); + ExternalHostName = (string)kvp["serverIP"]; + } + else + ExternalHostName = "127.0.0.1"; + + if (kvp.ContainsKey("serverPort")) + { + Int32 port = 0; + Int32.TryParse((string)kvp["serverPort"], out port); + InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), port); + } + + if (kvp.ContainsKey("serverHttpPort")) + { + UInt32 port = 0; + UInt32.TryParse((string)kvp["serverHttpPort"], out port); + HttpPort = port; + } + + if (kvp.ContainsKey("serverURI")) + ServerURI = (string)kvp["serverURI"]; + + if (kvp.ContainsKey("regionMapTexture")) + UUID.TryParse((string)kvp["regionMapTexture"], out TerrainImage); + + if (kvp.ContainsKey("parcelMapTexture")) + UUID.TryParse((string)kvp["parcelMapTexture"], out ParcelImage); + + if (kvp.ContainsKey("access")) + Access = Byte.Parse((string)kvp["access"]); + + if (kvp.ContainsKey("regionSecret")) + RegionSecret =(string)kvp["regionSecret"]; + + if (kvp.ContainsKey("owner_uuid")) + EstateOwner = new UUID(kvp["owner_uuid"].ToString()); + + if (kvp.ContainsKey("Token")) + Token = kvp["Token"].ToString(); + + // m_log.DebugFormat("{0} New GridRegion. id={1}, loc=<{2},{3}>, size=<{4},{5}>", + // LogHeader, RegionID, RegionLocX, RegionLocY, RegionSizeX, RegionSizeY); + } + + public Dictionary ToKeyValuePairs() + { + Dictionary kvp = new Dictionary(); + kvp["uuid"] = RegionID.ToString(); + kvp["locX"] = RegionLocX.ToString(); + kvp["locY"] = RegionLocY.ToString(); + kvp["sizeX"] = RegionSizeX.ToString(); + kvp["sizeY"] = RegionSizeY.ToString(); + kvp["regionName"] = RegionName; + + if (RegionFlags != null) + kvp["flags"] = ((int)RegionFlags).ToString(); + + kvp["serverIP"] = ExternalHostName; //ExternalEndPoint.Address.ToString(); + kvp["serverHttpPort"] = HttpPort.ToString(); + kvp["serverURI"] = ServerURI; + kvp["serverPort"] = InternalEndPoint.Port.ToString(); + kvp["regionMapTexture"] = TerrainImage.ToString(); + kvp["parcelMapTexture"] = ParcelImage.ToString(); + kvp["access"] = Access.ToString(); + kvp["regionSecret"] = RegionSecret; + kvp["owner_uuid"] = EstateOwner.ToString(); + kvp["Token"] = Token.ToString(); + // Maturity doesn't seem to exist in the DB + + return kvp; + } + + #region Definition of equality + + /// + /// Define equality as two regions having the same, non-zero UUID. + /// + public bool Equals(GridRegion region) + { + if ((object)region == null) + return false; + // Return true if the non-zero UUIDs are equal: + return (RegionID != UUID.Zero) && RegionID.Equals(region.RegionID); + } + + public override bool Equals(Object obj) + { + if (obj == null) + return false; + return Equals(obj as GridRegion); + } + + public override int GetHashCode() + { + return RegionID.GetHashCode() ^ TerrainImage.GetHashCode() ^ ParcelImage.GetHashCode(); + } + + #endregion + + /// + /// This accessor can throw all the exceptions that Dns.GetHostAddresses can throw. + /// + /// XXX Isn't this really doing too much to be a simple getter, rather than an explict method? + /// + public IPEndPoint ExternalEndPoint + { + get + { + // Old one defaults to IPv6 + //return new IPEndPoint(Dns.GetHostAddresses(m_externalHostName)[0], m_internalEndPoint.Port); + + IPAddress ia = null; + // If it is already an IP, don't resolve it - just return directly + if (IPAddress.TryParse(m_externalHostName, out ia)) + return new IPEndPoint(ia, m_internalEndPoint.Port); + + // Reset for next check + ia = null; + try + { + foreach (IPAddress Adr in Dns.GetHostAddresses(m_externalHostName)) + { + if (ia == null) + ia = Adr; + + if (Adr.AddressFamily == AddressFamily.InterNetwork) + { + ia = Adr; + break; + } + } + } + catch (SocketException e) + { + throw new Exception( + "Unable to resolve local hostname " + m_externalHostName + " innerException of type '" + + e + "' attached to this exception", e); + } + + return new IPEndPoint(ia, m_internalEndPoint.Port); + } + } + + public string ExternalHostName + { + get { return m_externalHostName; } + set { m_externalHostName = value; } + } + + public IPEndPoint InternalEndPoint + { + get { return m_internalEndPoint; } + set { m_internalEndPoint = value; } + } + + public ulong RegionHandle + { + get { return Util.UIntsToLong((uint)RegionLocX, (uint)RegionLocY); } + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/IGridUserService.cs b/OpenSim/Services/Interfaces/IGridUserService.cs new file mode 100644 index 0000000000..2e7237e104 --- /dev/null +++ b/OpenSim/Services/Interfaces/IGridUserService.cs @@ -0,0 +1,135 @@ +/* + * 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; + +namespace OpenSim.Services.Interfaces +{ + /// + /// Records user information specific to a grid but which is not part of a user's account. + /// + public class GridUserInfo + { + public string UserID; + + public UUID HomeRegionID; + public Vector3 HomePosition; + public Vector3 HomeLookAt; + + public UUID LastRegionID; + public Vector3 LastPosition; + public Vector3 LastLookAt; + + public bool Online; + public DateTime Login; + public DateTime Logout; + + public GridUserInfo() {} + + public GridUserInfo(Dictionary kvp) + { + if (kvp.ContainsKey("UserID")) + UserID = kvp["UserID"].ToString(); + + if (kvp.ContainsKey("HomeRegionID")) + UUID.TryParse(kvp["HomeRegionID"].ToString(), out HomeRegionID); + if (kvp.ContainsKey("HomePosition")) + Vector3.TryParse(kvp["HomePosition"].ToString(), out HomePosition); + if (kvp.ContainsKey("HomeLookAt")) + Vector3.TryParse(kvp["HomeLookAt"].ToString(), out HomeLookAt); + + if (kvp.ContainsKey("LastRegionID")) + UUID.TryParse(kvp["LastRegionID"].ToString(), out LastRegionID); + if (kvp.ContainsKey("LastPosition")) + Vector3.TryParse(kvp["LastPosition"].ToString(), out LastPosition); + if (kvp.ContainsKey("LastLookAt")) + Vector3.TryParse(kvp["LastLookAt"].ToString(), out LastLookAt); + + if (kvp.ContainsKey("Login")) + DateTime.TryParse(kvp["Login"].ToString(), out Login); + if (kvp.ContainsKey("Logout")) + DateTime.TryParse(kvp["Logout"].ToString(), out Logout); + if (kvp.ContainsKey("Online")) + Boolean.TryParse(kvp["Online"].ToString(), out Online); + + } + + public virtual Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["UserID"] = UserID; + + result["HomeRegionID"] = HomeRegionID.ToString(); + result["HomePosition"] = HomePosition.ToString(); + result["HomeLookAt"] = HomeLookAt.ToString(); + + result["LastRegionID"] = LastRegionID.ToString(); + result["LastPosition"] = LastPosition.ToString(); + result["LastLookAt"] = LastLookAt.ToString(); + + result["Online"] = Online.ToString(); + result["Login"] = Login.ToString(); + result["Logout"] = Logout.ToString(); + + return result; + } + } + + public interface IGridUserService + { + GridUserInfo LoggedIn(string userID); + + /// + /// Informs the grid that a user is logged out and to remove any session data for them + /// + /// Ignore if your connector does not use userID for logouts + /// Ignore if your connector does not use sessionID for logouts + /// RegionID where the user was last located + /// Last region-relative position of the user + /// Last normalized look direction for the user + /// True if the logout request was successfully processed, otherwise false + bool LoggedOut(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + + bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt); + + /// + /// Stores the last known user position at the grid level + /// + /// Ignore if your connector does not use userID for position updates + /// Ignore if your connector does not use sessionID for position updates + /// RegionID where the user is currently located + /// Region-relative position + /// Normalized look direction + /// True if the user's last position was successfully updated, otherwise false + bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt); + + GridUserInfo GetGridUserInfo(string userID); + GridUserInfo[] GetGridUserInfo(string[] userID); + } +} diff --git a/OpenSim/Services/Interfaces/IHypergridServices.cs b/OpenSim/Services/Interfaces/IHypergridServices.cs new file mode 100644 index 0000000000..5e012fb173 --- /dev/null +++ b/OpenSim/Services/Interfaces/IHypergridServices.cs @@ -0,0 +1,143 @@ +/* + * 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.Net; +using System.Collections.Generic; + +using OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface IGatekeeperService + { + bool LinkRegion(string regionDescriptor, out UUID regionID, out ulong regionHandle, out string externalName, out string imageURL, out string reason); + + /// + /// Returns the region a Hypergrid visitor should enter. + /// + /// + /// Usually the returned region will be the requested region. But the grid can choose to + /// redirect the user to another region: e.g., a default gateway region. + /// + /// The region the visitor *wants* to enter + /// The visitor's User ID. Will be missing (UUID.Zero) in older OpenSims. + /// The visitor's Home URI. Will be missing (null) in older OpenSims. + /// [out] A message to show to the user (optional, may be null) + /// The region the visitor should enter, or null if no region can be found / is allowed + GridRegion GetHyperlinkRegion(UUID regionID, UUID agentID, string agentHomeURI, out string message); + + bool LoginAgent(GridRegion source, AgentCircuitData aCircuit, GridRegion destination, out string reason); + + } + + public interface IUserAgentService + { + bool LoginAgentToGrid(GridRegion source, AgentCircuitData agent, GridRegion gatekeeper, GridRegion finalDestination, bool fromLogin, out string reason); + + void LogoutAgent(UUID userID, UUID sessionID); + + /// + /// Returns the home region of a remote user. + /// + /// On success: the user's home region. If the user doesn't exist: null. + /// Throws an exception if an error occurs (e.g., can't contact the server). + GridRegion GetHomeRegion(UUID userID, out Vector3 position, out Vector3 lookAt); + + /// + /// Returns the Server URLs of a remote user. + /// + /// On success: the user's Server URLs. If the user doesn't exist: an empty dictionary. + /// Throws an exception if an error occurs (e.g., can't contact the server). + Dictionary GetServerURLs(UUID userID); + + /// + /// Returns the UserInfo of a remote user. + /// + /// On success: the user's UserInfo. If the user doesn't exist: an empty dictionary. + /// Throws an exception if an error occurs (e.g., can't contact the server). + Dictionary GetUserInfo(UUID userID); + + /// + /// Returns the current location of a remote user. + /// + /// On success: the user's Server URLs. If the user doesn't exist: "". + /// Throws an exception if an error occurs (e.g., can't contact the server). + string LocateUser(UUID userID); + + /// + /// Returns the Universal User Identifier for 'targetUserID' on behalf of 'userID'. + /// + /// On success: the user's UUI. If the user doesn't exist: "". + /// Throws an exception if an error occurs (e.g., can't contact the server). + string GetUUI(UUID userID, UUID targetUserID); + + /// + /// Returns the remote user that has the given name. + /// + /// On success: the user's UUID. If the user doesn't exist: UUID.Zero. + /// Throws an exception if an error occurs (e.g., can't contact the server). + UUID GetUUID(String first, String last); + + // Returns the local friends online + [Obsolete] + List StatusNotification(List friends, UUID userID, bool online); + + bool IsAgentComingHome(UUID sessionID, string thisGridExternalName); + bool VerifyAgent(UUID sessionID, string token); + bool VerifyClient(UUID sessionID, string reportedIP); + } + + public interface IInstantMessage + { + bool IncomingInstantMessage(GridInstantMessage im); + bool OutgoingInstantMessage(GridInstantMessage im, string url, bool foreigner); + } + public interface IFriendsSimConnector + { + bool StatusNotify(UUID userID, UUID friendID, bool online); + bool LocalFriendshipOffered(UUID toID, GridInstantMessage im); + bool LocalFriendshipApproved(UUID userID, string userName, UUID friendID); + } + + public interface IHGFriendsService + { + int GetFriendPerms(UUID userID, UUID friendID); + bool NewFriendship(FriendInfo finfo, bool verified); + bool DeleteFriendship(FriendInfo finfo, string secret); + bool FriendshipOffered(UUID from, string fromName, UUID to, string message); + bool ValidateFriendshipOffered(UUID fromID, UUID toID); + // Returns the local friends online + List StatusNotification(List friends, UUID userID, bool online); + } + + public interface IInstantMessageSimConnector + { + bool SendInstantMessage(GridInstantMessage im); + } +} diff --git a/OpenSim/Services/Interfaces/IInventoryService.cs b/OpenSim/Services/Interfaces/IInventoryService.cs new file mode 100644 index 0000000000..4289bbaef7 --- /dev/null +++ b/OpenSim/Services/Interfaces/IInventoryService.cs @@ -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 System.Collections.Generic; +using OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + /// + /// Callback used when a user's inventory is received from the inventory service + /// + public delegate void InventoryReceiptCallback( + ICollection folders, ICollection items); + + public interface IInventoryService + { + /// + /// Create the entire inventory for a given user + /// + /// + /// + bool CreateUserInventory(UUID user); + + /// + /// Gets the skeleton of the inventory -- folders only + /// + /// + /// + List GetInventorySkeleton(UUID userId); + + /// + /// Retrieve the root inventory folder for the given user. + /// + /// + /// null if no root folder was found + InventoryFolderBase GetRootFolder(UUID userID); + + /// + /// Gets the user folder for the given folder-type + /// + /// + /// + /// + InventoryFolderBase GetFolderForType(UUID userID, FolderType type); + + /// + /// Gets everything (folders and items) inside a folder + /// + /// + /// + /// Inventory content. null if the request failed. + InventoryCollection GetFolderContent(UUID userID, UUID folderID); + + /// + /// Gets everything (folders and items) inside a folder + /// + /// + /// + /// Inventory content. + InventoryCollection[] GetMultipleFoldersContent(UUID userID, UUID[] folderIDs); + + /// + /// Gets the items inside a folder + /// + /// + /// + /// + List GetFolderItems(UUID userID, UUID folderID); + + /// + /// Add a new folder to the user's inventory + /// + /// + /// true if the folder was successfully added + bool AddFolder(InventoryFolderBase folder); + + /// + /// Update a folder in the user's inventory + /// + /// + /// true if the folder was successfully updated + bool UpdateFolder(InventoryFolderBase folder); + + /// + /// Move an inventory folder to a new location + /// + /// A folder containing the details of the new location + /// true if the folder was successfully moved + bool MoveFolder(InventoryFolderBase folder); + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + //bool DeleteItem(InventoryItemBase item); + bool DeleteFolders(UUID userID, List folderIDs); + + /// + /// Purge an inventory folder of all its items and subfolders. + /// + /// + /// true if the folder was successfully purged + bool PurgeFolder(InventoryFolderBase folder); + + /// + /// Add a new item to the user's inventory + /// + /// + /// The item to be added. If item.FolderID == UUID.Zero then the item is added to the most suitable system + /// folder. If there is no suitable folder then the item is added to the user's root inventory folder. + /// + /// true if the item was successfully added, false if it was not + bool AddItem(InventoryItemBase item); + + /// + /// Update an item in the user's inventory + /// + /// + /// true if the item was successfully updated + bool UpdateItem(InventoryItemBase item); + + bool MoveItems(UUID ownerID, List items); + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + //bool DeleteItem(InventoryItemBase item); + bool DeleteItems(UUID userID, List itemIDs); + + /// + /// Get an item, given by its UUID + /// + /// + /// null if no item was found, otherwise the found item + InventoryItemBase GetItem(InventoryItemBase item); + + /// + /// Get multiple items, given by their UUIDs + /// + /// + /// null if no item was found, otherwise the found item + InventoryItemBase[] GetMultipleItems(UUID userID, UUID[] ids); + + /// + /// Get a folder, given by its UUID + /// + /// + /// + InventoryFolderBase GetFolder(InventoryFolderBase folder); + + /// + /// Does the given user have an inventory structure? + /// + /// + /// + bool HasInventoryForUser(UUID userID); + + /// + /// Get the active gestures of the agent. + /// + /// + /// + List GetActiveGestures(UUID userId); + + /// + /// Get the union of permissions of all inventory items + /// that hold the given assetID. + /// + /// + /// + /// The permissions or 0 if no such asset is found in + /// the user's inventory + int GetAssetPermissions(UUID userID, UUID assetID); + } +} diff --git a/OpenSim/Services/Interfaces/ILandService.cs b/OpenSim/Services/Interfaces/ILandService.cs new file mode 100644 index 0000000000..63d9a271c3 --- /dev/null +++ b/OpenSim/Services/Interfaces/ILandService.cs @@ -0,0 +1,38 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface ILandService + { + LandData GetLandData(UUID scopeID, ulong regionHandle, uint x, uint y, out byte regionAccess); + } +} diff --git a/OpenSim/Services/Interfaces/ILibraryService.cs b/OpenSim/Services/Interfaces/ILibraryService.cs new file mode 100644 index 0000000000..861cf0ef55 --- /dev/null +++ b/OpenSim/Services/Interfaces/ILibraryService.cs @@ -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. + */ + +using System; +using System.Collections.Generic; + +using OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface ILibraryService + { + InventoryFolderImpl LibraryRootFolder { get; } + + Dictionary GetAllFolders(); + } + +} diff --git a/OpenSim/Services/Interfaces/ILoginService.cs b/OpenSim/Services/Interfaces/ILoginService.cs new file mode 100644 index 0000000000..ee9b0b1720 --- /dev/null +++ b/OpenSim/Services/Interfaces/ILoginService.cs @@ -0,0 +1,56 @@ +/* + * 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 OpenMetaverse.StructuredData; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public abstract class LoginResponse + { + public abstract Hashtable ToHashtable(); + public abstract OSD ToOSDMap(); + } + + public abstract class FailedLoginResponse : LoginResponse + { + } + + public interface ILoginService + { + LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, + string clientVersion, string channel, string mac, string id0, IPEndPoint clientIP); + Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP); + } + + +} diff --git a/OpenSim/Services/Interfaces/IMapImageService.cs b/OpenSim/Services/Interfaces/IMapImageService.cs new file mode 100644 index 0000000000..78daa5f0e4 --- /dev/null +++ b/OpenSim/Services/Interfaces/IMapImageService.cs @@ -0,0 +1,41 @@ +/* + * 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 OpenSim.Framework; +using System.Collections.Generic; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface IMapImageService + { + //List GetMapBlocks(UUID scopeID, int minX, int minY, int maxX, int maxY); + bool AddMapTile(int x, int y, byte[] imageData, out string reason); + bool RemoveMapTile(int x, int y, out string reason); + byte[] GetMapTile(string fileName, out string format); + } +} diff --git a/OpenSim/Services/Interfaces/INeighbourService.cs b/OpenSim/Services/Interfaces/INeighbourService.cs new file mode 100644 index 0000000000..960e13d036 --- /dev/null +++ b/OpenSim/Services/Interfaces/INeighbourService.cs @@ -0,0 +1,39 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Interfaces +{ + public interface INeighbourService + { + GridRegion HelloNeighbour(ulong regionHandle, RegionInfo otherRegion); + } +} diff --git a/OpenSim/Services/Interfaces/IOfflineIMService.cs b/OpenSim/Services/Interfaces/IOfflineIMService.cs new file mode 100644 index 0000000000..588aaafb6b --- /dev/null +++ b/OpenSim/Services/Interfaces/IOfflineIMService.cs @@ -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.Collections.Generic; + +using OpenSim.Framework; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public interface IOfflineIMService + { + List GetMessages(UUID principalID); + + bool StoreMessage(GridInstantMessage im, out string reason); + + /// + /// Delete messages to or from this user (or group). + /// + /// A user or group ID + void DeleteMessages(UUID userID); + } + + public class OfflineIMDataUtils + { + public static GridInstantMessage GridInstantMessage(Dictionary dict) + { + GridInstantMessage im = new GridInstantMessage(); + + if (dict.ContainsKey("BinaryBucket") && dict["BinaryBucket"] != null) + im.binaryBucket = OpenMetaverse.Utils.HexStringToBytes(dict["BinaryBucket"].ToString(), true); + + if (dict.ContainsKey("Dialog") && dict["Dialog"] != null) + im.dialog = byte.Parse(dict["Dialog"].ToString()); + + if (dict.ContainsKey("FromAgentID") && dict["FromAgentID"] != null) + im.fromAgentID = new Guid(dict["FromAgentID"].ToString()); + + if (dict.ContainsKey("FromAgentName") && dict["FromAgentName"] != null) + im.fromAgentName = dict["FromAgentName"].ToString(); + else + im.fromAgentName = string.Empty; + + if (dict.ContainsKey("FromGroup") && dict["FromGroup"] != null) + im.fromGroup = bool.Parse(dict["FromGroup"].ToString()); + + if (dict.ContainsKey("SessionID") && dict["SessionID"] != null) + im.imSessionID = new Guid(dict["SessionID"].ToString()); + + if (dict.ContainsKey("Message") && dict["Message"] != null) + im.message = dict["Message"].ToString(); + else + im.message = string.Empty; + + if (dict.ContainsKey("Offline") && dict["Offline"] != null) + im.offline = byte.Parse(dict["Offline"].ToString()); + + if (dict.ContainsKey("EstateID") && dict["EstateID"] != null) + im.ParentEstateID = UInt32.Parse(dict["EstateID"].ToString()); + + if (dict.ContainsKey("Position") && dict["Position"] != null) + im.Position = Vector3.Parse(dict["Position"].ToString()); + + if (dict.ContainsKey("RegionID") && dict["RegionID"] != null) + im.RegionID = new Guid(dict["RegionID"].ToString()); + + if (dict.ContainsKey("Timestamp") && dict["Timestamp"] != null) + im.timestamp = UInt32.Parse(dict["Timestamp"].ToString()); + + if (dict.ContainsKey("ToAgentID") && dict["ToAgentID"] != null) + im.toAgentID = new Guid(dict["ToAgentID"].ToString()); + + return im; + } + + public static Dictionary GridInstantMessage(GridInstantMessage im) + { + Dictionary dict = new Dictionary(); + + dict["BinaryBucket"] = OpenMetaverse.Utils.BytesToHexString(im.binaryBucket, im.binaryBucket.Length, null); + dict["Dialog"] = im.dialog.ToString(); + dict["FromAgentID"] = im.fromAgentID.ToString(); + dict["FromAgentName"] = im.fromAgentName == null ? string.Empty : im.fromAgentName; + dict["FromGroup"] = im.fromGroup.ToString(); + dict["SessionID"] = im.imSessionID.ToString(); + dict["Message"] = im.message == null ? string.Empty : im.message; + dict["Offline"] = im.offline.ToString(); + dict["EstateID"] = im.ParentEstateID.ToString(); + dict["Position"] = im.Position.ToString(); + dict["RegionID"] = im.RegionID.ToString(); + dict["Timestamp"] = im.timestamp.ToString(); + dict["ToAgentID"] = im.toAgentID.ToString(); + + return dict; + } + + } +} diff --git a/OpenSim/Services/Interfaces/IPresenceService.cs b/OpenSim/Services/Interfaces/IPresenceService.cs new file mode 100644 index 0000000000..90f98426f2 --- /dev/null +++ b/OpenSim/Services/Interfaces/IPresenceService.cs @@ -0,0 +1,109 @@ +/* + * 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 OpenSim.Framework; +using System.Collections.Generic; +using OpenMetaverse; + +namespace OpenSim.Services.Interfaces +{ + public class PresenceInfo + { + public string UserID; + public UUID RegionID; + + public PresenceInfo() + { + } + + public PresenceInfo(Dictionary kvp) + { + if (kvp.ContainsKey("UserID")) + UserID = kvp["UserID"].ToString(); + if (kvp.ContainsKey("RegionID")) + UUID.TryParse(kvp["RegionID"].ToString(), out RegionID); + } + + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["UserID"] = UserID; + result["RegionID"] = RegionID.ToString(); + + return result; + } + } + + public interface IPresenceService + { + /// + /// Store session information. + /// + /// /returns> + /// + /// + /// + bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID); + + /// + /// Remove session information. + /// + /// + /// + bool LogoutAgent(UUID sessionID); + + /// + /// Remove session information for all agents in the given region. + /// + /// + /// + bool LogoutRegionAgents(UUID regionID); + + /// + /// Update data for an existing session. + /// + /// + /// + /// + bool ReportAgent(UUID sessionID, UUID regionID); + + /// + /// Get session information for a given session ID. + /// + /// + /// + PresenceInfo GetAgent(UUID sessionID); + + /// + /// Get session information for a collection of users. + /// + /// Session information for the users. + /// + PresenceInfo[] GetAgents(string[] userIDs); + } +} \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/ISimulationService.cs b/OpenSim/Services/Interfaces/ISimulationService.cs new file mode 100644 index 0000000000..42c414d145 --- /dev/null +++ b/OpenSim/Services/Interfaces/ISimulationService.cs @@ -0,0 +1,131 @@ +/* + * 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; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Interfaces +{ + public interface ISimulationService + { + /// + /// Retrieve the scene with the given region ID. + /// + /// + /// Region identifier. + /// + /// + /// The scene. + /// + IScene GetScene(UUID regionId); + + ISimulationService GetInnerService(); + + #region Agents + + /// + /// Ask the simulator hosting the destination to create an agent on that region. + /// + /// The region that the user is coming from. Will be null if the user + /// logged-in directly, or arrived from a simulator that doesn't send this parameter. + /// + /// + /// + /// Reason message in the event of a failure. + bool CreateAgent(GridRegion source, GridRegion destination, AgentCircuitData aCircuit, uint flags, out string reason); + + /// + /// Full child agent update. + /// + /// + /// + /// + bool UpdateAgent(GridRegion destination, AgentData data); + + /// + /// Short child agent update, mostly for position. + /// + /// + /// + /// + bool UpdateAgent(GridRegion destination, AgentPosition data); + + /// + /// Returns whether a propspective user is allowed to visit the region. + /// + /// Desired destination + /// The visitor's User ID + /// The visitor's Home URI. Will be missing (null) in older OpenSims. + /// True: via teleport; False: via cross (walking) + /// Position in the region + /// + /// Version that the requesting simulator is runing. If null then no version check is carried out. + /// + /// Version that the target simulator is running + /// [out] Optional error message + /// True: ok; False: not allowed + bool QueryAccess(GridRegion destination, UUID agentID, string agentHomeURI, bool viaTeleport, Vector3 position, string sversion, List features, out string version, out string reason); + + /// + /// Message from receiving region to departing region, telling it got contacted by the client. + /// When sent over REST, it invokes the opaque uri. + /// + /// + /// + /// + /// + bool ReleaseAgent(UUID originRegion, UUID id, string uri); + + /// + /// Close agent. + /// + /// + /// + /// + bool CloseAgent(GridRegion destination, UUID id, string auth_token); + + #endregion Agents + + #region Objects + + /// + /// Create an object in the destination region. This message is used primarily for prim crossing. + /// + /// + /// + /// + /// + bool CreateObject(GridRegion destination, Vector3 newPosition, ISceneObject sog, bool isLocalCall); + + #endregion Objects + + } +} diff --git a/OpenSim/Services/Interfaces/IUserAccountService.cs b/OpenSim/Services/Interfaces/IUserAccountService.cs new file mode 100644 index 0000000000..2f7702c98a --- /dev/null +++ b/OpenSim/Services/Interfaces/IUserAccountService.cs @@ -0,0 +1,195 @@ +/* + * 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.Services.Interfaces +{ + public class UserAccount + { + public UserAccount() + { + } + + public UserAccount(UUID principalID) + { + PrincipalID = principalID; + } + + /// + /// Initializes a new instance of the class. + /// This method is used by externasl/3rd party management applications that need us to create a + /// random UUID for the new user. + /// + /// + /// Scope I. + /// + /// + /// First name. + /// + /// + /// Last name. + /// + /// + /// Email. + /// + public UserAccount(UUID scopeID, string firstName, string lastName, string email) + { + PrincipalID = UUID.Random(); + ScopeID = scopeID; + FirstName = firstName; + LastName = lastName; + Email = email; + ServiceURLs = new Dictionary(); + Created = Util.UnixTimeSinceEpoch(); + } + + public UserAccount(UUID scopeID, UUID principalID, string firstName, string lastName, string email) + { + PrincipalID = principalID; + ScopeID = scopeID; + FirstName = firstName; + LastName = lastName; + Email = email; + ServiceURLs = new Dictionary(); + Created = Util.UnixTimeSinceEpoch(); + } + + public string FirstName; + public string LastName; + public string Email; + public UUID PrincipalID; + public UUID ScopeID; + public int UserLevel; + public int UserFlags; + public string UserTitle; + public Boolean LocalToGrid = true; + + public Dictionary ServiceURLs; + + public int Created; + + public string Name + { + get { return FirstName + " " + LastName; } + } + + public UserAccount(Dictionary kvp) + { + if (kvp.ContainsKey("FirstName")) + FirstName = kvp["FirstName"].ToString(); + if (kvp.ContainsKey("LastName")) + LastName = kvp["LastName"].ToString(); + if (kvp.ContainsKey("Email")) + Email = kvp["Email"].ToString(); + if (kvp.ContainsKey("PrincipalID")) + UUID.TryParse(kvp["PrincipalID"].ToString(), out PrincipalID); + if (kvp.ContainsKey("ScopeID")) + UUID.TryParse(kvp["ScopeID"].ToString(), out ScopeID); + if (kvp.ContainsKey("UserLevel")) + UserLevel = Convert.ToInt32(kvp["UserLevel"].ToString()); + if (kvp.ContainsKey("UserFlags")) + UserFlags = Convert.ToInt32(kvp["UserFlags"].ToString()); + if (kvp.ContainsKey("UserTitle")) + UserTitle = kvp["UserTitle"].ToString(); + if (kvp.ContainsKey("LocalToGrid")) + Boolean.TryParse(kvp["LocalToGrid"].ToString(), out LocalToGrid); + + if (kvp.ContainsKey("Created")) + Created = Convert.ToInt32(kvp["Created"].ToString()); + if (kvp.ContainsKey("ServiceURLs") && kvp["ServiceURLs"] != null) + { + ServiceURLs = new Dictionary(); + string str = kvp["ServiceURLs"].ToString(); + if (str != string.Empty) + { + string[] parts = str.Split(new char[] { ';' }); +// Dictionary dic = new Dictionary(); + foreach (string s in parts) + { + string[] parts2 = s.Split(new char[] { '*' }); + if (parts2.Length == 2) + ServiceURLs[parts2[0]] = parts2[1]; + } + } + } + } + + public Dictionary ToKeyValuePairs() + { + Dictionary result = new Dictionary(); + result["FirstName"] = FirstName; + result["LastName"] = LastName; + result["Email"] = Email; + result["PrincipalID"] = PrincipalID.ToString(); + result["ScopeID"] = ScopeID.ToString(); + result["Created"] = Created.ToString(); + result["UserLevel"] = UserLevel.ToString(); + result["UserFlags"] = UserFlags.ToString(); + result["UserTitle"] = UserTitle; + result["LocalToGrid"] = LocalToGrid.ToString(); + + string str = string.Empty; + foreach (KeyValuePair kvp in ServiceURLs) + { + str += kvp.Key + "*" + (kvp.Value == null ? "" : kvp.Value) + ";"; + } + result["ServiceURLs"] = str; + + return result; + } + + }; + + public interface IUserAccountService + { + UserAccount GetUserAccount(UUID scopeID, UUID userID); + UserAccount GetUserAccount(UUID scopeID, string FirstName, string LastName); + UserAccount GetUserAccount(UUID scopeID, string Email); + + /// + /// Returns the list of avatars that matches both the search criterion and the scope ID passed + /// + /// + /// + /// + List GetUserAccounts(UUID scopeID, string query); + + /// + /// Store the data given, wich replaces the stored data, therefore must be complete. + /// + /// + /// + bool StoreUserAccount(UserAccount data); + + void InvalidateCache(UUID userID); + } +} diff --git a/OpenSim/Services/Interfaces/IUserManagement.cs b/OpenSim/Services/Interfaces/IUserManagement.cs new file mode 100644 index 0000000000..9e560d5f89 --- /dev/null +++ b/OpenSim/Services/Interfaces/IUserManagement.cs @@ -0,0 +1,97 @@ +/* + * 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; + +namespace OpenSim.Services.Interfaces +{ + /// + /// This maintains the relationship between a UUID and a user name. + /// + public interface IUserManagement + { + string GetUserName(UUID uuid); + string GetUserHomeURL(UUID uuid); + string GetUserUUI(UUID uuid); + bool GetUserUUI(UUID userID, out string uui); + string GetUserServerURL(UUID uuid, string serverType); + + /// + /// Get user ID by the given name. + /// + /// + /// UUID.Zero if no user with that name is found or if the name is "Unknown User" + UUID GetUserIdByName(string name); + + /// + /// Get user ID by the given name. + /// + /// + /// + /// UUID.Zero if no user with that name is found or if the name is "Unknown User" + UUID GetUserIdByName(string firstName, string lastName); + + /// + /// Add a user. + /// + /// + /// If an account is found for the UUID, then the names in this will be used rather than any information + /// extracted from creatorData. + /// + /// + /// The creator data for this user. + void AddUser(UUID uuid, string creatorData); + + /// + /// Add a user. + /// + /// + /// The UUID is related to the name without any other checks being performed, such as user account presence. + /// + /// + /// + /// + void AddUser(UUID uuid, string firstName, string lastName); + + /// + /// Add a user. + /// + /// + /// The arguments apart from uuid are formed into a creatorData string and processing proceeds as for the + /// AddUser(UUID uuid, string creatorData) method. + /// + /// + /// + /// + void AddUser(UUID uuid, string firstName, string lastName, string homeURL); + + bool IsLocalGridUser(UUID uuid); + } +} diff --git a/OpenSim/Services/Interfaces/IUserProfilesService.cs b/OpenSim/Services/Interfaces/IUserProfilesService.cs new file mode 100644 index 0000000000..121baa8b1f --- /dev/null +++ b/OpenSim/Services/Interfaces/IUserProfilesService.cs @@ -0,0 +1,80 @@ +/* + * 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 OpenSim.Framework; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Interfaces +{ + public interface IUserProfilesService + { + #region Classifieds + OSD AvatarClassifiedsRequest(UUID creatorId); + bool ClassifiedUpdate(UserClassifiedAdd ad, ref string result); + bool ClassifiedInfoRequest(ref UserClassifiedAdd ad, ref string result); + bool ClassifiedDelete(UUID recordId); + #endregion Classifieds + + #region Picks + OSD AvatarPicksRequest(UUID creatorId); + bool PickInfoRequest(ref UserProfilePick pick, ref string result); + bool PicksUpdate(ref UserProfilePick pick, ref string result); + bool PicksDelete(UUID pickId); + #endregion Picks + + #region Notes + bool AvatarNotesRequest(ref UserProfileNotes note); + bool NotesUpdate(ref UserProfileNotes note, ref string result); + #endregion Notes + + #region Profile Properties + bool AvatarPropertiesRequest(ref UserProfileProperties prop, ref string result); + bool AvatarPropertiesUpdate(ref UserProfileProperties prop, ref string result); + #endregion Profile Properties + + #region User Preferences + bool UserPreferencesRequest(ref UserPreferences pref, ref string result); + bool UserPreferencesUpdate(ref UserPreferences pref, ref string result); + #endregion User Preferences + + #region Interests + bool AvatarInterestsUpdate(UserProfileProperties prop, ref string result); + #endregion Interests + + #region Utility + OSD AvatarImageAssetsRequest(UUID avatarId); + #endregion Utility + + #region UserData + bool RequestUserAppData(ref UserAppData prop, ref string result); + bool SetUserAppData(UserAppData prop, ref string result); + #endregion UserData + } +} + diff --git a/OpenSim/Services/Interfaces/OpenProfileClient.cs b/OpenSim/Services/Interfaces/OpenProfileClient.cs new file mode 100644 index 0000000000..bda8151048 --- /dev/null +++ b/OpenSim/Services/Interfaces/OpenProfileClient.cs @@ -0,0 +1,134 @@ +/* + * 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.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Xml; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; + +namespace OpenSim.Services.UserProfilesService +{ + /// + /// A client for accessing a profile server using the OpenProfile protocol. + /// + /// + /// This class was adapted from the full OpenProfile class. Since it's only a client, and not a server, + /// it's much simpler. + /// + public class OpenProfileClient + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverURI; + + /// + /// Creates a client for accessing a foreign grid's profile server using the OpenProfile protocol. + /// + /// The grid's profile server URL + public OpenProfileClient(string serverURI) + { + m_serverURI = serverURI; + } + + /// + /// Gets an avatar's profile using the OpenProfile protocol. + /// + /// On success, this will contain the avatar's profile + /// Success/failure + /// + /// There are two profile modules currently in use in OpenSim: the older one is OpenProfile, and the newer + /// one is UserProfileModule (this file). This method attempts to read an avatar's profile from a foreign + /// grid using the OpenProfile protocol. + /// + public bool RequestAvatarPropertiesUsingOpenProfile(ref UserProfileProperties props) + { + Hashtable ReqHash = new Hashtable(); + ReqHash["avatar_id"] = props.UserId.ToString(); + + Hashtable profileData = XMLRPCRequester.SendRequest(ReqHash, "avatar_properties_request", m_serverURI); + + if (profileData == null) + return false; + if (!profileData.ContainsKey("data")) + return false; + + ArrayList dataArray = (ArrayList)profileData["data"]; + + if (dataArray == null || dataArray[0] == null) + return false; + profileData = (Hashtable)dataArray[0]; + + props.WebUrl = string.Empty; + props.AboutText = String.Empty; + props.FirstLifeText = String.Empty; + props.ImageId = UUID.Zero; + props.FirstLifeImageId = UUID.Zero; + props.PartnerId = UUID.Zero; + + if (profileData["ProfileUrl"] != null) + props.WebUrl = profileData["ProfileUrl"].ToString(); + if (profileData["AboutText"] != null) + props.AboutText = profileData["AboutText"].ToString(); + if (profileData["FirstLifeAboutText"] != null) + props.FirstLifeText = profileData["FirstLifeAboutText"].ToString(); + if (profileData["Image"] != null) + props.ImageId = new UUID(profileData["Image"].ToString()); + if (profileData["FirstLifeImage"] != null) + props.FirstLifeImageId = new UUID(profileData["FirstLifeImage"].ToString()); + if (profileData["Partner"] != null) + props.PartnerId = new UUID(profileData["Partner"].ToString()); + + props.WantToMask = 0; + props.WantToText = String.Empty; + props.SkillsMask = 0; + props.SkillsText = String.Empty; + props.Language = String.Empty; + + if (profileData["wantmask"] != null) + props.WantToMask = Convert.ToInt32(profileData["wantmask"].ToString()); + if (profileData["wanttext"] != null) + props.WantToText = profileData["wanttext"].ToString(); + + if (profileData["skillsmask"] != null) + props.SkillsMask = Convert.ToInt32(profileData["skillsmask"].ToString()); + if (profileData["skillstext"] != null) + props.SkillsText = profileData["skillstext"].ToString(); + + if (profileData["languages"] != null) + props.Language = profileData["languages"].ToString(); + + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs b/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..a2f6c4f6cf --- /dev/null +++ b/OpenSim/Services/Interfaces/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.Interfaces")] +[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("39091de1-1c4c-4ebe-bb01-31551ec1749d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/InventoryService/LibraryService.cs b/OpenSim/Services/InventoryService/LibraryService.cs new file mode 100644 index 0000000000..c4a557284a --- /dev/null +++ b/OpenSim/Services/InventoryService/LibraryService.cs @@ -0,0 +1,285 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; + +using log4net; +using Nini.Config; +using OpenMetaverse; +using PermissionMask = OpenSim.Framework.PermissionMask; + +namespace OpenSim.Services.InventoryService +{ + /// + /// Basically a hack to give us a Inventory library while we don't have a inventory server + /// once the server is fully implemented then should read the data from that + /// + public class LibraryService : ServiceBase, ILibraryService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private InventoryFolderImpl m_LibraryRootFolder; + + public InventoryFolderImpl LibraryRootFolder + { + get { return m_LibraryRootFolder; } + } + + private UUID libOwner = new UUID("11111111-1111-0000-0000-000100bba000"); + + /// + /// Holds the root library folder and all its descendents. This is really only used during inventory + /// setup so that we don't have to repeatedly search the tree of library folders. + /// + protected Dictionary libraryFolders + = new Dictionary(); + + public LibraryService(IConfigSource config) + : base(config) + { + string pLibrariesLocation = Path.Combine("inventory", "Libraries.xml"); + string pLibName = "OpenSim Library"; + + IConfig libConfig = config.Configs["LibraryService"]; + if (libConfig != null) + { + pLibrariesLocation = libConfig.GetString("DefaultLibrary", pLibrariesLocation); + pLibName = libConfig.GetString("LibraryName", pLibName); + } + + m_log.Debug("[LIBRARY]: Starting library service..."); + + m_LibraryRootFolder = new InventoryFolderImpl(); + m_LibraryRootFolder.Owner = libOwner; + m_LibraryRootFolder.ID = new UUID("00000112-000f-0000-0000-000100bba000"); + m_LibraryRootFolder.Name = pLibName; + m_LibraryRootFolder.ParentID = UUID.Zero; + m_LibraryRootFolder.Type = (short)8; + m_LibraryRootFolder.Version = (ushort)1; + + libraryFolders.Add(m_LibraryRootFolder.ID, m_LibraryRootFolder); + + LoadLibraries(pLibrariesLocation); + } + + public InventoryItemBase CreateItem(UUID inventoryID, UUID assetID, string name, string description, + int assetType, int invType, UUID parentFolderID) + { + InventoryItemBase item = new InventoryItemBase(); + item.Owner = libOwner; + item.CreatorId = libOwner.ToString(); + item.ID = inventoryID; + item.AssetID = assetID; + item.Description = description; + item.Name = name; + item.AssetType = assetType; + item.InvType = invType; + item.Folder = parentFolderID; + item.BasePermissions = 0x7FFFFFFF; + item.EveryOnePermissions = 0x7FFFFFFF; + item.CurrentPermissions = 0x7FFFFFFF; + item.NextPermissions = 0x7FFFFFFF; + return item; + } + + /// + /// Use the asset set information at path to load assets + /// + /// + /// + protected void LoadLibraries(string librariesControlPath) + { + m_log.InfoFormat("[LIBRARY INVENTORY]: Loading library control file {0}", librariesControlPath); + LoadFromFile(librariesControlPath, "Libraries control", ReadLibraryFromConfig); + } + + /// + /// Read a library set from config + /// + /// + protected void ReadLibraryFromConfig(IConfig config, string path) + { + string basePath = Path.GetDirectoryName(path); + string foldersPath + = Path.Combine( + basePath, config.GetString("foldersFile", String.Empty)); + + LoadFromFile(foldersPath, "Library folders", ReadFolderFromConfig); + + string itemsPath + = Path.Combine( + basePath, config.GetString("itemsFile", String.Empty)); + + LoadFromFile(itemsPath, "Library items", ReadItemFromConfig); + } + + /// + /// Read a library inventory folder from a loaded configuration + /// + /// + private void ReadFolderFromConfig(IConfig config, string path) + { + InventoryFolderImpl folderInfo = new InventoryFolderImpl(); + + folderInfo.ID = new UUID(config.GetString("folderID", m_LibraryRootFolder.ID.ToString())); + folderInfo.Name = config.GetString("name", "unknown"); + folderInfo.ParentID = new UUID(config.GetString("parentFolderID", m_LibraryRootFolder.ID.ToString())); + folderInfo.Type = (short)config.GetInt("type", 8); + + folderInfo.Owner = libOwner; + folderInfo.Version = 1; + + if (libraryFolders.ContainsKey(folderInfo.ParentID)) + { + InventoryFolderImpl parentFolder = libraryFolders[folderInfo.ParentID]; + + libraryFolders.Add(folderInfo.ID, folderInfo); + parentFolder.AddChildFolder(folderInfo); + +// m_log.InfoFormat("[LIBRARY INVENTORY]: Adding folder {0} ({1})", folderInfo.name, folderInfo.folderID); + } + else + { + m_log.WarnFormat( + "[LIBRARY INVENTORY]: Couldn't add folder {0} ({1}) since parent folder with ID {2} does not exist!", + folderInfo.Name, folderInfo.ID, folderInfo.ParentID); + } + } + + /// + /// Read a library inventory item metadata from a loaded configuration + /// + /// + private void ReadItemFromConfig(IConfig config, string path) + { + InventoryItemBase item = new InventoryItemBase(); + item.Owner = libOwner; + item.CreatorId = libOwner.ToString(); + item.ID = new UUID(config.GetString("inventoryID", m_LibraryRootFolder.ID.ToString())); + item.AssetID = new UUID(config.GetString("assetID", item.ID.ToString())); + item.Folder = new UUID(config.GetString("folderID", m_LibraryRootFolder.ID.ToString())); + item.Name = config.GetString("name", String.Empty); + item.Description = config.GetString("description", item.Name); + item.InvType = config.GetInt("inventoryType", 0); + item.AssetType = config.GetInt("assetType", item.InvType); + item.CurrentPermissions = (uint)config.GetLong("currentPermissions", (uint)PermissionMask.All); + item.NextPermissions = (uint)config.GetLong("nextPermissions", (uint)PermissionMask.All); + item.EveryOnePermissions + = (uint)config.GetLong("everyonePermissions", (uint)PermissionMask.All - (uint)PermissionMask.Modify); + item.BasePermissions = (uint)config.GetLong("basePermissions", (uint)PermissionMask.All); + item.Flags = (uint)config.GetInt("flags", 0); + + if (libraryFolders.ContainsKey(item.Folder)) + { + InventoryFolderImpl parentFolder = libraryFolders[item.Folder]; + try + { + parentFolder.Items.Add(item.ID, item); + } + catch (Exception) + { + m_log.WarnFormat("[LIBRARY INVENTORY] Item {1} [{0}] not added, duplicate item", item.ID, item.Name); + } + } + else + { + m_log.WarnFormat( + "[LIBRARY INVENTORY]: Couldn't add item {0} ({1}) since parent folder with ID {2} does not exist!", + item.Name, item.ID, item.Folder); + } + } + + private delegate void ConfigAction(IConfig config, string path); + + /// + /// Load the given configuration at a path and perform an action on each Config contained within it + /// + /// + /// + /// + private static void LoadFromFile(string path, string fileDescription, ConfigAction action) + { + if (File.Exists(path)) + { + try + { + XmlConfigSource source = new XmlConfigSource(path); + + for (int i = 0; i < source.Configs.Count; i++) + { + action(source.Configs[i], path); + } + } + catch (XmlException e) + { + m_log.ErrorFormat("[LIBRARY INVENTORY]: Error loading {0} : {1}", path, e); + } + } + else + { + m_log.ErrorFormat("[LIBRARY INVENTORY]: {0} file {1} does not exist!", fileDescription, path); + } + } + + /// + /// Looks like a simple getter, but is written like this for some consistency with the other Request + /// methods in the superclass + /// + /// + public Dictionary GetAllFolders() + { + Dictionary fs = new Dictionary(); + fs.Add(m_LibraryRootFolder.ID, m_LibraryRootFolder); + List fis = TraverseFolder(m_LibraryRootFolder); + foreach (InventoryFolderImpl f in fis) + { + fs.Add(f.ID, f); + } + //return libraryFolders; + return fs; + } + + private List TraverseFolder(InventoryFolderImpl node) + { + List folders = node.RequestListOfFolderImpls(); + List subs = new List(); + foreach (InventoryFolderImpl f in folders) + subs.AddRange(TraverseFolder(f)); + + folders.AddRange(subs); + return folders; + } + } +} diff --git a/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs b/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6a1187c81e --- /dev/null +++ b/OpenSim/Services/InventoryService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.InventoryService")] +[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("d96d6d8c-9769-47e7-88dc-dbeb8fe7105a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/InventoryService/Tests/XInventoryServiceTests.cs b/OpenSim/Services/InventoryService/Tests/XInventoryServiceTests.cs new file mode 100644 index 0000000000..9e3fa695af --- /dev/null +++ b/OpenSim/Services/InventoryService/Tests/XInventoryServiceTests.cs @@ -0,0 +1,177 @@ +/* + * 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 Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Tests.Common; + +namespace OpenSim.Services.InventoryService.Tests +{ + /// + /// Tests for the XInventoryService + /// + /// + /// TODO: Fill out more tests. + /// + [TestFixture] + public class XInventoryServiceTests : OpenSimTestCase + { + private IInventoryService CreateXInventoryService() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("InventoryService"); + config.Configs["InventoryService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + + return ServerUtils.LoadPlugin( + "OpenSim.Services.InventoryService.dll:XInventoryService", new Object[] { config }); + } + + /// + /// Tests add item operation. + /// + /// + /// TODO: Test all operations. + /// + [Test] + public void TestAddItem() + { + TestHelpers.InMethod(); + + string creatorId = TestHelpers.ParseTail(0x1).ToString(); + UUID ownerId = TestHelpers.ParseTail(0x2); + UUID itemId = TestHelpers.ParseTail(0x10); + UUID assetId = TestHelpers.ParseTail(0x20); + UUID folderId = TestHelpers.ParseTail(0x30); + int invType = (int)InventoryType.Animation; + int assetType = (int)AssetType.Animation; + string itemName = "item1"; + + IInventoryService xis = CreateXInventoryService(); + + InventoryItemBase itemToStore + = new InventoryItemBase(itemId, ownerId) + { + CreatorIdentification = creatorId.ToString(), + AssetID = assetId, + Name = itemName, + Folder = folderId, + InvType = invType, + AssetType = assetType + }; + + Assert.That(xis.AddItem(itemToStore), Is.True); + + InventoryItemBase itemRetrieved = new InventoryItemBase(itemId); + itemRetrieved = xis.GetItem(itemRetrieved); + + Assert.That(itemRetrieved, Is.Not.Null); + Assert.That(itemRetrieved.CreatorId, Is.EqualTo(creatorId)); + Assert.That(itemRetrieved.Owner, Is.EqualTo(ownerId)); + Assert.That(itemRetrieved.AssetID, Is.EqualTo(assetId)); + Assert.That(itemRetrieved.Folder, Is.EqualTo(folderId)); + Assert.That(itemRetrieved.InvType, Is.EqualTo(invType)); + Assert.That(itemRetrieved.AssetType, Is.EqualTo(assetType)); + Assert.That(itemRetrieved.Name, Is.EqualTo(itemName)); + } + + [Test] + public void TestUpdateItem() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + string creatorId = TestHelpers.ParseTail(0x1).ToString(); + UUID ownerId = TestHelpers.ParseTail(0x2); + UUID itemId = TestHelpers.ParseTail(0x10); + UUID assetId = TestHelpers.ParseTail(0x20); + UUID folderId = TestHelpers.ParseTail(0x30); + int invType = (int)InventoryType.Animation; + int assetType = (int)AssetType.Animation; + string itemName = "item1"; + string itemName2 = "item2"; + + IInventoryService xis = CreateXInventoryService(); + + InventoryItemBase itemToStore + = new InventoryItemBase(itemId, ownerId) + { + CreatorIdentification = creatorId.ToString(), + AssetID = assetId, + Name = itemName, + Folder = folderId, + InvType = invType, + AssetType = assetType + }; + + Assert.That(xis.AddItem(itemToStore), Is.True); + + // Normal update + itemToStore.Name = itemName2; + + Assert.That(xis.UpdateItem(itemToStore), Is.True); + + InventoryItemBase itemRetrieved = new InventoryItemBase(itemId); + itemRetrieved = xis.GetItem(itemRetrieved); + + Assert.That(itemRetrieved, Is.Not.Null); + Assert.That(itemRetrieved.Name, Is.EqualTo(itemName2)); + + // Attempt to update properties that should never change + string creatorId2 = TestHelpers.ParseTail(0x7).ToString(); + UUID ownerId2 = TestHelpers.ParseTail(0x8); + UUID folderId2 = TestHelpers.ParseTail(0x70); + int invType2 = (int)InventoryType.CallingCard; + int assetType2 = (int)AssetType.CallingCard; + string itemName3 = "item3"; + + itemToStore.CreatorIdentification = creatorId2.ToString(); + itemToStore.Owner = ownerId2; + itemToStore.Folder = folderId2; + itemToStore.InvType = invType2; + itemToStore.AssetType = assetType2; + itemToStore.Name = itemName3; + + Assert.That(xis.UpdateItem(itemToStore), Is.True); + + itemRetrieved = xis.GetItem(itemRetrieved); + + Assert.That(itemRetrieved, Is.Not.Null); + Assert.That(itemRetrieved.CreatorId, Is.EqualTo(creatorId)); + Assert.That(itemRetrieved.Owner, Is.EqualTo(ownerId)); + Assert.That(itemRetrieved.AssetID, Is.EqualTo(assetId)); + Assert.That(itemRetrieved.Folder, Is.EqualTo(folderId)); + Assert.That(itemRetrieved.InvType, Is.EqualTo(invType)); + Assert.That(itemRetrieved.AssetType, Is.EqualTo(assetType)); + Assert.That(itemRetrieved.Name, Is.EqualTo(itemName3)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/InventoryService/XInventoryService.cs b/OpenSim/Services/InventoryService/XInventoryService.cs new file mode 100644 index 0000000000..b75193f625 --- /dev/null +++ b/OpenSim/Services/InventoryService/XInventoryService.cs @@ -0,0 +1,789 @@ +/* + * 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 log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Data; +using OpenSim.Framework; + +namespace OpenSim.Services.InventoryService +{ + public class XInventoryService : ServiceBase, IInventoryService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + protected IXInventoryData m_Database; + protected bool m_AllowDelete = true; + protected string m_ConfigName = "InventoryService"; + + public XInventoryService(IConfigSource config) + : this(config, "InventoryService") + { + } + + public XInventoryService(IConfigSource config, string configName) : base(config) + { + if (configName != string.Empty) + m_ConfigName = configName; + + string dllName = String.Empty; + string connString = String.Empty; + //string realm = "Inventory"; // OSG version doesn't use this + + // + // Try reading the [InventoryService] section first, if it exists + // + IConfig authConfig = config.Configs[m_ConfigName]; + if (authConfig != null) + { + dllName = authConfig.GetString("StorageProvider", dllName); + connString = authConfig.GetString("ConnectionString", connString); + m_AllowDelete = authConfig.GetBoolean("AllowDelete", true); + // realm = authConfig.GetString("Realm", realm); + } + + // + // 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); + } + + // + // We tried, but this doesn't exist. We can't proceed. + // + if (dllName == String.Empty) + throw new Exception("No StorageProvider configured"); + + m_Database = LoadPlugin(dllName, + new Object[] {connString, String.Empty}); + + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + } + + public virtual bool CreateUserInventory(UUID principalID) + { + // This is braindeaad. We can't ever communicate that we fixed + // an existing inventory. Well, just return root folder status, + // but check sanity anyway. + // + bool result = false; + + InventoryFolderBase rootFolder = GetRootFolder(principalID); + + if (rootFolder == null) + { + rootFolder = ConvertToOpenSim(CreateFolder(principalID, UUID.Zero, (int)FolderType.Root, InventoryFolderBase.ROOT_FOLDER_NAME)); + result = true; + } + + XInventoryFolder[] sysFolders = GetSystemFolders(principalID, rootFolder.ID); + + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Animation) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Animation, "Animations"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.BodyPart) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.BodyPart, "Body Parts"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.CallingCard) return true; return false; })) + { + XInventoryFolder folder = CreateFolder(principalID, rootFolder.ID, (int)FolderType.CallingCard, "Calling Cards"); + folder = CreateFolder(principalID, folder.folderID, (int)FolderType.CallingCard, "Friends"); + CreateFolder(principalID, folder.folderID, (int)FolderType.CallingCard, "All"); + } + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Clothing) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Clothing, "Clothing"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.CurrentOutfit) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.CurrentOutfit, "Current Outfit"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Favorites) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Favorites, "Favorites"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Gesture) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Gesture, "Gestures"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Landmark) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Landmark, "Landmarks"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.LostAndFound) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.LostAndFound, "Lost And Found"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Notecard) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Notecard, "Notecards"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Object) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Object, "Objects"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Snapshot) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Snapshot, "Photo Album"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.LSLText) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.LSLText, "Scripts"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Sound) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Sound, "Sounds"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Texture) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Texture, "Textures"); + if (!Array.Exists(sysFolders, delegate(XInventoryFolder f) { if (f.type == (int)FolderType.Trash) return true; return false; })) + CreateFolder(principalID, rootFolder.ID, (int)FolderType.Trash, "Trash"); + + return result; + } + + protected XInventoryFolder CreateFolder(UUID principalID, UUID parentID, int type, string name) + { + XInventoryFolder newFolder = new XInventoryFolder(); + + newFolder.folderName = name; + newFolder.type = type; + newFolder.version = 1; + newFolder.folderID = UUID.Random(); + newFolder.agentID = principalID; + newFolder.parentFolderID = parentID; + + m_Database.StoreFolder(newFolder); + + return newFolder; + } + + protected virtual XInventoryFolder[] GetSystemFolders(UUID principalID, UUID rootID) + { +// m_log.DebugFormat("[XINVENTORY SERVICE]: Getting system folders for {0}", principalID); + + XInventoryFolder[] allFolders = m_Database.GetFolders( + new string[] { "agentID", "parentFolderID" }, + new string[] { principalID.ToString(), rootID.ToString() }); + + XInventoryFolder[] sysFolders = Array.FindAll( + allFolders, + delegate (XInventoryFolder f) + { + if (f.type > 0) + return true; + return false; + }); + +// m_log.DebugFormat( +// "[XINVENTORY SERVICE]: Found {0} system folders for {1}", sysFolders.Length, principalID); + + return sysFolders; + } + + public virtual List GetInventorySkeleton(UUID principalID) + { + XInventoryFolder[] allFolders = m_Database.GetFolders( + new string[] { "agentID" }, + new string[] { principalID.ToString() }); + + if (allFolders.Length == 0) + return null; + + List folders = new List(); + + foreach (XInventoryFolder x in allFolders) + { + //m_log.DebugFormat("[XINVENTORY SERVICE]: Adding folder {0} to skeleton", x.folderName); + folders.Add(ConvertToOpenSim(x)); + } + + return folders; + } + + public virtual InventoryFolderBase GetRootFolder(UUID principalID) + { + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "parentFolderID"}, + new string[] { principalID.ToString(), UUID.Zero.ToString() }); + + if (folders.Length == 0) + return null; + + XInventoryFolder root = null; + foreach (XInventoryFolder folder in folders) + { + if (folder.folderName == InventoryFolderBase.ROOT_FOLDER_NAME) + { + root = folder; + break; + } + } + + if (root == null) // oops + root = folders[0]; + + return ConvertToOpenSim(root); + } + + public virtual InventoryFolderBase GetFolderForType(UUID principalID, FolderType type) + { +// m_log.DebugFormat("[XINVENTORY SERVICE]: Getting folder type {0} for user {1}", type, principalID); + + InventoryFolderBase rootFolder = GetRootFolder(principalID); + + if (rootFolder == null) + { + m_log.WarnFormat( + "[XINVENTORY]: Found no root folder for {0} in GetFolderForType() when looking for {1}", + principalID, type); + + return null; + } + + return GetSystemFolderForType(rootFolder, type); + } + + private InventoryFolderBase GetSystemFolderForType(InventoryFolderBase rootFolder, FolderType type) + { + //m_log.DebugFormat("[XINVENTORY SERVICE]: Getting folder type {0}", type); + + if (type == FolderType.Root) + return rootFolder; + + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "agentID", "parentFolderID", "type"}, + new string[] { rootFolder.Owner.ToString(), rootFolder.ID.ToString(), ((int)type).ToString() }); + + if (folders.Length == 0) + { + //m_log.WarnFormat("[XINVENTORY SERVICE]: Found no folder for type {0} ", type); + return null; + } + + //m_log.DebugFormat( + // "[XINVENTORY SERVICE]: Found folder {0} {1} for type {2}", + // folders[0].folderName, folders[0].folderID, type); + + return ConvertToOpenSim(folders[0]); + } + + public virtual InventoryCollection GetFolderContent(UUID principalID, UUID folderID) + { + // This method doesn't receive a valud principal id from the + // connector. So we disregard the principal and look + // by ID. + // + //m_log.DebugFormat("[XINVENTORY SERVICE]: Fetch contents for folder {0}", folderID.ToString()); + InventoryCollection inventory = new InventoryCollection(); + inventory.OwnerID = principalID; + inventory.Folders = new List(); + inventory.Items = new List(); + + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "parentFolderID"}, + new string[] { folderID.ToString() }); + + foreach (XInventoryFolder x in folders) + { + //m_log.DebugFormat("[XINVENTORY]: Adding folder {0} to response", x.folderName); + inventory.Folders.Add(ConvertToOpenSim(x)); + } + + XInventoryItem[] items = m_Database.GetItems( + new string[] { "parentFolderID"}, + new string[] { folderID.ToString() }); + + foreach (XInventoryItem i in items) + { + //m_log.DebugFormat("[XINVENTORY]: Adding item {0} to response", i.inventoryName); + inventory.Items.Add(ConvertToOpenSim(i)); + } + + InventoryFolderBase f = new InventoryFolderBase(folderID, principalID); + f = GetFolder(f); + if (f != null) + { + inventory.Version = f.Version; + inventory.OwnerID = f.Owner; + } + inventory.FolderID = folderID; + + return inventory; + } + + public virtual InventoryCollection[] GetMultipleFoldersContent(UUID principalID, UUID[] folderIDs) + { + InventoryCollection[] multiple = new InventoryCollection[folderIDs.Length]; + int i = 0; + foreach (UUID fid in folderIDs) + multiple[i++] = GetFolderContent(principalID, fid); + + return multiple; + } + + public virtual List GetFolderItems(UUID principalID, UUID folderID) + { +// m_log.DebugFormat("[XINVENTORY]: Fetch items for folder {0}", folderID); + + // Since we probably don't get a valid principal here, either ... + // + List invItems = new List(); + + XInventoryItem[] items = m_Database.GetItems( + new string[] { "parentFolderID" }, + new string[] { folderID.ToString() }); + + foreach (XInventoryItem i in items) + invItems.Add(ConvertToOpenSim(i)); + + return invItems; + } + + public virtual bool AddFolder(InventoryFolderBase folder) + { +// m_log.DebugFormat("[XINVENTORY]: Add folder {0} type {1} in parent {2}", folder.Name, folder.Type, folder.ParentID); + + InventoryFolderBase check = GetFolder(folder); + if (check != null) + return false; + + if (folder.Type != (short)FolderType.None) + { + InventoryFolderBase rootFolder = GetRootFolder(folder.Owner); + + if (rootFolder == null) + { + m_log.WarnFormat( + "[XINVENTORY]: Found no root folder for {0} in AddFolder() when looking for {1}", + folder.Owner, folder.Type); + + return false; + } + + // Check we're not trying to add this as a system folder. + if (folder.ParentID == rootFolder.ID) + { + InventoryFolderBase existingSystemFolder + = GetSystemFolderForType(rootFolder, (FolderType)folder.Type); + + if (existingSystemFolder != null) + { + m_log.WarnFormat( + "[XINVENTORY]: System folder of type {0} already exists when tried to add {1} to {2} for {3}", + folder.Type, folder.Name, folder.ParentID, folder.Owner); + + return false; + } + } + } + + XInventoryFolder xFolder = ConvertFromOpenSim(folder); + return m_Database.StoreFolder(xFolder); + } + + public virtual bool UpdateFolder(InventoryFolderBase folder) + { +// m_log.DebugFormat("[XINVENTORY]: Update folder {0} {1} ({2})", folder.Name, folder.Type, folder.ID); + + XInventoryFolder xFolder = ConvertFromOpenSim(folder); + InventoryFolderBase check = GetFolder(folder); + + if (check == null) + return AddFolder(folder); + + if ((check.Type != (short)FolderType.None || xFolder.type != (short)FolderType.None) + && (check.Type != (short)FolderType.Outfit || xFolder.type != (short)FolderType.Outfit)) + { + if (xFolder.version < check.Version) + { +// m_log.DebugFormat("[XINVENTORY]: {0} < {1} can't do", xFolder.version, check.Version); + return false; + } + + check.Version = (ushort)xFolder.version; + xFolder = ConvertFromOpenSim(check); + +// m_log.DebugFormat( +// "[XINVENTORY]: Storing version only update to system folder {0} {1} {2}", +// xFolder.folderName, xFolder.version, xFolder.type); + + return m_Database.StoreFolder(xFolder); + } + + if (xFolder.version < check.Version) + xFolder.version = check.Version; + + xFolder.folderID = check.ID; + + return m_Database.StoreFolder(xFolder); + } + + public virtual bool MoveFolder(InventoryFolderBase folder) + { + return m_Database.MoveFolder(folder.ID.ToString(), folder.ParentID.ToString()); + } + + // We don't check the principal's ID here + // + public virtual bool DeleteFolders(UUID principalID, List folderIDs) + { + return DeleteFolders(principalID, folderIDs, true); + } + + public virtual bool DeleteFolders(UUID principalID, List folderIDs, bool onlyIfTrash) + { + if (!m_AllowDelete) + return false; + + // Ignore principal ID, it's bogus at connector level + // + foreach (UUID id in folderIDs) + { + if (onlyIfTrash && !ParentIsTrash(id)) + continue; + //m_log.InfoFormat("[XINVENTORY SERVICE]: Delete folder {0}", id); + InventoryFolderBase f = new InventoryFolderBase(); + f.ID = id; + PurgeFolder(f, onlyIfTrash); + m_Database.DeleteFolders("folderID", id.ToString()); + } + + return true; + } + + public virtual bool PurgeFolder(InventoryFolderBase folder) + { + return PurgeFolder(folder, true); + } + + public virtual bool PurgeFolder(InventoryFolderBase folder, bool onlyIfTrash) + { + if (!m_AllowDelete) + return false; + + if (onlyIfTrash && !ParentIsTrash(folder.ID)) + return false; + + XInventoryFolder[] subFolders = m_Database.GetFolders( + new string[] { "parentFolderID" }, + new string[] { folder.ID.ToString() }); + + foreach (XInventoryFolder x in subFolders) + { + PurgeFolder(ConvertToOpenSim(x), onlyIfTrash); + m_Database.DeleteFolders("folderID", x.folderID.ToString()); + } + + m_Database.DeleteItems("parentFolderID", folder.ID.ToString()); + + return true; + } + + public virtual bool AddItem(InventoryItemBase item) + { +// m_log.DebugFormat( +// "[XINVENTORY SERVICE]: Adding item {0} {1} to folder {2} for {3}", item.Name, item.ID, item.Folder, item.Owner); + + return m_Database.StoreItem(ConvertFromOpenSim(item)); + } + + public virtual bool UpdateItem(InventoryItemBase item) + { + if (!m_AllowDelete) + if (item.AssetType == (sbyte)AssetType.Link || item.AssetType == (sbyte)AssetType.LinkFolder) + return false; + +// m_log.InfoFormat( +// "[XINVENTORY SERVICE]: Updating item {0} {1} in folder {2}", item.Name, item.ID, item.Folder); + + InventoryItemBase retrievedItem = GetItem(item); + + if (retrievedItem == null) + { + m_log.WarnFormat( + "[XINVENTORY SERVICE]: Tried to update item {0} {1}, owner {2} but no existing item found.", + item.Name, item.ID, item.Owner); + + return false; + } + + // Do not allow invariants to change. Changes to folder ID occur in MoveItems() + if (retrievedItem.InvType != item.InvType + || retrievedItem.AssetType != item.AssetType + || retrievedItem.Folder != item.Folder + || retrievedItem.CreatorIdentification != item.CreatorIdentification + || retrievedItem.Owner != item.Owner) + { + m_log.WarnFormat( + "[XINVENTORY SERVICE]: Caller to UpdateItem() for {0} {1} tried to alter property(s) that should be invariant, (InvType, AssetType, Folder, CreatorIdentification, Owner), existing ({2}, {3}, {4}, {5}, {6}), update ({7}, {8}, {9}, {10}, {11})", + retrievedItem.Name, + retrievedItem.ID, + retrievedItem.InvType, + retrievedItem.AssetType, + retrievedItem.Folder, + retrievedItem.CreatorIdentification, + retrievedItem.Owner, + item.InvType, + item.AssetType, + item.Folder, + item.CreatorIdentification, + item.Owner); + + item.InvType = retrievedItem.InvType; + item.AssetType = retrievedItem.AssetType; + item.Folder = retrievedItem.Folder; + item.CreatorIdentification = retrievedItem.CreatorIdentification; + item.Owner = retrievedItem.Owner; + } + + return m_Database.StoreItem(ConvertFromOpenSim(item)); + } + + public virtual bool MoveItems(UUID principalID, List items) + { + // Principal is b0rked. *sigh* + // + foreach (InventoryItemBase i in items) + { + m_Database.MoveItem(i.ID.ToString(), i.Folder.ToString()); + } + + return true; + } + + public virtual bool DeleteItems(UUID principalID, List itemIDs) + { + if (!m_AllowDelete) + { + // We must still allow links and links to folders to be deleted, otherwise they will build up + // in the player's inventory until they can no longer log in. Deletions of links due to code bugs or + // similar is inconvenient but on a par with accidental movement of items. The original item is never + // touched. + foreach (UUID id in itemIDs) + { + if (!m_Database.DeleteItems( + new string[] { "inventoryID", "assetType" }, + new string[] { id.ToString(), ((sbyte)AssetType.Link).ToString() })) + { + m_Database.DeleteItems( + new string[] { "inventoryID", "assetType" }, + new string[] { id.ToString(), ((sbyte)AssetType.LinkFolder).ToString() }); + } + } + } + else + { + // Just use the ID... *facepalms* + // + foreach (UUID id in itemIDs) + m_Database.DeleteItems("inventoryID", id.ToString()); + } + + return true; + } + + public virtual InventoryItemBase GetItem(InventoryItemBase item) + { + XInventoryItem[] items = m_Database.GetItems( + new string[] { "inventoryID" }, + new string[] { item.ID.ToString() }); + + if (items.Length == 0) + return null; + + return ConvertToOpenSim(items[0]); + } + + public virtual InventoryItemBase[] GetMultipleItems(UUID userID, UUID[] ids) + { + InventoryItemBase[] items = new InventoryItemBase[ids.Length]; + int i = 0; + InventoryItemBase item = new InventoryItemBase(); + item.Owner = userID; + foreach (UUID id in ids) + { + item.ID = id; + items[i++] = GetItem(item); + } + + return items; + } + + public virtual InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + XInventoryFolder[] folders = m_Database.GetFolders( + new string[] { "folderID"}, + new string[] { folder.ID.ToString() }); + + if (folders.Length == 0) + return null; + + return ConvertToOpenSim(folders[0]); + } + + public virtual List GetActiveGestures(UUID principalID) + { + XInventoryItem[] items = m_Database.GetActiveGestures(principalID); + + if (items.Length == 0) + return new List(); + + List ret = new List(); + + foreach (XInventoryItem x in items) + ret.Add(ConvertToOpenSim(x)); + + return ret; + } + + public virtual int GetAssetPermissions(UUID principalID, UUID assetID) + { + return m_Database.GetAssetPermissions(principalID, assetID); + } + + // Unused. + // + public bool HasInventoryForUser(UUID userID) + { + return false; + } + + // CM Helpers + // + protected InventoryFolderBase ConvertToOpenSim(XInventoryFolder folder) + { + InventoryFolderBase newFolder = new InventoryFolderBase(); + + newFolder.ParentID = folder.parentFolderID; + newFolder.Type = (short)folder.type; + //// Viewer can't understand anything that's not in it's LLFolderType enum + //if (newFolder.Type == InventoryItemBase.SUITCASE_FOLDER_TYPE) + // newFolder.Type = InventoryItemBase.SUITCASE_FOLDER_FAKE_TYPE; + newFolder.Version = (ushort)folder.version; + newFolder.Name = folder.folderName; + newFolder.Owner = folder.agentID; + newFolder.ID = folder.folderID; + + return newFolder; + } + + protected XInventoryFolder ConvertFromOpenSim(InventoryFolderBase folder) + { + XInventoryFolder newFolder = new XInventoryFolder(); + + newFolder.parentFolderID = folder.ParentID; + newFolder.type = (int)folder.Type; + newFolder.version = (int)folder.Version; + newFolder.folderName = folder.Name; + newFolder.agentID = folder.Owner; + newFolder.folderID = folder.ID; + + return newFolder; + } + + protected InventoryItemBase ConvertToOpenSim(XInventoryItem item) + { + InventoryItemBase newItem = new InventoryItemBase(); + + newItem.AssetID = item.assetID; + newItem.AssetType = item.assetType; + newItem.Name = item.inventoryName; + newItem.Owner = item.avatarID; + newItem.ID = item.inventoryID; + newItem.InvType = item.invType; + newItem.Folder = item.parentFolderID; + newItem.CreatorIdentification = item.creatorID; + newItem.Description = item.inventoryDescription; + newItem.NextPermissions = (uint)item.inventoryNextPermissions; + newItem.CurrentPermissions = (uint)item.inventoryCurrentPermissions; + newItem.BasePermissions = (uint)item.inventoryBasePermissions; + newItem.EveryOnePermissions = (uint)item.inventoryEveryOnePermissions; + newItem.GroupPermissions = (uint)item.inventoryGroupPermissions; + newItem.GroupID = item.groupID; + if (item.groupOwned == 0) + newItem.GroupOwned = false; + else + newItem.GroupOwned = true; + newItem.SalePrice = item.salePrice; + newItem.SaleType = (byte)item.saleType; + newItem.Flags = (uint)item.flags; + newItem.CreationDate = item.creationDate; + + return newItem; + } + + protected XInventoryItem ConvertFromOpenSim(InventoryItemBase item) + { + XInventoryItem newItem = new XInventoryItem(); + + newItem.assetID = item.AssetID; + newItem.assetType = item.AssetType; + newItem.inventoryName = item.Name; + newItem.avatarID = item.Owner; + newItem.inventoryID = item.ID; + newItem.invType = item.InvType; + newItem.parentFolderID = item.Folder; + newItem.creatorID = item.CreatorIdentification; + newItem.inventoryDescription = item.Description; + newItem.inventoryNextPermissions = (int)item.NextPermissions; + newItem.inventoryCurrentPermissions = (int)item.CurrentPermissions; + newItem.inventoryBasePermissions = (int)item.BasePermissions; + newItem.inventoryEveryOnePermissions = (int)item.EveryOnePermissions; + newItem.inventoryGroupPermissions = (int)item.GroupPermissions; + newItem.groupID = item.GroupID; + if (item.GroupOwned) + newItem.groupOwned = 1; + else + newItem.groupOwned = 0; + newItem.salePrice = item.SalePrice; + newItem.saleType = (int)item.SaleType; + newItem.flags = (int)item.Flags; + newItem.creationDate = item.CreationDate; + + return newItem; + } + + private bool ParentIsTrash(UUID folderID) + { + XInventoryFolder[] folder = m_Database.GetFolders(new string[] {"folderID"}, new string[] {folderID.ToString()}); + if (folder.Length < 1) + return false; + + if (folder[0].type == (int)FolderType.Trash) + return true; + + UUID parentFolder = folder[0].parentFolderID; + + while (parentFolder != UUID.Zero) + { + XInventoryFolder[] parent = m_Database.GetFolders(new string[] {"folderID"}, new string[] {parentFolder.ToString()}); + if (parent.Length < 1) + return false; + + if (parent[0].type == (int)FolderType.Trash) + return true; + if (parent[0].type == (int)FolderType.Root) + return false; + + parentFolder = parent[0].parentFolderID; + } + return false; + } + } +} diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs new file mode 100644 index 0000000000..c3756d0db1 --- /dev/null +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -0,0 +1,1186 @@ +/* + * 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 OpenSim.Framework; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OSDArray = OpenMetaverse.StructuredData.OSDArray; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; + +namespace OpenSim.Services.LLLoginService +{ + public class LLFailedLoginResponse : OpenSim.Services.Interfaces.FailedLoginResponse + { + protected string m_key; + protected string m_value; + protected string m_login; + + public static LLFailedLoginResponse UserProblem; + public static LLFailedLoginResponse GridProblem; + public static LLFailedLoginResponse InventoryProblem; + public static LLFailedLoginResponse DeadRegionProblem; + public static LLFailedLoginResponse LoginBlockedProblem; + public static LLFailedLoginResponse AlreadyLoggedInProblem; + public static LLFailedLoginResponse InternalError; + + static LLFailedLoginResponse() + { + UserProblem = new LLFailedLoginResponse("key", + "Could not authenticate your avatar. Please check your username and password, and check the grid if problems persist.", + "false"); + GridProblem = new LLFailedLoginResponse("key", + "Error connecting to the desired location. Try connecting to another region.", + "false"); + InventoryProblem = new LLFailedLoginResponse("key", + "The inventory service is not responding. Please notify your login region operator.", + "false"); + DeadRegionProblem = new LLFailedLoginResponse("key", + "The region you are attempting to log into is not responding. Please select another region and try again.", + "false"); + LoginBlockedProblem = new LLFailedLoginResponse("presence", + "Logins are currently restricted. Please try again later.", + "false"); + AlreadyLoggedInProblem = new LLFailedLoginResponse("presence", + "You appear to be already logged in. " + + "If this is not the case please wait for your session to timeout. " + + "If this takes longer than a few minutes please contact the grid owner. " + + "Please wait 5 minutes if you are going to connect to a region nearby to the region you were at previously.", + "false"); + InternalError = new LLFailedLoginResponse("Internal Error", "Error generating Login Response", "false"); + } + + public LLFailedLoginResponse(string key, string value, string login) + { + m_key = key; + m_value = value; + m_login = login; + } + + public override Hashtable ToHashtable() + { + Hashtable loginError = new Hashtable(); + loginError["reason"] = m_key; + loginError["message"] = m_value; + loginError["login"] = m_login; + return loginError; + } + + public override OSD ToOSDMap() + { + OSDMap map = new OSDMap(); + + map["reason"] = OSD.FromString(m_key); + map["message"] = OSD.FromString(m_value); + map["login"] = OSD.FromString(m_login); + + return map; + } + } + + /// + /// A class to handle LL login response. + /// + public class LLLoginResponse : OpenSim.Services.Interfaces.LoginResponse + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Hashtable globalTexturesHash; + // Global Textures + private static string sunTexture = "cce0f112-878f-4586-a2e2-a8f104bba271"; + private static string cloudTexture = "dc4b9f0b-d008-45c6-96a4-01dd947ac621"; + private static string moonTexture = "ec4b9f0b-d008-45c6-96a4-01dd947ac621"; + + private Hashtable loginFlagsHash; + private Hashtable uiConfigHash; + + private ArrayList loginFlags; + private ArrayList globalTextures; + private ArrayList eventCategories; + private ArrayList uiConfig; + private ArrayList classifiedCategories; + private ArrayList inventoryRoot; + private ArrayList initialOutfit; + private ArrayList agentInventory; + private ArrayList inventoryLibraryOwner; + private ArrayList inventoryLibRoot; + private ArrayList inventoryLibrary; + private ArrayList activeGestures; + + private UserInfo userProfile; + + private UUID agentID; + private UUID sessionID; + private UUID secureSessionID; + + // Login Flags + private string dst; + private string stipendSinceLogin; + private string gendered; + private string everLoggedIn; + private string login; + private uint simPort; + private uint simHttpPort; + private string simAddress; + private string agentAccess; + private string agentAccessMax; + private Int32 circuitCode; + private uint regionX; + private uint regionY; + + // Login + private string firstname; + private string lastname; + + // Web map + private string mapTileURL; + + // Web Profiles + private string profileURL; + + // OpenID + private string openIDURL; + + private string searchURL; + + // Error Flags + private string errorReason; + private string errorMessage; + + private string welcomeMessage; + private string startLocation; + private string allowFirstLife; + private string home; + private string seedCapability; + private string lookAt; + + private BuddyList m_buddyList = null; + + private string currency; + private string classifiedFee; + private int maxAgentGroups; + + static LLLoginResponse() + { + // This is being set, but it's not used + // not sure why. + globalTexturesHash = new Hashtable(); + globalTexturesHash["sun_texture_id"] = sunTexture; + globalTexturesHash["cloud_texture_id"] = cloudTexture; + globalTexturesHash["moon_texture_id"] = moonTexture; + } + + public LLLoginResponse() + { + loginFlags = new ArrayList(); + globalTextures = new ArrayList(); + eventCategories = new ArrayList(); + uiConfig = new ArrayList(); + classifiedCategories = new ArrayList(); + + uiConfigHash = new Hashtable(); + + // defaultXmlRpcResponse = new XmlRpcResponse(); + userProfile = new UserInfo(); + inventoryRoot = new ArrayList(); + initialOutfit = new ArrayList(); + agentInventory = new ArrayList(); + inventoryLibrary = new ArrayList(); + inventoryLibraryOwner = new ArrayList(); + activeGestures = new ArrayList(); + + SetDefaultValues(); + } + + public LLLoginResponse(UserAccount account, AgentCircuitData aCircuit, GridUserInfo pinfo, + GridRegion destination, List invSkel, FriendInfo[] friendsList, ILibraryService libService, + string where, string startlocation, Vector3 position, Vector3 lookAt, List gestures, string message, + GridRegion home, IPEndPoint clientIP, string mapTileURL, string searchURL, string currency, + string DSTZone, string destinationsURL, string avatarsURL, string classifiedFee, int maxAgentGroups) + : this() + { + FillOutInventoryData(invSkel, libService); + + FillOutActiveGestures(gestures); + + CircuitCode = (int)aCircuit.circuitcode; + Lastname = account.LastName; + Firstname = account.FirstName; + AgentID = account.PrincipalID; + SessionID = aCircuit.SessionID; + SecureSessionID = aCircuit.SecureSessionID; + Message = message; + BuddList = ConvertFriendListItem(friendsList); + StartLocation = where; + MapTileURL = mapTileURL; + ProfileURL = profileURL; + OpenIDURL = openIDURL; + DestinationsURL = destinationsURL; + AvatarsURL = avatarsURL; + + SearchURL = searchURL; + Currency = currency; + ClassifiedFee = classifiedFee; + MaxAgentGroups = maxAgentGroups; + + FillOutHomeData(pinfo, home); + LookAt = String.Format("[r{0},r{1},r{2}]", lookAt.X, lookAt.Y, lookAt.Z); + + FillOutRegionData(destination); + m_log.DebugFormat("[LOGIN RESPONSE] LLLoginResponse create. sizeX={0}, sizeY={1}", RegionSizeX, RegionSizeY); + + FillOutSeedCap(aCircuit, destination, clientIP); + + switch (DSTZone) + { + case "none": + DST = "N"; + break; + case "local": + DST = TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now) ? "Y" : "N"; + break; + default: + TimeZoneInfo dstTimeZone = null; + string[] tzList = DSTZone.Split(';'); + + foreach (string tzName in tzList) + { + try + { + dstTimeZone = TimeZoneInfo.FindSystemTimeZoneById(tzName); + } + catch + { + continue; + } + break; + } + + if (dstTimeZone == null) + { + m_log.WarnFormat( + "[LLOGIN RESPONSE]: No valid timezone found for DST in {0}, falling back to system time.", tzList); + DST = TimeZone.CurrentTimeZone.IsDaylightSavingTime(DateTime.Now) ? "Y" : "N"; + } + else + { + DST = dstTimeZone.IsDaylightSavingTime(DateTime.Now) ? "Y" : "N"; + } + + break; + } + } + + private void FillOutInventoryData(List invSkel, ILibraryService libService) + { + InventoryData inventData = null; + + try + { + inventData = GetInventorySkeleton(invSkel); + } + catch (Exception e) + { + m_log.WarnFormat( + "[LLLOGIN SERVICE]: Error processing inventory skeleton of agent {0} - {1}", + agentID, e); + + // ignore and continue + } + + if (inventData != null) + { + ArrayList AgentInventoryArray = inventData.InventoryArray; + + Hashtable InventoryRootHash = new Hashtable(); + InventoryRootHash["folder_id"] = inventData.RootFolderID.ToString(); + InventoryRoot = new ArrayList(); + InventoryRoot.Add(InventoryRootHash); + InventorySkeleton = AgentInventoryArray; + } + + // Inventory Library Section + if (libService != null && libService.LibraryRootFolder != null) + { + Hashtable InventoryLibRootHash = new Hashtable(); + InventoryLibRootHash["folder_id"] = "00000112-000f-0000-0000-000100bba000"; + InventoryLibRoot = new ArrayList(); + InventoryLibRoot.Add(InventoryLibRootHash); + + InventoryLibraryOwner = GetLibraryOwner(libService.LibraryRootFolder); + InventoryLibrary = GetInventoryLibrary(libService); + } + } + + private void FillOutActiveGestures(List gestures) + { + ArrayList list = new ArrayList(); + if (gestures != null) + { + foreach (InventoryItemBase gesture in gestures) + { + Hashtable item = new Hashtable(); + item["item_id"] = gesture.ID.ToString(); + item["asset_id"] = gesture.AssetID.ToString(); + list.Add(item); + } + } + ActiveGestures = list; + } + + private void FillOutHomeData(GridUserInfo pinfo, GridRegion home) + { + int x = (int)Util.RegionToWorldLoc(1000); + int y = (int)Util.RegionToWorldLoc(1000); + if (home != null) + { + x = home.RegionLocX; + y = home.RegionLocY; + } + + Home = string.Format( + "{{'region_handle':[r{0},r{1}], 'position':[r{2},r{3},r{4}], 'look_at':[r{5},r{6},r{7}]}}", + x, + y, + pinfo.HomePosition.X, pinfo.HomePosition.Y, pinfo.HomePosition.Z, + pinfo.HomeLookAt.X, pinfo.HomeLookAt.Y, pinfo.HomeLookAt.Z); + + } + + private void FillOutRegionData(GridRegion destination) + { + IPEndPoint endPoint = destination.ExternalEndPoint; + SimAddress = endPoint.Address.ToString(); + SimPort = (uint)endPoint.Port; + RegionX = (uint)destination.RegionLocX; + RegionY = (uint)destination.RegionLocY; + RegionSizeX = destination.RegionSizeX; + RegionSizeY = destination.RegionSizeY; + } + + private void FillOutSeedCap(AgentCircuitData aCircuit, GridRegion destination, IPEndPoint ipepClient) + { + SeedCapability = destination.ServerURI + CapsUtil.GetCapsSeedPath(aCircuit.CapsPath); + } + + private void SetDefaultValues() + { + TimeZoneInfo gridTimeZone; + + // Disabled for now pending making timezone a config value, which can at some point have a default of + // a ; separated list of possible timezones. + // The problem here is that US/Pacific (or even the Olsen America/Los_Angeles) is not universal across + // windows, mac and various distributions of linux, introducing another element of consistency. + // The server operator needs to be able to control this setting +// try +// { +// // First try to fetch DST from Pacific Standard Time, because this is +// // the one expected by the viewer. "US/Pacific" is the string to search +// // on linux and mac, and should work also on Windows (to confirm) +// gridTimeZone = TimeZoneInfo.FindSystemTimeZoneById("US/Pacific"); +// } +// catch (Exception e) +// { +// m_log.WarnFormat( +// "[TIMEZONE]: {0} Falling back to system time. System time should be set to Pacific Standard Time to provide the expected time", +// e.Message); + + gridTimeZone = TimeZoneInfo.Local; +// } + + DST = gridTimeZone.IsDaylightSavingTime(DateTime.Now) ? "Y" : "N"; + + StipendSinceLogin = "N"; + Gendered = "Y"; + EverLoggedIn = "Y"; + login = "false"; + firstname = "Test"; + lastname = "User"; + agentAccess = "M"; + agentAccessMax = "A"; + startLocation = "last"; + allowFirstLife = "Y"; + + ErrorMessage = "You have entered an invalid name/password combination. Check Caps/lock."; + ErrorReason = "key"; + welcomeMessage = "Welcome to OpenSim!"; + seedCapability = String.Empty; + home = "{'region_handle':[" + + "r" + Util.RegionToWorldLoc(1000).ToString() + + "," + + "r" + Util.RegionToWorldLoc(1000).ToString() + + "], 'position':[" + + "r" + userProfile.homepos.X.ToString() + + "," + + "r" + userProfile.homepos.Y.ToString() + + "," + + "r" + userProfile.homepos.Z.ToString() + + "], 'look_at':[" + + "r" + userProfile.homelookat.X.ToString() + + "," + + "r" + userProfile.homelookat.Y.ToString() + + "," + + "r" + userProfile.homelookat.Z.ToString() + + "]}"; + lookAt = "[r0.99949799999999999756,r0.03166859999999999814,r0]"; + RegionX = (uint) 255232; + RegionY = (uint) 254976; + + // Classifieds; + AddClassifiedCategory((Int32) 1, "Shopping"); + AddClassifiedCategory((Int32) 2, "Land Rental"); + AddClassifiedCategory((Int32) 3, "Property Rental"); + AddClassifiedCategory((Int32) 4, "Special Attraction"); + AddClassifiedCategory((Int32) 5, "New Products"); + AddClassifiedCategory((Int32) 6, "Employment"); + AddClassifiedCategory((Int32) 7, "Wanted"); + AddClassifiedCategory((Int32) 8, "Service"); + AddClassifiedCategory((Int32) 9, "Personal"); + + SessionID = UUID.Random(); + SecureSessionID = UUID.Random(); + AgentID = UUID.Random(); + + Hashtable InitialOutfitHash = new Hashtable(); + InitialOutfitHash["folder_name"] = "Nightclub Female"; + InitialOutfitHash["gender"] = "female"; + initialOutfit.Add(InitialOutfitHash); + mapTileURL = String.Empty; + profileURL = String.Empty; + openIDURL = String.Empty; + searchURL = String.Empty; + + currency = String.Empty; + ClassifiedFee = "0"; + MaxAgentGroups = 42; + } + + + public override Hashtable ToHashtable() + { + try + { + Hashtable responseData = new Hashtable(); + + loginFlagsHash = new Hashtable(); + loginFlagsHash["daylight_savings"] = DST; + loginFlagsHash["stipend_since_login"] = StipendSinceLogin; + loginFlagsHash["gendered"] = Gendered; + loginFlagsHash["ever_logged_in"] = EverLoggedIn; + loginFlags.Add(loginFlagsHash); + + responseData["first_name"] = Firstname; + responseData["last_name"] = Lastname; + responseData["agent_access"] = agentAccess; + responseData["agent_access_max"] = agentAccessMax; + + globalTextures.Add(globalTexturesHash); + // this.eventCategories.Add(this.eventCategoriesHash); + + AddToUIConfig("allow_first_life", allowFirstLife); + uiConfig.Add(uiConfigHash); + + responseData["sim_port"] = (Int32) SimPort; + responseData["sim_ip"] = SimAddress; + responseData["http_port"] = (Int32)SimHttpPort; + + responseData["agent_id"] = AgentID.ToString(); + responseData["session_id"] = SessionID.ToString(); + responseData["secure_session_id"] = SecureSessionID.ToString(); + responseData["circuit_code"] = CircuitCode; + responseData["seconds_since_epoch"] = (Int32) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; + responseData["login-flags"] = loginFlags; + responseData["global-textures"] = globalTextures; + responseData["seed_capability"] = seedCapability; + + responseData["event_categories"] = eventCategories; + responseData["event_notifications"] = new ArrayList(); // todo + responseData["classified_categories"] = classifiedCategories; + responseData["ui-config"] = uiConfig; + + if (agentInventory != null) + { + responseData["inventory-skeleton"] = agentInventory; + responseData["inventory-root"] = inventoryRoot; + } + responseData["inventory-skel-lib"] = inventoryLibrary; + responseData["inventory-lib-root"] = inventoryLibRoot; + responseData["gestures"] = activeGestures; + responseData["inventory-lib-owner"] = inventoryLibraryOwner; + responseData["initial-outfit"] = initialOutfit; + responseData["start_location"] = startLocation; + responseData["seed_capability"] = seedCapability; + responseData["home"] = home; + responseData["look_at"] = lookAt; + responseData["max-agent-groups"] = MaxAgentGroups; + responseData["message"] = welcomeMessage; + responseData["region_x"] = (Int32)(RegionX); + responseData["region_y"] = (Int32)(RegionY); + responseData["region_size_x"] = (Int32)RegionSizeX; + responseData["region_size_y"] = (Int32)RegionSizeY; + m_log.DebugFormat("[LOGIN RESPONSE] returning sizeX={0}, sizeY={1}", RegionSizeX, RegionSizeY); + + if (searchURL != String.Empty) + responseData["search"] = searchURL; + + if (mapTileURL != String.Empty) + responseData["map-server-url"] = mapTileURL; + + if (profileURL != String.Empty) + responseData["profile-server-url"] = profileURL; + + if (DestinationsURL != String.Empty) + responseData["destination_guide_url"] = DestinationsURL; + + if (AvatarsURL != String.Empty) + responseData["avatar_picker_url"] = AvatarsURL; + + // We need to send an openid_token back in the response too + if (openIDURL != String.Empty) + responseData["openid_url"] = openIDURL; + + if (m_buddyList != null) + { + responseData["buddy-list"] = m_buddyList.ToArray(); + } + + if (currency != String.Empty) + { + // responseData["real_currency"] = currency; + responseData["currency"] = currency; + } + + if (ClassifiedFee != String.Empty) + responseData["classified_fee"] = ClassifiedFee; + + responseData["login"] = "true"; + + return responseData; + } + catch (Exception e) + { + m_log.Warn("[CLIENT]: LoginResponse: Error creating Hashtable Response: " + e.Message); + + return LLFailedLoginResponse.InternalError.ToHashtable(); + } + } + + public override OSD ToOSDMap() + { + try + { + OSDMap map = new OSDMap(); + + map["first_name"] = OSD.FromString(Firstname); + map["last_name"] = OSD.FromString(Lastname); + map["agent_access"] = OSD.FromString(agentAccess); + map["agent_access_max"] = OSD.FromString(agentAccessMax); + + map["sim_port"] = OSD.FromInteger(SimPort); + map["sim_ip"] = OSD.FromString(SimAddress); + + map["agent_id"] = OSD.FromUUID(AgentID); + map["session_id"] = OSD.FromUUID(SessionID); + map["secure_session_id"] = OSD.FromUUID(SecureSessionID); + map["circuit_code"] = OSD.FromInteger(CircuitCode); + map["seconds_since_epoch"] = OSD.FromInteger((int)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds); + + #region Login Flags + + OSDMap loginFlagsLLSD = new OSDMap(); + loginFlagsLLSD["daylight_savings"] = OSD.FromString(DST); + loginFlagsLLSD["stipend_since_login"] = OSD.FromString(StipendSinceLogin); + loginFlagsLLSD["gendered"] = OSD.FromString(Gendered); + loginFlagsLLSD["ever_logged_in"] = OSD.FromString(EverLoggedIn); + map["login-flags"] = WrapOSDMap(loginFlagsLLSD); + + #endregion Login Flags + + #region Global Textures + + OSDMap globalTexturesLLSD = new OSDMap(); + globalTexturesLLSD["sun_texture_id"] = OSD.FromString(SunTexture); + globalTexturesLLSD["cloud_texture_id"] = OSD.FromString(CloudTexture); + globalTexturesLLSD["moon_texture_id"] = OSD.FromString(MoonTexture); + + map["global-textures"] = WrapOSDMap(globalTexturesLLSD); + + #endregion Global Textures + + map["seed_capability"] = OSD.FromString(seedCapability); + + map["event_categories"] = ArrayListToOSDArray(eventCategories); + //map["event_notifications"] = new OSDArray(); // todo + map["classified_categories"] = ArrayListToOSDArray(classifiedCategories); + + #region UI Config + + OSDMap uiConfigLLSD = new OSDMap(); + uiConfigLLSD["allow_first_life"] = OSD.FromString(allowFirstLife); + map["ui-config"] = WrapOSDMap(uiConfigLLSD); + + #endregion UI Config + + #region Inventory + + map["inventory-skeleton"] = ArrayListToOSDArray(agentInventory); + + map["inventory-skel-lib"] = ArrayListToOSDArray(inventoryLibrary); + map["inventory-root"] = ArrayListToOSDArray(inventoryRoot); ; + map["inventory-lib-root"] = ArrayListToOSDArray(inventoryLibRoot); + map["inventory-lib-owner"] = ArrayListToOSDArray(inventoryLibraryOwner); + + #endregion Inventory + + map["gestures"] = ArrayListToOSDArray(activeGestures); + + map["initial-outfit"] = ArrayListToOSDArray(initialOutfit); + map["start_location"] = OSD.FromString(startLocation); + + map["seed_capability"] = OSD.FromString(seedCapability); + map["home"] = OSD.FromString(home); + map["look_at"] = OSD.FromString(lookAt); + map["max-agent-groups"] = OSD.FromInteger(MaxAgentGroups); + map["message"] = OSD.FromString(welcomeMessage); + map["region_x"] = OSD.FromInteger(RegionX); + map["region_y"] = OSD.FromInteger(RegionY); + + if (mapTileURL != String.Empty) + map["map-server-url"] = OSD.FromString(mapTileURL); + + if (profileURL != String.Empty) + map["profile-server-url"] = OSD.FromString(profileURL); + + if (openIDURL != String.Empty) + map["openid_url"] = OSD.FromString(openIDURL); + + if (searchURL != String.Empty) + map["search"] = OSD.FromString(searchURL); + + if (ClassifiedFee != String.Empty) + map["classified_fee"] = OSD.FromString(ClassifiedFee); + + if (m_buddyList != null) + { + map["buddy-list"] = ArrayListToOSDArray(m_buddyList.ToArray()); + } + + map["login"] = OSD.FromString("true"); + + return map; + } + catch (Exception e) + { + m_log.Warn("[CLIENT]: LoginResponse: Error creating LLSD Response: " + e.Message); + + return LLFailedLoginResponse.InternalError.ToOSDMap(); + } + } + + public OSDArray ArrayListToOSDArray(ArrayList arrlst) + { + OSDArray llsdBack = new OSDArray(); + foreach (Hashtable ht in arrlst) + { + OSDMap mp = new OSDMap(); + foreach (DictionaryEntry deHt in ht) + { + mp.Add((string)deHt.Key, OSDString.FromObject(deHt.Value)); + } + llsdBack.Add(mp); + } + return llsdBack; + } + + private static OSDArray WrapOSDMap(OSDMap wrapMe) + { + OSDArray array = new OSDArray(); + array.Add(wrapMe); + return array; + } + + public void SetEventCategories(string category, string value) + { + // this.eventCategoriesHash[category] = value; + //TODO + } + + public void AddToUIConfig(string itemName, string item) + { + uiConfigHash[itemName] = item; + } + + public void AddClassifiedCategory(Int32 ID, string categoryName) + { + Hashtable hash = new Hashtable(); + hash["category_name"] = categoryName; + hash["category_id"] = ID; + classifiedCategories.Add(hash); + // this.classifiedCategoriesHash.Clear(); + } + + + private static LLLoginResponse.BuddyList ConvertFriendListItem(FriendInfo[] friendsList) + { + LLLoginResponse.BuddyList buddylistreturn = new LLLoginResponse.BuddyList(); + foreach (FriendInfo finfo in friendsList) + { + if (finfo.TheirFlags == -1) + continue; + LLLoginResponse.BuddyList.BuddyInfo buddyitem = new LLLoginResponse.BuddyList.BuddyInfo(finfo.Friend); + // finfo.Friend may not be a simple uuid + UUID friendID = UUID.Zero; + if (UUID.TryParse(finfo.Friend, out friendID)) + buddyitem.BuddyID = finfo.Friend; + else + { + string tmp; + if (Util.ParseUniversalUserIdentifier(finfo.Friend, out friendID, out tmp, out tmp, out tmp, out tmp)) + buddyitem.BuddyID = friendID.ToString(); + else + // junk entry + continue; + } + buddyitem.BuddyRightsHave = (int)finfo.TheirFlags; + buddyitem.BuddyRightsGiven = (int)finfo.MyFlags; + buddylistreturn.AddNewBuddy(buddyitem); + } + return buddylistreturn; + } + + private InventoryData GetInventorySkeleton(List folders) + { + UUID rootID = UUID.Zero; + ArrayList AgentInventoryArray = new ArrayList(); + Hashtable TempHash; + foreach (InventoryFolderBase InvFolder in folders) + { + if (InvFolder.ParentID == UUID.Zero && InvFolder.Name == InventoryFolderBase.ROOT_FOLDER_NAME) + { + rootID = InvFolder.ID; + } + TempHash = new Hashtable(); + TempHash["name"] = InvFolder.Name; + TempHash["parent_id"] = InvFolder.ParentID.ToString(); + TempHash["version"] = (Int32)InvFolder.Version; + TempHash["type_default"] = (Int32)InvFolder.Type; + TempHash["folder_id"] = InvFolder.ID.ToString(); + AgentInventoryArray.Add(TempHash); + } + + return new InventoryData(AgentInventoryArray, rootID); + + } + + /// + /// Converts the inventory library skeleton into the form required by the rpc request. + /// + /// + protected virtual ArrayList GetInventoryLibrary(ILibraryService library) + { + Dictionary rootFolders = library.GetAllFolders(); +// m_log.DebugFormat("[LLOGIN]: Library has {0} folders", rootFolders.Count); + //Dictionary rootFolders = new Dictionary(); + ArrayList folderHashes = new ArrayList(); + + foreach (InventoryFolderBase folder in rootFolders.Values) + { + Hashtable TempHash = new Hashtable(); + TempHash["name"] = folder.Name; + TempHash["parent_id"] = folder.ParentID.ToString(); + TempHash["version"] = (Int32)folder.Version; + TempHash["type_default"] = (Int32)folder.Type; + TempHash["folder_id"] = folder.ID.ToString(); + folderHashes.Add(TempHash); + } + + return folderHashes; + } + + /// + /// + /// + /// + protected virtual ArrayList GetLibraryOwner(InventoryFolderImpl libFolder) + { + //for now create random inventory library owner + Hashtable TempHash = new Hashtable(); + TempHash["agent_id"] = "11111111-1111-0000-0000-000100bba000"; // libFolder.Owner + ArrayList inventoryLibOwner = new ArrayList(); + inventoryLibOwner.Add(TempHash); + return inventoryLibOwner; + } + + public class InventoryData + { + public ArrayList InventoryArray = null; + public UUID RootFolderID = UUID.Zero; + + public InventoryData(ArrayList invList, UUID rootID) + { + InventoryArray = invList; + RootFolderID = rootID; + } + } + + #region Properties + + public string Login + { + get { return login; } + set { login = value; } + } + + public string DST + { + get { return dst; } + set { dst = value; } + } + + public string StipendSinceLogin + { + get { return stipendSinceLogin; } + set { stipendSinceLogin = value; } + } + + public string Gendered + { + get { return gendered; } + set { gendered = value; } + } + + public string EverLoggedIn + { + get { return everLoggedIn; } + set { everLoggedIn = value; } + } + + public uint SimPort + { + get { return simPort; } + set { simPort = value; } + } + + public uint SimHttpPort + { + get { return simHttpPort; } + set { simHttpPort = value; } + } + + public string SimAddress + { + get { return simAddress; } + set { simAddress = value; } + } + + public UUID AgentID + { + get { return agentID; } + set { agentID = value; } + } + + public UUID SessionID + { + get { return sessionID; } + set { sessionID = value; } + } + + public UUID SecureSessionID + { + get { return secureSessionID; } + set { secureSessionID = value; } + } + + public Int32 CircuitCode + { + get { return circuitCode; } + set { circuitCode = value; } + } + + public uint RegionX + { + get { return regionX; } + set { regionX = value; } + } + + public uint RegionY + { + get { return regionY; } + set { regionY = value; } + } + + public int RegionSizeX { get; private set; } + public int RegionSizeY { get; private set; } + + public string SunTexture + { + get { return sunTexture; } + set { sunTexture = value; } + } + + public string CloudTexture + { + get { return cloudTexture; } + set { cloudTexture = value; } + } + + public string MoonTexture + { + get { return moonTexture; } + set { moonTexture = value; } + } + + public string Firstname + { + get { return firstname; } + set { firstname = value; } + } + + public string Lastname + { + get { return lastname; } + set { lastname = value; } + } + + public string AgentAccess + { + get { return agentAccess; } + set { agentAccess = value; } + } + + public string AgentAccessMax + { + get { return agentAccessMax; } + set { agentAccessMax = value; } + } + + public string StartLocation + { + get { return startLocation; } + set { startLocation = value; } + } + + public string LookAt + { + get { return lookAt; } + set { lookAt = value; } + } + + public string SeedCapability + { + get { return seedCapability; } + set { seedCapability = value; } + } + + public string ErrorReason + { + get { return errorReason; } + set { errorReason = value; } + } + + public string ErrorMessage + { + get { return errorMessage; } + set { errorMessage = value; } + } + + public ArrayList InventoryRoot + { + get { return inventoryRoot; } + set { inventoryRoot = value; } + } + + public ArrayList InventorySkeleton + { + get { return agentInventory; } + set { agentInventory = value; } + } + + public ArrayList InventoryLibrary + { + get { return inventoryLibrary; } + set { inventoryLibrary = value; } + } + + public ArrayList InventoryLibraryOwner + { + get { return inventoryLibraryOwner; } + set { inventoryLibraryOwner = value; } + } + + public ArrayList InventoryLibRoot + { + get { return inventoryLibRoot; } + set { inventoryLibRoot = value; } + } + + public ArrayList ActiveGestures + { + get { return activeGestures; } + set { activeGestures = value; } + } + + public string Home + { + get { return home; } + set { home = value; } + } + + public string MapTileURL + { + get { return mapTileURL; } + set { mapTileURL = value; } + } + + public string ProfileURL + { + get { return profileURL; } + set { profileURL = value; } + } + + public string OpenIDURL + { + get { return openIDURL; } + set { openIDURL = value; } + } + + public string SearchURL + { + get { return searchURL; } + set { searchURL = value; } + } + + public string Message + { + get { return welcomeMessage; } + set { welcomeMessage = value; } + } + + public BuddyList BuddList + { + get { return m_buddyList; } + set { m_buddyList = value; } + } + + public string Currency + { + get { return currency; } + set { currency = value; } + } + + public string ClassifiedFee + { + get { return classifiedFee; } + set { classifiedFee = value; } + } + + public int MaxAgentGroups + { + get { return maxAgentGroups; } + set { maxAgentGroups = value; } + } + + public string DestinationsURL + { + get; set; + } + + public string AvatarsURL + { + get; set; + } + + #endregion + + public class UserInfo + { + public string firstname; + public string lastname; + public ulong homeregionhandle; + public Vector3 homepos; + public Vector3 homelookat; + } + + public class BuddyList + { + public List Buddies = new List(); + + public void AddNewBuddy(BuddyInfo buddy) + { + if (!Buddies.Contains(buddy)) + { + Buddies.Add(buddy); + } + } + + public ArrayList ToArray() + { + ArrayList buddyArray = new ArrayList(); + foreach (BuddyInfo buddy in Buddies) + { + buddyArray.Add(buddy.ToHashTable()); + } + return buddyArray; + } + + public class BuddyInfo + { + public int BuddyRightsHave = 1; + public int BuddyRightsGiven = 1; + public string BuddyID; + + public BuddyInfo(string buddyID) + { + BuddyID = buddyID; + } + + public BuddyInfo(UUID buddyID) + { + BuddyID = buddyID.ToString(); + } + + public Hashtable ToHashTable() + { + Hashtable hTable = new Hashtable(); + hTable["buddy_rights_has"] = BuddyRightsHave; + hTable["buddy_rights_given"] = BuddyRightsGiven; + hTable["buddy_id"] = BuddyID; + return hTable; + } + } + } + } +} diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs new file mode 100644 index 0000000000..d67bc4df25 --- /dev/null +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -0,0 +1,1057 @@ +/* + * 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.Linq; +using System.Net; +using System.Reflection; +using System.Text.RegularExpressions; + +using log4net; +using Nini.Config; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; +using OpenSim.Services.Connectors.Hypergrid; + +namespace OpenSim.Services.LLLoginService +{ + public class LLLoginService : ILoginService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[LLOGIN SERVICE]"; + + private static bool Initialized = false; + + protected IUserAccountService m_UserAccountService; + protected IGridUserService m_GridUserService; + protected IAuthenticationService m_AuthenticationService; + protected IInventoryService m_InventoryService; + protected IInventoryService m_HGInventoryService; + protected IGridService m_GridService; + protected IPresenceService m_PresenceService; + protected ISimulationService m_LocalSimulationService; + protected ISimulationService m_RemoteSimulationService; + protected ILibraryService m_LibraryService; + protected IFriendsService m_FriendsService; + protected IAvatarService m_AvatarService; + protected IUserAgentService m_UserAgentService; + + protected GatekeeperServiceConnector m_GatekeeperConnector; + + protected string m_DefaultRegionName; + protected string m_WelcomeMessage; + protected bool m_RequireInventory; + protected int m_MinLoginLevel; + protected string m_GatekeeperURL; + protected bool m_AllowRemoteSetLoginLevel; + protected string m_MapTileURL; + protected string m_SearchURL; + protected string m_Currency; + protected string m_ClassifiedFee; + protected int m_MaxAgentGroups; + protected string m_DestinationGuide; + protected string m_AvatarPicker; + protected string m_AllowedClients; + protected string m_DeniedClients; + protected string m_MessageUrl; + protected string m_DSTZone; + + IConfig m_LoginServerConfig; +// IConfig m_ClientsConfig; + + public LLLoginService(IConfigSource config, ISimulationService simService, ILibraryService libraryService) + { + m_LoginServerConfig = config.Configs["LoginService"]; + if (m_LoginServerConfig == null) + throw new Exception(String.Format("No section LoginService in config file")); + + string accountService = m_LoginServerConfig.GetString("UserAccountService", String.Empty); + string gridUserService = m_LoginServerConfig.GetString("GridUserService", String.Empty); + string agentService = m_LoginServerConfig.GetString("UserAgentService", String.Empty); + string authService = m_LoginServerConfig.GetString("AuthenticationService", String.Empty); + string invService = m_LoginServerConfig.GetString("InventoryService", String.Empty); + string gridService = m_LoginServerConfig.GetString("GridService", String.Empty); + string presenceService = m_LoginServerConfig.GetString("PresenceService", String.Empty); + string libService = m_LoginServerConfig.GetString("LibraryService", String.Empty); + string friendsService = m_LoginServerConfig.GetString("FriendsService", String.Empty); + string avatarService = m_LoginServerConfig.GetString("AvatarService", String.Empty); + string simulationService = m_LoginServerConfig.GetString("SimulationService", String.Empty); + + m_DefaultRegionName = m_LoginServerConfig.GetString("DefaultRegion", String.Empty); + m_WelcomeMessage = m_LoginServerConfig.GetString("WelcomeMessage", "Welcome to OpenSim!"); + m_RequireInventory = m_LoginServerConfig.GetBoolean("RequireInventory", true); + m_AllowRemoteSetLoginLevel = m_LoginServerConfig.GetBoolean("AllowRemoteSetLoginLevel", false); + m_MinLoginLevel = m_LoginServerConfig.GetInt("MinLoginLevel", 0); + m_GatekeeperURL = Util.GetConfigVarFromSections(config, "GatekeeperURI", + new string[] { "Startup", "Hypergrid", "LoginService" }, String.Empty); + m_MapTileURL = m_LoginServerConfig.GetString("MapTileURL", string.Empty); + m_SearchURL = m_LoginServerConfig.GetString("SearchURL", string.Empty); + m_Currency = m_LoginServerConfig.GetString("Currency", string.Empty); + m_ClassifiedFee = m_LoginServerConfig.GetString("ClassifiedFee", string.Empty); + m_DestinationGuide = m_LoginServerConfig.GetString ("DestinationGuide", string.Empty); + m_AvatarPicker = m_LoginServerConfig.GetString ("AvatarPicker", string.Empty); + + m_AllowedClients = m_LoginServerConfig.GetString("AllowedClients", string.Empty); + m_DeniedClients = m_LoginServerConfig.GetString("DeniedClients", string.Empty); + m_MessageUrl = m_LoginServerConfig.GetString("MessageUrl", string.Empty); + m_DSTZone = m_LoginServerConfig.GetString("DSTZone", "America/Los_Angeles;Pacific Standard Time"); + + IConfig groupConfig = config.Configs["Groups"]; + if (groupConfig != null) + m_MaxAgentGroups = groupConfig.GetInt("MaxAgentGroups", 42); + + + // Clean up some of these vars + if (m_MapTileURL != String.Empty) + { + m_MapTileURL = m_MapTileURL.Trim(); + if (!m_MapTileURL.EndsWith("/")) + m_MapTileURL = m_MapTileURL + "/"; + } + + // These are required; the others aren't + if (accountService == string.Empty || authService == string.Empty) + throw new Exception("LoginService is missing service specifications"); + + // replace newlines in welcome message + m_WelcomeMessage = m_WelcomeMessage.Replace("\\n", "\n"); + + Object[] args = new Object[] { config }; + m_UserAccountService = ServerUtils.LoadPlugin(accountService, args); + m_GridUserService = ServerUtils.LoadPlugin(gridUserService, args); + m_AuthenticationService = ServerUtils.LoadPlugin(authService, args); + m_InventoryService = ServerUtils.LoadPlugin(invService, args); + + if (gridService != string.Empty) + m_GridService = ServerUtils.LoadPlugin(gridService, args); + if (presenceService != string.Empty) + m_PresenceService = ServerUtils.LoadPlugin(presenceService, args); + if (avatarService != string.Empty) + m_AvatarService = ServerUtils.LoadPlugin(avatarService, args); + if (friendsService != string.Empty) + m_FriendsService = ServerUtils.LoadPlugin(friendsService, args); + if (simulationService != string.Empty) + m_RemoteSimulationService = ServerUtils.LoadPlugin(simulationService, args); + if (agentService != string.Empty) + m_UserAgentService = ServerUtils.LoadPlugin(agentService, args); + + // Get the Hypergrid inventory service (exists only if Hypergrid is enabled) + string hgInvServicePlugin = m_LoginServerConfig.GetString("HGInventoryServicePlugin", String.Empty); + if (hgInvServicePlugin != string.Empty) + { + string hgInvServiceArg = m_LoginServerConfig.GetString("HGInventoryServiceConstructorArg", String.Empty); + Object[] args2 = new Object[] { config, hgInvServiceArg }; + m_HGInventoryService = ServerUtils.LoadPlugin(hgInvServicePlugin, args2); + } + + // + // deal with the services given as argument + // + m_LocalSimulationService = simService; + if (libraryService != null) + { + m_log.DebugFormat("[LLOGIN SERVICE]: Using LibraryService given as argument"); + m_LibraryService = libraryService; + } + else if (libService != string.Empty) + { + m_log.DebugFormat("[LLOGIN SERVICE]: Using instantiated LibraryService"); + m_LibraryService = ServerUtils.LoadPlugin(libService, args); + } + + m_GatekeeperConnector = new GatekeeperServiceConnector(); + + if (!Initialized) + { + Initialized = true; + RegisterCommands(); + } + + m_log.DebugFormat("[LLOGIN SERVICE]: Starting..."); + + } + + public LLLoginService(IConfigSource config) : this(config, null, null) + { + } + + public Hashtable SetLevel(string firstName, string lastName, string passwd, int level, IPEndPoint clientIP) + { + Hashtable response = new Hashtable(); + response["success"] = "false"; + + if (!m_AllowRemoteSetLoginLevel) + return response; + + try + { + UserAccount account = m_UserAccountService.GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) + { + m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, user {0} {1} not found", firstName, lastName); + return response; + } + + if (account.UserLevel < 200) + { + m_log.InfoFormat("[LLOGIN SERVICE]: Set Level failed, reason: user level too low"); + return response; + } + + // + // Authenticate this user + // + // We don't support clear passwords here + // + string token = m_AuthenticationService.Authenticate(account.PrincipalID, passwd, 30); + UUID secureSession = UUID.Zero; + if ((token == string.Empty) || (token != string.Empty && !UUID.TryParse(token, out secureSession))) + { + m_log.InfoFormat("[LLOGIN SERVICE]: SetLevel failed, reason: authentication failed"); + return response; + } + } + catch (Exception e) + { + m_log.Error("[LLOGIN SERVICE]: SetLevel failed, exception " + e.ToString()); + return response; + } + + m_MinLoginLevel = level; + m_log.InfoFormat("[LLOGIN SERVICE]: Login level set to {0} by {1} {2}", level, firstName, lastName); + + response["success"] = true; + return response; + } + + public LoginResponse Login(string firstName, string lastName, string passwd, string startLocation, UUID scopeID, + string clientVersion, string channel, string mac, string id0, IPEndPoint clientIP) + { + bool success = false; + UUID session = UUID.Random(); + string processedMessage; + + m_log.InfoFormat("[LLOGIN SERVICE]: Login request for {0} {1} at {2} using viewer {3}, channel {4}, IP {5}, Mac {6}, Id0 {7}", + firstName, lastName, startLocation, clientVersion, channel, clientIP.Address.ToString(), mac, id0); + + try + { + // + // Check client + // + if (m_AllowedClients != string.Empty) + { + Regex arx = new Regex(m_AllowedClients); + Match am = arx.Match(clientVersion); + + if (!am.Success) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: client {2} is not allowed", + firstName, lastName, clientVersion); + return LLFailedLoginResponse.LoginBlockedProblem; + } + } + + if (m_DeniedClients != string.Empty) + { + Regex drx = new Regex(m_DeniedClients); + Match dm = drx.Match(clientVersion); + + if (dm.Success) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: client {2} is denied", + firstName, lastName, clientVersion); + return LLFailedLoginResponse.LoginBlockedProblem; + } + } + + // + // Get the account and check that it exists + // + UserAccount account = m_UserAccountService.GetUserAccount(scopeID, firstName, lastName); + if (account == null) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: user not found", firstName, lastName); + return LLFailedLoginResponse.UserProblem; + } + + if (account.UserLevel < m_MinLoginLevel) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: user level is {2} but minimum login level is {3}", + firstName, lastName, account.UserLevel, m_MinLoginLevel); + return LLFailedLoginResponse.LoginBlockedProblem; + } + + // If a scope id is requested, check that the account is in + // that scope, or unscoped. + // + if (scopeID != UUID.Zero) + { + if (account.ScopeID != scopeID && account.ScopeID != UUID.Zero) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed, reason: user {0} {1} not found", firstName, lastName); + return LLFailedLoginResponse.UserProblem; + } + } + else + { + scopeID = account.ScopeID; + } + + // + // Authenticate this user + // + if (!passwd.StartsWith("$1$")) + passwd = "$1$" + Util.Md5Hash(passwd); + passwd = passwd.Remove(0, 3); //remove $1$ + string token = m_AuthenticationService.Authenticate(account.PrincipalID, passwd, 30); + UUID secureSession = UUID.Zero; + if ((token == string.Empty) || (token != string.Empty && !UUID.TryParse(token, out secureSession))) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: authentication failed", + firstName, lastName); + return LLFailedLoginResponse.UserProblem; + } + + // + // Get the user's inventory + // + if (m_RequireInventory && m_InventoryService == null) + { + m_log.WarnFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: inventory service not set up", + firstName, lastName); + return LLFailedLoginResponse.InventoryProblem; + } + + if (m_HGInventoryService != null) + { + // Give the Suitcase service a chance to create the suitcase folder. + // (If we're not using the Suitcase inventory service then this won't do anything.) + m_HGInventoryService.GetRootFolder(account.PrincipalID); + } + + List inventorySkel = m_InventoryService.GetInventorySkeleton(account.PrincipalID); + if (m_RequireInventory && ((inventorySkel == null) || (inventorySkel != null && inventorySkel.Count == 0))) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed, for {0} {1}, reason: unable to retrieve user inventory", + firstName, lastName); + return LLFailedLoginResponse.InventoryProblem; + } + + // Get active gestures + List gestures = m_InventoryService.GetActiveGestures(account.PrincipalID); +// m_log.DebugFormat("[LLOGIN SERVICE]: {0} active gestures", gestures.Count); + + // + // Login the presence + // + if (m_PresenceService != null) + { + success = m_PresenceService.LoginAgent(account.PrincipalID.ToString(), session, secureSession); + + if (!success) + { + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: could not login presence", + firstName, lastName); + return LLFailedLoginResponse.GridProblem; + } + } + + // + // Change Online status and get the home region + // + GridRegion home = null; + GridUserInfo guinfo = m_GridUserService.LoggedIn(account.PrincipalID.ToString()); + + // We are only going to complain about no home if the user actually tries to login there, to avoid + // spamming the console. + if (guinfo != null) + { + if (guinfo.HomeRegionID == UUID.Zero && startLocation == "home") + { + m_log.WarnFormat( + "[LLOGIN SERVICE]: User {0} tried to login to a 'home' start location but they have none set", + account.Name); + } + else if (m_GridService != null) + { + home = m_GridService.GetRegionByUUID(scopeID, guinfo.HomeRegionID); + + if (home == null && startLocation == "home") + { + m_log.WarnFormat( + "[LLOGIN SERVICE]: User {0} tried to login to a 'home' start location with ID {1} but this was not found.", + account.Name, guinfo.HomeRegionID); + } + } + } + else + { + // something went wrong, make something up, so that we don't have to test this anywhere else + m_log.DebugFormat("{0} Failed to fetch GridUserInfo. Creating empty GridUserInfo as home", LogHeader); + guinfo = new GridUserInfo(); + guinfo.LastPosition = guinfo.HomePosition = new Vector3(128, 128, 30); + } + + // + // Find the destination region/grid + // + string where = string.Empty; + Vector3 position = Vector3.Zero; + Vector3 lookAt = Vector3.Zero; + GridRegion gatekeeper = null; + TeleportFlags flags; + GridRegion destination = FindDestination(account, scopeID, guinfo, session, startLocation, home, out gatekeeper, out where, out position, out lookAt, out flags); + if (destination == null) + { + m_PresenceService.LogoutAgent(session); + + m_log.InfoFormat( + "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: destination not found", + firstName, lastName); + return LLFailedLoginResponse.GridProblem; + } + else + { + m_log.DebugFormat( + "[LLOGIN SERVICE]: Found destination {0}, endpoint {1} for {2} {3}", + destination.RegionName, destination.ExternalEndPoint, firstName, lastName); + } + + if (account.UserLevel >= 200) + flags |= TeleportFlags.Godlike; + // + // Get the avatar + // + AvatarAppearance avatar = null; + if (m_AvatarService != null) + { + avatar = m_AvatarService.GetAppearance(account.PrincipalID); + } + + // + // Instantiate/get the simulation interface and launch an agent at the destination + // + string reason = string.Empty; + GridRegion dest; + AgentCircuitData aCircuit = LaunchAgentAtGrid(gatekeeper, destination, account, avatar, session, secureSession, position, where, + clientVersion, channel, mac, id0, clientIP, flags, out where, out reason, out dest); + destination = dest; + if (aCircuit == null) + { + m_PresenceService.LogoutAgent(session); + m_log.InfoFormat("[LLOGIN SERVICE]: Login failed for {0} {1}, reason: {2}", firstName, lastName, reason); + return new LLFailedLoginResponse("key", reason, "false"); + + } + // Get Friends list + FriendInfo[] friendsList = new FriendInfo[0]; + if (m_FriendsService != null) + { + friendsList = m_FriendsService.GetFriends(account.PrincipalID); +// m_log.DebugFormat("[LLOGIN SERVICE]: Retrieved {0} friends", friendsList.Length); + } + + // + // Finally, fill out the response and return it + // + if (m_MessageUrl != String.Empty) + { + WebClient client = new WebClient(); + processedMessage = client.DownloadString(m_MessageUrl); + } + else + { + processedMessage = m_WelcomeMessage; + } + processedMessage = processedMessage.Replace("\\n", "\n").Replace("", firstName + " " + lastName); + + LLLoginResponse response + = new LLLoginResponse( + account, aCircuit, guinfo, destination, inventorySkel, friendsList, m_LibraryService, + where, startLocation, position, lookAt, gestures, processedMessage, home, clientIP, + m_MapTileURL, m_SearchURL, m_Currency, m_DSTZone, + m_DestinationGuide, m_AvatarPicker, m_ClassifiedFee, m_MaxAgentGroups); + + m_log.DebugFormat("[LLOGIN SERVICE]: All clear. Sending login response to {0} {1}", firstName, lastName); + + return response; + } + catch (Exception e) + { + m_log.WarnFormat("[LLOGIN SERVICE]: Exception processing login for {0} {1}: {2} {3}", firstName, lastName, e.ToString(), e.StackTrace); + if (m_PresenceService != null) + m_PresenceService.LogoutAgent(session); + return LLFailedLoginResponse.InternalError; + } + } + + protected GridRegion FindDestination( + UserAccount account, UUID scopeID, GridUserInfo pinfo, UUID sessionID, string startLocation, + GridRegion home, out GridRegion gatekeeper, + out string where, out Vector3 position, out Vector3 lookAt, out TeleportFlags flags) + { + flags = TeleportFlags.ViaLogin; + + m_log.DebugFormat( + "[LLOGIN SERVICE]: Finding destination matching start location {0} for {1}", + startLocation, account.Name); + + gatekeeper = null; + where = "home"; + position = new Vector3(128, 128, 0); + lookAt = new Vector3(0, 1, 0); + + if (m_GridService == null) + return null; + + if (startLocation.Equals("home")) + { + // logging into home region + if (pinfo == null) + return null; + + GridRegion region = null; + + bool tryDefaults = false; + + if (home == null) + { + tryDefaults = true; + } + else + { + region = home; + + position = pinfo.HomePosition; + lookAt = pinfo.HomeLookAt; + flags |= TeleportFlags.ViaHome; + } + + if (tryDefaults) + { + List defaults = m_GridService.GetDefaultRegions(scopeID); + if (defaults != null && defaults.Count > 0) + { + region = defaults[0]; + where = "safe"; + } + else + { + m_log.WarnFormat("[LLOGIN SERVICE]: User {0} {1} does not have a valid home and this grid does not have default locations. Attempting to find random region", + account.FirstName, account.LastName); + region = FindAlternativeRegion(scopeID); + if (region != null) + where = "safe"; + } + } + + return region; + } + else if (startLocation.Equals("last")) + { + // logging into last visited region + where = "last"; + + if (pinfo == null) + return null; + + GridRegion region = null; + + if (pinfo.LastRegionID.Equals(UUID.Zero) || (region = m_GridService.GetRegionByUUID(scopeID, pinfo.LastRegionID)) == null) + { + List defaults = m_GridService.GetDefaultRegions(scopeID); + if (defaults != null && defaults.Count > 0) + { + region = defaults[0]; + where = "safe"; + } + else + { + m_log.Info("[LLOGIN SERVICE]: Last Region Not Found Attempting to find random region"); + region = FindAlternativeRegion(scopeID); + if (region != null) + where = "safe"; + } + + } + else + { + position = pinfo.LastPosition; + lookAt = pinfo.LastLookAt; + } + + return region; + } + else + { + flags |= TeleportFlags.ViaRegionID; + + // free uri form + // e.g. New Moon&135&46 New Moon@osgrid.org:8002&153&34 + where = "url"; + GridRegion region = null; + Regex reURI = new Regex(@"^uri:(?[^&]+)&(?\d+[.]?\d*)&(?\d+[.]?\d*)&(?\d+[.]?\d*)$"); + Match uriMatch = reURI.Match(startLocation); + if (uriMatch == null) + { + m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, but can't process it", startLocation); + return null; + } + else + { + position = new Vector3(float.Parse(uriMatch.Groups["x"].Value, Culture.NumberFormatInfo), + float.Parse(uriMatch.Groups["y"].Value, Culture.NumberFormatInfo), + float.Parse(uriMatch.Groups["z"].Value, Culture.NumberFormatInfo)); + + string regionName = uriMatch.Groups["region"].ToString(); + if (regionName != null) + { + if (!regionName.Contains("@")) + { + List regions = m_GridService.GetRegionsByName(scopeID, regionName, 1); + if ((regions == null) || (regions != null && regions.Count == 0)) + { + m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, can't locate region {1}. Trying defaults.", startLocation, regionName); + regions = m_GridService.GetDefaultRegions(scopeID); + if (regions != null && regions.Count > 0) + { + where = "safe"; + return regions[0]; + } + else + { + m_log.Info("[LLOGIN SERVICE]: Last Region Not Found Attempting to find random region"); + region = FindAlternativeRegion(scopeID); + if (region != null) + { + where = "safe"; + return region; + } + else + { + m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, Grid does not provide default regions and no alternative found.", startLocation); + return null; + } + } + } + return regions[0]; + } + else + { + if (m_UserAgentService == null) + { + m_log.WarnFormat("[LLLOGIN SERVICE]: This llogin service is not running a user agent service, as such it can't lauch agents at foreign grids"); + return null; + } + string[] parts = regionName.Split(new char[] { '@' }); + if (parts.Length < 2) + { + m_log.InfoFormat("[LLLOGIN SERVICE]: Got Custom Login URI {0}, can't locate region {1}", startLocation, regionName); + return null; + } + // Valid specification of a remote grid + + regionName = parts[0]; + string domainLocator = parts[1]; + parts = domainLocator.Split(new char[] {':'}); + string domainName = parts[0]; + uint port = 0; + if (parts.Length > 1) + UInt32.TryParse(parts[1], out port); + + region = FindForeignRegion(domainName, port, regionName, account, out gatekeeper); + return region; + } + } + else + { + List defaults = m_GridService.GetDefaultRegions(scopeID); + if (defaults != null && defaults.Count > 0) + { + where = "safe"; + return defaults[0]; + } + else + return null; + } + } + //response.LookAt = "[r0,r1,r0]"; + //// can be: last, home, safe, url + //response.StartLocation = "url"; + + } + + } + + private GridRegion FindAlternativeRegion(UUID scopeID) + { + List hyperlinks = null; + List regions = m_GridService.GetFallbackRegions(scopeID, (int)Util.RegionToWorldLoc(1000), (int)Util.RegionToWorldLoc(1000)); + if (regions != null && regions.Count > 0) + { + hyperlinks = m_GridService.GetHyperlinks(scopeID); + IEnumerable availableRegions = regions.Except(hyperlinks); + if (availableRegions.Count() > 0) + return availableRegions.ElementAt(0); + } + // No fallbacks, try to find an arbitrary region that is not a hyperlink + // maxNumber is fixed for now; maybe use some search pattern with increasing maxSize here? + regions = m_GridService.GetRegionsByName(scopeID, "", 10); + if (regions != null && regions.Count > 0) + { + if (hyperlinks == null) + hyperlinks = m_GridService.GetHyperlinks(scopeID); + IEnumerable availableRegions = regions.Except(hyperlinks); + if (availableRegions.Count() > 0) + return availableRegions.ElementAt(0); + } + return null; + } + + private GridRegion FindForeignRegion(string domainName, uint port, string regionName, UserAccount account, out GridRegion gatekeeper) + { + m_log.Debug("[LLLOGIN SERVICE]: attempting to findforeignregion " + domainName + ":" + port.ToString() + ":" + regionName); + gatekeeper = new GridRegion(); + gatekeeper.ExternalHostName = domainName; + gatekeeper.HttpPort = port; + gatekeeper.RegionName = regionName; + gatekeeper.InternalEndPoint = new IPEndPoint(IPAddress.Parse("0.0.0.0"), 0); + + UUID regionID; + ulong handle; + string imageURL = string.Empty, reason = string.Empty; + string message; + if (m_GatekeeperConnector.LinkRegion(gatekeeper, out regionID, out handle, out domainName, out imageURL, out reason)) + { + string homeURI = null; + if (account.ServiceURLs != null && account.ServiceURLs.ContainsKey("HomeURI")) + homeURI = (string)account.ServiceURLs["HomeURI"]; + + GridRegion destination = m_GatekeeperConnector.GetHyperlinkRegion(gatekeeper, regionID, account.PrincipalID, homeURI, out message); + return destination; + } + + return null; + } + + private string hostName = string.Empty; + private int port = 0; + + private void SetHostAndPort(string url) + { + try + { + Uri uri = new Uri(url); + hostName = uri.Host; + port = uri.Port; + } + catch + { + m_log.WarnFormat("[LLLogin SERVICE]: Unable to parse GatekeeperURL {0}", url); + } + } + + protected AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarAppearance avatar, + UUID session, UUID secureSession, Vector3 position, string currentWhere, string viewer, string channel, string mac, string id0, + IPEndPoint clientIP, TeleportFlags flags, out string where, out string reason, out GridRegion dest) + { + where = currentWhere; + ISimulationService simConnector = null; + reason = string.Empty; + uint circuitCode = 0; + AgentCircuitData aCircuit = null; + + if (m_UserAgentService == null) + { + // HG standalones have both a localSimulatonDll and a remoteSimulationDll + // non-HG standalones have just a localSimulationDll + // independent login servers have just a remoteSimulationDll + if (m_LocalSimulationService != null) + simConnector = m_LocalSimulationService; + else if (m_RemoteSimulationService != null) + simConnector = m_RemoteSimulationService; + } + else // User Agent Service is on + { + if (gatekeeper == null) // login to local grid + { + if (hostName == string.Empty) + SetHostAndPort(m_GatekeeperURL); + + gatekeeper = new GridRegion(destination); + gatekeeper.ExternalHostName = hostName; + gatekeeper.HttpPort = (uint)port; + gatekeeper.ServerURI = m_GatekeeperURL; + } + m_log.Debug("[LLLOGIN SERVICE]: no gatekeeper detected..... using " + m_GatekeeperURL); + } + + bool success = false; + + if (m_UserAgentService == null && simConnector != null) + { + circuitCode = (uint)Util.RandomClass.Next(); ; + aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, clientIP.Address.ToString(), viewer, channel, mac, id0); + success = LaunchAgentDirectly(simConnector, destination, aCircuit, flags, out reason); + if (!success && m_GridService != null) + { + // Try the fallback regions + List fallbacks = m_GridService.GetFallbackRegions(account.ScopeID, destination.RegionLocX, destination.RegionLocY); + if (fallbacks != null) + { + foreach (GridRegion r in fallbacks) + { + success = LaunchAgentDirectly(simConnector, r, aCircuit, flags | TeleportFlags.ViaRegionID, out reason); + if (success) + { + where = "safe"; + destination = r; + break; + } + } + } + } + } + + if (m_UserAgentService != null) + { + circuitCode = (uint)Util.RandomClass.Next(); ; + aCircuit = MakeAgent(destination, account, avatar, session, secureSession, circuitCode, position, clientIP.Address.ToString(), viewer, channel, mac, id0); + aCircuit.teleportFlags |= (uint)flags; + success = LaunchAgentIndirectly(gatekeeper, destination, aCircuit, clientIP, out reason); + if (!success && m_GridService != null) + { + // Try the fallback regions + List fallbacks = m_GridService.GetFallbackRegions(account.ScopeID, destination.RegionLocX, destination.RegionLocY); + if (fallbacks != null) + { + foreach (GridRegion r in fallbacks) + { + success = LaunchAgentIndirectly(gatekeeper, r, aCircuit, clientIP, out reason); + if (success) + { + where = "safe"; + destination = r; + break; + } + } + } + } + } + dest = destination; + if (success) + return aCircuit; + else + return null; + } + + private AgentCircuitData MakeAgent(GridRegion region, UserAccount account, + AvatarAppearance avatar, UUID session, UUID secureSession, uint circuit, Vector3 position, + string ipaddress, string viewer, string channel, string mac, string id0) + { + AgentCircuitData aCircuit = new AgentCircuitData(); + + aCircuit.AgentID = account.PrincipalID; + if (avatar != null) + aCircuit.Appearance = new AvatarAppearance(avatar); + else + aCircuit.Appearance = new AvatarAppearance(); + + //aCircuit.BaseFolder = irrelevant + aCircuit.CapsPath = CapsUtil.GetRandomCapsObjectPath(); + aCircuit.child = false; // the first login agent is root + aCircuit.ChildrenCapSeeds = new Dictionary(); + aCircuit.circuitcode = circuit; + aCircuit.firstname = account.FirstName; + //aCircuit.InventoryFolder = irrelevant + aCircuit.lastname = account.LastName; + aCircuit.SecureSessionID = secureSession; + aCircuit.SessionID = session; + aCircuit.startpos = position; + aCircuit.IPAddress = ipaddress; + aCircuit.Viewer = viewer; + aCircuit.Channel = channel; + aCircuit.Mac = mac; + aCircuit.Id0 = id0; + SetServiceURLs(aCircuit, account); + + return aCircuit; + } + + private void SetServiceURLs(AgentCircuitData aCircuit, UserAccount account) + { + aCircuit.ServiceURLs = new Dictionary(); + if (account.ServiceURLs == null) + return; + + // Old style: get the service keys from the DB + foreach (KeyValuePair kvp in account.ServiceURLs) + { + if (kvp.Value != null) + { + aCircuit.ServiceURLs[kvp.Key] = kvp.Value; + + if (!aCircuit.ServiceURLs[kvp.Key].ToString().EndsWith("/")) + aCircuit.ServiceURLs[kvp.Key] = aCircuit.ServiceURLs[kvp.Key] + "/"; + } + } + + // New style: service keys start with SRV_; override the previous + string[] keys = m_LoginServerConfig.GetKeys(); + + if (keys.Length > 0) + { + bool newUrls = false; + IEnumerable serviceKeys = keys.Where(value => value.StartsWith("SRV_")); + foreach (string serviceKey in serviceKeys) + { + string keyName = serviceKey.Replace("SRV_", ""); + string keyValue = m_LoginServerConfig.GetString(serviceKey, string.Empty); + if (!keyValue.EndsWith("/")) + keyValue = keyValue + "/"; + + if (!account.ServiceURLs.ContainsKey(keyName) || (account.ServiceURLs.ContainsKey(keyName) && (string)account.ServiceURLs[keyName] != keyValue)) + { + account.ServiceURLs[keyName] = keyValue; + newUrls = true; + } + aCircuit.ServiceURLs[keyName] = keyValue; + + m_log.DebugFormat("[LLLOGIN SERVICE]: found new key {0} {1}", keyName, aCircuit.ServiceURLs[keyName]); + } + + if (!account.ServiceURLs.ContainsKey("GatekeeperURI") && !string.IsNullOrEmpty(m_GatekeeperURL)) + { + m_log.DebugFormat("[LLLOGIN SERVICE]: adding gatekeeper uri {0}", m_GatekeeperURL); + account.ServiceURLs["GatekeeperURI"] = m_GatekeeperURL; + newUrls = true; + } + + // The grid operator decided to override the defaults in the + // [LoginService] configuration. Let's store the correct ones. + if (newUrls) + m_UserAccountService.StoreUserAccount(account); + } + + } + + private bool LaunchAgentDirectly(ISimulationService simConnector, GridRegion region, AgentCircuitData aCircuit, TeleportFlags flags, out string reason) + { + string version; + + if ( + !simConnector.QueryAccess( + region, aCircuit.AgentID, null, true, aCircuit.startpos, "SIMULATION/0.3", new List(), out version, out reason)) + return false; + + return simConnector.CreateAgent(null, region, aCircuit, (uint)flags, out reason); + } + + private bool LaunchAgentIndirectly(GridRegion gatekeeper, GridRegion destination, AgentCircuitData aCircuit, IPEndPoint clientIP, out string reason) + { + m_log.Debug("[LLOGIN SERVICE]: Launching agent at " + destination.RegionName); + + if (m_UserAgentService.LoginAgentToGrid(null, aCircuit, gatekeeper, destination, true, out reason)) + return true; + return false; + } + + #region Console Commands + private void RegisterCommands() + { + //MainConsole.Instance.Commands.AddCommand + MainConsole.Instance.Commands.AddCommand("Users", false, "login level", + "login level ", + "Set the minimum user level to log in", HandleLoginCommand); + + MainConsole.Instance.Commands.AddCommand("Users", false, "login reset", + "login reset", + "Reset the login level to allow all users", + HandleLoginCommand); + + MainConsole.Instance.Commands.AddCommand("Users", false, "login text", + "login text ", + "Set the text users will see on login", HandleLoginCommand); + + } + + private void HandleLoginCommand(string module, string[] cmd) + { + string subcommand = cmd[1]; + + switch (subcommand) + { + case "level": + // Set the minimum level to allow login + // Useful to allow grid update without worrying about users. + // or fixing critical issues + // + if (cmd.Length > 2) + { + if (Int32.TryParse(cmd[2], out m_MinLoginLevel)) + MainConsole.Instance.OutputFormat("Set minimum login level to {0}", m_MinLoginLevel); + else + MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid login level", cmd[2]); + } + break; + + case "reset": + m_MinLoginLevel = 0; + MainConsole.Instance.OutputFormat("Reset min login level to {0}", m_MinLoginLevel); + break; + + case "text": + if (cmd.Length > 2) + { + m_WelcomeMessage = cmd[2]; + MainConsole.Instance.OutputFormat("Login welcome message set to '{0}'", m_WelcomeMessage); + } + break; + } + } + } + + #endregion +} diff --git a/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs b/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4dc492aff0 --- /dev/null +++ b/OpenSim/Services/LLLoginService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.LLLoginService")] +[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("cbeb8f23-3896-4076-97fd-f955b0af6a93")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/MapImageService/MapImageService.cs b/OpenSim/Services/MapImageService/MapImageService.cs new file mode 100644 index 0000000000..a816411cc9 --- /dev/null +++ b/OpenSim/Services/MapImageService/MapImageService.cs @@ -0,0 +1,382 @@ +/* + * 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. + * + * The design of this map service is based on SimianGrid's PHP-based + * map service. See this URL for the original PHP version: + * https://github.com/openmetaversefoundation/simiangrid/ + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Net; +using System.Reflection; +using System.Threading; + +using Nini.Config; +using log4net; +using OpenMetaverse; + +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Services.Interfaces; + + +namespace OpenSim.Services.MapImageService +{ + public class MapImageService : IMapImageService + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); +#pragma warning disable 414 + private string LogHeader = "[MAP IMAGE SERVICE]"; +#pragma warning restore 414 + + private const int ZOOM_LEVELS = 8; + private const int IMAGE_WIDTH = 256; + private const int HALF_WIDTH = 128; + private const int JPEG_QUALITY = 80; + + private static string m_TilesStoragePath = "maptiles"; + + private static object m_Sync = new object(); + private static bool m_Initialized = false; + private static string m_WaterTileFile = string.Empty; + private static Color m_Watercolor = Color.FromArgb(29, 71, 95); + + public MapImageService(IConfigSource config) + { + if (!m_Initialized) + { + m_Initialized = true; + m_log.Debug("[MAP IMAGE SERVICE]: Starting MapImage service"); + + IConfig serviceConfig = config.Configs["MapImageService"]; + if (serviceConfig != null) + { + m_TilesStoragePath = serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath); + if (!Directory.Exists(m_TilesStoragePath)) + Directory.CreateDirectory(m_TilesStoragePath); + + + m_WaterTileFile = Path.Combine(m_TilesStoragePath, "water.jpg"); + if (!File.Exists(m_WaterTileFile)) + { + Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH); + FillImage(waterTile, m_Watercolor); + waterTile.Save(m_WaterTileFile, ImageFormat.Jpeg); + } + } + } + } + + #region IMapImageService + + public bool AddMapTile(int x, int y, byte[] imageData, out string reason) + { + reason = string.Empty; + string fileName = GetFileName(1, x, y); + + lock (m_Sync) + { + try + { + using (FileStream f = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write)) + f.Write(imageData, 0, imageData.Length); + } + catch (Exception e) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save image file {0}: {1}", fileName, e); + reason = e.Message; + return false; + } + } + + return UpdateMultiResolutionFilesAsync(x, y, out reason); + } + + public bool RemoveMapTile(int x, int y, out string reason) + { + reason = String.Empty; + string fileName = GetFileName(1, x, y); + + lock (m_Sync) + { + try + { + File.Delete(fileName); + } + catch (Exception e) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save delete file {0}: {1}", fileName, e); + reason = e.Message; + return false; + } + } + + return UpdateMultiResolutionFilesAsync(x, y, out reason); + } + + // When large varregions start up, they can send piles of new map tiles. This causes + // this multi-resolution routine to be called a zillion times an causes much CPU + // time to be spent creating multi-resolution tiles that will be replaced when + // the next maptile arrives. + private class mapToMultiRez + { + public int xx; + public int yy; + public mapToMultiRez(int pX, int pY) + { + xx = pX; + yy = pY; + } + }; + private Queue multiRezToBuild = new Queue(); + private bool UpdateMultiResolutionFilesAsync(int x, int y, out string reason) + { + reason = String.Empty; + lock (multiRezToBuild) + { + // m_log.DebugFormat("{0} UpdateMultiResolutionFilesAsync: scheduling update for <{1},{2}>", LogHeader, x, y); + multiRezToBuild.Enqueue(new mapToMultiRez(x, y)); + if (multiRezToBuild.Count == 1) + Util.FireAndForget( + DoUpdateMultiResolutionFilesAsync, null, "MapImageService.DoUpdateMultiResolutionFilesAsync"); + } + + return true; + } + + private void DoUpdateMultiResolutionFilesAsync(object o) + { + // This sleep causes the FireAndForget thread to be different than the invocation thread. + // It also allows other tiles to be uploaded so the multi-rez images are more likely + // to be correct. + Thread.Sleep(1 * 1000); + + while (multiRezToBuild.Count > 0) + { + mapToMultiRez toMultiRez = null; + lock (multiRezToBuild) + { + if (multiRezToBuild.Count > 0) + toMultiRez = multiRezToBuild.Dequeue(); + } + if (toMultiRez != null) + { + int x = toMultiRez.xx; + int y = toMultiRez.yy; + // m_log.DebugFormat("{0} DoUpdateMultiResolutionFilesAsync: doing build for <{1},{2}>", LogHeader, x, y); + + // Stitch seven more aggregate tiles together + for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++) + { + // Calculate the width (in full resolution tiles) and bottom-left + // corner of the current zoom level + int width = (int)Math.Pow(2, (double)(zoomLevel - 1)); + int x1 = x - (x % width); + int y1 = y - (y % width); + + lock (m_Sync) // must lock the reading and writing of the maptile files + { + if (!CreateTile(zoomLevel, x1, y1)) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0},{1} at zoom level {1}", x, y, zoomLevel); + return; + } + } + } + } + } + + return; + } + + public byte[] GetMapTile(string fileName, out string format) + { +// m_log.DebugFormat("[MAP IMAGE SERVICE]: Getting map tile {0}", fileName); + + format = ".jpg"; + string fullName = Path.Combine(m_TilesStoragePath, fileName); + if (File.Exists(fullName)) + { + format = Path.GetExtension(fileName).ToLower(); + //m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format); + return File.ReadAllBytes(fullName); + } + else if (File.Exists(m_WaterTileFile)) + { + return File.ReadAllBytes(m_WaterTileFile); + } + else + { + m_log.DebugFormat("[MAP IMAGE SERVICE]: unable to get file {0}", fileName); + return new byte[0]; + } + } + + #endregion + + + private string GetFileName(uint zoomLevel, int x, int y) + { + string extension = "jpg"; + return Path.Combine(m_TilesStoragePath, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension)); + } + + private Bitmap GetInputTileImage(string fileName) + { + try + { + if (File.Exists(fileName)) + return new Bitmap(fileName); + } + catch (Exception e) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e); + } + + return null; + } + + private Bitmap GetOutputTileImage(string fileName) + { + try + { + if (File.Exists(fileName)) + return new Bitmap(fileName); + + else + { + // Create a new output tile with a transparent background + Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH, PixelFormat.Format24bppRgb); + bm.MakeTransparent(); + return bm; + } + } + catch (Exception e) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e); + } + + return null; + } + + private bool CreateTile(uint zoomLevel, int x, int y) + { +// m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel); + int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2); + int thisWidth = (int)Math.Pow(2, (double)zoomLevel - 1); + + // Convert x and y to the bottom left tile for this zoom level + int xIn = x - (x % prevWidth); + int yIn = y - (y % prevWidth); + + // Convert x and y to the bottom left tile for the next zoom level + int xOut = x - (x % thisWidth); + int yOut = y - (y % thisWidth); + + // Try to open the four input tiles from the previous zoom level + Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn)); + Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn)); + Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth)); + Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth)); + + // Open the output tile (current zoom level) + string outputFile = GetFileName(zoomLevel, xOut, yOut); + Bitmap output = GetOutputTileImage(outputFile); + if (output == null) + return false; + FillImage(output, m_Watercolor); + + if (inputBL != null) + { + ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0); + inputBL.Dispose(); + } + if (inputBR != null) + { + ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0); + inputBR.Dispose(); + } + if (inputTL != null) + { + ImageCopyResampled(output, inputTL, 0, 0, 0, 0); + inputTL.Dispose(); + } + if (inputTR != null) + { + ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0); + inputTR.Dispose(); + } + + // Write the modified output + try + { + using (Bitmap final = new Bitmap(output)) + { + output.Dispose(); + final.Save(outputFile, ImageFormat.Jpeg); + } + } + catch (Exception e) + { + m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e); + } + + // Save also as png? + + return true; + } + + #region Image utilities + + private void FillImage(Bitmap bm, Color c) + { + for (int x = 0; x < bm.Width; x++) + for (int y = 0; y < bm.Height; y++) + bm.SetPixel(x, y, c); + } + + private void ImageCopyResampled(Bitmap output, Bitmap input, int destX, int destY, int srcX, int srcY) + { + int resamplingRateX = 2; // (input.Width - srcX) / (output.Width - destX); + int resamplingRateY = 2; // (input.Height - srcY) / (output.Height - destY); + + for (int x = destX; x < destX + HALF_WIDTH; x++) + for (int y = destY; y < destY + HALF_WIDTH; y++) + { + Color p = input.GetPixel(srcX + (x - destX) * resamplingRateX, srcY + (y - destY) * resamplingRateY); + output.SetPixel(x, y, p); + } + } + + #endregion + } +} diff --git a/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs b/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e5f1bf9b3d --- /dev/null +++ b/OpenSim/Services/MapImageService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.MapImageService")] +[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("5e679df7-1d2a-401a-8966-b93677bb5839")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/PresenceService/PresenceService.cs b/OpenSim/Services/PresenceService/PresenceService.cs new file mode 100644 index 0000000000..0fe0200671 --- /dev/null +++ b/OpenSim/Services/PresenceService/PresenceService.cs @@ -0,0 +1,201 @@ +/* + * 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.Net; +using System.Reflection; +using Nini.Config; +using log4net; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenMetaverse; + +namespace OpenSim.Services.PresenceService +{ + public class PresenceService : PresenceServiceBase, IPresenceService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected bool m_allowDuplicatePresences = false; + + public PresenceService(IConfigSource config) + : base(config) + { + m_log.Debug("[PRESENCE SERVICE]: Starting presence service"); + + IConfig presenceConfig = config.Configs["PresenceService"]; + if (presenceConfig != null) + { + m_allowDuplicatePresences = presenceConfig.GetBoolean("AllowDuplicatePresences", m_allowDuplicatePresences); + } + } + + public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) + { + PresenceData prevUser = GetUser(userID); + + if (!m_allowDuplicatePresences && (prevUser != null)) + m_Database.Delete("UserID", userID.ToString()); + + PresenceData data = new PresenceData(); + + data.UserID = userID; + data.RegionID = UUID.Zero; + data.SessionID = sessionID; + data.Data = new Dictionary(); + data.Data["SecureSessionID"] = secureSessionID.ToString(); + + m_Database.Store(data); + + string prevUserStr = ""; + if (prevUser != null) + prevUserStr = string.Format(". This user was already logged-in: session {0}, region {1}", prevUser.SessionID, prevUser.RegionID); + + m_log.DebugFormat("[PRESENCE SERVICE]: LoginAgent: session {0}, user {1}, region {2}, secure session {3}{4}", + data.SessionID, data.UserID, data.RegionID, secureSessionID, prevUserStr); + + return true; + } + + public bool LogoutAgent(UUID sessionID) + { + PresenceInfo presence = GetAgent(sessionID); + + m_log.DebugFormat("[PRESENCE SERVICE]: LogoutAgent: session {0}, user {1}, region {2}", + sessionID, + (presence == null) ? null : presence.UserID, + (presence == null) ? null : presence.RegionID.ToString()); + + return m_Database.Delete("SessionID", sessionID.ToString()); + } + + public bool LogoutRegionAgents(UUID regionID) + { + PresenceData[] prevSessions = GetRegionAgents(regionID); + + if ((prevSessions == null) || (prevSessions.Length == 0)) + return true; + + m_log.DebugFormat("[PRESENCE SERVICE]: Logout users in region {0}: {1}", regionID, + string.Join(", ", Array.ConvertAll(prevSessions, session => session.UserID))); + + // There's a small chance that LogoutRegionAgents() will logout different users than the + // list that was logged above, but it's unlikely and not worth dealing with. + + m_Database.LogoutRegionAgents(regionID); + + return true; + } + + + public bool ReportAgent(UUID sessionID, UUID regionID) + { + try + { + PresenceData presence = m_Database.Get(sessionID); + + bool success; + if (presence == null) + success = false; + else + success = m_Database.ReportAgent(sessionID, regionID); + + m_log.DebugFormat("[PRESENCE SERVICE]: ReportAgent{0}: session {1}, user {2}, region {3}. Previously: {4}", + success ? "" : " failed", + sessionID, (presence == null) ? null : presence.UserID, regionID, + (presence == null) ? "not logged-in" : "region " + presence.RegionID); + + return success; + } + catch (Exception e) + { + m_log.Debug(string.Format("[PRESENCE SERVICE]: ReportAgent for session {0} threw exception ", sessionID), e); + return false; + } + } + + public PresenceInfo GetAgent(UUID sessionID) + { + PresenceInfo ret = new PresenceInfo(); + + PresenceData data = m_Database.Get(sessionID); + if (data == null) + return null; + + ret.UserID = data.UserID; + ret.RegionID = data.RegionID; + + return ret; + } + + public PresenceInfo[] GetAgents(string[] userIDs) + { + List info = new List(); + + foreach (string userIDStr in userIDs) + { + PresenceData[] data = m_Database.Get("UserID", userIDStr); + + foreach (PresenceData d in data) + { + PresenceInfo ret = new PresenceInfo(); + + ret.UserID = d.UserID; + ret.RegionID = d.RegionID; + + info.Add(ret); + } + +// m_log.DebugFormat( +// "[PRESENCE SERVICE]: GetAgents for {0} found {1} presences", userIDStr, data.Length); + } + + return info.ToArray(); + } + + /// + /// Return the user's Presence. This only really works well if !AllowDuplicatePresences, but that's the default. + /// + private PresenceData GetUser(string userID) + { + PresenceData[] data = m_Database.Get("UserID", userID); + if (data.Length > 0) + return data[0]; + else + return null; + } + + private PresenceData[] GetRegionAgents(UUID regionID) + { + return m_Database.Get("RegionID", regionID.ToString()); + } + + } +} \ No newline at end of file diff --git a/OpenSim/Services/PresenceService/PresenceServiceBase.cs b/OpenSim/Services/PresenceService/PresenceServiceBase.cs new file mode 100644 index 0000000000..a4adb2f0f1 --- /dev/null +++ b/OpenSim/Services/PresenceService/PresenceServiceBase.cs @@ -0,0 +1,84 @@ +/* + * 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.Services.PresenceService +{ + public class PresenceServiceBase : ServiceBase + { + protected IPresenceData m_Database = null; + + public PresenceServiceBase(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "Presence"; + + // + // 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); + } + + // + // [PresenceService] section overrides [DatabaseService], if it exists + // + IConfig presenceConfig = config.Configs["PresenceService"]; + if (presenceConfig != null) + { + dllName = presenceConfig.GetString("StorageProvider", dllName); + connString = presenceConfig.GetString("ConnectionString", connString); + realm = presenceConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + + } + } +} diff --git a/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs b/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..9ef0ff36e1 --- /dev/null +++ b/OpenSim/Services/PresenceService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.PresenceService")] +[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("a875a0bd-eab0-40a2-b5c4-3afddc3b4d2d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/SimulationService/SimulationDataService.cs b/OpenSim/Services/SimulationService/SimulationDataService.cs new file mode 100644 index 0000000000..d9684c49d6 --- /dev/null +++ b/OpenSim/Services/SimulationService/SimulationDataService.cs @@ -0,0 +1,191 @@ +/* + * 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 log4net; +using Nini.Config; +using System.Reflection; +using OpenSim.Services.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Services.SimulationService +{ + public class SimulationDataService : ServiceBase, ISimulationDataService + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + protected ISimulationDataStore m_database; + + public SimulationDataService(IConfigSource config) + : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + + // Try reading the [DatabaseService] section, if it exists + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + dllName = dbConfig.GetString("StorageProvider", String.Empty); + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + // Try reading the [SimulationDataStore] section + IConfig simConfig = config.Configs["SimulationDataStore"]; + if (simConfig != null) + { + dllName = simConfig.GetString("StorageProvider", dllName); + connString = simConfig.GetString("ConnectionString", connString); + } + + // We tried, but this doesn't exist. We can't proceed + if (dllName == String.Empty) + throw new Exception("No StorageProvider configured"); + + m_database = LoadPlugin(dllName, new Object[] { connString }); + if (m_database == null) + throw new Exception("Could not find a storage interface in the given module"); + } + + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + m_database.StoreObject(obj, regionUUID); + } + + public void RemoveObject(UUID uuid, UUID regionUUID) + { + m_database.RemoveObject(uuid, regionUUID); + } + + public void StorePrimInventory(UUID primID, ICollection items) + { + m_database.StorePrimInventory(primID, items); + } + + public List LoadObjects(UUID regionUUID) + { + return m_database.LoadObjects(regionUUID); + } + + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_database.StoreTerrain(terrain, regionID); + } + + public void StoreTerrain(double[,] terrain, UUID regionID) + { + m_database.StoreTerrain(terrain, regionID); + } + + public double[,] LoadTerrain(UUID regionID) + { + return m_database.LoadTerrain(regionID); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_database.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + + public void StoreLandObject(ILandObject Parcel) + { + m_database.StoreLandObject(Parcel); + } + + public void RemoveLandObject(UUID globalID) + { + m_database.RemoveLandObject(globalID); + } + + public List LoadLandObjects(UUID regionUUID) + { + return m_database.LoadLandObjects(regionUUID); + } + + public void StoreRegionSettings(RegionSettings rs) + { + m_database.StoreRegionSettings(rs); + } + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + return m_database.LoadRegionSettings(regionUUID); + } + + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + return m_database.LoadRegionWindlightSettings(regionUUID); + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + m_database.StoreRegionWindlightSettings(wl); + } + public void RemoveRegionWindlightSettings(UUID regionID) + { + m_database.RemoveRegionWindlightSettings(regionID); + } + + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + return m_database.LoadRegionEnvironmentSettings(regionUUID); + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + m_database.StoreRegionEnvironmentSettings(regionUUID, settings); + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + m_database.RemoveRegionEnvironmentSettings(regionUUID); + } + + public void SaveExtra(UUID regionID, string name, string val) + { + m_database.SaveExtra(regionID, name, val); + } + + public void RemoveExtra(UUID regionID, string name) + { + m_database.RemoveExtra(regionID, name); + } + + public Dictionary GetExtra(UUID regionID) + { + return m_database.GetExtra(regionID); + } + } +} diff --git a/OpenSim/Services/UserAccountService/AgentPreferencesService.cs b/OpenSim/Services/UserAccountService/AgentPreferencesService.cs new file mode 100644 index 0000000000..1808ee5009 --- /dev/null +++ b/OpenSim/Services/UserAccountService/AgentPreferencesService.cs @@ -0,0 +1,82 @@ +/* + * 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 Nini.Config; +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.UserAccountService +{ + public class AgentPreferencesService : AgentPreferencesServiceBase, IAgentPreferencesService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public AgentPreferencesService(IConfigSource config) : base(config) + { + m_log.Debug("[AGENT PREFERENCES SERVICE]: Starting agent preferences service"); + } + + public AgentPrefs GetAgentPreferences(UUID principalID) + { + AgentPreferencesData d = m_Database.GetPrefs(principalID); + AgentPrefs prefs = (d == null) ? new AgentPrefs(principalID) : new AgentPrefs(d.Data); + return prefs; + } + + public bool StoreAgentPreferences(AgentPrefs data) + { + AgentPreferencesData d = new AgentPreferencesData(); + d.Data = new Dictionary(); + d.Data["PrincipalID"] = data.PrincipalID.ToString(); + d.Data["AccessPrefs"] = data.AccessPrefs; + d.Data["HoverHeight"] = data.HoverHeight.ToString(); + d.Data["Language"] = data.Language; + d.Data["LanguageIsPublic"] = (data.LanguageIsPublic ? "1" : "0"); + d.Data["PermEveryone"] = data.PermEveryone.ToString(); + d.Data["PermGroup"] = data.PermGroup.ToString(); + d.Data["PermNextOwner"] = data.PermNextOwner.ToString(); + return m_Database.Store(d); + } + + public string GetLang(UUID principalID) + { + AgentPrefs data = GetAgentPreferences(principalID); + if (data != null) + { + if (data.LanguageIsPublic) + return data.Language; + } + return "en-us"; + } + } +} diff --git a/OpenSim/Services/UserAccountService/AgentPreferencesServiceBase.cs b/OpenSim/Services/UserAccountService/AgentPreferencesServiceBase.cs new file mode 100644 index 0000000000..597434903a --- /dev/null +++ b/OpenSim/Services/UserAccountService/AgentPreferencesServiceBase.cs @@ -0,0 +1,73 @@ +/* + * 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.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Services.UserAccountService +{ + public class AgentPreferencesServiceBase: ServiceBase + { + protected IAgentPreferencesData m_Database = null; + + public AgentPreferencesServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "AgentPrefs"; + + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + dllName = dbConfig.GetString("StorageProvider", String.Empty); + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + IConfig userConfig = config.Configs["AgentPreferencesService"]; + if (userConfig == null) + throw new Exception("No AgentPreferencesService configuration"); + + dllName = userConfig.GetString("StorageProvider", dllName); + + if (dllName == String.Empty) + throw new Exception("No StorageProvider configured"); + + connString = userConfig.GetString("ConnectionString", connString); + + realm = userConfig.GetString("Realm", realm); + + m_Database = LoadPlugin(dllName, new Object[] {connString, realm}); + + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + } + } +} diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs new file mode 100644 index 0000000000..e4bcf15fa5 --- /dev/null +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -0,0 +1,265 @@ +/* + * 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 Nini.Config; +using OpenSim.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +using OpenMetaverse; +using log4net; + +namespace OpenSim.Services.UserAccountService +{ + public class GridUserService : GridUserServiceBase, IGridUserService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static bool m_Initialized; + + public GridUserService(IConfigSource config) : base(config) + { + m_log.Debug("[GRID USER SERVICE]: Starting user grid service"); + + if (!m_Initialized) + { + m_Initialized = true; + + MainConsole.Instance.Commands.AddCommand( + "Users", false, + "show grid user", + "show grid user ", + "Show grid user entry or entries that match or start with the given ID. This will normally be a UUID.", + "This is for debug purposes to see what data is found for a particular user id.", + HandleShowGridUser); + + MainConsole.Instance.Commands.AddCommand( + "Users", false, + "show grid users online", + "show grid users online", + "Show number of grid users registered as online.", + "This number may not be accurate as a region may crash or not be cleanly shutdown and leave grid users shown as online\n." + + "For this reason, users online for more than 5 days are not currently counted", + HandleShowGridUsersOnline); + } + } + + protected void HandleShowGridUser(string module, string[] cmdparams) + { + if (cmdparams.Length != 4) + { + MainConsole.Instance.Output("Usage: show grid user "); + return; + } + + GridUserData[] data = m_Database.GetAll(cmdparams[3]); + + foreach (GridUserData gu in data) + { + ConsoleDisplayList cdl = new ConsoleDisplayList(); + + cdl.AddRow("User ID", gu.UserID); + + foreach (KeyValuePair kvp in gu.Data) + cdl.AddRow(kvp.Key, kvp.Value); + + MainConsole.Instance.Output(cdl.ToString()); + } + + MainConsole.Instance.OutputFormat("Entries: {0}", data.Length); + } + + protected void HandleShowGridUsersOnline(string module, string[] cmdparams) + { +// if (cmdparams.Length != 4) +// { +// MainConsole.Instance.Output("Usage: show grid users online"); +// return; +// } + +// int onlineCount; + int onlineRecentlyCount = 0; + + DateTime now = DateTime.UtcNow; + + foreach (GridUserData gu in m_Database.GetAll("")) + { + if (bool.Parse(gu.Data["Online"])) + { +// onlineCount++; + + int unixLoginTime = int.Parse(gu.Data["Login"]); + + if ((now - Util.ToDateTime(unixLoginTime)).Days < 5) + onlineRecentlyCount++; + } + } + + MainConsole.Instance.OutputFormat("Users online: {0}", onlineRecentlyCount); + } + + private GridUserData GetGridUserData(string userID) + { + GridUserData d = null; + if (userID.Length > 36) // it's a UUI + { + d = m_Database.Get(userID); + } + else // it's a UUID + { + GridUserData[] ds = m_Database.GetAll(userID); + if (ds == null) + return null; + + if (ds.Length > 0) + { + d = ds[0]; + foreach (GridUserData dd in ds) + if (dd.UserID.Length > d.UserID.Length) // find the longest + d = dd; + } + } + + return d; + } + + public virtual GridUserInfo GetGridUserInfo(string userID) + { + GridUserData d = GetGridUserData(userID); + + if (d == null) + return null; + + GridUserInfo info = new GridUserInfo(); + info.UserID = d.UserID; + info.HomeRegionID = new UUID(d.Data["HomeRegionID"]); + info.HomePosition = Vector3.Parse(d.Data["HomePosition"]); + info.HomeLookAt = Vector3.Parse(d.Data["HomeLookAt"]); + + info.LastRegionID = new UUID(d.Data["LastRegionID"]); + info.LastPosition = Vector3.Parse(d.Data["LastPosition"]); + info.LastLookAt = Vector3.Parse(d.Data["LastLookAt"]); + + info.Online = bool.Parse(d.Data["Online"]); + info.Login = Util.ToDateTime(Convert.ToInt32(d.Data["Login"])); + info.Logout = Util.ToDateTime(Convert.ToInt32(d.Data["Logout"])); + + return info; + } + + public virtual GridUserInfo[] GetGridUserInfo(string[] userIDs) + { + List ret = new List(); + + foreach (string id in userIDs) + ret.Add(GetGridUserInfo(id)); + + return ret.ToArray(); + } + + public GridUserInfo LoggedIn(string userID) + { + m_log.DebugFormat("[GRID USER SERVICE]: User {0} is online", userID); + + GridUserData d = GetGridUserData(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["Online"] = true.ToString(); + d.Data["Login"] = Util.UnixTimeSinceEpoch().ToString(); + + m_Database.Store(d); + + return GetGridUserInfo(userID); + } + + public bool LoggedOut(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { + m_log.DebugFormat("[GRID USER SERVICE]: User {0} is offline", userID); + + GridUserData d = GetGridUserData(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["Online"] = false.ToString(); + d.Data["Logout"] = Util.UnixTimeSinceEpoch().ToString(); + d.Data["LastRegionID"] = regionID.ToString(); + d.Data["LastPosition"] = lastPosition.ToString(); + d.Data["LastLookAt"] = lastLookAt.ToString(); + + return m_Database.Store(d); + } + + public bool SetHome(string userID, UUID homeID, Vector3 homePosition, Vector3 homeLookAt) + { + GridUserData d = GetGridUserData(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["HomeRegionID"] = homeID.ToString(); + d.Data["HomePosition"] = homePosition.ToString(); + d.Data["HomeLookAt"] = homeLookAt.ToString(); + + return m_Database.Store(d); + } + + public bool SetLastPosition(string userID, UUID sessionID, UUID regionID, Vector3 lastPosition, Vector3 lastLookAt) + { +// m_log.DebugFormat("[GRID USER SERVICE]: SetLastPosition for {0}", userID); + + GridUserData d = GetGridUserData(userID); + + if (d == null) + { + d = new GridUserData(); + d.UserID = userID; + } + + d.Data["LastRegionID"] = regionID.ToString(); + d.Data["LastPosition"] = lastPosition.ToString(); + d.Data["LastLookAt"] = lastLookAt.ToString(); + + return m_Database.Store(d); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/UserAccountService/GridUserServiceBase.cs b/OpenSim/Services/UserAccountService/GridUserServiceBase.cs new file mode 100644 index 0000000000..8c5f5df2b7 --- /dev/null +++ b/OpenSim/Services/UserAccountService/GridUserServiceBase.cs @@ -0,0 +1,82 @@ +/* + * 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.Services.UserAccountService +{ + public class GridUserServiceBase : ServiceBase + { + protected IGridUserData m_Database = null; + + public GridUserServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "GridUser"; + + // + // 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); + } + + // + // [GridUsetService] section overrides [DatabaseService], if it exists + // + IConfig usersConfig = config.Configs["GridUserService"]; + if (usersConfig != null) + { + dllName = usersConfig.GetString("StorageProvider", dllName); + connString = usersConfig.GetString("ConnectionString", connString); + realm = usersConfig.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(dllName, new Object[] { connString, realm }); + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module " + dllName); + } + } +} \ No newline at end of file diff --git a/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs b/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..33933a028b --- /dev/null +++ b/OpenSim/Services/UserAccountService/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Services.UserAccountService")] +[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("fdb4771d-9928-4db4-aeb5-90cac2976584")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs new file mode 100644 index 0000000000..2e19ece4d0 --- /dev/null +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -0,0 +1,714 @@ +/* + * 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 Nini.Config; +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Framework.Console; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using PermissionMask = OpenSim.Framework.PermissionMask; + +namespace OpenSim.Services.UserAccountService +{ + public class UserAccountService : UserAccountServiceBase, IUserAccountService + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static UserAccountService m_RootInstance; + + /// + /// Should we create default entries (minimum body parts/clothing, avatar wearable entries) for a new avatar? + /// + private bool m_CreateDefaultAvatarEntries; + + protected IGridService m_GridService; + protected IAuthenticationService m_AuthenticationService; + protected IGridUserService m_GridUserService; + protected IInventoryService m_InventoryService; + protected IAvatarService m_AvatarService; + + public UserAccountService(IConfigSource config) + : base(config) + { + IConfig userConfig = config.Configs["UserAccountService"]; + if (userConfig == null) + throw new Exception("No UserAccountService configuration"); + + string gridServiceDll = userConfig.GetString("GridService", string.Empty); + if (gridServiceDll != string.Empty) + m_GridService = LoadPlugin(gridServiceDll, new Object[] { config }); + + string authServiceDll = userConfig.GetString("AuthenticationService", string.Empty); + if (authServiceDll != string.Empty) + m_AuthenticationService = LoadPlugin(authServiceDll, new Object[] { config }); + + string presenceServiceDll = userConfig.GetString("GridUserService", string.Empty); + if (presenceServiceDll != string.Empty) + m_GridUserService = LoadPlugin(presenceServiceDll, new Object[] { config }); + + string invServiceDll = userConfig.GetString("InventoryService", string.Empty); + if (invServiceDll != string.Empty) + m_InventoryService = LoadPlugin(invServiceDll, new Object[] { config }); + + string avatarServiceDll = userConfig.GetString("AvatarService", string.Empty); + if (avatarServiceDll != string.Empty) + m_AvatarService = LoadPlugin(avatarServiceDll, new Object[] { config }); + + m_CreateDefaultAvatarEntries = userConfig.GetBoolean("CreateDefaultAvatarEntries", false); + + // In case there are several instances of this class in the same process, + // the console commands are only registered for the root instance + if (m_RootInstance == null && MainConsole.Instance != null) + { + m_RootInstance = this; + MainConsole.Instance.Commands.AddCommand("Users", false, + "create user", + "create user [ [ [ [ []]]]]", + "Create a new user", HandleCreateUser); + + MainConsole.Instance.Commands.AddCommand("Users", false, + "reset user password", + "reset user password [ [ []]]", + "Reset a user password", HandleResetUserPassword); + + MainConsole.Instance.Commands.AddCommand("Users", false, + "reset user email", + "reset user email [ [ []]]", + "Reset a user email address", HandleResetUserEmail); + + MainConsole.Instance.Commands.AddCommand("Users", false, + "set user level", + "set user level [ [ []]]", + "Set user level. If >= 200 and 'allow_grid_gods = true' in OpenSim.ini, " + + "this account will be treated as god-moded. " + + "It will also affect the 'login level' command. ", + HandleSetUserLevel); + + MainConsole.Instance.Commands.AddCommand("Users", false, + "show account", + "show account ", + "Show account details for the given user", HandleShowAccount); + } + } + + #region IUserAccountService + + public UserAccount GetUserAccount(UUID scopeID, string firstName, + string lastName) + { +// m_log.DebugFormat( +// "[USER ACCOUNT SERVICE]: Retrieving account by username for {0} {1}, scope {2}", +// firstName, lastName, scopeID); + + UserAccountData[] d; + + if (scopeID != UUID.Zero) + { + d = m_Database.Get( + new string[] { "ScopeID", "FirstName", "LastName" }, + new string[] { scopeID.ToString(), firstName, lastName }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "FirstName", "LastName" }, + new string[] { UUID.Zero.ToString(), firstName, lastName }); + } + } + else + { + d = m_Database.Get( + new string[] { "FirstName", "LastName" }, + new string[] { firstName, lastName }); + } + + if (d.Length < 1) + return null; + + return MakeUserAccount(d[0]); + } + + private UserAccount MakeUserAccount(UserAccountData d) + { + UserAccount u = new UserAccount(); + u.FirstName = d.FirstName; + u.LastName = d.LastName; + u.PrincipalID = d.PrincipalID; + u.ScopeID = d.ScopeID; + if (d.Data.ContainsKey("Email") && d.Data["Email"] != null) + u.Email = d.Data["Email"].ToString(); + else + u.Email = string.Empty; + u.Created = Convert.ToInt32(d.Data["Created"].ToString()); + if (d.Data.ContainsKey("UserTitle") && d.Data["UserTitle"] != null) + u.UserTitle = d.Data["UserTitle"].ToString(); + else + u.UserTitle = string.Empty; + if (d.Data.ContainsKey("UserLevel") && d.Data["UserLevel"] != null) + Int32.TryParse(d.Data["UserLevel"], out u.UserLevel); + if (d.Data.ContainsKey("UserFlags") && d.Data["UserFlags"] != null) + Int32.TryParse(d.Data["UserFlags"], out u.UserFlags); + + if (d.Data.ContainsKey("ServiceURLs") && d.Data["ServiceURLs"] != null) + { + string[] URLs = d.Data["ServiceURLs"].ToString().Split(new char[] { ' ' }); + u.ServiceURLs = new Dictionary(); + + foreach (string url in URLs) + { + string[] parts = url.Split(new char[] { '=' }); + + if (parts.Length != 2) + continue; + + string name = System.Web.HttpUtility.UrlDecode(parts[0]); + string val = System.Web.HttpUtility.UrlDecode(parts[1]); + + u.ServiceURLs[name] = val; + } + } + else + u.ServiceURLs = new Dictionary(); + + return u; + } + + public UserAccount GetUserAccount(UUID scopeID, string email) + { + UserAccountData[] d; + + if (scopeID != UUID.Zero) + { + d = m_Database.Get( + new string[] { "ScopeID", "Email" }, + new string[] { scopeID.ToString(), email }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "Email" }, + new string[] { UUID.Zero.ToString(), email }); + } + } + else + { + d = m_Database.Get( + new string[] { "Email" }, + new string[] { email }); + } + + if (d.Length < 1) + return null; + + return MakeUserAccount(d[0]); + } + + public UserAccount GetUserAccount(UUID scopeID, UUID principalID) + { + UserAccountData[] d; + + if (scopeID != UUID.Zero) + { + d = m_Database.Get( + new string[] { "ScopeID", "PrincipalID" }, + new string[] { scopeID.ToString(), principalID.ToString() }); + if (d.Length < 1) + { + d = m_Database.Get( + new string[] { "ScopeID", "PrincipalID" }, + new string[] { UUID.Zero.ToString(), principalID.ToString() }); + } + } + else + { + d = m_Database.Get( + new string[] { "PrincipalID" }, + new string[] { principalID.ToString() }); + } + + if (d.Length < 1) + { + return null; + } + + return MakeUserAccount(d[0]); + } + + public void InvalidateCache(UUID userID) + { + } + + public bool StoreUserAccount(UserAccount data) + { +// m_log.DebugFormat( +// "[USER ACCOUNT SERVICE]: Storing user account for {0} {1} {2}, scope {3}", +// data.FirstName, data.LastName, data.PrincipalID, data.ScopeID); + + UserAccountData d = new UserAccountData(); + + d.FirstName = data.FirstName; + d.LastName = data.LastName; + d.PrincipalID = data.PrincipalID; + d.ScopeID = data.ScopeID; + d.Data = new Dictionary(); + d.Data["Email"] = data.Email; + d.Data["Created"] = data.Created.ToString(); + d.Data["UserLevel"] = data.UserLevel.ToString(); + d.Data["UserFlags"] = data.UserFlags.ToString(); + if (data.UserTitle != null) + d.Data["UserTitle"] = data.UserTitle.ToString(); + + List parts = new List(); + + foreach (KeyValuePair kvp in data.ServiceURLs) + { + string key = System.Web.HttpUtility.UrlEncode(kvp.Key); + string val = System.Web.HttpUtility.UrlEncode(kvp.Value.ToString()); + parts.Add(key + "=" + val); + } + + d.Data["ServiceURLs"] = string.Join(" ", parts.ToArray()); + + return m_Database.Store(d); + } + + public List GetUserAccounts(UUID scopeID, string query) + { + UserAccountData[] d = m_Database.GetUsers(scopeID, query); + + if (d == null) + return new List(); + + List ret = new List(); + + foreach (UserAccountData data in d) + ret.Add(MakeUserAccount(data)); + + return ret; + } + + #endregion + + #region Console commands + + /// + /// Handle the create user command from the console. + /// + /// string array with parameters: firstname, lastname, password, locationX, locationY, email + protected void HandleCreateUser(string module, string[] cmdparams) + { + string firstName; + string lastName; + string password; + string email; + string rawPrincipalId; + + List excluded = new List(new char[]{' '}); + + if (cmdparams.Length < 3) + firstName = MainConsole.Instance.CmdPrompt("First name", "Default", excluded); + else firstName = cmdparams[2]; + + if (cmdparams.Length < 4) + lastName = MainConsole.Instance.CmdPrompt("Last name", "User", excluded); + else lastName = cmdparams[3]; + + if (cmdparams.Length < 5) + password = MainConsole.Instance.PasswdPrompt("Password"); + else password = cmdparams[4]; + + if (cmdparams.Length < 6) + email = MainConsole.Instance.CmdPrompt("Email", ""); + else email = cmdparams[5]; + + if (cmdparams.Length < 7) + rawPrincipalId = MainConsole.Instance.CmdPrompt("User ID", UUID.Random().ToString()); + else + rawPrincipalId = cmdparams[6]; + + UUID principalId = UUID.Zero; + if (!UUID.TryParse(rawPrincipalId, out principalId)) + throw new Exception(string.Format("ID {0} is not a valid UUID", rawPrincipalId)); + + CreateUser(UUID.Zero, principalId, firstName, lastName, password, email); + } + + protected void HandleShowAccount(string module, string[] cmdparams) + { + if (cmdparams.Length != 4) + { + MainConsole.Instance.Output("Usage: show account "); + return; + } + + string firstName = cmdparams[2]; + string lastName = cmdparams[3]; + + UserAccount ua = GetUserAccount(UUID.Zero, firstName, lastName); + + if (ua == null) + { + MainConsole.Instance.OutputFormat("No user named {0} {1}", firstName, lastName); + return; + } + + MainConsole.Instance.OutputFormat("Name: {0}", ua.Name); + MainConsole.Instance.OutputFormat("ID: {0}", ua.PrincipalID); + MainConsole.Instance.OutputFormat("Title: {0}", ua.UserTitle); + MainConsole.Instance.OutputFormat("E-mail: {0}", ua.Email); + MainConsole.Instance.OutputFormat("Created: {0}", Utils.UnixTimeToDateTime(ua.Created)); + MainConsole.Instance.OutputFormat("Level: {0}", ua.UserLevel); + MainConsole.Instance.OutputFormat("Flags: {0}", ua.UserFlags); + foreach (KeyValuePair kvp in ua.ServiceURLs) + MainConsole.Instance.OutputFormat("{0}: {1}", kvp.Key, kvp.Value); + } + + protected void HandleResetUserPassword(string module, string[] cmdparams) + { + string firstName; + string lastName; + string newPassword; + + if (cmdparams.Length < 4) + firstName = MainConsole.Instance.CmdPrompt("First name"); + else firstName = cmdparams[3]; + + if (cmdparams.Length < 5) + lastName = MainConsole.Instance.CmdPrompt("Last name"); + else lastName = cmdparams[4]; + + if (cmdparams.Length < 6) + newPassword = MainConsole.Instance.PasswdPrompt("New password"); + else newPassword = cmdparams[5]; + + UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) + { + MainConsole.Instance.OutputFormat("No such user as {0} {1}", firstName, lastName); + return; + } + + bool success = false; + if (m_AuthenticationService != null) + success = m_AuthenticationService.SetPassword(account.PrincipalID, newPassword); + + if (!success) + MainConsole.Instance.OutputFormat("Unable to reset password for account {0} {1}.", firstName, lastName); + else + MainConsole.Instance.OutputFormat("Password reset for user {0} {1}", firstName, lastName); + } + + protected void HandleResetUserEmail(string module, string[] cmdparams) + { + string firstName; + string lastName; + string newEmail; + + if (cmdparams.Length < 4) + firstName = MainConsole.Instance.CmdPrompt("First name"); + else firstName = cmdparams[3]; + + if (cmdparams.Length < 5) + lastName = MainConsole.Instance.CmdPrompt("Last name"); + else lastName = cmdparams[4]; + + if (cmdparams.Length < 6) + newEmail = MainConsole.Instance.PasswdPrompt("New Email"); + else newEmail = cmdparams[5]; + + UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) + { + MainConsole.Instance.OutputFormat("No such user as {0} {1}", firstName, lastName); + return; + } + + bool success = false; + + account.Email = newEmail; + + success = StoreUserAccount(account); + if (!success) + MainConsole.Instance.OutputFormat("Unable to set Email for account {0} {1}.", firstName, lastName); + else + MainConsole.Instance.OutputFormat("User Email set for user {0} {1} to {2}", firstName, lastName, account.Email); + } + + + protected void HandleSetUserLevel(string module, string[] cmdparams) + { + string firstName; + string lastName; + string rawLevel; + int level; + + if (cmdparams.Length < 4) + firstName = MainConsole.Instance.CmdPrompt("First name"); + else firstName = cmdparams[3]; + + if (cmdparams.Length < 5) + lastName = MainConsole.Instance.CmdPrompt("Last name"); + else lastName = cmdparams[4]; + + UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); + if (account == null) { + MainConsole.Instance.OutputFormat("No such user"); + return; + } + + if (cmdparams.Length < 6) + rawLevel = MainConsole.Instance.CmdPrompt("User level"); + else rawLevel = cmdparams[5]; + + if(int.TryParse(rawLevel, out level) == false) { + MainConsole.Instance.OutputFormat("Invalid user level"); + return; + } + + account.UserLevel = level; + + bool success = StoreUserAccount(account); + if (!success) + MainConsole.Instance.OutputFormat("Unable to set user level for account {0} {1}.", firstName, lastName); + else + MainConsole.Instance.OutputFormat("User level set for user {0} {1} to {2}", firstName, lastName, level); + } + + #endregion + + /// + /// Create a user + /// + /// Allows hosting of multiple grids in a single database. Normally left as UUID.Zero + /// ID of the user + /// + /// + /// + /// + public UserAccount CreateUser(UUID scopeID, UUID principalID, string firstName, string lastName, string password, string email) + { + UserAccount account = GetUserAccount(UUID.Zero, firstName, lastName); + if (null == account) + { + account = new UserAccount(UUID.Zero, principalID, firstName, lastName, email); + if (account.ServiceURLs == null || (account.ServiceURLs != null && account.ServiceURLs.Count == 0)) + { + account.ServiceURLs = new Dictionary(); + account.ServiceURLs["HomeURI"] = string.Empty; + account.ServiceURLs["InventoryServerURI"] = string.Empty; + account.ServiceURLs["AssetServerURI"] = string.Empty; + } + + if (StoreUserAccount(account)) + { + bool success; + if (m_AuthenticationService != null) + { + success = m_AuthenticationService.SetPassword(account.PrincipalID, password); + if (!success) + m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to set password for account {0} {1}.", + firstName, lastName); + } + + GridRegion home = null; + if (m_GridService != null) + { + List defaultRegions = m_GridService.GetDefaultRegions(UUID.Zero); + if (defaultRegions != null && defaultRegions.Count >= 1) + home = defaultRegions[0]; + + if (m_GridUserService != null && home != null) + m_GridUserService.SetHome(account.PrincipalID.ToString(), home.RegionID, new Vector3(128, 128, 0), new Vector3(0, 1, 0)); + else + m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to set home for account {0} {1}.", + firstName, lastName); + } + else + { + m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to retrieve home region for account {0} {1}.", + firstName, lastName); + } + + if (m_InventoryService != null) + { + success = m_InventoryService.CreateUserInventory(account.PrincipalID); + if (!success) + { + m_log.WarnFormat("[USER ACCOUNT SERVICE]: Unable to create inventory for account {0} {1}.", + firstName, lastName); + } + else + { + m_log.DebugFormat( + "[USER ACCOUNT SERVICE]: Created user inventory for {0} {1}", firstName, lastName); + } + + if (m_CreateDefaultAvatarEntries) + CreateDefaultAppearanceEntries(account.PrincipalID); + } + + m_log.InfoFormat( + "[USER ACCOUNT SERVICE]: Account {0} {1} {2} created successfully", + firstName, lastName, account.PrincipalID); + } + else + { + m_log.ErrorFormat("[USER ACCOUNT SERVICE]: Account creation failed for account {0} {1}", firstName, lastName); + } + } + else + { + m_log.ErrorFormat("[USER ACCOUNT SERVICE]: A user with the name {0} {1} already exists!", firstName, lastName); + } + + return account; + } + + protected void CreateDefaultAppearanceEntries(UUID principalID) + { + m_log.DebugFormat("[USER ACCOUNT SERVICE]: Creating default appearance items for {0}", principalID); + + InventoryFolderBase bodyPartsFolder = m_InventoryService.GetFolderForType(principalID, FolderType.BodyPart); + + InventoryItemBase eyes = new InventoryItemBase(UUID.Random(), principalID); + eyes.AssetID = new UUID("4bb6fa4d-1cd2-498a-a84c-95c1a0e745a7"); + eyes.Name = "Default Eyes"; + eyes.CreatorId = principalID.ToString(); + eyes.AssetType = (int)AssetType.Bodypart; + eyes.InvType = (int)InventoryType.Wearable; + eyes.Folder = bodyPartsFolder.ID; + eyes.BasePermissions = (uint)PermissionMask.All; + eyes.CurrentPermissions = (uint)PermissionMask.All; + eyes.EveryOnePermissions = (uint)PermissionMask.All; + eyes.GroupPermissions = (uint)PermissionMask.All; + eyes.NextPermissions = (uint)PermissionMask.All; + eyes.Flags = (uint)WearableType.Eyes; + m_InventoryService.AddItem(eyes); + + InventoryItemBase shape = new InventoryItemBase(UUID.Random(), principalID); + shape.AssetID = AvatarWearable.DEFAULT_BODY_ASSET; + shape.Name = "Default Shape"; + shape.CreatorId = principalID.ToString(); + shape.AssetType = (int)AssetType.Bodypart; + shape.InvType = (int)InventoryType.Wearable; + shape.Folder = bodyPartsFolder.ID; + shape.BasePermissions = (uint)PermissionMask.All; + shape.CurrentPermissions = (uint)PermissionMask.All; + shape.EveryOnePermissions = (uint)PermissionMask.All; + shape.GroupPermissions = (uint)PermissionMask.All; + shape.NextPermissions = (uint)PermissionMask.All; + shape.Flags = (uint)WearableType.Shape; + m_InventoryService.AddItem(shape); + + InventoryItemBase skin = new InventoryItemBase(UUID.Random(), principalID); + skin.AssetID = AvatarWearable.DEFAULT_SKIN_ASSET; + skin.Name = "Default Skin"; + skin.CreatorId = principalID.ToString(); + skin.AssetType = (int)AssetType.Bodypart; + skin.InvType = (int)InventoryType.Wearable; + skin.Folder = bodyPartsFolder.ID; + skin.BasePermissions = (uint)PermissionMask.All; + skin.CurrentPermissions = (uint)PermissionMask.All; + skin.EveryOnePermissions = (uint)PermissionMask.All; + skin.GroupPermissions = (uint)PermissionMask.All; + skin.NextPermissions = (uint)PermissionMask.All; + skin.Flags = (uint)WearableType.Skin; + m_InventoryService.AddItem(skin); + + InventoryItemBase hair = new InventoryItemBase(UUID.Random(), principalID); + hair.AssetID = AvatarWearable.DEFAULT_HAIR_ASSET; + hair.Name = "Default Hair"; + hair.CreatorId = principalID.ToString(); + hair.AssetType = (int)AssetType.Bodypart; + hair.InvType = (int)InventoryType.Wearable; + hair.Folder = bodyPartsFolder.ID; + hair.BasePermissions = (uint)PermissionMask.All; + hair.CurrentPermissions = (uint)PermissionMask.All; + hair.EveryOnePermissions = (uint)PermissionMask.All; + hair.GroupPermissions = (uint)PermissionMask.All; + hair.NextPermissions = (uint)PermissionMask.All; + hair.Flags = (uint)WearableType.Hair; + m_InventoryService.AddItem(hair); + + InventoryFolderBase clothingFolder = m_InventoryService.GetFolderForType(principalID, FolderType.Clothing); + + InventoryItemBase shirt = new InventoryItemBase(UUID.Random(), principalID); + shirt.AssetID = AvatarWearable.DEFAULT_SHIRT_ASSET; + shirt.Name = "Default Shirt"; + shirt.CreatorId = principalID.ToString(); + shirt.AssetType = (int)AssetType.Clothing; + shirt.InvType = (int)InventoryType.Wearable; + shirt.Folder = clothingFolder.ID; + shirt.BasePermissions = (uint)PermissionMask.All; + shirt.CurrentPermissions = (uint)PermissionMask.All; + shirt.EveryOnePermissions = (uint)PermissionMask.All; + shirt.GroupPermissions = (uint)PermissionMask.All; + shirt.NextPermissions = (uint)PermissionMask.All; + shirt.Flags = (uint)WearableType.Shirt; + m_InventoryService.AddItem(shirt); + + InventoryItemBase pants = new InventoryItemBase(UUID.Random(), principalID); + pants.AssetID = AvatarWearable.DEFAULT_PANTS_ASSET; + pants.Name = "Default Pants"; + pants.CreatorId = principalID.ToString(); + pants.AssetType = (int)AssetType.Clothing; + pants.InvType = (int)InventoryType.Wearable; + pants.Folder = clothingFolder.ID; + pants.BasePermissions = (uint)PermissionMask.All; + pants.CurrentPermissions = (uint)PermissionMask.All; + pants.EveryOnePermissions = (uint)PermissionMask.All; + pants.GroupPermissions = (uint)PermissionMask.All; + pants.NextPermissions = (uint)PermissionMask.All; + pants.Flags = (uint)WearableType.Pants; + m_InventoryService.AddItem(pants); + + if (m_AvatarService != null) + { + m_log.DebugFormat("[USER ACCOUNT SERVICE]: Creating default avatar entries for {0}", principalID); + + AvatarWearable[] wearables = new AvatarWearable[6]; + wearables[AvatarWearable.EYES] = new AvatarWearable(eyes.ID, eyes.AssetID); + wearables[AvatarWearable.BODY] = new AvatarWearable(shape.ID, shape.AssetID); + wearables[AvatarWearable.SKIN] = new AvatarWearable(skin.ID, skin.AssetID); + wearables[AvatarWearable.HAIR] = new AvatarWearable(hair.ID, hair.AssetID); + wearables[AvatarWearable.SHIRT] = new AvatarWearable(shirt.ID, shirt.AssetID); + wearables[AvatarWearable.PANTS] = new AvatarWearable(pants.ID, pants.AssetID); + + AvatarAppearance ap = new AvatarAppearance(); + for (int i = 0; i < 6; i++) + { + ap.SetWearable(i, wearables[i]); + } + + m_AvatarService.SetAppearance(principalID, ap); + } + } + } +} diff --git a/OpenSim/Services/UserAccountService/UserAccountServiceBase.cs b/OpenSim/Services/UserAccountService/UserAccountServiceBase.cs new file mode 100644 index 0000000000..c1a7b768d7 --- /dev/null +++ b/OpenSim/Services/UserAccountService/UserAccountServiceBase.cs @@ -0,0 +1,73 @@ +/* + * 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.Data; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Base; + +namespace OpenSim.Services.UserAccountService +{ + public class UserAccountServiceBase: ServiceBase + { + protected IUserAccountData m_Database = null; + + public UserAccountServiceBase(IConfigSource config) : base(config) + { + string dllName = String.Empty; + string connString = String.Empty; + string realm = "UserAccounts"; + + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + dllName = dbConfig.GetString("StorageProvider", String.Empty); + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + IConfig userConfig = config.Configs["UserAccountService"]; + if (userConfig == null) + throw new Exception("No UserAccountService configuration"); + + dllName = userConfig.GetString("StorageProvider", dllName); + + if (dllName == String.Empty) + throw new Exception("No StorageProvider configured"); + + connString = userConfig.GetString("ConnectionString", connString); + + realm = userConfig.GetString("Realm", realm); + + m_Database = LoadPlugin(dllName, new Object[] {connString, realm}); + + if (m_Database == null) + throw new Exception("Could not find a storage interface in the given module"); + } + } +} diff --git a/OpenSim/Services/UserProfilesService/UserProfilesService.cs b/OpenSim/Services/UserProfilesService/UserProfilesService.cs new file mode 100644 index 0000000000..96c13c02a9 --- /dev/null +++ b/OpenSim/Services/UserProfilesService/UserProfilesService.cs @@ -0,0 +1,263 @@ +/* + * 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 Nini.Config; +using log4net; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Services.UserAccountService; +using OpenSim.Data; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Services.ProfilesService +{ + public class UserProfilesService: UserProfilesServiceBase, IUserProfilesService + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + IUserAccountService userAccounts; + + public UserProfilesService(IConfigSource config, string configName): + base(config, configName) + { + IConfig Config = config.Configs[configName]; + if (Config == null) + { + m_log.Warn("[PROFILES SERVICE]: No configuration found!"); + return; + } + Object[] args = null; + + args = new Object[] { config }; + string accountService = Config.GetString("UserAccountService", String.Empty); + if (accountService != string.Empty) + userAccounts = ServerUtils.LoadPlugin(accountService, args); + + args = new Object[] { config }; + } + + #region Classifieds + public OSD AvatarClassifiedsRequest(UUID creatorId) + { + OSDArray records = ProfilesData.GetClassifiedRecords(creatorId); + + return records; + } + + public bool ClassifiedUpdate(UserClassifiedAdd ad, ref string result) + { + if(!ProfilesData.UpdateClassifiedRecord(ad, ref result)) + { + return false; + } + result = "success"; + return true; + } + + public bool ClassifiedDelete(UUID recordId) + { + if(ProfilesData.DeleteClassifiedRecord(recordId)) + return true; + + return false; + } + + public bool ClassifiedInfoRequest(ref UserClassifiedAdd ad, ref string result) + { + if(ProfilesData.GetClassifiedInfo(ref ad, ref result)) + return true; + + return false; + } + #endregion Classifieds + + #region Picks + public OSD AvatarPicksRequest(UUID creatorId) + { + OSDArray records = ProfilesData.GetAvatarPicks(creatorId); + + return records; + } + + public bool PickInfoRequest(ref UserProfilePick pick, ref string result) + { + pick = ProfilesData.GetPickInfo(pick.CreatorId, pick.PickId); + result = "OK"; + return true; + } + + public bool PicksUpdate(ref UserProfilePick pick, ref string result) + { + return ProfilesData.UpdatePicksRecord(pick); + } + + public bool PicksDelete(UUID pickId) + { + return ProfilesData.DeletePicksRecord(pickId); + } + #endregion Picks + + #region Notes + public bool AvatarNotesRequest(ref UserProfileNotes note) + { + return ProfilesData.GetAvatarNotes(ref note); + } + + public bool NotesUpdate(ref UserProfileNotes note, ref string result) + { + return ProfilesData.UpdateAvatarNotes(ref note, ref result); + } + #endregion Notes + + #region Profile Properties + public bool AvatarPropertiesRequest(ref UserProfileProperties prop, ref string result) + { + return ProfilesData.GetAvatarProperties(ref prop, ref result); + } + + public bool AvatarPropertiesUpdate(ref UserProfileProperties prop, ref string result) + { + return ProfilesData.UpdateAvatarProperties(ref prop, ref result); + } + #endregion Profile Properties + + #region Interests + public bool AvatarInterestsUpdate(UserProfileProperties prop, ref string result) + { + return ProfilesData.UpdateAvatarInterests(prop, ref result); + } + #endregion Interests + + #region User Preferences + public bool UserPreferencesUpdate(ref UserPreferences pref, ref string result) + { + if(string.IsNullOrEmpty(pref.EMail)) + { + UserAccount account = new UserAccount(); + if(userAccounts is UserAccountService.UserAccountService) + { + try + { + account = userAccounts.GetUserAccount(UUID.Zero, pref.UserId); + if(string.IsNullOrEmpty(account.Email)) + { + pref.EMail = string.Empty; + } + else + pref.EMail = account.Email; + } + catch + { + m_log.Error ("[PROFILES SERVICE]: UserAccountService Exception: Could not get user account"); + result = "UserAccountService settings error in UserProfileService!"; + return false; + } + } + else + { + m_log.Error ("[PROFILES SERVICE]: UserAccountService: Could not get user account"); + result = "UserAccountService settings error in UserProfileService!"; + return false; + } + } + return ProfilesData.UpdateUserPreferences(ref pref, ref result); + } + + public bool UserPreferencesRequest(ref UserPreferences pref, ref string result) + { + if (!ProfilesData.GetUserPreferences(ref pref, ref result)) + return false; + + if(string.IsNullOrEmpty(pref.EMail)) + { + UserAccount account = new UserAccount(); + if(userAccounts is UserAccountService.UserAccountService) + { + try + { + account = userAccounts.GetUserAccount(UUID.Zero, pref.UserId); + if(string.IsNullOrEmpty(account.Email)) + { + pref.EMail = string.Empty; + } + else + { + pref.EMail = account.Email; + UserPreferencesUpdate(ref pref, ref result); + } + } + catch + { + m_log.Error ("[PROFILES SERVICE]: UserAccountService Exception: Could not get user account"); + result = "UserAccountService settings error in UserProfileService!"; + return false; + } + } + else + { + m_log.Error ("[PROFILES SERVICE]: UserAccountService: Could not get user account"); + result = "UserAccountService settings error in UserProfileService!"; + return false; + } + } + + if(string.IsNullOrEmpty(pref.EMail)) + pref.EMail = "No Email Address On Record"; + + return true; + } + #endregion User Preferences + + #region Utility + public OSD AvatarImageAssetsRequest(UUID avatarId) + { + OSDArray records = ProfilesData.GetUserImageAssets(avatarId); + return records; + } + #endregion Utility + + #region UserData + public bool RequestUserAppData(ref UserAppData prop, ref string result) + { + return ProfilesData.GetUserAppData(ref prop, ref result); + } + + public bool SetUserAppData(UserAppData prop, ref string result) + { + return true; + } + #endregion UserData + } +} + diff --git a/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs b/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs new file mode 100644 index 0000000000..c31578f4ad --- /dev/null +++ b/OpenSim/Services/UserProfilesService/UserProfilesServiceBase.cs @@ -0,0 +1,87 @@ +/* + * 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 log4net; +using OpenSim.Services.Base; +using OpenSim.Data; + +namespace OpenSim.Services.ProfilesService +{ + public class UserProfilesServiceBase: ServiceBase + { + static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public IProfilesData ProfilesData; + + public string ConfigName + { + get; private set; + } + + public UserProfilesServiceBase(IConfigSource config, string configName): + base(config) + { + if(string.IsNullOrEmpty(configName)) + { + m_log.WarnFormat("[PROFILES SERVICE]: Configuration section not given!"); + return; + } + + string dllName = String.Empty; + string connString = null; + string realm = String.Empty; + + IConfig dbConfig = config.Configs["DatabaseService"]; + if (dbConfig != null) + { + if (dllName == String.Empty) + dllName = dbConfig.GetString("StorageProvider", String.Empty); + if (string.IsNullOrEmpty(connString)) + connString = dbConfig.GetString("ConnectionString", String.Empty); + } + + IConfig ProfilesConfig = config.Configs[configName]; + if (ProfilesConfig != null) + { + dllName = ProfilesConfig.GetString("StorageProvider", dllName); + connString = ProfilesConfig.GetString("ConnectionString", connString); + realm = ProfilesConfig.GetString("Realm", realm); + } + + ProfilesData = LoadPlugin(dllName, new Object[] { connString }); + if (ProfilesData == null) + throw new Exception("Could not find a storage interface in the given module"); + + } + } +} + diff --git a/OpenSim/Tests/Clients/Assets/AssetsClient.cs b/OpenSim/Tests/Clients/Assets/AssetsClient.cs new file mode 100644 index 0000000000..e988d0e36a --- /dev/null +++ b/OpenSim/Tests/Clients/Assets/AssetsClient.cs @@ -0,0 +1,126 @@ +/* + * 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.Net; +using System.Text; +using System.Reflection; +using System.Threading; + +using OpenMetaverse; +using log4net; +using log4net.Appender; +using log4net.Layout; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; + +namespace OpenSim.Tests.Clients.AssetsClient +{ + public class AssetsClient + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private static int m_MaxThreadID = 0; + private static readonly int NREQS = 150; + private static int m_NReceived = 0; + + public static void Main(string[] args) + { + ConsoleAppender consoleAppender = new ConsoleAppender(); + consoleAppender.Layout = + new PatternLayout("[%thread] - %message%newline"); + log4net.Config.BasicConfigurator.Configure(consoleAppender); + + string serverURI = "http://127.0.0.1:8003"; + if (args.Length > 1) + serverURI = args[1]; + int max1, max2; + ThreadPool.GetMaxThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: Connecting to {0} max threads = {1} - {2}", serverURI, max1, max2); + ThreadPool.GetMinThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: Connecting to {0} min threads = {1} - {2}", serverURI, max1, max2); + + if (!ThreadPool.SetMinThreads(1, 1)) + m_log.WarnFormat("[ASSET CLIENT]: Failed to set min threads"); + + if (!ThreadPool.SetMaxThreads(10, 3)) + m_log.WarnFormat("[ASSET CLIENT]: Failed to set max threads"); + + ThreadPool.GetMaxThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: Post set max threads = {1} - {2}", serverURI, max1, max2); + ThreadPool.GetMinThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: Post set min threads = {1} - {2}", serverURI, max1, max2); + + ServicePointManager.DefaultConnectionLimit = 12; + + AssetServicesConnector m_Connector = new AssetServicesConnector(serverURI); + m_Connector.MaxAssetRequestConcurrency = 30; + + for (int i = 0; i < NREQS; i++) + { + UUID uuid = UUID.Random(); + m_Connector.Get(uuid.ToString(), null, ResponseReceived); + m_log.InfoFormat("[ASSET CLIENT]: [{0}] requested asset {1}", i, uuid); + } + + for (int i = 0; i < 500; i++) + { + var x = i; + ThreadPool.QueueUserWorkItem(delegate + { + Dummy(x); + }); + } + + Thread.Sleep(30 * 1000); + m_log.InfoFormat("[ASSET CLIENT]: Received responses {0}", m_NReceived); + } + + private static void ResponseReceived(string id, Object sender, AssetBase asset) + { + if (Thread.CurrentThread.ManagedThreadId > m_MaxThreadID) + m_MaxThreadID = Thread.CurrentThread.ManagedThreadId; + int max1, max2; + ThreadPool.GetAvailableThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: Received asset {0} ({1}) ({2}-{3}) {4}", id, m_MaxThreadID, max1, max2, DateTime.Now.ToString("hh:mm:ss")); + m_NReceived++; + } + + private static void Dummy(int i) + { + int max1, max2; + ThreadPool.GetAvailableThreads(out max1, out max2); + m_log.InfoFormat("[ASSET CLIENT]: ({0}) Hello! {1} - {2} {3}", i, max1, max2, DateTime.Now.ToString("hh:mm:ss")); + Thread.Sleep(2000); + } + } +} diff --git a/OpenSim/Tests/Common/ANumericalToleranceConstraint.cs b/OpenSim/Tests/Common/ANumericalToleranceConstraint.cs new file mode 100644 index 0000000000..15c88021fc --- /dev/null +++ b/OpenSim/Tests/Common/ANumericalToleranceConstraint.cs @@ -0,0 +1,56 @@ +/* + * 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 NUnit.Framework.Constraints; + +namespace OpenSim.Tests.Common +{ + public abstract class ANumericalToleranceConstraint : Constraint + { + protected double _tolerance; + + public ANumericalToleranceConstraint(double tolerance) + { + if (tolerance < 0) + { + throw new ArgumentException("Tolerance cannot be negative."); + } + _tolerance = tolerance; + } + + protected bool IsWithinDoubleConstraint(double doubleValue, double baseValue) + { + if (doubleValue >= baseValue - _tolerance && doubleValue <= baseValue + _tolerance) + { + return true; + } + + return false; + } + } +} diff --git a/OpenSim/Tests/Common/DatabaseTestAttribute.cs b/OpenSim/Tests/Common/DatabaseTestAttribute.cs new file mode 100644 index 0000000000..d027d598f7 --- /dev/null +++ b/OpenSim/Tests/Common/DatabaseTestAttribute.cs @@ -0,0 +1,44 @@ +/* + * 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.Text; +using NUnit.Framework; + +namespace OpenSim.Tests.Common +{ + [AttributeUsage(AttributeTargets.All, + AllowMultiple=false, + Inherited=true)] + public class DatabaseTestAttribute : LongRunningAttribute + { + public DatabaseTestAttribute() : base("Database") + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/DoubleToleranceConstraint.cs b/OpenSim/Tests/Common/DoubleToleranceConstraint.cs new file mode 100644 index 0000000000..b2f20571ae --- /dev/null +++ b/OpenSim/Tests/Common/DoubleToleranceConstraint.cs @@ -0,0 +1,77 @@ +/* + * 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 NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace OpenSim.Tests.Common +{ + public class DoubleToleranceConstraint : ANumericalToleranceConstraint + { + private double _baseValue; + private double _valueToBeTested; + + public DoubleToleranceConstraint(double baseValue, double tolerance) : base(tolerance) + { + _baseValue = baseValue; + } + + /// + ///Test whether the constraint is satisfied by a given value + /// + ///The value to be tested + /// + ///True for success, false for failure + /// + public override bool Matches(object valueToBeTested) + { + if (valueToBeTested == null) + { + throw new ArgumentException("Constraint cannot be used upon null values."); + } + if (valueToBeTested.GetType() != typeof(double)) + { + throw new ArgumentException("Constraint cannot be used upon non double-values."); + } + + _valueToBeTested = (double)valueToBeTested; + + return IsWithinDoubleConstraint(_valueToBeTested, _baseValue); + } + + public override void WriteDescriptionTo(MessageWriter writer) + { + writer.WriteExpectedValue(string.Format("A value {0} within tolerance of plus or minus {1}",_baseValue,_tolerance)); + } + + public override void WriteActualValueTo(MessageWriter writer) + { + writer.WriteActualValue(_valueToBeTested); + } + } +} diff --git a/OpenSim/Tests/Common/Helpers/AssetHelpers.cs b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs new file mode 100644 index 0000000000..7af8bedf7a --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/AssetHelpers.cs @@ -0,0 +1,168 @@ +/* + * 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.Text; +using OpenMetaverse; +using OpenMetaverse.Assets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common +{ + public class AssetHelpers + { + /// + /// Create a notecard asset with a random uuids and dummy text. + /// + /// + public static AssetBase CreateNotecardAsset() + { + return CreateNotecardAsset(UUID.Random()); + } + + /// + /// Create a notecard asset with dummy text and a random owner. + /// + /// /param> + /// + public static AssetBase CreateNotecardAsset(UUID assetId) + { + return CreateNotecardAsset(assetId, "hello"); + } + + /// + /// Create a notecard asset with a random owner. + /// + /// /param> + /// + /// + public static AssetBase CreateNotecardAsset(UUID assetId, string text) + { + return CreateAsset(assetId, AssetType.Notecard, text, UUID.Random()); + } + +// /// +// /// Create and store a notecard asset with a random uuid and dummy text. +// /// +// /// /param> +// /// +// public static AssetBase CreateNotecardAsset(Scene scene, UUID creatorId) +// { +// AssetBase asset = CreateAsset(UUID.Random(), AssetType.Notecard, "hello", creatorId); +// scene.AssetService.Store(asset); +// return asset; +// } + + /// + /// Create an asset from the given object. + /// + /// + /// The hexadecimal last part of the UUID for the asset created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be used. + /// + /// + /// + public static AssetBase CreateAsset(int assetUuidTail, SceneObjectGroup sog) + { + return CreateAsset(new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", assetUuidTail)), sog); + } + + /// + /// Create an asset from the given object. + /// + /// + /// + /// + public static AssetBase CreateAsset(UUID assetUuid, SceneObjectGroup sog) + { + return CreateAsset( + assetUuid, + AssetType.Object, + Encoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(sog)), + sog.OwnerID); + } + + /// + /// Create an asset from the given scene object. + /// + /// + /// The hexadecimal last part of the UUID for the asset created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be used. + /// + /// + /// + public static AssetBase CreateAsset(int assetUuidTail, CoalescedSceneObjects coa) + { + return CreateAsset(new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", assetUuidTail)), coa); + } + + /// + /// Create an asset from the given scene object. + /// + /// + /// + /// + public static AssetBase CreateAsset(UUID assetUuid, CoalescedSceneObjects coa) + { + return CreateAsset( + assetUuid, + AssetType.Object, + Encoding.ASCII.GetBytes(CoalescedSceneObjectsSerializer.ToXml(coa)), + coa.CreatorId); + } + + /// + /// Create an asset from the given data. + /// + public static AssetBase CreateAsset(UUID assetUuid, AssetType assetType, string text, UUID creatorID) + { + AssetNotecard anc = new AssetNotecard(); + anc.BodyText = text; + anc.Encode(); + + return CreateAsset(assetUuid, assetType, anc.AssetData, creatorID); + } + + /// + /// Create an asset from the given data. + /// + public static AssetBase CreateAsset(UUID assetUuid, AssetType assetType, byte[] data, UUID creatorID) + { + AssetBase asset = new AssetBase(assetUuid, assetUuid.ToString(), (sbyte)assetType, creatorID.ToString()); + asset.Data = data; + return asset; + } + + public static string ReadAssetAsString(IAssetService assetService, UUID uuid) + { + byte[] assetData = assetService.GetData(uuid.ToString()); + return Encoding.ASCII.GetString(assetData); + } + } +} diff --git a/OpenSim/Tests/Common/Helpers/BaseRequestHandlerHelpers.cs b/OpenSim/Tests/Common/Helpers/BaseRequestHandlerHelpers.cs new file mode 100644 index 0000000000..82ecf9aede --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/BaseRequestHandlerHelpers.cs @@ -0,0 +1,75 @@ +/* + * 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.Text; +using NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Tests.Common +{ + public class BaseRequestHandlerHelpers + { + private static string[] m_emptyStringArray = new string[] { }; + + public static void BaseTestGetParams(BaseRequestHandler handler, string assetsPath) + { + Assert.AreEqual(String.Empty, handler.GetParam(null), "Failed on null path."); + Assert.AreEqual(String.Empty, handler.GetParam(""), "Failed on empty path."); + Assert.AreEqual(String.Empty, handler.GetParam("s"), "Failed on short url."); + Assert.AreEqual(String.Empty, handler.GetParam("corruptUrl"), "Failed on corruptUrl."); + + Assert.AreEqual(String.Empty, handler.GetParam(assetsPath)); + Assert.AreEqual("/", handler.GetParam(assetsPath + "/")); + Assert.AreEqual("/a", handler.GetParam(assetsPath + "/a")); + Assert.AreEqual("/b/", handler.GetParam(assetsPath + "/b/")); + Assert.AreEqual("/c/d", handler.GetParam(assetsPath + "/c/d")); + Assert.AreEqual("/e/f/", handler.GetParam(assetsPath + "/e/f/")); + } + + public static void BaseTestSplitParams(BaseRequestHandler handler, string assetsPath) + { + Assert.AreEqual(m_emptyStringArray, handler.SplitParams(null), "Failed on null."); + Assert.AreEqual(m_emptyStringArray, handler.SplitParams(""), "Failed on empty path."); + Assert.AreEqual(m_emptyStringArray, handler.SplitParams("corruptUrl"), "Failed on corrupt url."); + + Assert.AreEqual(m_emptyStringArray, handler.SplitParams(assetsPath), "Failed on empty params."); + Assert.AreEqual(m_emptyStringArray, handler.SplitParams(assetsPath + "/"), "Failed on single slash."); + + Assert.AreEqual(new string[] { "a" }, handler.SplitParams(assetsPath + "/a"), "Failed on first segment."); + Assert.AreEqual(new string[] { "b" }, handler.SplitParams(assetsPath + "/b/"), "Failed on second slash."); + Assert.AreEqual(new string[] { "c", "d" }, handler.SplitParams(assetsPath + "/c/d"), "Failed on second segment."); + Assert.AreEqual(new string[] { "e", "f" }, handler.SplitParams(assetsPath + "/e/f/"), "Failed on trailing slash."); + } + + public static byte[] EmptyByteArray = new byte[] {}; + + } +} diff --git a/OpenSim/Tests/Common/Helpers/ClientStackHelpers.cs b/OpenSim/Tests/Common/Helpers/ClientStackHelpers.cs new file mode 100644 index 0000000000..33cd8a2cd4 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/ClientStackHelpers.cs @@ -0,0 +1,95 @@ +/* + * 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.Net; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.ClientStack.LindenUDP; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Tests.Common +{ + /// + /// This class adds full UDP client classes and associated scene presence to scene. + /// + /// + /// This is used for testing client stack code. For testing other code, use SceneHelper methods instead since + /// they operate without the burden of setting up UDP structures which should be unnecessary for testing scene + /// code. + /// + public static class ClientStackHelpers + { + public static ScenePresence AddChildClient( + Scene scene, LLUDPServer udpServer, UUID agentId, UUID sessionId, uint circuitCode) + { + IPEndPoint testEp = new IPEndPoint(IPAddress.Loopback, 999); + + UseCircuitCodePacket uccp = new UseCircuitCodePacket(); + + UseCircuitCodePacket.CircuitCodeBlock uccpCcBlock + = new UseCircuitCodePacket.CircuitCodeBlock(); + uccpCcBlock.Code = circuitCode; + uccpCcBlock.ID = agentId; + uccpCcBlock.SessionID = sessionId; + uccp.CircuitCode = uccpCcBlock; + + byte[] uccpBytes = uccp.ToBytes(); + UDPPacketBuffer upb = new UDPPacketBuffer(testEp, uccpBytes.Length); + upb.DataLength = uccpBytes.Length; // God knows why this isn't set by the constructor. + Buffer.BlockCopy(uccpBytes, 0, upb.Data, 0, uccpBytes.Length); + + AgentCircuitData acd = new AgentCircuitData(); + acd.AgentID = agentId; + acd.SessionID = sessionId; + + scene.AuthenticateHandler.AddNewCircuit(circuitCode, acd); + + udpServer.PacketReceived(upb); + + return scene.GetScenePresence(agentId); + } + + public static TestLLUDPServer AddUdpServer(Scene scene) + { + return AddUdpServer(scene, new IniConfigSource()); + } + + public static TestLLUDPServer AddUdpServer(Scene scene, IniConfigSource configSource) + { + uint port = 0; + AgentCircuitManager acm = scene.AuthenticateHandler; + + TestLLUDPServer udpServer = new TestLLUDPServer(IPAddress.Any, ref port, 0, false, configSource, acm); + udpServer.AddScene(scene); + + return udpServer; + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs b/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs new file mode 100644 index 0000000000..cf7583e3d4 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/EntityTransferHelpers.cs @@ -0,0 +1,123 @@ +/* + * 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.Net; +using System.Reflection; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.Framework; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests.Common +{ + public static class EntityTransferHelpers + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Set up correct handling of the InformClientOfNeighbour call from the source region that triggers the + /// viewer to setup a connection with the destination region. + /// + /// + /// + /// A list that will be populated with any TestClients set up in response to + /// being informed about a destination region. + /// + public static void SetupInformClientOfNeighbourTriggersNeighbourClientCreate( + TestClient tc, List neighbourTcs) + { + // XXX: Confusingly, this is also used for non-neighbour notification (as in teleports that do not use the + // event queue). + + tc.OnTestClientInformClientOfNeighbour += (neighbourHandle, neighbourExternalEndPoint) => + { + uint x, y; + Util.RegionHandleToRegionLoc(neighbourHandle, out x, out y); + + m_log.DebugFormat( + "[TEST CLIENT]: Processing inform client of neighbour located at {0},{1} at {2}", + x, y, neighbourExternalEndPoint); + + AgentCircuitData newAgent = tc.RequestClientInfo(); + + Scene neighbourScene; + SceneManager.Instance.TryGetScene(x, y, out neighbourScene); + + TestClient neighbourTc = new TestClient(newAgent, neighbourScene); + neighbourTcs.Add(neighbourTc); + neighbourScene.AddNewAgent(neighbourTc, PresenceType.User); + }; + } + + /// + /// Set up correct handling of the InformClientOfNeighbour call from the source region that triggers the + /// viewer to setup a connection with the destination region. + /// + /// + /// + /// A list that will be populated with any TestClients set up in response to + /// being informed about a destination region. + /// + public static void SetupSendRegionTeleportTriggersDestinationClientCreateAndCompleteMovement( + TestClient client, List destinationClients) + { + client.OnTestClientSendRegionTeleport + += (regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL) => + { + uint x, y; + Util.RegionHandleToRegionLoc(regionHandle, out x, out y); + + m_log.DebugFormat( + "[TEST CLIENT]: Processing send region teleport for destination at {0},{1} at {2}", + x, y, regionExternalEndPoint); + + AgentCircuitData newAgent = client.RequestClientInfo(); + + Scene destinationScene; + SceneManager.Instance.TryGetScene(x, y, out destinationScene); + + TestClient destinationClient = new TestClient(newAgent, destinationScene); + destinationClients.Add(destinationClient); + destinationScene.AddNewAgent(destinationClient, PresenceType.User); + + ThreadPool.UnsafeQueueUserWorkItem(o => destinationClient.CompleteMovement(), null); + }; + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/SceneHelpers.cs b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs new file mode 100644 index 0000000000..1fb1c5c961 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs @@ -0,0 +1,724 @@ +/* + * 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.Net; +using System.Collections.Generic; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Data.Null; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Console; +using OpenSim.Framework.Servers; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.Avatar.Gods; +using OpenSim.Region.CoreModules.Asset; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Authentication; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Inventory; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Tests.Common +{ + /// + /// Helpers for setting up scenes. + /// + public class SceneHelpers + { + /// + /// We need a scene manager so that test clients can retrieve a scene when performing teleport tests. + /// + public SceneManager SceneManager { get; private set; } + + public ISimulationDataService SimDataService { get; private set; } + + private AgentCircuitManager m_acm = new AgentCircuitManager(); + private IEstateDataService m_estateDataService = null; + + private LocalAssetServicesConnector m_assetService; + private LocalAuthenticationServicesConnector m_authenticationService; + private LocalInventoryServicesConnector m_inventoryService; + private LocalGridServicesConnector m_gridService; + private LocalUserAccountServicesConnector m_userAccountService; + private LocalPresenceServicesConnector m_presenceService; + + private CoreAssetCache m_cache; + + public SceneHelpers() : this(null) {} + + public SceneHelpers(CoreAssetCache cache) + { + SceneManager = new SceneManager(); + + m_assetService = StartAssetService(cache); + m_authenticationService = StartAuthenticationService(); + m_inventoryService = StartInventoryService(); + m_gridService = StartGridService(); + m_userAccountService = StartUserAccountService(); + m_presenceService = StartPresenceService(); + + m_inventoryService.PostInitialise(); + m_assetService.PostInitialise(); + m_userAccountService.PostInitialise(); + m_presenceService.PostInitialise(); + + m_cache = cache; + + SimDataService + = OpenSim.Server.Base.ServerUtils.LoadPlugin("OpenSim.Tests.Common.dll", null); + } + + /// + /// Set up a test scene + /// + /// + /// Automatically starts services, as would the normal runtime. + /// + /// + public TestScene SetupScene() + { + return SetupScene("Unit test region", UUID.Random(), 1000, 1000); + } + + public TestScene SetupScene(string name, UUID id, uint x, uint y) + { + return SetupScene(name, id, x, y, new IniConfigSource()); + } + + public TestScene SetupScene(string name, UUID id, uint x, uint y, IConfigSource configSource) + { + return SetupScene(name, id, x, y, Constants.RegionSize, Constants.RegionSize, configSource); + } + + /// + /// Set up a scene. + /// + /// Name of the region + /// ID of the region + /// X co-ordinate of the region + /// Y co-ordinate of the region + /// X size of scene + /// Y size of scene + /// + /// + public TestScene SetupScene( + string name, UUID id, uint x, uint y, uint sizeX, uint sizeY, IConfigSource configSource) + { + Console.WriteLine("Setting up test scene {0}", name); + + // We must set up a console otherwise setup of some modules may fail + MainConsole.Instance = new MockConsole(); + + RegionInfo regInfo = new RegionInfo(x, y, new IPEndPoint(IPAddress.Loopback, 9000), "127.0.0.1"); + regInfo.RegionName = name; + regInfo.RegionID = id; + regInfo.RegionSizeX = sizeX; + regInfo.RegionSizeY = sizeY; + + SceneCommunicationService scs = new SceneCommunicationService(); + + PhysicsPluginManager physicsPluginManager = new PhysicsPluginManager(); + physicsPluginManager.LoadPluginsFromAssembly("Physics/OpenSim.Region.Physics.BasicPhysicsPlugin.dll"); + Vector3 regionExtent = new Vector3( regInfo.RegionSizeX, regInfo.RegionSizeY, regInfo.RegionSizeZ); + PhysicsScene physicsScene + = physicsPluginManager.GetPhysicsScene( + "basicphysics", "ZeroMesher", new IniConfigSource(), "test", regionExtent); + + TestScene testScene = new TestScene( + regInfo, m_acm, physicsScene, scs, SimDataService, m_estateDataService, configSource, null); + + INonSharedRegionModule godsModule = new GodsModule(); + godsModule.Initialise(new IniConfigSource()); + godsModule.AddRegion(testScene); + + // Add scene to services + m_assetService.AddRegion(testScene); + + if (m_cache != null) + { + m_cache.AddRegion(testScene); + m_cache.RegionLoaded(testScene); + testScene.AddRegionModule(m_cache.Name, m_cache); + } + + m_assetService.RegionLoaded(testScene); + testScene.AddRegionModule(m_assetService.Name, m_assetService); + + m_authenticationService.AddRegion(testScene); + m_authenticationService.RegionLoaded(testScene); + testScene.AddRegionModule(m_authenticationService.Name, m_authenticationService); + + m_inventoryService.AddRegion(testScene); + m_inventoryService.RegionLoaded(testScene); + testScene.AddRegionModule(m_inventoryService.Name, m_inventoryService); + + m_gridService.AddRegion(testScene); + m_gridService.RegionLoaded(testScene); + testScene.AddRegionModule(m_gridService.Name, m_gridService); + + m_userAccountService.AddRegion(testScene); + m_userAccountService.RegionLoaded(testScene); + testScene.AddRegionModule(m_userAccountService.Name, m_userAccountService); + + m_presenceService.AddRegion(testScene); + m_presenceService.RegionLoaded(testScene); + testScene.AddRegionModule(m_presenceService.Name, m_presenceService); + + testScene.RegionInfo.EstateSettings.EstateOwner = UUID.Random(); + testScene.SetModuleInterfaces(); + + testScene.LandChannel = new TestLandChannel(testScene); + testScene.LoadWorldMap(); + + testScene.RegionInfo.EstateSettings = new EstateSettings(); + testScene.LoginsEnabled = true; + testScene.RegisterRegionWithGrid(); + + SceneManager.Add(testScene); + + return testScene; + } + + private static LocalAssetServicesConnector StartAssetService(CoreAssetCache cache) + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector"); + config.AddConfig("AssetService"); + config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService"); + config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + + LocalAssetServicesConnector assetService = new LocalAssetServicesConnector(); + assetService.Initialise(config); + + if (cache != null) + { + IConfigSource cacheConfig = new IniConfigSource(); + cacheConfig.AddConfig("Modules"); + cacheConfig.Configs["Modules"].Set("AssetCaching", "CoreAssetCache"); + cacheConfig.AddConfig("AssetCache"); + + cache.Initialise(cacheConfig); + } + + return assetService; + } + + private static LocalAuthenticationServicesConnector StartAuthenticationService() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("AuthenticationService"); + config.Configs["Modules"].Set("AuthenticationServices", "LocalAuthenticationServicesConnector"); + config.Configs["AuthenticationService"].Set( + "LocalServiceModule", "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService"); + config.Configs["AuthenticationService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + + LocalAuthenticationServicesConnector service = new LocalAuthenticationServicesConnector(); + service.Initialise(config); + + return service; + } + + private static LocalInventoryServicesConnector StartInventoryService() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("InventoryService"); + config.Configs["Modules"].Set("InventoryServices", "LocalInventoryServicesConnector"); + config.Configs["InventoryService"].Set("LocalServiceModule", "OpenSim.Services.InventoryService.dll:XInventoryService"); + config.Configs["InventoryService"].Set("StorageProvider", "OpenSim.Tests.Common.dll"); + + LocalInventoryServicesConnector inventoryService = new LocalInventoryServicesConnector(); + inventoryService.Initialise(config); + + return inventoryService; + } + + private static LocalGridServicesConnector StartGridService() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("GridService"); + config.Configs["Modules"].Set("GridServices", "LocalGridServicesConnector"); + config.Configs["GridService"].Set("StorageProvider", "OpenSim.Data.Null.dll:NullRegionData"); + config.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Services.GridService.dll:GridService"); + config.Configs["GridService"].Set("ConnectionString", "!static"); + + LocalGridServicesConnector gridService = new LocalGridServicesConnector(); + gridService.Initialise(config); + + return gridService; + } + + /// + /// Start a user account service + /// + /// + /// + private static LocalUserAccountServicesConnector StartUserAccountService() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("UserAccountService"); + config.Configs["Modules"].Set("UserAccountServices", "LocalUserAccountServicesConnector"); + config.Configs["UserAccountService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + config.Configs["UserAccountService"].Set( + "LocalServiceModule", "OpenSim.Services.UserAccountService.dll:UserAccountService"); + + LocalUserAccountServicesConnector userAccountService = new LocalUserAccountServicesConnector(); + userAccountService.Initialise(config); + + return userAccountService; + } + + /// + /// Start a presence service + /// + /// + private static LocalPresenceServicesConnector StartPresenceService() + { + // Unfortunately, some services share data via statics, so we need to null every time to stop interference + // between tests. + // This is a massive non-obvious pita. + NullPresenceData.Instance = null; + + IConfigSource config = new IniConfigSource(); + config.AddConfig("Modules"); + config.AddConfig("PresenceService"); + config.Configs["Modules"].Set("PresenceServices", "LocalPresenceServicesConnector"); + config.Configs["PresenceService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + config.Configs["PresenceService"].Set( + "LocalServiceModule", "OpenSim.Services.PresenceService.dll:PresenceService"); + + LocalPresenceServicesConnector presenceService = new LocalPresenceServicesConnector(); + presenceService.Initialise(config); + + return presenceService; + } + + /// + /// Setup modules for a scene using their default settings. + /// + /// + /// + public static void SetupSceneModules(Scene scene, params object[] modules) + { + SetupSceneModules(scene, new IniConfigSource(), modules); + } + + /// + /// Setup modules for a scene. + /// + /// + /// If called directly, then all the modules must be shared modules. + /// + /// + /// + /// + public static void SetupSceneModules(Scene scene, IConfigSource config, params object[] modules) + { + SetupSceneModules(new Scene[] { scene }, config, modules); + } + + /// + /// Setup modules for a scene using their default settings. + /// + /// + /// + public static void SetupSceneModules(Scene[] scenes, params object[] modules) + { + SetupSceneModules(scenes, new IniConfigSource(), modules); + } + + /// + /// Setup modules for scenes. + /// + /// + /// If called directly, then all the modules must be shared modules. + /// + /// We are emulating here the normal calls made to setup region modules + /// (Initialise(), PostInitialise(), AddRegion, RegionLoaded()). + /// TODO: Need to reuse normal runtime module code. + /// + /// + /// + /// + public static void SetupSceneModules(Scene[] scenes, IConfigSource config, params object[] modules) + { + List newModules = new List(); + foreach (object module in modules) + { + IRegionModuleBase m = (IRegionModuleBase)module; +// Console.WriteLine("MODULE {0}", m.Name); + m.Initialise(config); + newModules.Add(m); + } + + foreach (IRegionModuleBase module in newModules) + { + if (module is ISharedRegionModule) ((ISharedRegionModule)module).PostInitialise(); + } + + foreach (IRegionModuleBase module in newModules) + { + foreach (Scene scene in scenes) + { + module.AddRegion(scene); + scene.AddRegionModule(module.Name, module); + } + } + + // RegionLoaded is fired after all modules have been appropriately added to all scenes + foreach (IRegionModuleBase module in newModules) + foreach (Scene scene in scenes) + module.RegionLoaded(scene); + + foreach (Scene scene in scenes) { scene.SetModuleInterfaces(); } + } + + /// + /// Generate some standard agent connection data. + /// + /// + /// + public static AgentCircuitData GenerateAgentData(UUID agentId) + { + AgentCircuitData acd = GenerateCommonAgentData(); + + acd.AgentID = agentId; + acd.firstname = "testfirstname"; + acd.lastname = "testlastname"; + acd.ServiceURLs = new Dictionary(); + + return acd; + } + + /// + /// Generate some standard agent connection data. + /// + /// + /// + public static AgentCircuitData GenerateAgentData(UserAccount ua) + { + AgentCircuitData acd = GenerateCommonAgentData(); + + acd.AgentID = ua.PrincipalID; + acd.firstname = ua.FirstName; + acd.lastname = ua.LastName; + acd.ServiceURLs = ua.ServiceURLs; + + return acd; + } + + private static AgentCircuitData GenerateCommonAgentData() + { + AgentCircuitData acd = new AgentCircuitData(); + + // XXX: Sessions must be unique, otherwise one presence can overwrite another in NullPresenceData. + acd.SessionID = UUID.Random(); + acd.SecureSessionID = UUID.Random(); + + acd.circuitcode = 123; + acd.BaseFolder = UUID.Zero; + acd.InventoryFolder = UUID.Zero; + acd.startpos = Vector3.Zero; + acd.CapsPath = "http://wibble.com"; + acd.Appearance = new AvatarAppearance(); + + return acd; + } + + /// + /// Add a root agent where the details of the agent connection (apart from the id) are unimportant for the test + /// + /// + /// XXX: Use the version of this method that takes the UserAccount structure wherever possible - this will + /// make the agent circuit data (e.g. first, lastname) consistent with the user account data. + /// + /// + /// + /// + public static ScenePresence AddScenePresence(Scene scene, UUID agentId) + { + return AddScenePresence(scene, GenerateAgentData(agentId)); + } + + /// + /// Add a root agent. + /// + /// + /// + /// + public static ScenePresence AddScenePresence(Scene scene, UserAccount ua) + { + return AddScenePresence(scene, GenerateAgentData(ua)); + } + + /// + /// Add a root agent. + /// + /// + /// This function + /// + /// 1) Tells the scene that an agent is coming. Normally, the login service (local if standalone, from the + /// userserver if grid) would give initial login data back to the client and separately tell the scene that the + /// agent was coming. + /// + /// 2) Connects the agent with the scene + /// + /// This function performs actions equivalent with notifying the scene that an agent is + /// coming and then actually connecting the agent to the scene. The one step missed out is the very first + /// + /// + /// + /// + public static ScenePresence AddScenePresence(Scene scene, AgentCircuitData agentData) + { + return AddScenePresence(scene, new TestClient(agentData, scene), agentData); + } + + /// + /// Add a root agent. + /// + /// + /// This function + /// + /// 1) Tells the scene that an agent is coming. Normally, the login service (local if standalone, from the + /// userserver if grid) would give initial login data back to the client and separately tell the scene that the + /// agent was coming. + /// + /// 2) Connects the agent with the scene + /// + /// This function performs actions equivalent with notifying the scene that an agent is + /// coming and then actually connecting the agent to the scene. The one step missed out is the very first + /// + /// + /// + /// + public static ScenePresence AddScenePresence( + Scene scene, IClientAPI client, AgentCircuitData agentData) + { + // We emulate the proper login sequence here by doing things in four stages + + // Stage 0: login + // We need to punch through to the underlying service because scene will not, correctly, let us call it + // through it's reference to the LPSC + LocalPresenceServicesConnector lpsc = (LocalPresenceServicesConnector)scene.PresenceService; + lpsc.m_PresenceService.LoginAgent(agentData.AgentID.ToString(), agentData.SessionID, agentData.SecureSessionID); + + // Stages 1 & 2 + ScenePresence sp = IntroduceClientToScene(scene, client, agentData, TeleportFlags.ViaLogin); + + // Stage 3: Complete the entrance into the region. This converts the child agent into a root agent. + sp.CompleteMovement(sp.ControllingClient, true); + + return sp; + } + + /// + /// Introduce an agent into the scene by adding a new client. + /// + /// The scene presence added + /// + /// + /// + /// + private static ScenePresence IntroduceClientToScene( + Scene scene, IClientAPI client, AgentCircuitData agentData, TeleportFlags tf) + { + string reason; + + // Stage 1: tell the scene to expect a new user connection + if (!scene.NewUserConnection(agentData, (uint)tf, null, out reason)) + Console.WriteLine("NewUserConnection failed: " + reason); + + // Stage 2: add the new client as a child agent to the scene + scene.AddNewAgent(client, PresenceType.User); + + return scene.GetScenePresence(client.AgentId); + } + + public static ScenePresence AddChildScenePresence(Scene scene, UUID agentId) + { + return AddChildScenePresence(scene, GenerateAgentData(agentId)); + } + + public static ScenePresence AddChildScenePresence(Scene scene, AgentCircuitData acd) + { + acd.child = true; + + // XXX: ViaLogin may not be correct for child agents + TestClient client = new TestClient(acd, scene); + return IntroduceClientToScene(scene, client, acd, TeleportFlags.ViaLogin); + } + + /// + /// Add a test object + /// + /// + /// + public static SceneObjectGroup AddSceneObject(Scene scene) + { + return AddSceneObject(scene, "Test Object", UUID.Zero); + } + + /// + /// Add a test object + /// + /// + /// + /// + /// + public static SceneObjectGroup AddSceneObject(Scene scene, string name, UUID ownerId) + { + SceneObjectGroup so = new SceneObjectGroup(CreateSceneObjectPart(name, UUID.Random(), ownerId)); + + //part.UpdatePrimFlags(false, false, true); + //part.ObjectFlags |= (uint)PrimFlags.Phantom; + + scene.AddNewSceneObject(so, true); + + return so; + } + + /// + /// Add a test object + /// + /// + /// + /// The number of parts that should be in the scene object + /// + /// + /// + /// The prefix to be given to part names. This will be suffixed with "Part" + /// (e.g. mynamePart1 for the root part) + /// + /// + /// The hexadecimal last part of the UUID for parts created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be given to the root part, and incremented for each part thereafter. + /// + /// + public static SceneObjectGroup AddSceneObject(Scene scene, int parts, UUID ownerId, string partNamePrefix, int uuidTail) + { + SceneObjectGroup so = CreateSceneObject(parts, ownerId, partNamePrefix, uuidTail); + + scene.AddNewSceneObject(so, false); + + return so; + } + + /// + /// Create a scene object part. + /// + /// + /// + /// + /// + public static SceneObjectPart CreateSceneObjectPart(string name, UUID id, UUID ownerId) + { + return new SceneObjectPart( + ownerId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero) + { Name = name, UUID = id, Scale = new Vector3(1, 1, 1) }; + } + + /// + /// Create a scene object but do not add it to the scene. + /// + /// + /// UUID always starts at 00000000-0000-0000-0000-000000000001. For some purposes, (e.g. serializing direct + /// to another object's inventory) we do not need a scene unique ID. So it would be better to add the + /// UUID when we actually add an object to a scene rather than on creation. + /// + /// The number of parts that should be in the scene object + /// + /// + public static SceneObjectGroup CreateSceneObject(int parts, UUID ownerId) + { + return CreateSceneObject(parts, ownerId, 0x1); + } + + /// + /// Create a scene object but do not add it to the scene. + /// + /// The number of parts that should be in the scene object + /// + /// + /// The hexadecimal last part of the UUID for parts created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be given to the root part, and incremented for each part thereafter. + /// + /// + public static SceneObjectGroup CreateSceneObject(int parts, UUID ownerId, int uuidTail) + { + return CreateSceneObject(parts, ownerId, "", uuidTail); + } + + /// + /// Create a scene object but do not add it to the scene. + /// + /// + /// The number of parts that should be in the scene object + /// + /// + /// + /// The prefix to be given to part names. This will be suffixed with "Part" + /// (e.g. mynamePart1 for the root part) + /// + /// + /// The hexadecimal last part of the UUID for parts created. A UUID of the form "00000000-0000-0000-0000-{0:XD12}" + /// will be given to the root part, and incremented for each part thereafter. + /// + /// + public static SceneObjectGroup CreateSceneObject(int parts, UUID ownerId, string partNamePrefix, int uuidTail) + { + string rawSogId = string.Format("00000000-0000-0000-0000-{0:X12}", uuidTail); + + SceneObjectGroup sog + = new SceneObjectGroup( + CreateSceneObjectPart(string.Format("{0}Part1", partNamePrefix), new UUID(rawSogId), ownerId)); + + if (parts > 1) + for (int i = 2; i <= parts; i++) + sog.AddPart( + CreateSceneObjectPart( + string.Format("{0}Part{1}", partNamePrefix, i), + new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", uuidTail + i - 1)), + ownerId)); + + return sog; + } + } +} diff --git a/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs b/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs new file mode 100644 index 0000000000..3a3b33a6a9 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs @@ -0,0 +1,210 @@ +/* + * 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 OpenMetaverse.Assets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common +{ + /// + /// Utility functions for carrying out task inventory tests. + /// + /// + public static class TaskInventoryHelpers + { + /// + /// Add a notecard item to the given part. + /// + /// + /// + /// + /// UUID or UUID stem + /// UUID or UUID stem + /// The tex to put in the notecard. + /// The item that was added + public static TaskInventoryItem AddNotecard( + IAssetService assetService, SceneObjectPart part, string itemName, string itemIDStem, string assetIDStem, string text) + { + return AddNotecard( + assetService, part, itemName, TestHelpers.ParseStem(itemIDStem), TestHelpers.ParseStem(assetIDStem), text); + } + + /// + /// Add a notecard item to the given part. + /// + /// + /// + /// + /// + /// + /// The tex to put in the notecard. + /// The item that was added + public static TaskInventoryItem AddNotecard( + IAssetService assetService, SceneObjectPart part, string itemName, UUID itemID, UUID assetID, string text) + { + AssetNotecard nc = new AssetNotecard(); + nc.BodyText = text; + nc.Encode(); + + AssetBase ncAsset + = AssetHelpers.CreateAsset(assetID, AssetType.Notecard, nc.AssetData, UUID.Zero); + assetService.Store(ncAsset); + + TaskInventoryItem ncItem + = new TaskInventoryItem + { Name = itemName, AssetID = assetID, ItemID = itemID, + Type = (int)AssetType.Notecard, InvType = (int)InventoryType.Notecard }; + part.Inventory.AddInventoryItem(ncItem, true); + + return ncItem; + } + + /// + /// Add a simple script to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs to avoid mysterious script failures that try to use any of these + /// functions more than once in a test. + /// + /// + /// + /// The item that was added + public static TaskInventoryItem AddScript(IAssetService assetService, SceneObjectPart part) + { + return AddScript(assetService, part, "scriptItem", "default { state_entry() { llSay(0, \"Hello World\"); } }"); + } + + /// + /// Add a simple script to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs so that we have completely replicatable regression tests rather + /// than a random component. + /// + /// + /// + /// Name of the script to add + /// LSL script source + /// The item that was added + public static TaskInventoryItem AddScript( + IAssetService assetService, SceneObjectPart part, string scriptName, string scriptSource) + { + return AddScript(assetService, part, UUID.Random(), UUID.Random(), scriptName, scriptSource); + } + + /// + /// Add a simple script to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs so that we have completely replicatable regression tests rather + /// than a random component. + /// + /// + /// + /// Item UUID for the script + /// Asset UUID for the script + /// Name of the script to add + /// LSL script source + /// The item that was added + public static TaskInventoryItem AddScript( + IAssetService assetService, SceneObjectPart part, UUID itemId, UUID assetId, string scriptName, string scriptSource) + { + AssetScriptText ast = new AssetScriptText(); + ast.Source = scriptSource; + ast.Encode(); + + AssetBase asset + = AssetHelpers.CreateAsset(assetId, AssetType.LSLText, ast.AssetData, UUID.Zero); + assetService.Store(asset); + TaskInventoryItem item + = new TaskInventoryItem + { Name = scriptName, AssetID = assetId, ItemID = itemId, + Type = (int)AssetType.LSLText, InvType = (int)InventoryType.LSL }; + part.Inventory.AddInventoryItem(item, true); + + return item; + } + + /// + /// Add a scene object item to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs to avoid mysterious script failures that try to use any of these + /// functions more than once in a test. + /// + /// + /// + /// + /// + /// + /// + /// + public static TaskInventoryItem AddSceneObject( + IAssetService assetService, SceneObjectPart sop, string itemName, UUID itemId, SceneObjectGroup soToAdd, UUID soAssetId) + { + AssetBase taskSceneObjectAsset = AssetHelpers.CreateAsset(soAssetId, soToAdd); + assetService.Store(taskSceneObjectAsset); + TaskInventoryItem taskSceneObjectItem + = new TaskInventoryItem + { Name = itemName, + AssetID = taskSceneObjectAsset.FullID, + ItemID = itemId, + OwnerID = soToAdd.OwnerID, + Type = (int)AssetType.Object, + InvType = (int)InventoryType.Object }; + sop.Inventory.AddInventoryItem(taskSceneObjectItem, true); + + return taskSceneObjectItem; + } + + /// + /// Add a scene object item to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs to avoid mysterious script failures that try to use any of these + /// functions more than once in a test. + /// + /// + /// + /// + /// + /// + /// + public static TaskInventoryItem AddSceneObject( + IAssetService assetService, SceneObjectPart sop, string itemName, UUID itemId, UUID userId) + { + SceneObjectGroup taskSceneObject = SceneHelpers.CreateSceneObject(1, userId); + + return TaskInventoryHelpers.AddSceneObject( + assetService, sop, itemName, itemId, taskSceneObject, TestHelpers.ParseTail(0x10)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/UserAccountHelpers.cs b/OpenSim/Tests/Common/Helpers/UserAccountHelpers.cs new file mode 100644 index 0000000000..2fbebc4ea9 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/UserAccountHelpers.cs @@ -0,0 +1,160 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework.Communications; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common +{ + /// + /// Utility functions for carrying out user profile related tests. + /// + public static class UserAccountHelpers + { +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, OnInventoryReceivedDelegate callback) +// { +// UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000099"); +// return CreateUserWithInventory(commsManager, userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, UUID userId, OnInventoryReceivedDelegate callback) +// { +// return CreateUserWithInventory(commsManager, "Bill", "Bailey", userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// First name of user +// /// Last name of user +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, string firstName, string lastName, +// UUID userId, OnInventoryReceivedDelegate callback) +// { +// return CreateUserWithInventory(commsManager, firstName, lastName, "troll", userId, callback); +// } +// +// /// +// /// Create a test user with a standard inventory +// /// +// /// +// /// First name of user +// /// Last name of user +// /// Password +// /// User ID +// /// +// /// Callback to invoke when inventory has been loaded. This is required because +// /// loading may be asynchronous, even on standalone +// /// +// /// +// public static CachedUserInfo CreateUserWithInventory( +// CommunicationsManager commsManager, string firstName, string lastName, string password, +// UUID userId, OnInventoryReceivedDelegate callback) +// { +// LocalUserServices lus = (LocalUserServices)commsManager.UserService; +// lus.AddUser(firstName, lastName, password, "bill@bailey.com", 1000, 1000, userId); +// +// CachedUserInfo userInfo = commsManager.UserProfileCacheService.GetUserDetails(userId); +// userInfo.OnInventoryReceived += callback; +// userInfo.FetchInventory(); +// +// return userInfo; +// } + + public static UserAccount CreateUserWithInventory(Scene scene) + { + return CreateUserWithInventory(scene, TestHelpers.ParseTail(99)); + } + + public static UserAccount CreateUserWithInventory(Scene scene, UUID userId) + { + return CreateUserWithInventory(scene, "Bill", "Bailey", userId, "troll"); + } + + public static UserAccount CreateUserWithInventory(Scene scene, int userId) + { + return CreateUserWithInventory(scene, "Bill", "Bailey", TestHelpers.ParseTail(userId), "troll"); + } + + public static UserAccount CreateUserWithInventory( + Scene scene, string firstName, string lastName, UUID userId, string pw) + { + UserAccount ua = new UserAccount(userId) { FirstName = firstName, LastName = lastName }; + CreateUserWithInventory(scene, ua, pw); + return ua; + } + + public static UserAccount CreateUserWithInventory( + Scene scene, string firstName, string lastName, int userId, string pw) + { + UserAccount ua + = new UserAccount(TestHelpers.ParseTail(userId)) { FirstName = firstName, LastName = lastName }; + CreateUserWithInventory(scene, ua, pw); + return ua; + } + + public static void CreateUserWithInventory(Scene scene, UserAccount ua, string pw) + { + // FIXME: This should really be set up by UserAccount itself + ua.ServiceURLs = new Dictionary(); + scene.UserAccountService.StoreUserAccount(ua); + scene.InventoryService.CreateUserInventory(ua.PrincipalID); + scene.AuthenticationService.SetPassword(ua.PrincipalID, pw); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs new file mode 100644 index 0000000000..5a36332a88 --- /dev/null +++ b/OpenSim/Tests/Common/Helpers/UserInventoryHelpers.cs @@ -0,0 +1,370 @@ +/* + * 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; +using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common +{ + /// + /// Utility functions for carrying out user inventory tests. + /// + public static class UserInventoryHelpers + { + public static readonly string PATH_DELIMITER = "/"; + + /// + /// Add an existing scene object as an item in the user's inventory. + /// + /// + /// Will be added to the system Objects folder. + /// + /// + /// + /// + /// + /// The inventory item created. + public static InventoryItemBase AddInventoryItem( + Scene scene, SceneObjectGroup so, int inventoryIdTail, int assetIdTail) + { + return AddInventoryItem( + scene, + so.Name, + TestHelpers.ParseTail(inventoryIdTail), + InventoryType.Object, + AssetHelpers.CreateAsset(TestHelpers.ParseTail(assetIdTail), so), + so.OwnerID); + } + + /// + /// Add an existing scene object as an item in the user's inventory at the given path. + /// + /// + /// + /// + /// + /// The inventory item created. + public static InventoryItemBase AddInventoryItem( + Scene scene, SceneObjectGroup so, int inventoryIdTail, int assetIdTail, string path) + { + return AddInventoryItem( + scene, + so.Name, + TestHelpers.ParseTail(inventoryIdTail), + InventoryType.Object, + AssetHelpers.CreateAsset(TestHelpers.ParseTail(assetIdTail), so), + so.OwnerID, + path); + } + + /// + /// Adds the given item to the existing system folder for its type (e.g. an object will go in the "Objects" + /// folder). + /// + /// + /// + /// + /// + /// The serialized asset for this item + /// + /// + private static InventoryItemBase AddInventoryItem( + Scene scene, string itemName, UUID itemId, InventoryType itemType, AssetBase asset, UUID userId) + { + return AddInventoryItem( + scene, itemName, itemId, itemType, asset, userId, + scene.InventoryService.GetFolderForType(userId, (FolderType)asset.Type).Name); + } + + /// + /// Adds the given item to an inventory folder + /// + /// + /// + /// + /// + /// The serialized asset for this item + /// + /// Existing inventory path at which to add. + /// + private static InventoryItemBase AddInventoryItem( + Scene scene, string itemName, UUID itemId, InventoryType itemType, AssetBase asset, UUID userId, string path) + { + scene.AssetService.Store(asset); + + InventoryItemBase item = new InventoryItemBase(); + item.Name = itemName; + item.AssetID = asset.FullID; + item.ID = itemId; + item.Owner = userId; + item.AssetType = asset.Type; + item.InvType = (int)itemType; + + InventoryFolderBase folder = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, userId, path)[0]; + + item.Folder = folder.ID; + scene.AddInventoryItem(item); + + return item; + } + + /// + /// Creates a notecard in the objects folder and specify an item id. + /// + /// + /// + /// + /// + /// + public static InventoryItemBase CreateInventoryItem(Scene scene, string itemName, UUID userId) + { + return CreateInventoryItem(scene, itemName, UUID.Random(), UUID.Random(), userId, InventoryType.Notecard); + } + + /// + /// Creates an item of the given type with an accompanying asset. + /// + /// + /// + /// + /// + /// Type of item to create + /// + public static InventoryItemBase CreateInventoryItem( + Scene scene, string itemName, UUID userId, InventoryType type) + { + return CreateInventoryItem(scene, itemName, UUID.Random(), UUID.Random(), userId, type); + } + + /// + /// Creates a notecard in the objects folder and specify an item id. + /// + /// + /// + /// + /// + /// + /// Type of item to create + /// + public static InventoryItemBase CreateInventoryItem( + Scene scene, string itemName, UUID itemId, UUID assetId, UUID userId, InventoryType itemType) + { + AssetBase asset = null; + + if (itemType == InventoryType.Notecard) + { + asset = AssetHelpers.CreateNotecardAsset(); + asset.CreatorID = userId.ToString(); + } + else if (itemType == InventoryType.Object) + { + asset = AssetHelpers.CreateAsset(assetId, SceneHelpers.CreateSceneObject(1, userId)); + } + else + { + throw new Exception(string.Format("Inventory type {0} not supported", itemType)); + } + + return AddInventoryItem(scene, itemName, itemId, itemType, asset, userId); + } + + /// + /// Create inventory folders starting from the user's root folder. + /// + /// + /// + /// + /// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER + /// + /// + /// If true, then folders in the path which already the same name are + /// used. This applies to the terminal folder as well. + /// If false, then all folders in the path are created, even if there is already a folder at a particular + /// level with the same name. + /// + /// + /// The folder created. If the path contains multiple folders then the last one created is returned. + /// Will return null if the root folder could not be found. + /// + public static InventoryFolderBase CreateInventoryFolder( + IInventoryService inventoryService, UUID userId, string path, bool useExistingFolders) + { + return CreateInventoryFolder(inventoryService, userId, UUID.Random(), path, useExistingFolders); + } + + /// + /// Create inventory folders starting from the user's root folder. + /// + /// + /// + /// + /// + /// The folders to create. Multiple folders can be specified on a path delimited by the PATH_DELIMITER + /// + /// + /// If true, then folders in the path which already the same name are + /// used. This applies to the terminal folder as well. + /// If false, then all folders in the path are created, even if there is already a folder at a particular + /// level with the same name. + /// + /// + /// The folder created. If the path contains multiple folders then the last one created is returned. + /// Will return null if the root folder could not be found. + /// + public static InventoryFolderBase CreateInventoryFolder( + IInventoryService inventoryService, UUID userId, UUID folderId, string path, bool useExistingFolders) + { + InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId); + + if (null == rootFolder) + return null; + + return CreateInventoryFolder(inventoryService, folderId, rootFolder, path, useExistingFolders); + } + + /// + /// Create inventory folders starting from a given parent folder + /// + /// + /// If any stem of the path names folders that already exist then these are not recreated. This includes the + /// final folder. + /// TODO: May need to make it an option to create duplicate folders. + /// + /// + /// ID of the folder to create + /// + /// + /// The folder to create. + /// + /// + /// If true, then folders in the path which already the same name are + /// used. This applies to the terminal folder as well. + /// If false, then all folders in the path are created, even if there is already a folder at a particular + /// level with the same name. + /// + /// + /// The folder created. If the path contains multiple folders then the last one created is returned. + /// + public static InventoryFolderBase CreateInventoryFolder( + IInventoryService inventoryService, UUID folderId, InventoryFolderBase parentFolder, string path, bool useExistingFolders) + { + string[] components = path.Split(new string[] { PATH_DELIMITER }, 2, StringSplitOptions.None); + + InventoryFolderBase folder = null; + + if (useExistingFolders) + folder = InventoryArchiveUtils.FindFolderByPath(inventoryService, parentFolder, components[0]); + + if (folder == null) + { +// Console.WriteLine("Creating folder {0} at {1}", components[0], parentFolder.Name); + + UUID folderIdForCreate; + + if (components.Length > 1) + folderIdForCreate = UUID.Random(); + else + folderIdForCreate = folderId; + + folder + = new InventoryFolderBase( + folderIdForCreate, components[0], parentFolder.Owner, (short)AssetType.Unknown, parentFolder.ID, 0); + + inventoryService.AddFolder(folder); + } +// else +// { +// Console.WriteLine("Found existing folder {0}", folder.Name); +// } + + if (components.Length > 1) + return CreateInventoryFolder(inventoryService, folderId, folder, components[1], useExistingFolders); + else + return folder; + } + + /// + /// Get the inventory folder that matches the path name. If there are multiple folders then only the first + /// is returned. + /// + /// + /// + /// + /// null if no folder matching the path was found + public static InventoryFolderBase GetInventoryFolder(IInventoryService inventoryService, UUID userId, string path) + { + List folders = GetInventoryFolders(inventoryService, userId, path); + + if (folders.Count != 0) + return folders[0]; + else + return null; + } + + /// + /// Get the inventory folders that match the path name. + /// + /// + /// + /// + /// An empty list if no matching folders were found + public static List GetInventoryFolders(IInventoryService inventoryService, UUID userId, string path) + { + return InventoryArchiveUtils.FindFoldersByPath(inventoryService, userId, path); + } + + /// + /// Get the inventory item that matches the path name. If there are multiple items then only the first + /// is returned. + /// + /// + /// + /// + /// null if no item matching the path was found + public static InventoryItemBase GetInventoryItem(IInventoryService inventoryService, UUID userId, string path) + { + return InventoryArchiveUtils.FindItemByPath(inventoryService, userId, path); + } + + /// + /// Get the inventory items that match the path name. + /// + /// + /// + /// + /// An empty list if no matching items were found. + public static List GetInventoryItems(IInventoryService inventoryService, UUID userId, string path) + { + return InventoryArchiveUtils.FindItemsByPath(inventoryService, userId, path); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/LongRunningAttribute.cs b/OpenSim/Tests/Common/LongRunningAttribute.cs new file mode 100644 index 0000000000..9831ea86fc --- /dev/null +++ b/OpenSim/Tests/Common/LongRunningAttribute.cs @@ -0,0 +1,49 @@ +/* + * 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.Text; +using NUnit.Framework; + +namespace OpenSim.Tests.Common +{ + [AttributeUsage(AttributeTargets.All, + AllowMultiple = false, + Inherited = true)] + public class LongRunningAttribute : CategoryAttribute + { + public LongRunningAttribute() : this("Long Running Test") + { + + } + + protected LongRunningAttribute(string category) : base(category) + { + } + } +} diff --git a/OpenSim/Tests/Common/Mock/BaseAssetRepository.cs b/OpenSim/Tests/Common/Mock/BaseAssetRepository.cs new file mode 100644 index 0000000000..cb4fb80fc3 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/BaseAssetRepository.cs @@ -0,0 +1,62 @@ +/* + * 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.Tests.Common +{ + public class BaseAssetRepository + { + protected Dictionary Assets = new Dictionary(); + + public AssetBase FetchAsset(UUID uuid) + { + if (AssetsExist(new[] { uuid })[0]) + return Assets[uuid]; + else + return null; + } + + public void CreateAsset(AssetBase asset) + { + Assets[asset.FullID] = asset; + } + + public void UpdateAsset(AssetBase asset) + { + CreateAsset(asset); + } + + public bool[] AssetsExist(UUID[] uuids) + { + return Array.ConvertAll(uuids, id => Assets.ContainsKey(id)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs new file mode 100644 index 0000000000..dddf75da21 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/MockAssetDataPlugin.cs @@ -0,0 +1,69 @@ +/* + * 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; +using OpenSim.Data; + +namespace OpenSim.Tests.Common +{ + /// + /// In memory asset data plugin for test purposes. Could be another dll when properly filled out and when the + /// mono addin plugin system starts co-operating with the unit test system. Currently no locking since unit + /// tests are single threaded. + /// + public class MockAssetDataPlugin : BaseAssetRepository, IAssetDataPlugin + { + public string Version { get { return "0"; } } + public string Name { get { return "MockAssetDataPlugin"; } } + + public void Initialise() {} + public void Initialise(string connect) {} + public void Dispose() {} + + private readonly List assets = new List(); + + public AssetBase GetAsset(UUID uuid) + { + return assets.Find(x=>x.FullID == uuid); + } + + public void StoreAsset(AssetBase asset) + { + assets.Add(asset); + } + + public List FetchAssetMetadataSet(int start, int count) { return new List(count); } + + public bool Delete(string id) + { + return false; + } + } +} diff --git a/OpenSim/Tests/Common/Mock/MockGroupsServicesConnector.cs b/OpenSim/Tests/Common/Mock/MockGroupsServicesConnector.cs new file mode 100644 index 0000000000..7f530d08e9 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/MockGroupsServicesConnector.cs @@ -0,0 +1,436 @@ +/* + * 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 Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Data; +using OpenSim.Data.Null; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups; + +namespace OpenSim.Tests.Common +{ + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class MockGroupsServicesConnector : ISharedRegionModule, IGroupsServicesConnector + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + IXGroupData m_data = new NullXGroupData(null, null); + + public string Name + { + get { return "MockGroupsServicesConnector"; } + } + + public Type ReplaceableInterface + { + get { return null; } + } + + public void Initialise(IConfigSource config) + { + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + m_log.DebugFormat("[MOCK GROUPS SERVICES CONNECTOR]: Adding to region {0}", scene.RegionInfo.RegionName); + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + } + + public void PostInitialise() + { + } + + public UUID CreateGroup(UUID requestingAgentID, string name, string charter, bool showInList, UUID insigniaID, + int membershipFee, bool openEnrollment, bool allowPublish, + bool maturePublish, UUID founderID) + { + XGroup group = new XGroup() + { + groupID = UUID.Random(), + ownerRoleID = UUID.Random(), + name = name, + charter = charter, + showInList = showInList, + insigniaID = insigniaID, + membershipFee = membershipFee, + openEnrollment = openEnrollment, + allowPublish = allowPublish, + maturePublish = maturePublish, + founderID = founderID, + everyonePowers = (ulong)XmlRpcGroupsServicesConnectorModule.DefaultEveryonePowers, + ownersPowers = (ulong)XmlRpcGroupsServicesConnectorModule.DefaultOwnerPowers + }; + + if (m_data.StoreGroup(group)) + { + m_log.DebugFormat("[MOCK GROUPS SERVICES CONNECTOR]: Created group {0} {1}", group.name, group.groupID); + return group.groupID; + } + else + { + m_log.ErrorFormat("[MOCK GROUPS SERVICES CONNECTOR]: Failed to create group {0}", name); + return UUID.Zero; + } + } + + public void UpdateGroup(UUID requestingAgentID, UUID groupID, string charter, bool showInList, + UUID insigniaID, int membershipFee, bool openEnrollment, + bool allowPublish, bool maturePublish) + { + } + + public void AddGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description, + string title, ulong powers) + { + } + + public void RemoveGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID) + { + } + + public void UpdateGroupRole(UUID requestingAgentID, UUID groupID, UUID roleID, string name, string description, + string title, ulong powers) + { + } + + private XGroup GetXGroup(UUID groupID, string name) + { + XGroup group = m_data.GetGroup(groupID); + + + if (group == null) + m_log.DebugFormat("[MOCK GROUPS SERVICES CONNECTOR]: No group found with ID {0}", groupID); + + return group; + } + + public GroupRecord GetGroupRecord(UUID requestingAgentID, UUID groupID, string groupName) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: Processing GetGroupRecord() for groupID {0}, name {1}", + groupID, groupName); + + XGroup xg = GetXGroup(groupID, groupName); + + if (xg == null) + return null; + + GroupRecord gr = new GroupRecord() + { + GroupID = xg.groupID, + GroupName = xg.name, + AllowPublish = xg.allowPublish, + MaturePublish = xg.maturePublish, + Charter = xg.charter, + FounderID = xg.founderID, + // FIXME: group picture storage location unknown + MembershipFee = xg.membershipFee, + OpenEnrollment = xg.openEnrollment, + OwnerRoleID = xg.ownerRoleID, + ShowInList = xg.showInList + }; + + return gr; + } + + public GroupProfileData GetMemberGroupProfile(UUID requestingAgentID, UUID GroupID, UUID AgentID) + { + return default(GroupProfileData); + } + + public void SetAgentActiveGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID) + { + } + + public void SetAgentActiveGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID) + { + } + + public void SetAgentGroupInfo(UUID requestingAgentID, UUID agentID, UUID groupID, bool acceptNotices, bool listInProfile) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: SetAgentGroupInfo, requestingAgentID {0}, agentID {1}, groupID {2}, acceptNotices {3}, listInProfile {4}", + requestingAgentID, agentID, groupID, acceptNotices, listInProfile); + + XGroup group = GetXGroup(groupID, null); + + if (group == null) + return; + + XGroupMember xgm = null; + if (!group.members.TryGetValue(agentID, out xgm)) + return; + + xgm.acceptNotices = acceptNotices; + xgm.listInProfile = listInProfile; + + m_data.StoreGroup(group); + } + + public void AddAgentToGroupInvite(UUID requestingAgentID, UUID inviteID, UUID groupID, UUID roleID, UUID agentID) + { + } + + public GroupInviteInfo GetAgentToGroupInvite(UUID requestingAgentID, UUID inviteID) + { + return null; + } + + public void RemoveAgentToGroupInvite(UUID requestingAgentID, UUID inviteID) + { + } + + public void AddAgentToGroup(UUID requestingAgentID, UUID agentID, UUID groupID, UUID roleID) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: AddAgentToGroup, requestingAgentID {0}, agentID {1}, groupID {2}, roleID {3}", + requestingAgentID, agentID, groupID, roleID); + + XGroup group = GetXGroup(groupID, null); + + if (group == null) + return; + + XGroupMember groupMember = new XGroupMember() + { + agentID = agentID, + groupID = groupID, + roleID = roleID + }; + + group.members[agentID] = groupMember; + + m_data.StoreGroup(group); + } + + public void RemoveAgentFromGroup(UUID requestingAgentID, UUID AgentID, UUID GroupID) + { + } + + public void AddAgentToGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID) + { + } + + public void RemoveAgentFromGroupRole(UUID requestingAgentID, UUID AgentID, UUID GroupID, UUID RoleID) + { + } + + public List FindGroups(UUID requestingAgentID, string search) + { + return null; + } + + public GroupMembershipData GetAgentGroupMembership(UUID requestingAgentID, UUID AgentID, UUID GroupID) + { + return null; + } + + public GroupMembershipData GetAgentActiveMembership(UUID requestingAgentID, UUID AgentID) + { + return null; + } + + public List GetAgentGroupMemberships(UUID requestingAgentID, UUID AgentID) + { + return new List(); + } + + public List GetAgentGroupRoles(UUID requestingAgentID, UUID AgentID, UUID GroupID) + { + return null; + } + + public List GetGroupRoles(UUID requestingAgentID, UUID GroupID) + { + return null; + } + + public List GetGroupMembers(UUID requestingAgentID, UUID groupID) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: GetGroupMembers, requestingAgentID {0}, groupID {1}", + requestingAgentID, groupID); + + List groupMembers = new List(); + + XGroup group = GetXGroup(groupID, null); + + if (group == null) + return groupMembers; + + foreach (XGroupMember xgm in group.members.Values) + { + GroupMembersData gmd = new GroupMembersData(); + gmd.AgentID = xgm.agentID; + gmd.IsOwner = group.founderID == gmd.AgentID; + gmd.AcceptNotices = xgm.acceptNotices; + gmd.ListInProfile = xgm.listInProfile; + + groupMembers.Add(gmd); + } + + return groupMembers; + } + + public List GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID) + { + return null; + } + + public List GetGroupNotices(UUID requestingAgentID, UUID groupID) + { + XGroup group = GetXGroup(groupID, null); + + if (group == null) + return null; + + List notices = new List(); + + foreach (XGroupNotice notice in group.notices.Values) + { + GroupNoticeData gnd = new GroupNoticeData() + { + NoticeID = notice.noticeID, + Timestamp = notice.timestamp, + FromName = notice.fromName, + Subject = notice.subject, + HasAttachment = notice.hasAttachment, + AssetType = (byte)notice.assetType + }; + + notices.Add(gnd); + } + + return notices; + } + + public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: GetGroupNotices, requestingAgentID {0}, noticeID {1}", + requestingAgentID, noticeID); + + // Yes, not an efficient way to do it. + Dictionary groups = m_data.GetGroups(); + + foreach (XGroup group in groups.Values) + { + if (group.notices.ContainsKey(noticeID)) + { + XGroupNotice n = group.notices[noticeID]; + + GroupNoticeInfo gni = new GroupNoticeInfo(); + gni.GroupID = n.groupID; + gni.Message = n.message; + gni.BinaryBucket = n.binaryBucket; + gni.noticeData.NoticeID = n.noticeID; + gni.noticeData.Timestamp = n.timestamp; + gni.noticeData.FromName = n.fromName; + gni.noticeData.Subject = n.subject; + gni.noticeData.HasAttachment = n.hasAttachment; + gni.noticeData.AssetType = (byte)n.assetType; + + return gni; + } + } + + return null; + } + + public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) + { + m_log.DebugFormat( + "[MOCK GROUPS SERVICES CONNECTOR]: AddGroupNotice, requestingAgentID {0}, groupID {1}, noticeID {2}, fromName {3}, subject {4}, message {5}, binaryBucket.Length {6}", + requestingAgentID, groupID, noticeID, fromName, subject, message, binaryBucket.Length); + + XGroup group = GetXGroup(groupID, null); + + if (group == null) + return; + + XGroupNotice groupNotice = new XGroupNotice() + { + groupID = groupID, + noticeID = noticeID, + fromName = fromName, + subject = subject, + message = message, + timestamp = (uint)Util.UnixTimeSinceEpoch(), + hasAttachment = false, + assetType = 0, + binaryBucket = binaryBucket + }; + + group.notices[noticeID] = groupNotice; + + m_data.StoreGroup(group); + } + + public void ResetAgentGroupChatSessions(UUID agentID) + { + } + + public bool hasAgentBeenInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + return false; + } + + public bool hasAgentDroppedGroupChatSession(UUID agentID, UUID groupID) + { + return false; + } + + public void AgentDroppedFromGroupChatSession(UUID agentID, UUID groupID) + { + } + + public void AgentInvitedToGroupChatSession(UUID agentID, UUID groupID) + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs new file mode 100644 index 0000000000..5df8e0456f --- /dev/null +++ b/OpenSim/Tests/Common/Mock/MockRegionDataPlugin.cs @@ -0,0 +1,371 @@ +/* + * 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.Reflection; +using System.Collections.Generic; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Data.Null +{ + public class NullDataService : ISimulationDataService + { + private NullDataStore m_store; + + public NullDataService() + { + m_store = new NullDataStore(); + } + + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + m_store.StoreObject(obj, regionUUID); + } + + public void RemoveObject(UUID uuid, UUID regionUUID) + { + m_store.RemoveObject(uuid, regionUUID); + } + + public void StorePrimInventory(UUID primID, ICollection items) + { + m_store.StorePrimInventory(primID, items); + } + + public List LoadObjects(UUID regionUUID) + { + return m_store.LoadObjects(regionUUID); + } + + public void StoreTerrain(double[,] terrain, UUID regionID) + { + m_store.StoreTerrain(terrain, regionID); + } + + public void StoreTerrain(TerrainData terrain, UUID regionID) + { + m_store.StoreTerrain(terrain, regionID); + } + + public double[,] LoadTerrain(UUID regionID) + { + return m_store.LoadTerrain(regionID); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + return m_store.LoadTerrain(regionID, pSizeX, pSizeY, pSizeZ); + } + + public void StoreLandObject(ILandObject Parcel) + { + m_store.StoreLandObject(Parcel); + } + + public void RemoveLandObject(UUID globalID) + { + m_store.RemoveLandObject(globalID); + } + + public List LoadLandObjects(UUID regionUUID) + { + return m_store.LoadLandObjects(regionUUID); + } + + public void StoreRegionSettings(RegionSettings rs) + { + m_store.StoreRegionSettings(rs); + } + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + return m_store.LoadRegionSettings(regionUUID); + } + + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + return m_store.LoadRegionWindlightSettings(regionUUID); + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + m_store.StoreRegionWindlightSettings(wl); + } + + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + return m_store.LoadRegionEnvironmentSettings(regionUUID); + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + m_store.StoreRegionEnvironmentSettings(regionUUID, settings); + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + m_store.RemoveRegionEnvironmentSettings(regionUUID); + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } + + /// + /// Mock region data plugin. This obeys the api contract for persistence but stores everything in memory, so that + /// tests can check correct persistence. + /// + public class NullDataStore : ISimulationDataStore + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + protected Dictionary m_regionSettings = new Dictionary(); + protected Dictionary m_sceneObjectParts = new Dictionary(); + protected Dictionary> m_primItems + = new Dictionary>(); + protected Dictionary m_terrains = new Dictionary(); + protected Dictionary m_landData = new Dictionary(); + + public void Initialise(string dbfile) + { + return; + } + + public void Dispose() + { + } + + public void StoreRegionSettings(RegionSettings rs) + { + m_regionSettings[rs.RegionUUID] = rs; + } + + public RegionLightShareData LoadRegionWindlightSettings(UUID regionUUID) + { + //This connector doesn't support the windlight module yet + //Return default LL windlight settings + return new RegionLightShareData(); + } + + public void RemoveRegionWindlightSettings(UUID regionID) + { + } + + public void StoreRegionWindlightSettings(RegionLightShareData wl) + { + //This connector doesn't support the windlight module yet + } + + #region Environment Settings + public string LoadRegionEnvironmentSettings(UUID regionUUID) + { + //This connector doesn't support the Environment module yet + return string.Empty; + } + + public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings) + { + //This connector doesn't support the Environment module yet + } + + public void RemoveRegionEnvironmentSettings(UUID regionUUID) + { + //This connector doesn't support the Environment module yet + } + #endregion + + public RegionSettings LoadRegionSettings(UUID regionUUID) + { + RegionSettings rs = null; + m_regionSettings.TryGetValue(regionUUID, out rs); + + if (rs == null) + rs = new RegionSettings(); + + return rs; + } + + public void StoreObject(SceneObjectGroup obj, UUID regionUUID) + { + // We can't simply store groups here because on delinking, OpenSim will not update the original group + // directly. Rather, the newly delinked parts will be updated to be in their own scene object group + // Therefore, we need to store parts rather than groups. + foreach (SceneObjectPart prim in obj.Parts) + { +// m_log.DebugFormat( +// "[MOCK REGION DATA PLUGIN]: Storing part {0} {1} in object {2} {3} in region {4}", +// prim.Name, prim.UUID, obj.Name, obj.UUID, regionUUID); + + m_sceneObjectParts[prim.UUID] = prim; + } + } + + public void RemoveObject(UUID obj, UUID regionUUID) + { + // All parts belonging to the object with the uuid are removed. + List parts = new List(m_sceneObjectParts.Values); + foreach (SceneObjectPart part in parts) + { + if (part.ParentGroup.UUID == obj) + { +// m_log.DebugFormat( +// "[MOCK REGION DATA PLUGIN]: Removing part {0} {1} as part of object {2} from {3}", +// part.Name, part.UUID, obj, regionUUID); + m_sceneObjectParts.Remove(part.UUID); + } + } + } + + public void StorePrimInventory(UUID primID, ICollection items) + { + m_primItems[primID] = items; + } + + public List LoadObjects(UUID regionUUID) + { + Dictionary objects = new Dictionary(); + + // Create all of the SOGs from the root prims first + foreach (SceneObjectPart prim in m_sceneObjectParts.Values) + { + if (prim.IsRoot) + { +// m_log.DebugFormat( +// "[MOCK REGION DATA PLUGIN]: Loading root part {0} {1} in {2}", prim.Name, prim.UUID, regionUUID); + objects[prim.UUID] = new SceneObjectGroup(prim); + } + } + + // Add all of the children objects to the SOGs + foreach (SceneObjectPart prim in m_sceneObjectParts.Values) + { + SceneObjectGroup sog; + if (prim.UUID != prim.ParentUUID) + { + if (objects.TryGetValue(prim.ParentUUID, out sog)) + { + int originalLinkNum = prim.LinkNum; + + sog.AddPart(prim); + + // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. + // We override that here + if (originalLinkNum != 0) + prim.LinkNum = originalLinkNum; + } + else + { +// m_log.WarnFormat( +// "[MOCK REGION DATA PLUGIN]: Database contains an orphan child prim {0} {1} in region {2} pointing to missing parent {3}. This prim will not be loaded.", +// prim.Name, prim.UUID, regionUUID, prim.ParentUUID); + } + } + } + + // TODO: Load items. This is assymetric - we store items as a separate method but don't retrieve them that + // way! + + return new List(objects.Values); + } + + public void StoreTerrain(TerrainData ter, UUID regionID) + { + m_terrains[regionID] = ter; + } + + public void StoreTerrain(double[,] ter, UUID regionID) + { + m_terrains[regionID] = new HeightmapTerrainData(ter); + } + + public TerrainData LoadTerrain(UUID regionID, int pSizeX, int pSizeY, int pSizeZ) + { + if (m_terrains.ContainsKey(regionID)) + return m_terrains[regionID]; + else + return null; + } + + public double[,] LoadTerrain(UUID regionID) + { + if (m_terrains.ContainsKey(regionID)) + return m_terrains[regionID].GetDoubles(); + else + return null; + } + + public void RemoveLandObject(UUID globalID) + { + if (m_landData.ContainsKey(globalID)) + m_landData.Remove(globalID); + } + + public void StoreLandObject(ILandObject land) + { + m_landData[land.LandData.GlobalID] = land.LandData; + } + + public List LoadLandObjects(UUID regionUUID) + { + return new List(m_landData.Values); + } + + public void Shutdown() + { + } + + public void SaveExtra(UUID regionID, string name, string value) + { + } + + public void RemoveExtra(UUID regionID, string name) + { + } + + public Dictionary GetExtra(UUID regionID) + { + return null; + } + } +} diff --git a/OpenSim/Tests/Common/Mock/MockScriptEngine.cs b/OpenSim/Tests/Common/Mock/MockScriptEngine.cs new file mode 100644 index 0000000000..d7a144c886 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/MockScriptEngine.cs @@ -0,0 +1,272 @@ +/* + * 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 Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.ScriptEngine.Interfaces; +using OpenSim.Region.ScriptEngine.Shared; + +namespace OpenSim.Tests.Common +{ + public class MockScriptEngine : INonSharedRegionModule, IScriptModule, IScriptEngine + { + public IConfigSource ConfigSource { get; private set; } + + public IConfig Config { get; private set; } + + private Scene m_scene; + + /// + /// Expose posted events to tests. + /// + public Dictionary> PostedEvents { get; private set; } + + /// + /// A very primitive way of hooking text cose to a posed event. + /// + /// + /// May be replaced with something that uses more original code in the future. + /// + public event Action PostEventHook; + + public void Initialise(IConfigSource source) + { + ConfigSource = source; + + // Can set later on if required + Config = new IniConfig("MockScriptEngine", ConfigSource); + + PostedEvents = new Dictionary>(); + } + + public void Close() + { + } + + public void AddRegion(Scene scene) + { + m_scene = scene; + + m_scene.StackModuleInterface(this); + } + + public void RemoveRegion(Scene scene) + { + } + + public void RegionLoaded(Scene scene) + { + } + + public string Name { get { return "Mock Script Engine"; } } + public string ScriptEngineName { get { return Name; } } + + public Type ReplaceableInterface { get { return null; } } + +#pragma warning disable 0067 + public event ScriptRemoved OnScriptRemoved; + public event ObjectRemoved OnObjectRemoved; +#pragma warning restore 0067 + + public string GetXMLState (UUID itemID) + { + throw new System.NotImplementedException (); + } + + public bool SetXMLState(UUID itemID, string xml) + { + throw new System.NotImplementedException (); + } + + public bool PostScriptEvent(UUID itemID, string name, object[] args) + { +// Console.WriteLine("Posting event {0} for {1}", name, itemID); + + return PostScriptEvent(itemID, new EventParams(name, args, null)); + } + + public bool PostScriptEvent(UUID itemID, EventParams evParams) + { + List eventsForItem; + + if (!PostedEvents.ContainsKey(itemID)) + { + eventsForItem = new List(); + PostedEvents.Add(itemID, eventsForItem); + } + else + { + eventsForItem = PostedEvents[itemID]; + } + + eventsForItem.Add(evParams); + + if (PostEventHook != null) + PostEventHook(itemID, evParams); + + return true; + } + + public bool PostObjectEvent(uint localID, EventParams evParams) + { + return PostObjectEvent(m_scene.GetSceneObjectPart(localID), evParams); + } + + public bool PostObjectEvent(UUID itemID, string name, object[] args) + { + return PostObjectEvent(m_scene.GetSceneObjectPart(itemID), new EventParams(name, args, null)); + } + + private bool PostObjectEvent(SceneObjectPart part, EventParams evParams) + { + foreach (TaskInventoryItem item in part.Inventory.GetInventoryItems(InventoryType.LSL)) + PostScriptEvent(item.ItemID, evParams); + + return true; + } + + public void SuspendScript(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public void ResumeScript(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public ArrayList GetScriptErrors(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public bool HasScript(UUID itemID, out bool running) + { + throw new System.NotImplementedException (); + } + + public bool GetScriptState(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public void SaveAllState() + { + throw new System.NotImplementedException (); + } + + public void StartProcessing() + { + throw new System.NotImplementedException (); + } + + public float GetScriptExecutionTime(List itemIDs) + { + throw new System.NotImplementedException (); + } + + public Dictionary GetObjectScriptsExecutionTimes() + { + throw new System.NotImplementedException (); + } + + public IScriptWorkItem QueueEventHandler(object parms) + { + throw new System.NotImplementedException (); + } + + public DetectParams GetDetectParams(UUID item, int number) + { + throw new System.NotImplementedException (); + } + + public void SetMinEventDelay(UUID itemID, double delay) + { + throw new System.NotImplementedException (); + } + + public int GetStartParameter(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public void SetScriptState(UUID itemID, bool state) + { + throw new System.NotImplementedException (); + } + + public void SetState(UUID itemID, string newState) + { + throw new System.NotImplementedException (); + } + + public void ApiResetScript(UUID itemID) + { + throw new System.NotImplementedException (); + } + + public void ResetScript (UUID itemID) + { + throw new System.NotImplementedException (); + } + + public IScriptApi GetApi(UUID itemID, string name) + { + throw new System.NotImplementedException (); + } + + public Scene World { get { return m_scene; } } + + public IScriptModule ScriptModule { get { return this; } } + + public string ScriptEnginePath { get { throw new System.NotImplementedException (); }} + + public string ScriptClassName { get { throw new System.NotImplementedException (); } } + + public string ScriptBaseClassName { get { throw new System.NotImplementedException (); } } + + public string[] ScriptReferencedAssemblies { get { throw new System.NotImplementedException (); } } + + public ParameterInfo[] ScriptBaseClassParameters { get { throw new System.NotImplementedException (); } } + + public void ClearPostedEvents() + { + PostedEvents.Clear(); + } + + public void SleepScript(UUID itemID, int delay) + { + } + } +} diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs new file mode 100644 index 0000000000..0e1bc8fcd4 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -0,0 +1,1323 @@ +/* + * 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.Net; +using System.Reflection; +using System.Threading; +using log4net; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Client; + +namespace OpenSim.Tests.Common +{ + public class TestClient : IClientAPI, IClientCore + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.AutoReset, "Crossing"); + + private Scene m_scene; + + // Properties so that we can get at received data for test purposes + public List ReceivedKills { get; private set; } + public List ReceivedOfflineNotifications { get; private set; } + public List ReceivedOnlineNotifications { get; private set; } + public List ReceivedFriendshipTerminations { get; private set; } + + public List SentImageDataPackets { get; private set; } + public List SentImagePacketPackets { get; private set; } + public List SentImageNotInDatabasePackets { get; private set; } + + // Test client specific events - for use by tests to implement some IClientAPI behaviour. + public event Action OnReceivedMoveAgentIntoRegion; + public event Action OnTestClientInformClientOfNeighbour; + public event TestClientOnSendRegionTeleportDelegate OnTestClientSendRegionTeleport; + + public event Action OnReceivedEntityUpdate; + + public event OnReceivedChatMessageDelegate OnReceivedChatMessage; + public event Action OnReceivedInstantMessage; + + public event Action OnReceivedSendRebakeAvatarTextures; + + public delegate void TestClientOnSendRegionTeleportDelegate( + ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, + uint locationID, uint flags, string capsURL); + + public delegate void OnReceivedChatMessageDelegate( + string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible); + + +// disable warning: public events, part of the public API +#pragma warning disable 67 + + public event Action OnLogout; + public event ObjectPermissions OnObjectPermissions; + + public event MoneyTransferRequest OnMoneyTransferRequest; + public event ParcelBuy OnParcelBuy; + public event Action OnConnectionClosed; + + public event ImprovedInstantMessage OnInstantMessage; + public event ChatMessage OnChatFromClient; + public event TextureRequest OnRequestTexture; + public event RezObject OnRezObject; + public event ModifyTerrain OnModifyTerrain; + public event BakeTerrain OnBakeTerrain; + public event SetAppearance OnSetAppearance; + public event AvatarNowWearing OnAvatarNowWearing; + public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv; + public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv; + public event UUIDNameRequest OnDetachAttachmentIntoInv; + public event ObjectAttach OnObjectAttach; + public event ObjectDeselect OnObjectDetach; + public event ObjectDrop OnObjectDrop; + public event StartAnim OnStartAnim; + public event StopAnim OnStopAnim; + public event LinkObjects OnLinkObjects; + public event DelinkObjects OnDelinkObjects; + public event RequestMapBlocks OnRequestMapBlocks; + public event RequestMapName OnMapNameRequest; + public event TeleportLocationRequest OnTeleportLocationRequest; + public event TeleportLandmarkRequest OnTeleportLandmarkRequest; + public event TeleportCancel OnTeleportCancel; + public event DisconnectUser OnDisconnectUser; + public event RequestAvatarProperties OnRequestAvatarProperties; + public event SetAlwaysRun OnSetAlwaysRun; + + public event DeRezObject OnDeRezObject; + public event Action OnRegionHandShakeReply; + public event GenericCall1 OnRequestWearables; + public event Action OnCompleteMovementToRegion; + public event UpdateAgent OnPreAgentUpdate; + public event UpdateAgent OnAgentUpdate; + public event UpdateAgent OnAgentCameraUpdate; + public event AgentRequestSit OnAgentRequestSit; + public event AgentSit OnAgentSit; + public event AvatarPickerRequest OnAvatarPickerRequest; + public event Action OnRequestAvatarsData; + public event AddNewPrim OnAddPrim; + public event RequestGodlikePowers OnRequestGodlikePowers; + public event GodKickUser OnGodKickUser; + public event ObjectDuplicate OnObjectDuplicate; + public event GrabObject OnGrabObject; + public event DeGrabObject OnDeGrabObject; + public event MoveObject OnGrabUpdate; + public event SpinStart OnSpinStart; + public event SpinObject OnSpinUpdate; + public event SpinStop OnSpinStop; + public event ViewerEffectEventHandler OnViewerEffect; + + public event FetchInventory OnAgentDataUpdateRequest; + public event TeleportLocationRequest OnSetStartLocationRequest; + + public event UpdateShape OnUpdatePrimShape; + public event ObjectExtraParams OnUpdateExtraParams; + public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily; + public event ObjectSelect OnObjectSelect; + public event ObjectRequest OnObjectRequest; + public event GenericCall7 OnObjectDescription; + public event GenericCall7 OnObjectName; + public event GenericCall7 OnObjectClickAction; + public event GenericCall7 OnObjectMaterial; + public event UpdatePrimFlags OnUpdatePrimFlags; + public event UpdatePrimTexture OnUpdatePrimTexture; + public event UpdateVector OnUpdatePrimGroupPosition; + public event UpdateVector OnUpdatePrimSinglePosition; + public event UpdatePrimRotation OnUpdatePrimGroupRotation; + public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation; + public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition; + public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation; + public event UpdateVector OnUpdatePrimScale; + public event UpdateVector OnUpdatePrimGroupScale; + public event StatusChange OnChildAgentStatus; + public event GenericCall2 OnStopMovement; + public event Action OnRemoveAvatar; + + public event CreateNewInventoryItem OnCreateNewInventoryItem; + public event LinkInventoryItem OnLinkInventoryItem; + public event CreateInventoryFolder OnCreateNewInventoryFolder; + public event UpdateInventoryFolder OnUpdateInventoryFolder; + public event MoveInventoryFolder OnMoveInventoryFolder; + public event RemoveInventoryFolder OnRemoveInventoryFolder; + public event RemoveInventoryItem OnRemoveInventoryItem; + public event FetchInventoryDescendents OnFetchInventoryDescendents; + public event PurgeInventoryDescendents OnPurgeInventoryDescendents; + public event FetchInventory OnFetchInventory; + public event RequestTaskInventory OnRequestTaskInventory; + public event UpdateInventoryItem OnUpdateInventoryItem; + public event CopyInventoryItem OnCopyInventoryItem; + public event MoveInventoryItem OnMoveInventoryItem; + public event UDPAssetUploadRequest OnAssetUploadRequest; + public event RequestTerrain OnRequestTerrain; + public event RequestTerrain OnUploadTerrain; + public event XferReceive OnXferReceive; + public event RequestXfer OnRequestXfer; + public event ConfirmXfer OnConfirmXfer; + public event AbortXfer OnAbortXfer; + public event RezScript OnRezScript; + public event UpdateTaskInventory OnUpdateTaskInventory; + public event MoveTaskInventory OnMoveTaskItem; + public event RemoveTaskInventory OnRemoveTaskItem; + public event RequestAsset OnRequestAsset; + public event GenericMessage OnGenericMessage; + public event UUIDNameRequest OnNameFromUUIDRequest; + public event UUIDNameRequest OnUUIDGroupNameRequest; + + public event ParcelPropertiesRequest OnParcelPropertiesRequest; + public event ParcelDivideRequest OnParcelDivideRequest; + public event ParcelJoinRequest OnParcelJoinRequest; + public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest; + public event ParcelAbandonRequest OnParcelAbandonRequest; + public event ParcelGodForceOwner OnParcelGodForceOwner; + public event ParcelReclaim OnParcelReclaim; + public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest; + public event ParcelAccessListRequest OnParcelAccessListRequest; + public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest; + public event ParcelSelectObjects OnParcelSelectObjects; + public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest; + public event ParcelDeedToGroup OnParcelDeedToGroup; + public event ObjectDeselect OnObjectDeselect; + public event RegionInfoRequest OnRegionInfoRequest; + public event EstateCovenantRequest OnEstateCovenantRequest; + public event EstateChangeInfo OnEstateChangeInfo; + public event EstateManageTelehub OnEstateManageTelehub; + public event CachedTextureRequest OnCachedTextureRequest; + + public event ObjectDuplicateOnRay OnObjectDuplicateOnRay; + + public event FriendActionDelegate OnApproveFriendRequest; + public event FriendActionDelegate OnDenyFriendRequest; + public event FriendshipTermination OnTerminateFriendship; + public event GrantUserFriendRights OnGrantUserRights; + + public event EconomyDataRequest OnEconomyDataRequest; + public event MoneyBalanceRequest OnMoneyBalanceRequest; + public event UpdateAvatarProperties OnUpdateAvatarProperties; + + public event ObjectIncludeInSearch OnObjectIncludeInSearch; + public event UUIDNameRequest OnTeleportHomeRequest; + + public event ScriptAnswer OnScriptAnswer; + public event RequestPayPrice OnRequestPayPrice; + public event ObjectSaleInfo OnObjectSaleInfo; + public event ObjectBuy OnObjectBuy; + public event BuyObjectInventory OnBuyObjectInventory; + public event AgentSit OnUndo; + public event AgentSit OnRedo; + public event LandUndo OnLandUndo; + + public event ForceReleaseControls OnForceReleaseControls; + + public event GodLandStatRequest OnLandStatRequest; + public event RequestObjectPropertiesFamily OnObjectGroupRequest; + + public event DetailedEstateDataRequest OnDetailedEstateDataRequest; + public event SetEstateFlagsRequest OnSetEstateFlagsRequest; + public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture; + public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture; + public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights; + public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest; + public event SetRegionTerrainSettings OnSetRegionTerrainSettings; + public event EstateRestartSimRequest OnEstateRestartSimRequest; + public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest; + public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest; + public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest; + public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest; + public event EstateDebugRegionRequest OnEstateDebugRegionRequest; + public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest; + public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest; + public event ScriptReset OnScriptReset; + public event GetScriptRunning OnGetScriptRunning; + public event SetScriptRunning OnSetScriptRunning; + public event Action OnAutoPilotGo; + + public event TerrainUnacked OnUnackedTerrain; + + public event RegionHandleRequest OnRegionHandleRequest; + public event ParcelInfoRequest OnParcelInfoRequest; + + public event ActivateGesture OnActivateGesture; + public event DeactivateGesture OnDeactivateGesture; + public event ObjectOwner OnObjectOwner; + + public event DirPlacesQuery OnDirPlacesQuery; + public event DirFindQuery OnDirFindQuery; + public event DirLandQuery OnDirLandQuery; + public event DirPopularQuery OnDirPopularQuery; + public event DirClassifiedQuery OnDirClassifiedQuery; + public event EventInfoRequest OnEventInfoRequest; + public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime; + + public event MapItemRequest OnMapItemRequest; + + public event OfferCallingCard OnOfferCallingCard; + public event AcceptCallingCard OnAcceptCallingCard; + public event DeclineCallingCard OnDeclineCallingCard; + + public event SoundTrigger OnSoundTrigger; + + public event StartLure OnStartLure; + public event TeleportLureRequest OnTeleportLureRequest; + public event NetworkStats OnNetworkStatsUpdate; + + public event ClassifiedInfoRequest OnClassifiedInfoRequest; + public event ClassifiedInfoUpdate OnClassifiedInfoUpdate; + public event ClassifiedDelete OnClassifiedDelete; + public event ClassifiedDelete OnClassifiedGodDelete; + + public event EventNotificationAddRequest OnEventNotificationAddRequest; + public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest; + public event EventGodDelete OnEventGodDelete; + + public event ParcelDwellRequest OnParcelDwellRequest; + + public event UserInfoRequest OnUserInfoRequest; + public event UpdateUserInfo OnUpdateUserInfo; + + public event RetrieveInstantMessages OnRetrieveInstantMessages; + + public event PickDelete OnPickDelete; + public event PickGodDelete OnPickGodDelete; + public event PickInfoUpdate OnPickInfoUpdate; + public event AvatarNotesUpdate OnAvatarNotesUpdate; + + public event MuteListRequest OnMuteListRequest; + + public event AvatarInterestUpdate OnAvatarInterestUpdate; + + public event PlacesQuery OnPlacesQuery; + + public event FindAgentUpdate OnFindAgent; + public event TrackAgentUpdate OnTrackAgent; + public event NewUserReport OnUserReport; + public event SaveStateHandler OnSaveState; + public event GroupAccountSummaryRequest OnGroupAccountSummaryRequest; + public event GroupAccountDetailsRequest OnGroupAccountDetailsRequest; + public event GroupAccountTransactionsRequest OnGroupAccountTransactionsRequest; + public event FreezeUserUpdate OnParcelFreezeUser; + public event EjectUserUpdate OnParcelEjectUser; + public event ParcelBuyPass OnParcelBuyPass; + public event ParcelGodMark OnParcelGodMark; + public event GroupActiveProposalsRequest OnGroupActiveProposalsRequest; + public event GroupVoteHistoryRequest OnGroupVoteHistoryRequest; + public event SimWideDeletesDelegate OnSimWideDeletes; + public event SendPostcard OnSendPostcard; + public event MuteListEntryUpdate OnUpdateMuteListEntry; + public event MuteListEntryRemove OnRemoveMuteListEntry; + public event GodlikeMessage onGodlikeMessage; + public event GodUpdateRegionInfoUpdate OnGodUpdateRegionInfoUpdate; + +#pragma warning restore 67 + + /// + /// This agent's UUID + /// + private UUID m_agentId; + + public ISceneAgent SceneAgent { get; set; } + + /// + /// The last caps seed url that this client was given. + /// + public string CapsSeedUrl; + + private Vector3 startPos = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 2); + + public virtual Vector3 StartPos + { + get { return startPos; } + set { } + } + + public virtual UUID AgentId + { + get { return m_agentId; } + } + + public UUID SessionId { get; set; } + + public UUID SecureSessionId { get; set; } + + public virtual string FirstName + { + get { return m_firstName; } + } + private string m_firstName; + + public virtual string LastName + { + get { return m_lastName; } + } + private string m_lastName; + + public virtual String Name + { + get { return FirstName + " " + LastName; } + } + + public bool IsActive + { + get { return true; } + set { } + } + + public bool IsLoggingOut { get; set; } + + public UUID ActiveGroupId + { + get { return UUID.Zero; } + } + + public string ActiveGroupName + { + get { return String.Empty; } + } + + public ulong ActiveGroupPowers + { + get { return 0; } + } + + public bool IsGroupMember(UUID groupID) + { + return false; + } + + public ulong GetGroupPowers(UUID groupID) + { + return 0; + } + + public virtual int NextAnimationSequenceNumber + { + get { return 1; } + } + + public IScene Scene + { + get { return m_scene; } + } + + public bool SendLogoutPacketWhenClosing + { + set { } + } + + private uint m_circuitCode; + + public uint CircuitCode + { + get { return m_circuitCode; } + set { m_circuitCode = value; } + } + + public IPEndPoint RemoteEndPoint + { + get { return new IPEndPoint(IPAddress.Loopback, (ushort)m_circuitCode); } + } + + /// + /// Constructor + /// + /// + /// + /// + public TestClient(AgentCircuitData agentData, Scene scene) + { + m_agentId = agentData.AgentID; + m_firstName = agentData.firstname; + m_lastName = agentData.lastname; + m_circuitCode = agentData.circuitcode; + m_scene = scene; + SessionId = agentData.SessionID; + SecureSessionId = agentData.SecureSessionID; + CapsSeedUrl = agentData.CapsPath; + + ReceivedKills = new List(); + ReceivedOfflineNotifications = new List(); + ReceivedOnlineNotifications = new List(); + ReceivedFriendshipTerminations = new List(); + + SentImageDataPackets = new List(); + SentImagePacketPackets = new List(); + SentImageNotInDatabasePackets = new List(); + } + + /// + /// Trigger chat coming from this connection. + /// + /// + /// + /// + public bool Chat(int channel, ChatTypeEnum type, string message) + { + ChatMessage handlerChatFromClient = OnChatFromClient; + + if (handlerChatFromClient != null) + { + OSChatMessage args = new OSChatMessage(); + args.Channel = channel; + args.From = Name; + args.Message = message; + args.Type = type; + + args.Scene = Scene; + args.Sender = this; + args.SenderUUID = AgentId; + + handlerChatFromClient(this, args); + } + + return true; + } + + /// + /// Attempt a teleport to the given region. + /// + /// + /// + /// + public void Teleport(ulong regionHandle, Vector3 position, Vector3 lookAt) + { + OnTeleportLocationRequest(this, regionHandle, position, lookAt, 16); + } + + public void CompleteMovement() + { + if (OnCompleteMovementToRegion != null) + OnCompleteMovementToRegion(this, true); + } + + /// + /// Emulate sending an IM from the viewer to the simulator. + /// + /// + public void HandleImprovedInstantMessage(GridInstantMessage im) + { + ImprovedInstantMessage handlerInstantMessage = OnInstantMessage; + + if (handlerInstantMessage != null) + handlerInstantMessage(this, im); + } + + public virtual void ActivateGesture(UUID assetId, UUID gestureId) + { + } + + public virtual void SendWearables(AvatarWearable[] wearables, int serial) + { + } + + public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry) + { + } + + public void SendCachedTextureResponse(ISceneEntity avatar, int serial, List cachedTextures) + { + + } + + public virtual void Kick(string message) + { + } + + public virtual void SendStartPingCheck(byte seq) + { + } + + public virtual void SendAvatarPickerReply(AvatarPickerReplyAgentDataArgs AgentData, List Data) + { + } + + public virtual void SendAgentDataUpdate(UUID agentid, UUID activegroupid, string firstname, string lastname, ulong grouppowers, string groupname, string grouptitle) + { + } + + public virtual void SendKillObject(List localID) + { + ReceivedKills.AddRange(localID); + } + + public virtual void SetChildAgentThrottle(byte[] throttle) + { + } + + public byte[] GetThrottlesPacked(float multiplier) + { + return new byte[0]; + } + + public virtual void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) + { + } + + public virtual void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible) + { +// Console.WriteLine("mmm {0} {1} {2}", message, Name, AgentId); + if (OnReceivedChatMessage != null) + OnReceivedChatMessage(message, type, fromPos, fromName, fromAgentID, ownerID, source, audible); + } + + public void SendInstantMessage(GridInstantMessage im) + { + if (OnReceivedInstantMessage != null) + OnReceivedInstantMessage(im); + } + + public void SendGenericMessage(string method, UUID invoice, List message) + { + + } + + public void SendGenericMessage(string method, UUID invoice, List message) + { + + } + + public virtual void SendLayerData(float[] map) + { + } + + public virtual void SendLayerData(int px, int py, float[] map) + { + } + public virtual void SendLayerData(int px, int py, float[] map, bool track) + { + } + + public virtual void SendWindData(Vector2[] windSpeeds) { } + + public virtual void SendCloudData(float[] cloudCover) { } + + public virtual void MoveAgentIntoRegion(RegionInfo regInfo, Vector3 pos, Vector3 look) + { + if (OnReceivedMoveAgentIntoRegion != null) + OnReceivedMoveAgentIntoRegion(regInfo, pos, look); + } + + public virtual AgentCircuitData RequestClientInfo() + { + AgentCircuitData agentData = new AgentCircuitData(); + agentData.AgentID = AgentId; + agentData.SessionID = SessionId; + agentData.SecureSessionID = UUID.Zero; + agentData.circuitcode = m_circuitCode; + agentData.child = false; + agentData.firstname = m_firstName; + agentData.lastname = m_lastName; + + ICapabilitiesModule capsModule = m_scene.RequestModuleInterface(); + if (capsModule != null) + { + agentData.CapsPath = capsModule.GetCapsPath(m_agentId); + agentData.ChildrenCapSeeds = new Dictionary(capsModule.GetChildrenSeeds(m_agentId)); + } + + return agentData; + } + + public virtual void InformClientOfNeighbour(ulong neighbourHandle, IPEndPoint neighbourExternalEndPoint) + { + if (OnTestClientInformClientOfNeighbour != null) + OnTestClientInformClientOfNeighbour(neighbourHandle, neighbourExternalEndPoint); + } + + public virtual void SendRegionTeleport( + ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, + uint locationID, uint flags, string capsURL) + { + m_log.DebugFormat( + "[TEST CLIENT]: Received SendRegionTeleport for {0} {1} on {2}", m_firstName, m_lastName, m_scene.Name); + + CapsSeedUrl = capsURL; + + if (OnTestClientSendRegionTeleport != null) + OnTestClientSendRegionTeleport( + regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL); + } + + public virtual void SendTeleportFailed(string reason) + { + m_log.DebugFormat( + "[TEST CLIENT]: Teleport failed for {0} {1} on {2} with reason {3}", + m_firstName, m_lastName, m_scene.Name, reason); + } + + public virtual void CrossRegion(ulong newRegionHandle, Vector3 pos, Vector3 lookAt, + IPEndPoint newRegionExternalEndPoint, string capsURL) + { + // This is supposed to send a packet to the client telling it's ready to start region crossing. + // Instead I will just signal I'm ready, mimicking the communication behavior. + // It's ugly, but avoids needless communication setup. This is used in ScenePresenceTests.cs. + // Arthur V. + + wh.Set(); + } + + public virtual void SendMapBlock(List mapBlocks, uint flag) + { + } + + public virtual void SendLocalTeleport(Vector3 position, Vector3 lookAt, uint flags) + { + } + + public virtual void SendTeleportStart(uint flags) + { + } + + public void SendTeleportProgress(uint flags, string message) + { + } + + public virtual void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance, int transactionType, UUID sourceID, bool sourceIsGroup, UUID destID, bool destIsGroup, int amount, string item) + { + } + + public virtual void SendPayPrice(UUID objectID, int[] payPrice) + { + } + + public virtual void SendCoarseLocationUpdate(List users, List CoarseLocations) + { + } + + public virtual void SendDialog(string objectname, UUID objectID, UUID ownerID, string ownerFirstName, string ownerLastName, string msg, UUID textureID, int ch, string[] buttonlabels) + { + } + + public void SendAvatarDataImmediate(ISceneEntity avatar) + { + } + + public void SendEntityUpdate(ISceneEntity entity, PrimUpdateFlags updateFlags) + { + if (OnReceivedEntityUpdate != null) + OnReceivedEntityUpdate(entity, updateFlags); + } + + public void ReprioritizeUpdates() + { + } + + public void FlushPrimUpdates() + { + } + + public virtual void SendInventoryFolderDetails(UUID ownerID, UUID folderID, + List items, + List folders, + int version, + bool fetchFolders, + bool fetchItems) + { + } + + public virtual void SendInventoryItemDetails(UUID ownerID, InventoryItemBase item) + { + } + + public virtual void SendInventoryItemCreateUpdate(InventoryItemBase Item, uint callbackID) + { + } + + public virtual void SendRemoveInventoryItem(UUID itemID) + { + } + + public virtual void SendBulkUpdateInventory(InventoryNodeBase node) + { + } + + public void SendTakeControls(int controls, bool passToAgent, bool TakeControls) + { + } + + public virtual void SendTaskInventory(UUID taskID, short serial, byte[] fileName) + { + } + + public virtual void SendXferPacket(ulong xferID, uint packet, byte[] data) + { + } + + public virtual void SendAbortXferPacket(ulong xferID) + { + + } + + public virtual void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, + int PriceGroupCreate, int PriceObjectClaim, float PriceObjectRent, float PriceObjectScaleFactor, + int PriceParcelClaim, float PriceParcelClaimFactor, int PriceParcelRent, int PricePublicObjectDecay, + int PricePublicObjectDelete, int PriceRentLight, int PriceUpload, int TeleportMinPrice, float TeleportPriceExponent) + { + } + + public virtual void SendNameReply(UUID profileId, string firstname, string lastname) + { + } + + public virtual void SendPreLoadSound(UUID objectID, UUID ownerID, UUID soundID) + { + } + + public virtual void SendPlayAttachedSound(UUID soundID, UUID objectID, UUID ownerID, float gain, + byte flags) + { + } + + public void SendTriggeredSound(UUID soundID, UUID ownerID, UUID objectID, UUID parentID, ulong handle, Vector3 position, float gain) + { + } + + public void SendAttachedSoundGainChange(UUID objectID, float gain) + { + + } + + public void SendAlertMessage(string message) + { + } + + public void SendAgentAlertMessage(string message, bool modal) + { + } + + public void SendSystemAlertMessage(string message) + { + } + + public void SendLoadURL(string objectname, UUID objectID, UUID ownerID, bool groupOwned, string message, + string url) + { + } + + public virtual void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args) + { + if (OnRegionHandShakeReply != null) + { + OnRegionHandShakeReply(this); + } + } + + public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, UUID AssetFullID) + { + } + + public void SendConfirmXfer(ulong xferID, uint PacketID) + { + } + + public void SendXferRequest(ulong XferID, short AssetType, UUID vFileID, byte FilePath, byte[] FileName) + { + } + + public void SendInitiateDownload(string simFileName, string clientFileName) + { + } + + public void SendImageFirstPart(ushort numParts, UUID ImageUUID, uint ImageSize, byte[] ImageData, byte imageCodec) + { + ImageDataPacket im = new ImageDataPacket(); + im.Header.Reliable = false; + im.ImageID.Packets = numParts; + im.ImageID.ID = ImageUUID; + + if (ImageSize > 0) + im.ImageID.Size = ImageSize; + + im.ImageData.Data = ImageData; + im.ImageID.Codec = imageCodec; + im.Header.Zerocoded = true; + SentImageDataPackets.Add(im); + } + + public void SendImageNextPart(ushort partNumber, UUID imageUuid, byte[] imageData) + { + ImagePacketPacket im = new ImagePacketPacket(); + im.Header.Reliable = false; + im.ImageID.Packet = partNumber; + im.ImageID.ID = imageUuid; + im.ImageData.Data = imageData; + SentImagePacketPackets.Add(im); + } + + public void SendImageNotFound(UUID imageid) + { + ImageNotInDatabasePacket p = new ImageNotInDatabasePacket(); + p.ImageID.ID = imageid; + + SentImageNotInDatabasePackets.Add(p); + } + + public void SendShutdownConnectionNotice() + { + } + + public void SendSimStats(SimStats stats) + { + } + + public void SendObjectPropertiesFamilyData(ISceneEntity Entity, uint RequestFlags) + { + } + + public void SendObjectPropertiesReply(ISceneEntity entity) + { + } + + public void SendAgentOffline(UUID[] agentIDs) + { + ReceivedOfflineNotifications.AddRange(agentIDs); + } + + public void SendAgentOnline(UUID[] agentIDs) + { + ReceivedOnlineNotifications.AddRange(agentIDs); + } + + public void SendSitResponse(UUID TargetID, Vector3 OffsetPos, Quaternion SitOrientation, bool autopilot, + Vector3 CameraAtOffset, Vector3 CameraEyeOffset, bool ForceMouseLook) + { + } + + public void SendAdminResponse(UUID Token, uint AdminLevel) + { + + } + + public void SendGroupMembership(GroupMembershipData[] GroupMembership) + { + + } + + public void SendSunPos(Vector3 sunPos, Vector3 sunVel, ulong time, uint dlen, uint ylen, float phase) + { + } + + public void SendViewerEffect(ViewerEffectPacket.EffectBlock[] effectBlocks) + { + } + + public void SendViewerTime(int phase) + { + } + + public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, + string flAbout, uint flags, UUID flImageID, UUID imageID, string profileURL, + UUID partnerID) + { + } + + public int DebugPacketLevel { get; set; } + + public void InPacket(object NewPack) + { + } + + public void ProcessInPacket(Packet NewPack) + { + } + + /// + /// This is a TestClient only method to do shutdown tasks that are normally carried out by LLUDPServer.RemoveClient() + /// + public void Logout() + { + // We must set this here so that the presence is removed from the PresenceService by the PresenceDetector + IsLoggingOut = true; + + Close(); + } + + public void Close() + { + Close(false); + } + + public void Close(bool force) + { + // Fire the callback for this connection closing + // This is necesary to get the presence detector to notice that a client has logged out. + if (OnConnectionClosed != null) + OnConnectionClosed(this); + + m_scene.RemoveClient(AgentId, true); + } + + public void Start() + { + throw new NotImplementedException(); + } + + public void Stop() + { + } + + public void SendBlueBoxMessage(UUID FromAvatarID, String FromAvatarName, String Message) + { + + } + public void SendLogoutPacket() + { + } + + public void Terminate() + { + } + + public ClientInfo GetClientInfo() + { + return null; + } + + public void SetClientInfo(ClientInfo info) + { + } + + public void SendScriptQuestion(UUID objectID, string taskName, string ownerName, UUID itemID, int question) + { + } + public void SendHealth(float health) + { + } + + public void SendTelehubInfo(UUID ObjectID, string ObjectName, Vector3 ObjectPos, Quaternion ObjectRot, List SpawnPoint) + { + } + + public void SendEstateList(UUID invoice, int code, UUID[] Data, uint estateID) + { + } + + public void SendBannedUserList(UUID invoice, EstateBan[] banlist, uint estateID) + { + } + + public void SendRegionInfoToEstateMenu(RegionInfoForEstateMenuArgs args) + { + } + + public void SendEstateCovenantInformation(UUID covenant) + { + } + + public void SendDetailedEstateData(UUID invoice, string estateName, uint estateID, uint parentEstate, uint estateFlags, uint sunPosition, UUID covenant, uint covenantChanged, string abuseEmail, UUID estateOwner) + { + } + + public void SendLandProperties(int sequence_id, bool snap_selection, int request_result, ILandObject lo, float simObjectBonusFactor, int parcelObjectCapacity, int simObjectCapacity, uint regionFlags) + { + } + + public void SendLandAccessListData(List accessList, uint accessFlag, int localLandID) + { + } + + public void SendForceClientSelectObjects(List objectIDs) + { + } + + public void SendCameraConstraint(Vector4 ConstraintPlane) + { + } + + public void SendLandObjectOwners(LandData land, List groups, Dictionary ownersAndCount) + { + } + + public void SendLandParcelOverlay(byte[] data, int sequence_id) + { + } + + public void SendParcelMediaCommand(uint flags, ParcelMediaCommandEnum command, float time) + { + } + + public void SendParcelMediaUpdate(string mediaUrl, UUID mediaTextureID, byte autoScale, string mediaType, + string mediaDesc, int mediaWidth, int mediaHeight, byte mediaLoop) + { + } + + public void SendGroupNameReply(UUID groupLLUID, string GroupName) + { + } + + public void SendLandStatReply(uint reportType, uint requestFlags, uint resultCount, LandStatReportItem[] lsrpia) + { + } + + public void SendScriptRunningReply(UUID objectID, UUID itemID, bool running) + { + } + + public void SendAsset(AssetRequestToClient req) + { + } + + public void SendTexture(AssetBase TextureAsset) + { + + } + + public void SendSetFollowCamProperties (UUID objectID, SortedDictionary parameters) + { + } + + public void SendClearFollowCamProperties (UUID objectID) + { + } + + public void SendRegionHandle (UUID regoinID, ulong handle) + { + } + + public void SendParcelInfo (RegionInfo info, LandData land, UUID parcelID, uint x, uint y) + { + } + + public void SetClientOption(string option, string value) + { + } + + public string GetClientOption(string option) + { + return string.Empty; + } + + public void SendScriptTeleportRequest(string objName, string simName, Vector3 pos, Vector3 lookAt) + { + } + + public void SendDirPlacesReply(UUID queryID, DirPlacesReplyData[] data) + { + } + + public void SendDirPeopleReply(UUID queryID, DirPeopleReplyData[] data) + { + } + + public void SendDirEventsReply(UUID queryID, DirEventsReplyData[] data) + { + } + + public void SendDirGroupsReply(UUID queryID, DirGroupsReplyData[] data) + { + } + + public void SendDirClassifiedReply(UUID queryID, DirClassifiedReplyData[] data) + { + } + + public void SendDirLandReply(UUID queryID, DirLandReplyData[] data) + { + } + + public void SendDirPopularReply(UUID queryID, DirPopularReplyData[] data) + { + } + + public void SendMapItemReply(mapItemReply[] replies, uint mapitemtype, uint flags) + { + } + + public void SendEventInfoReply (EventData info) + { + } + + public void SendOfferCallingCard (UUID destID, UUID transactionID) + { + } + + public void SendAcceptCallingCard (UUID transactionID) + { + } + + public void SendDeclineCallingCard (UUID transactionID) + { + } + + public void SendAvatarGroupsReply(UUID avatarID, GroupMembershipData[] data) + { + } + + public void SendJoinGroupReply(UUID groupID, bool success) + { + } + + public void SendEjectGroupMemberReply(UUID agentID, UUID groupID, bool succss) + { + } + + public void SendLeaveGroupReply(UUID groupID, bool success) + { + } + + public void SendTerminateFriend(UUID exFriendID) + { + ReceivedFriendshipTerminations.Add(exFriendID); + } + + public bool AddGenericPacketHandler(string MethodName, GenericMessage handler) + { + //throw new NotImplementedException(); + return false; + } + + public void SendAvatarClassifiedReply(UUID targetID, UUID[] classifiedID, string[] name) + { + } + + public void SendClassifiedInfoReply(UUID classifiedID, UUID creatorID, uint creationDate, uint expirationDate, uint category, string name, string description, UUID parcelID, uint parentEstate, UUID snapshotID, string simName, Vector3 globalPos, string parcelName, byte classifiedFlags, int price) + { + } + + public void SendAgentDropGroup(UUID groupID) + { + } + + public void SendAvatarNotesReply(UUID targetID, string text) + { + } + + public void SendAvatarPicksReply(UUID targetID, Dictionary picks) + { + } + + public void SendAvatarClassifiedReply(UUID targetID, Dictionary classifieds) + { + } + + public void SendParcelDwellReply(int localID, UUID parcelID, float dwell) + { + } + + public void SendUserInfoReply(bool imViaEmail, bool visible, string email) + { + } + + public void SendCreateGroupReply(UUID groupID, bool success, string message) + { + } + + public void RefreshGroupMembership() + { + } + + public void SendUseCachedMuteList() + { + } + + public void SendMuteListUpdate(string filename) + { + } + + public void SendPickInfoReply(UUID pickID,UUID creatorID, bool topPick, UUID parcelID, string name, string desc, UUID snapshotID, string user, string originalName, string simName, Vector3 posGlobal, int sortOrder, bool enabled) + { + } + + public bool TryGet(out T iface) + { + iface = default(T); + return false; + } + + public T Get() + { + return default(T); + } + + public void Disconnect(string reason) + { + } + + public void Disconnect() + { + } + + public void SendRebakeAvatarTextures(UUID textureID) + { + if (OnReceivedSendRebakeAvatarTextures != null) + OnReceivedSendRebakeAvatarTextures(textureID); + } + + public void SendAvatarInterestsReply(UUID avatarID, uint wantMask, string wantText, uint skillsMask, string skillsText, string languages) + { + } + + public void SendGroupAccountingDetails(IClientAPI sender,UUID groupID, UUID transactionID, UUID sessionID, int amt) + { + } + + public void SendGroupAccountingSummary(IClientAPI sender,UUID groupID, uint moneyAmt, int totalTier, int usedTier) + { + } + + public void SendGroupTransactionsSummaryDetails(IClientAPI sender,UUID groupID, UUID transactionID, UUID sessionID,int amt) + { + } + + public void SendGroupVoteHistory(UUID groupID, UUID transactionID, GroupVoteHistory[] Votes) + { + } + + public void SendGroupActiveProposals(UUID groupID, UUID transactionID, GroupActiveProposals[] Proposals) + { + } + + public void SendChangeUserRights(UUID agentID, UUID friendID, int rights) + { + } + + public void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId) + { + } + + public void SendAgentTerseUpdate(ISceneEntity presence) + { + } + + public void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data) + { + } + + public void SendPartPhysicsProprieties(ISceneEntity entity) + { + } + + } +} diff --git a/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs b/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs new file mode 100644 index 0000000000..f2bae5876d --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestEventQueueGetModule.cs @@ -0,0 +1,182 @@ +/* + * 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 System.Threading; +using log4net; +using Nini.Config; +using Mono.Addins; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Region.ClientStack.Linden; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Tests.Common +{ + public class TestEventQueueGetModule : IEventQueue, INonSharedRegionModule + { + public class Event + { + public string Name { get; set; } + public object[] Args { get; set; } + + public Event(string name, object[] args) + { + name = Name; + args = Args; + } + } + + public Dictionary> Events { get; set; } + + public void Initialise(IConfigSource source) {} + + public void Close() {} + + public void AddRegion(Scene scene) + { + Events = new Dictionary>(); + scene.RegisterModuleInterface(this); + } + + public void RemoveRegion (Scene scene) {} + + public void RegionLoaded (Scene scene) {} + + public string Name { get { return "TestEventQueueGetModule"; } } + + public Type ReplaceableInterface { get { return null; } } + + private void AddEvent(UUID avatarID, string name, params object[] args) + { + Console.WriteLine("Adding event {0} for {1}", name, avatarID); + + List avEvents; + + if (!Events.ContainsKey(avatarID)) + { + avEvents = new List(); + Events[avatarID] = avEvents; + } + else + { + avEvents = Events[avatarID]; + } + + avEvents.Add(new Event(name, args)); + } + + public void ClearEvents() + { + if (Events != null) + Events.Clear(); + } + + public bool Enqueue(OSD o, UUID avatarID) + { + AddEvent(avatarID, "Enqueue", o); + return true; + } + + public void DisableSimulator(ulong handle, UUID avatarID) + { + AddEvent(avatarID, "DisableSimulator", handle); + } + + public void EnableSimulator (ulong handle, IPEndPoint endPoint, UUID avatarID, int regionSizeX, int regionSizeY) + { + AddEvent(avatarID, "EnableSimulator", handle); + } + + public void EstablishAgentCommunication (UUID avatarID, IPEndPoint endPoint, string capsPath, + ulong regionHandle, int regionSizeX, int regionSizeY) + { + AddEvent(avatarID, "EstablishAgentCommunication", endPoint, capsPath); + } + + public void TeleportFinishEvent (ulong regionHandle, byte simAccess, IPEndPoint regionExternalEndPoint, + uint locationID, uint flags, string capsURL, UUID agentID, int regionSizeX, int regionSizeY) + { + AddEvent(agentID, "TeleportFinishEvent", regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL); + } + + public void CrossRegion (ulong handle, Vector3 pos, Vector3 lookAt, IPEndPoint newRegionExternalEndPoint, + string capsURL, UUID avatarID, UUID sessionID, int regionSizeX, int regionSizeY) + { + AddEvent(avatarID, "CrossRegion", handle, pos, lookAt, newRegionExternalEndPoint, capsURL, sessionID); + } + + public void ChatterboxInvitation( + UUID sessionID, string sessionName, UUID fromAgent, string message, UUID toAgent, string fromName, + byte dialog, uint timeStamp, bool offline, int parentEstateID, Vector3 position, uint ttl, + UUID transactionID, bool fromGroup, byte[] binaryBucket) + { + AddEvent( + toAgent, "ChatterboxInvitation", sessionID, sessionName, fromAgent, message, toAgent, fromName, dialog, + timeStamp, offline, parentEstateID, position, ttl, transactionID, fromGroup, binaryBucket); + } + + public void ChatterBoxSessionAgentListUpdates (UUID sessionID, UUID fromAgent, UUID toAgent, bool canVoiceChat, bool isModerator, bool textMute) + { + AddEvent(toAgent, "ChatterBoxSessionAgentListUpdates", sessionID, fromAgent, canVoiceChat, isModerator, textMute); + } + + public void ParcelProperties (OpenMetaverse.Messages.Linden.ParcelPropertiesMessage parcelPropertiesMessage, UUID avatarID) + { + AddEvent(avatarID, "ParcelProperties", parcelPropertiesMessage); + } + + public void GroupMembership (OpenMetaverse.Packets.AgentGroupDataUpdatePacket groupUpdate, UUID avatarID) + { + AddEvent(avatarID, "GroupMembership", groupUpdate); + } + + public OSD ScriptRunningEvent (UUID objectID, UUID itemID, bool running, bool mono) + { + Console.WriteLine("ONE"); + throw new System.NotImplementedException (); + } + + public OSD BuildEvent(string eventName, OSD eventBody) + { + Console.WriteLine("TWO"); + throw new System.NotImplementedException (); + } + + public void partPhysicsProperties (uint localID, byte physhapetype, float density, float friction, float bounce, float gravmod, UUID avatarID) + { + AddEvent(avatarID, "partPhysicsProperties", localID, physhapetype, density, friction, bounce, gravmod); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestHttpClientContext.cs b/OpenSim/Tests/Common/Mock/TestHttpClientContext.cs new file mode 100644 index 0000000000..5a55b09590 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestHttpClientContext.cs @@ -0,0 +1,110 @@ +/* + * 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.Net; +using System.Net.Sockets; +using System.Text; +using HttpServer; +using OpenSim.Framework; + +namespace OpenSim.Tests.Common +{ + public class TestHttpClientContext: IHttpClientContext + { + /// + /// Bodies of responses from the server. + /// + public string ResponseBody + { + get { return Encoding.UTF8.GetString(m_responseStream.ToArray()); } + } + + public Byte[] ResponseBodyBytes + { + get{ return m_responseStream.ToArray(); } + } + + private MemoryStream m_responseStream = new MemoryStream(); + + public bool IsSecured { get; set; } + + public bool Secured + { + get { return IsSecured; } + set { IsSecured = value; } + } + + public TestHttpClientContext(bool secured) + { + Secured = secured; + } + + public void Disconnect(SocketError error) + { +// Console.WriteLine("TestHttpClientContext.Disconnect Received disconnect with status {0}", error); + } + + public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body) {Console.WriteLine("x");} + public void Respond(string httpVersion, HttpStatusCode statusCode, string reason) {Console.WriteLine("xx");} + public void Respond(string body) { Console.WriteLine("xxx");} + + public void Send(byte[] buffer) + { + // Getting header data here +// Console.WriteLine("xxxx: Got {0}", Encoding.UTF8.GetString(buffer)); + } + + public void Send(byte[] buffer, int offset, int size) + { +// Util.PrintCallStack(); +// +// Console.WriteLine( +// "TestHttpClientContext.Send(byte[], int, int) got offset={0}, size={1}, buffer={2}", +// offset, size, Encoding.UTF8.GetString(buffer)); + + m_responseStream.Write(buffer, offset, size); + } + + public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) {Console.WriteLine("xxxxxx");} + public void Close() { } + public bool EndWhenDone { get { return false;} set { return;}} + + public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing() + { + return new HTTPNetworkContext(); + } + + public event EventHandler Disconnected = delegate { }; + /// + /// A request have been received in the context. + /// + public event EventHandler RequestReceived = delegate { }; + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestHttpRequest.cs b/OpenSim/Tests/Common/Mock/TestHttpRequest.cs new file mode 100644 index 0000000000..b86889575f --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestHttpRequest.cs @@ -0,0 +1,174 @@ +/* + * 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.Specialized; +using System.IO; +using HttpServer; +using HttpServer.FormDecoders; + +namespace OpenSim.Tests.Common +{ + public class TestHttpRequest: IHttpRequest + { + private string _uriPath; + public bool BodyIsComplete + { + get { return true; } + } + public string[] AcceptTypes + { + get {return _acceptTypes; } + } + private string[] _acceptTypes; + public Stream Body + { + get { return _body; } + set { _body = value;} + } + private Stream _body; + public ConnectionType Connection + { + get { return _connection; } + set { _connection = value; } + } + private ConnectionType _connection; + public int ContentLength + { + get { return _contentLength; } + set { _contentLength = value; } + } + private int _contentLength; + public NameValueCollection Headers + { + get { return _headers; } + } + private NameValueCollection _headers = new NameValueCollection(); + + public string HttpVersion { get; set; } + + public string Method + { + get { return _method; } + set { _method = value; } + } + private string _method = null; + public HttpInput QueryString + { + get { return _queryString; } + } + private HttpInput _queryString = null; + public Uri Uri + { + get { return _uri; } + set { _uri = value; } + } + private Uri _uri = null; + public string[] UriParts + { + get { return _uri.Segments; } + } + public HttpParam Param + { + get { return null; } + } + public HttpForm Form + { + get { return null; } + } + public bool IsAjax + { + get { return false; } + } + public RequestCookies Cookies + { + get { return null; } + } + + public TestHttpRequest() + { + HttpVersion = "HTTP/1.1"; + } + + public TestHttpRequest(string contentEncoding, string contentType, string userAgent, + string remoteAddr, string remotePort, string[] acceptTypes, + ConnectionType connectionType, int contentLength, Uri uri) : base() + { + _headers["content-encoding"] = contentEncoding; + _headers["content-type"] = contentType; + _headers["user-agent"] = userAgent; + _headers["remote_addr"] = remoteAddr; + _headers["remote_port"] = remotePort; + + _acceptTypes = acceptTypes; + _connection = connectionType; + _contentLength = contentLength; + _uri = uri; + } + + public void DecodeBody(FormDecoderProvider providers) {} + public void SetCookies(RequestCookies cookies) {} + public void AddHeader(string name, string value) + { + _headers.Add(name, value); + } + public int AddToBody(byte[] bytes, int offset, int length) + { + return 0; + } + public void Clear() {} + + public object Clone() + { + TestHttpRequest clone = new TestHttpRequest(); + clone._acceptTypes = _acceptTypes; + clone._connection = _connection; + clone._contentLength = _contentLength; + clone._uri = _uri; + clone._headers = new NameValueCollection(_headers); + + return clone; + } + public IHttpResponse CreateResponse(IHttpClientContext context) + { + return new HttpResponse(context, this); + } + /// + /// Path and query (will be merged with the host header) and put in Uri + /// + /// + public string UriPath + { + get { return _uriPath; } + set + { + _uriPath = value; + + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestHttpResponse.cs b/OpenSim/Tests/Common/Mock/TestHttpResponse.cs new file mode 100644 index 0000000000..ff47c1014d --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestHttpResponse.cs @@ -0,0 +1,171 @@ +/* + * 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.Text; +using HttpServer; + +namespace OpenSim.Tests.Common +{ + public class TestHttpResponse: IHttpResponse + { + public Stream Body + { + get { return _body; } + + set { _body = value; } + } + private Stream _body; + + public string ProtocolVersion + { + get { return _protocolVersion; } + set { _protocolVersion = value; } + } + private string _protocolVersion; + + public bool Chunked + { + get { return _chunked; } + + set { _chunked = value; } + } + private bool _chunked; + + public ConnectionType Connection + { + get { return _connection; } + + set { _connection = value; } + } + private ConnectionType _connection; + + public Encoding Encoding + { + get { return _encoding; } + + set { _encoding = value; } + } + private Encoding _encoding; + + public int KeepAlive + { + get { return _keepAlive; } + + set { _keepAlive = value; } + } + private int _keepAlive; + + public HttpStatusCode Status + { + get { return _status; } + + set { _status = value; } + } + private HttpStatusCode _status; + + public string Reason + { + get { return _reason; } + + set { _reason = value; } + } + private string _reason; + + public long ContentLength + { + get { return _contentLength; } + + set { _contentLength = value; } + } + private long _contentLength; + + public string ContentType + { + get { return _contentType; } + + set { _contentType = value; } + } + private string _contentType; + + public bool HeadersSent + { + get { return _headersSent; } + } + private bool _headersSent; + + public bool Sent + { + get { return _sent; } + } + private bool _sent; + + public ResponseCookies Cookies + { + get { return _cookies; } + } + private ResponseCookies _cookies = null; + + public TestHttpResponse() + { + _headersSent = false; + _sent = false; + } + + public void AddHeader(string name, string value) {} + + public void Send() + { + if (!_headersSent) SendHeaders(); + if (_sent) throw new InvalidOperationException("stuff already sent"); + _sent = true; + } + + public void SendBody(byte[] buffer, int offset, int count) + { + if (!_headersSent) SendHeaders(); + _sent = true; + } + + public void SendBody(byte[] buffer) + { + if (!_headersSent) SendHeaders(); + _sent = true; + } + + public void SendHeaders() + { + if (_headersSent) throw new InvalidOperationException("headers already sent"); + _headersSent = true; + } + + public void Redirect(Uri uri) {} + public void Redirect(string url) {} + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestInventoryDataPlugin.cs b/OpenSim/Tests/Common/Mock/TestInventoryDataPlugin.cs new file mode 100644 index 0000000000..c97a765f50 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestInventoryDataPlugin.cs @@ -0,0 +1,219 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; + +namespace OpenSim.Tests.Common +{ + /// + /// In memory inventory data plugin for test purposes. Could be another dll when properly filled out and when the + /// mono addin plugin system starts co-operating with the unit test system. Currently no locking since unit + /// tests are single threaded. + /// + public class TestInventoryDataPlugin : IInventoryDataPlugin + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Inventory folders + /// + private Dictionary m_folders = new Dictionary(); + + //// + /// Inventory items + /// + private Dictionary m_items = new Dictionary(); + + /// + /// User root folders + /// + private Dictionary m_rootFolders = new Dictionary(); + + public string Version { get { return "0"; } } + public string Name { get { return "TestInventoryDataPlugin"; } } + + public void Initialise() {} + public void Initialise(string connect) {} + public void Dispose() {} + + public List getFolderHierarchy(UUID parentID) + { + List folders = new List(); + + foreach (InventoryFolderBase folder in m_folders.Values) + { + if (folder.ParentID == parentID) + { + folders.AddRange(getFolderHierarchy(folder.ID)); + folders.Add(folder); + } + } + + return folders; + } + + public List getInventoryInFolder(UUID folderID) + { +// InventoryFolderBase folder = m_folders[folderID]; + +// m_log.DebugFormat("[MOCK INV DB]: Getting items in folder {0} {1}", folder.Name, folder.ID); + + List items = new List(); + + foreach (InventoryItemBase item in m_items.Values) + { + if (item.Folder == folderID) + { +// m_log.DebugFormat("[MOCK INV DB]: getInventoryInFolder() adding item {0}", item.Name); + items.Add(item); + } + } + + return items; + } + + public List getUserRootFolders(UUID user) { return null; } + + public InventoryFolderBase getUserRootFolder(UUID user) + { +// m_log.DebugFormat("[MOCK INV DB]: Looking for root folder for {0}", user); + + InventoryFolderBase folder = null; + m_rootFolders.TryGetValue(user, out folder); + + return folder; + } + + public List getInventoryFolders(UUID parentID) + { +// InventoryFolderBase parentFolder = m_folders[parentID]; + +// m_log.DebugFormat("[MOCK INV DB]: Getting folders in folder {0} {1}", parentFolder.Name, parentFolder.ID); + + List folders = new List(); + + foreach (InventoryFolderBase folder in m_folders.Values) + { + if (folder.ParentID == parentID) + { +// m_log.DebugFormat( +// "[MOCK INV DB]: Found folder {0} {1} in {2} {3}", +// folder.Name, folder.ID, parentFolder.Name, parentFolder.ID); + + folders.Add(folder); + } + } + + return folders; + } + + public InventoryFolderBase getInventoryFolder(UUID folderId) + { + InventoryFolderBase folder = null; + m_folders.TryGetValue(folderId, out folder); + + return folder; + } + + public InventoryFolderBase queryInventoryFolder(UUID folderID) + { + return getInventoryFolder(folderID); + } + + public void addInventoryFolder(InventoryFolderBase folder) + { +// m_log.DebugFormat( +// "[MOCK INV DB]: Adding inventory folder {0} {1} type {2}", +// folder.Name, folder.ID, (AssetType)folder.Type); + + m_folders[folder.ID] = folder; + + if (folder.ParentID == UUID.Zero) + { +// m_log.DebugFormat( +// "[MOCK INV DB]: Adding root folder {0} {1} for {2}", folder.Name, folder.ID, folder.Owner); + m_rootFolders[folder.Owner] = folder; + } + } + + public void updateInventoryFolder(InventoryFolderBase folder) + { + m_folders[folder.ID] = folder; + } + + public void moveInventoryFolder(InventoryFolderBase folder) + { + // Simple replace + updateInventoryFolder(folder); + } + + public void deleteInventoryFolder(UUID folderId) + { + if (m_folders.ContainsKey(folderId)) + m_folders.Remove(folderId); + } + + public void addInventoryItem(InventoryItemBase item) + { + InventoryFolderBase folder = m_folders[item.Folder]; + +// m_log.DebugFormat( +// "[MOCK INV DB]: Adding inventory item {0} {1} in {2} {3}", item.Name, item.ID, folder.Name, folder.ID); + + m_items[item.ID] = item; + } + + public void updateInventoryItem(InventoryItemBase item) { addInventoryItem(item); } + + public void deleteInventoryItem(UUID itemId) + { + if (m_items.ContainsKey(itemId)) + m_items.Remove(itemId); + } + + public InventoryItemBase getInventoryItem(UUID itemId) + { + if (m_items.ContainsKey(itemId)) + return m_items[itemId]; + else + return null; + } + + public InventoryItemBase queryInventoryItem(UUID item) + { + return null; + } + + public List fetchActiveGestures(UUID avatarID) { return null; } + } +} diff --git a/OpenSim/Tests/Common/Mock/TestLLUDPServer.cs b/OpenSim/Tests/Common/Mock/TestLLUDPServer.cs new file mode 100644 index 0000000000..26887c9ebd --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestLLUDPServer.cs @@ -0,0 +1,171 @@ +/* + * 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.Net; +using System.Net.Sockets; +using Nini.Config; +using OpenMetaverse.Packets; +using OpenSim.Framework; +using OpenSim.Region.ClientStack.LindenUDP; + +namespace OpenSim.Tests.Common +{ + /// + /// This class enables regression testing of the LLUDPServer by allowing us to intercept outgoing data. + /// + public class TestLLUDPServer : LLUDPServer + { + public List PacketsSent { get; private set; } + + public TestLLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) + : base(listenIP, ref port, proxyPortOffsetParm, allow_alternate_port, configSource, circuitManager) + { + PacketsSent = new List(); + } + + public override void SendAckImmediate(IPEndPoint remoteEndpoint, PacketAckPacket ack) + { + PacketsSent.Add(ack); + } + + public override void SendPacket( + LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting, UnackedPacketMethod method) + { + PacketsSent.Add(packet); + } + + public void ClientOutgoingPacketHandler(IClientAPI client, bool resendUnacked, bool sendAcks, bool sendPing) + { + m_resendUnacked = resendUnacked; + m_sendAcks = sendAcks; + m_sendPing = sendPing; + + ClientOutgoingPacketHandler(client); + } + +//// /// +//// /// The chunks of data to pass to the LLUDPServer when it calls EndReceive +//// /// +//// protected Queue m_chunksToLoad = new Queue(); +// +//// protected override void BeginReceive() +//// { +//// if (m_chunksToLoad.Count > 0 && m_chunksToLoad.Peek().BeginReceiveException) +//// { +//// ChunkSenderTuple tuple = m_chunksToLoad.Dequeue(); +//// reusedEpSender = tuple.Sender; +//// throw new SocketException(); +//// } +//// } +// +//// protected override bool EndReceive(out int numBytes, IAsyncResult result, ref EndPoint epSender) +//// { +//// numBytes = 0; +//// +//// //m_log.Debug("Queue size " + m_chunksToLoad.Count); +//// +//// if (m_chunksToLoad.Count <= 0) +//// return false; +//// +//// ChunkSenderTuple tuple = m_chunksToLoad.Dequeue(); +//// RecvBuffer = tuple.Data; +//// numBytes = tuple.Data.Length; +//// epSender = tuple.Sender; +//// +//// return true; +//// } +// +//// public override void SendPacketTo(byte[] buffer, int size, SocketFlags flags, uint circuitcode) +//// { +//// // Don't do anything just yet +//// } +// +// /// +// /// Signal that this chunk should throw an exception on Socket.BeginReceive() +// /// +// /// +// public void LoadReceiveWithBeginException(EndPoint epSender) +// { +// ChunkSenderTuple tuple = new ChunkSenderTuple(epSender); +// tuple.BeginReceiveException = true; +// m_chunksToLoad.Enqueue(tuple); +// } +// +// /// +// /// Load some data to be received by the LLUDPServer on the next receive call +// /// +// /// +// /// +// public void LoadReceive(byte[] data, EndPoint epSender) +// { +// m_chunksToLoad.Enqueue(new ChunkSenderTuple(data, epSender)); +// } +// +// /// +// /// Load a packet to be received by the LLUDPServer on the next receive call +// /// +// /// +// public void LoadReceive(Packet packet, EndPoint epSender) +// { +// LoadReceive(packet.ToBytes(), epSender); +// } +// +// /// +// /// Calls the protected asynchronous result method. This fires out all data chunks currently queued for send +// /// +// /// +// public void ReceiveData(IAsyncResult result) +// { +// // Doesn't work the same way anymore +//// while (m_chunksToLoad.Count > 0) +//// OnReceivedData(result); +// } + } + + /// + /// Record the data and sender tuple + /// + public class ChunkSenderTuple + { + public byte[] Data; + public EndPoint Sender; + public bool BeginReceiveException; + + public ChunkSenderTuple(byte[] data, EndPoint sender) + { + Data = data; + Sender = sender; + } + + public ChunkSenderTuple(EndPoint sender) + { + Sender = sender; + } + } +} diff --git a/OpenSim/Tests/Common/Mock/TestLandChannel.cs b/OpenSim/Tests/Common/Mock/TestLandChannel.cs new file mode 100644 index 0000000000..89ebcd5c59 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestLandChannel.cs @@ -0,0 +1,115 @@ +/* + * 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.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.CoreModules.World.Land; + +namespace OpenSim.Tests.Common +{ + /// + /// Land channel for test purposes + /// + public class TestLandChannel : ILandChannel + { + private Scene m_scene; + private List m_parcels; + + public TestLandChannel(Scene scene) + { + m_scene = scene; + m_parcels = new List(); + SetupDefaultParcel(); + } + + private void SetupDefaultParcel() + { + ILandObject obj = new LandObject(UUID.Zero, false, m_scene); + obj.LandData.Name = "Your Parcel"; + m_parcels.Add(obj); + } + + public List ParcelsNearPoint(Vector3 position) + { + return new List(); + } + + public List AllParcels() + { + return m_parcels; + } + + public void Clear(bool setupDefaultParcel) + { + m_parcels.Clear(); + + if (setupDefaultParcel) + SetupDefaultParcel(); + } + + protected ILandObject GetNoLand() + { + ILandObject obj = new LandObject(UUID.Zero, false, m_scene); + obj.LandData.Name = "NO LAND"; + return obj; + } + + public ILandObject GetLandObject(Vector3 position) + { + return GetLandObject(position.X, position.Y); + } + + public ILandObject GetLandObject(int x, int y) + { + return GetNoLand(); + } + + public ILandObject GetLandObject(int localID) + { + return GetNoLand(); + } + + public ILandObject GetLandObject(float x, float y) + { + return GetNoLand(); + } + + public bool IsLandPrimCountTainted() { return false; } + public bool IsForcefulBansAllowed() { return false; } + public void UpdateLandObject(int localID, LandData data) {} + public void ReturnObjectsInParcel(int localID, uint returnType, UUID[] agentIDs, UUID[] taskIDs, IClientAPI remoteClient) {} + public void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel) {} + public void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel) {} + public void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime) {} + + public void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) {} + public void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id) {} + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestOSHttpRequest.cs b/OpenSim/Tests/Common/Mock/TestOSHttpRequest.cs new file mode 100644 index 0000000000..7b1d2b54d0 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestOSHttpRequest.cs @@ -0,0 +1,179 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Text; +using System.Web; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Tests.Common +{ + public class TestOSHttpRequest : IOSHttpRequest + { + public string[] AcceptTypes + { + get + { + throw new NotImplementedException (); + } + } + + public Encoding ContentEncoding + { + get + { + throw new NotImplementedException (); + } + } + + public long ContentLength + { + get + { + throw new NotImplementedException (); + } + } + + public long ContentLength64 + { + get + { + throw new NotImplementedException (); + } + } + + public string ContentType + { + get + { + throw new NotImplementedException (); + } + } + + public HttpCookieCollection Cookies + { + get + { + throw new NotImplementedException (); + } + } + + public bool HasEntityBody + { + get + { + throw new NotImplementedException (); + } + } + + public NameValueCollection Headers { get; set; } + + public string HttpMethod + { + get + { + throw new NotImplementedException (); + } + } + + public Stream InputStream + { + get + { + throw new NotImplementedException (); + } + } + + public bool IsSecured + { + get + { + throw new NotImplementedException (); + } + } + + public bool KeepAlive + { + get + { + throw new NotImplementedException (); + } + } + + public NameValueCollection QueryString + { + get + { + throw new NotImplementedException (); + } + } + + public Hashtable Query + { + get + { + throw new NotImplementedException (); + } + } + + public string RawUrl + { + get + { + throw new NotImplementedException (); + } + } + + public IPEndPoint RemoteIPEndPoint + { + get + { + throw new NotImplementedException (); + } + } + + public Uri Url { get; set; } + + public string UserAgent + { + get + { + throw new NotImplementedException (); + } + } + + public TestOSHttpRequest() + { + Headers = new NameValueCollection(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestOSHttpResponse.cs b/OpenSim/Tests/Common/Mock/TestOSHttpResponse.cs new file mode 100644 index 0000000000..2e17f1ea79 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestOSHttpResponse.cs @@ -0,0 +1,133 @@ +/* + * 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.Text; +using System.Web; +using OpenSim.Framework.Servers.HttpServer; + +namespace OpenSim.Tests.Common +{ + public class TestOSHttpResponse : IOSHttpResponse + { + /// + /// Content type property. + /// + /// + /// Setting this property will also set IsContentTypeSet to + /// true. + /// + public string ContentType { get; set; } + + /// + /// Boolean property indicating whether the content type + /// property actively has been set. + /// + /// + /// IsContentTypeSet will go away together with .NET base. + /// + // public bool IsContentTypeSet + // { + // get { return _contentTypeSet; } + // } + // private bool _contentTypeSet; + + /// + /// Length of the body content; 0 if there is no body. + /// + public long ContentLength { get; set; } + + /// + /// Alias for ContentLength. + /// + public long ContentLength64 { get; set; } + + /// + /// Encoding of the body content. + /// + public Encoding ContentEncoding { get; set; } + + public bool KeepAlive { get; set; } + + /// + /// Get or set the keep alive timeout property (default is + /// 20). Setting this to 0 also disables KeepAlive. Setting + /// this to something else but 0 also enable KeepAlive. + /// + public int KeepAliveTimeout { get; set; } + + /// + /// Return the output stream feeding the body. + /// + /// + /// On its way out... + /// + public Stream OutputStream { get; private set; } + + public string ProtocolVersion { get; set; } + + /// + /// Return the output stream feeding the body. + /// + public Stream Body { get; private set; } + + /// + /// Set a redirct location. + /// + public string RedirectLocation { private get; set; } + + /// + /// Chunk transfers. + /// + public bool SendChunked { get; set; } + + /// + /// HTTP status code. + /// + public int StatusCode { get; set; } + + /// + /// HTTP status description. + /// + public string StatusDescription { get; set; } + + public bool ReuseContext { get; set; } + + /// + /// Add a header field and content to the response. + /// + /// string containing the header field + /// name + /// string containing the header field + /// value + public void AddHeader(string key, string value) { throw new NotImplementedException(); } + + public void Send() { } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestScene.cs b/OpenSim/Tests/Common/Mock/TestScene.cs new file mode 100644 index 0000000000..45acf91f38 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestScene.cs @@ -0,0 +1,77 @@ +/* + * 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 Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Physics.Manager; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common +{ + public class TestScene : Scene + { + public TestScene( + RegionInfo regInfo, AgentCircuitManager authen, PhysicsScene physicsScene, + SceneCommunicationService sceneGridService, ISimulationDataService simDataService, IEstateDataService estateDataService, + IConfigSource config, string simulatorVersion) + : base(regInfo, authen, physicsScene, sceneGridService, simDataService, estateDataService, + config, simulatorVersion) + { + } + + ~TestScene() + { + //Console.WriteLine("TestScene destructor called for {0}", RegionInfo.RegionName); + Console.WriteLine("TestScene destructor called"); + } + + /// + /// Temporarily override session authentication for tests (namely teleport). + /// + /// + /// TODO: This needs to be mocked out properly. + /// + /// + /// + public override bool VerifyUserPresence(AgentCircuitData agent, out string reason) + { + reason = String.Empty; + return true; + } + + public AsyncSceneObjectGroupDeleter SceneObjectGroupDeleter + { + get { return m_asyncSceneObjectDeleter; } + } + } +} diff --git a/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs new file mode 100644 index 0000000000..2b272e6168 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/TestXInventoryDataPlugin.cs @@ -0,0 +1,152 @@ +/* + * 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 OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Data; +using OpenSim.Data.Null; + +namespace OpenSim.Tests.Common +{ + public class TestXInventoryDataPlugin : NullGenericDataHandler, IXInventoryData + { + private Dictionary m_allFolders = new Dictionary(); + private Dictionary m_allItems = new Dictionary(); + + public TestXInventoryDataPlugin(string conn, string realm) {} + + public XInventoryItem[] GetItems(string[] fields, string[] vals) + { +// Console.WriteLine( +// "Requesting items, fields {0}, vals {1}", string.Join(", ", fields), string.Join(", ", vals)); + + List origItems = Get(fields, vals, m_allItems.Values.ToList()); + + XInventoryItem[] items = origItems.Select(i => i.Clone()).ToArray(); + +// Console.WriteLine("Found {0} items", items.Length); +// Array.ForEach(items, i => Console.WriteLine("Found item {0} {1}", i.inventoryName, i.inventoryID)); + + return items; + } + + public XInventoryFolder[] GetFolders(string[] fields, string[] vals) + { +// Console.WriteLine( +// "Requesting folders, fields {0}, vals {1}", string.Join(", ", fields), string.Join(", ", vals)); + + List origFolders + = Get(fields, vals, m_allFolders.Values.ToList()); + + XInventoryFolder[] folders = origFolders.Select(f => f.Clone()).ToArray(); + +// Console.WriteLine("Found {0} folders", folders.Length); +// Array.ForEach(folders, f => Console.WriteLine("Found folder {0} {1}", f.folderName, f.folderID)); + + return folders; + } + + public bool StoreFolder(XInventoryFolder folder) + { + m_allFolders[folder.folderID] = folder.Clone(); + +// Console.WriteLine("Added folder {0} {1}", folder.folderName, folder.folderID); + + return true; + } + + public bool StoreItem(XInventoryItem item) + { + m_allItems[item.inventoryID] = item.Clone(); + +// Console.WriteLine( +// "Added item {0} {1}, folder {2}, creator {3}, owner {4}", +// item.inventoryName, item.inventoryID, item.parentFolderID, item.creatorID, item.avatarID); + + return true; + } + + public bool DeleteFolders(string field, string val) + { + return DeleteFolders(new string[] { field }, new string[] { val }); + } + + public bool DeleteFolders(string[] fields, string[] vals) + { + XInventoryFolder[] foldersToDelete = GetFolders(fields, vals); + Array.ForEach(foldersToDelete, f => m_allFolders.Remove(f.folderID)); + + return true; + } + + public bool DeleteItems(string field, string val) + { + return DeleteItems(new string[] { field }, new string[] { val }); + } + + public bool DeleteItems(string[] fields, string[] vals) + { + XInventoryItem[] itemsToDelete = GetItems(fields, vals); + Array.ForEach(itemsToDelete, i => m_allItems.Remove(i.inventoryID)); + + return true; + } + + public bool MoveItem(string id, string newParent) { throw new NotImplementedException(); } + + public bool MoveFolder(string id, string newParent) + { + // Don't use GetFolders() here - it takes a clone! + XInventoryFolder folder = m_allFolders[new UUID(id)]; + + if (folder == null) + return false; + + folder.parentFolderID = new UUID(newParent); + +// XInventoryFolder[] newParentFolders +// = GetFolders(new string[] { "folderID" }, new string[] { folder.parentFolderID.ToString() }); + +// Console.WriteLine( +// "Moved folder {0} {1}, to {2} {3}", +// folder.folderName, folder.folderID, newParentFolders[0].folderName, folder.parentFolderID); + + // TODO: Really need to implement folder version incrementing, though this should be common code anyway, + // not reimplemented in each db plugin. + + return true; + } + + public XInventoryItem[] GetActiveGestures(UUID principalID) { throw new NotImplementedException(); } + public int GetAssetPermissions(UUID principalID, UUID assetID) { throw new NotImplementedException(); } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/OpenSimTestCase.cs b/OpenSim/Tests/Common/OpenSimTestCase.cs new file mode 100644 index 0000000000..c1415afe43 --- /dev/null +++ b/OpenSim/Tests/Common/OpenSimTestCase.cs @@ -0,0 +1,55 @@ +/* + * 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 NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Framework.Servers; + +namespace OpenSim.Tests.Common +{ + [TestFixture] + public class OpenSimTestCase + { + [SetUp] + public virtual void SetUp() + { +// TestHelpers.InMethod(); + // Disable logging for each test so that one where logging is enabled doesn't cause all subsequent tests + // to have logging on if it failed with an exception. + TestHelpers.DisableLogging(); + + // This is an unfortunate bit of clean up we have to do because MainServer manages things through static + // variables and the VM is not restarted between tests. + if (MainServer.Instance != null) + { + MainServer.RemoveHttpServer(MainServer.Instance.Port); +// MainServer.Instance = null; + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/QuaternionToleranceConstraint.cs b/OpenSim/Tests/Common/QuaternionToleranceConstraint.cs new file mode 100644 index 0000000000..b38c382f53 --- /dev/null +++ b/OpenSim/Tests/Common/QuaternionToleranceConstraint.cs @@ -0,0 +1,82 @@ +/* + * 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 NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace OpenSim.Tests.Common +{ + public class QuaternionToleranceConstraint : ANumericalToleranceConstraint + { + private Quaternion _baseValue; + private Quaternion _valueToBeTested; + + public QuaternionToleranceConstraint(Quaternion baseValue, double tolerance) : base(tolerance) + { + _baseValue = baseValue; + } + + /// + /// Test whether the constraint is satisfied by a given value + /// + /// The value to be tested + /// + /// True for success, false for failure + /// + public override bool Matches(object valueToBeTested) + { + if (valueToBeTested == null) + { + throw new ArgumentException("Constraint cannot be used upon null values."); + } + if (valueToBeTested.GetType() != typeof (Quaternion)) + { + throw new ArgumentException("Constraint cannot be used upon non quaternion values."); + } + + _valueToBeTested = (Quaternion)valueToBeTested; + + return (IsWithinDoubleConstraint(_valueToBeTested.X, _baseValue.X) && + IsWithinDoubleConstraint(_valueToBeTested.Y, _baseValue.Y) && + IsWithinDoubleConstraint(_valueToBeTested.Z, _baseValue.Z) && + IsWithinDoubleConstraint(_valueToBeTested.W, _baseValue.W)); + } + + public override void WriteDescriptionTo(MessageWriter writer) + { + writer.WriteExpectedValue( + string.Format("A value {0} within tolerance of plus or minus {1}", _baseValue, _tolerance)); + } + + public override void WriteActualValueTo(MessageWriter writer) + { + writer.WriteActualValue(_valueToBeTested); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/TestHelpers.cs b/OpenSim/Tests/Common/TestHelpers.cs new file mode 100644 index 0000000000..6bf23f8e67 --- /dev/null +++ b/OpenSim/Tests/Common/TestHelpers.cs @@ -0,0 +1,164 @@ +/* + * 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.Diagnostics; +using System.IO; +using System.Text; +using NUnit.Framework; +using OpenMetaverse; + +namespace OpenSim.Tests.Common +{ + public class TestHelpers + { + private static Stream EnableLoggingConfigStream + = new MemoryStream( + Encoding.UTF8.GetBytes( +@" + + + + + + + + + + + + + + + + +")); + + private static MemoryStream DisableLoggingConfigStream + = new MemoryStream( + Encoding.UTF8.GetBytes( +// "")); + //""))); +// "")); +// ""))); +// "")); + "")); + + public static bool AssertThisDelegateCausesArgumentException(TestDelegate d) + { + try + { + d(); + } + catch(ArgumentException) + { + return true; + } + + return false; + } + + /// + /// A debugging method that can be used to print out which test method you are in + /// + public static void InMethod() + { + StackTrace stackTrace = new StackTrace(); + Console.WriteLine(); + Console.WriteLine("===> In Test Method : {0} <===", stackTrace.GetFrame(1).GetMethod().Name); + } + + public static void EnableLogging() + { + log4net.Config.XmlConfigurator.Configure(EnableLoggingConfigStream); + EnableLoggingConfigStream.Position = 0; + } + + /// + /// Disable logging whilst running the tests. + /// + /// + /// Remember, if a regression test throws an exception before completing this will not be invoked if it's at + /// the end of the test. + /// TODO: Always invoke this after every test - probably need to make all test cases inherit from a common + /// TestCase class where this can be done. + /// + public static void DisableLogging() + { + log4net.Config.XmlConfigurator.Configure(DisableLoggingConfigStream); + DisableLoggingConfigStream.Position = 0; + } + + /// + /// Parse a UUID stem into a full UUID. + /// + /// + /// The fragment will come at the start of the UUID. The rest will be 0s + /// + /// + /// + /// A UUID fragment that will be parsed into a full UUID. Therefore, it can only contain + /// cahracters which are valid in a UUID, except for "-" which is currently only allowed if a full UUID is + /// given as the 'fragment'. + /// + public static UUID ParseStem(string stem) + { + string rawUuid = stem.PadRight(32, '0'); + + return UUID.Parse(rawUuid); + } + + /// + /// Parse tail section into full UUID. + /// + /// + /// + public static UUID ParseTail(int tail) + { + return new UUID(string.Format("00000000-0000-0000-0000-{0:X12}", tail)); + } + + /// + /// Parse a UUID tail section into a full UUID. + /// + /// + /// The fragment will come at the end of the UUID. The rest will be 0s + /// + /// + /// + /// A UUID fragment that will be parsed into a full UUID. Therefore, it can only contain + /// cahracters which are valid in a UUID, except for "-" which is currently only allowed if a full UUID is + /// given as the 'fragment'. + /// + public static UUID ParseTail(string stem) + { + string rawUuid = stem.PadLeft(32, '0'); + + return UUID.Parse(rawUuid); + } + } +} diff --git a/OpenSim/Tests/Common/TestLogging.cs b/OpenSim/Tests/Common/TestLogging.cs new file mode 100644 index 0000000000..4a08344321 --- /dev/null +++ b/OpenSim/Tests/Common/TestLogging.cs @@ -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. + */ + +using System; +using System.Collections.Generic; +using System.Text; +using log4net.Appender; +using log4net.Layout; + +namespace OpenSim.Tests.Common +{ + public static class TestLogging + { + public static void LogToConsole() + { + ConsoleAppender consoleAppender = new ConsoleAppender(); + consoleAppender.Layout = + new PatternLayout("%date [%thread] %-5level %logger [%property{NDC}] - %message%newline"); + log4net.Config.BasicConfigurator.Configure(consoleAppender); + } + } +} diff --git a/OpenSim/Tests/Common/VectorToleranceConstraint.cs b/OpenSim/Tests/Common/VectorToleranceConstraint.cs new file mode 100644 index 0000000000..2fa20edec9 --- /dev/null +++ b/OpenSim/Tests/Common/VectorToleranceConstraint.cs @@ -0,0 +1,81 @@ +/* + * 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 NUnit.Framework; +using NUnit.Framework.Constraints; + +namespace OpenSim.Tests.Common +{ + public class VectorToleranceConstraint : ANumericalToleranceConstraint + { + private Vector3 _baseValue; + private Vector3 _valueToBeTested; + + public VectorToleranceConstraint(Vector3 baseValue, double tolerance) : base(tolerance) + { + _baseValue = baseValue; + } + + /// + ///Test whether the constraint is satisfied by a given value + /// + ///The value to be tested + /// + ///True for success, false for failure + /// + public override bool Matches(object valueToBeTested) + { + if (valueToBeTested == null) + { + throw new ArgumentException("Constraint cannot be used upon null values."); + } + if (valueToBeTested.GetType() != typeof (Vector3)) + { + throw new ArgumentException("Constraint cannot be used upon non vector values."); + } + + _valueToBeTested = (Vector3) valueToBeTested; + + return (IsWithinDoubleConstraint(_valueToBeTested.X, _baseValue.X) && + IsWithinDoubleConstraint(_valueToBeTested.Y, _baseValue.Y) && + IsWithinDoubleConstraint(_valueToBeTested.Z, _baseValue.Z)); + } + + public override void WriteDescriptionTo(MessageWriter writer) + { + writer.WriteExpectedValue( + string.Format("A value {0} within tolerance of plus or minus {1}", _baseValue, _tolerance)); + } + + public override void WriteActualValueTo(MessageWriter writer) + { + writer.WriteActualValue(_valueToBeTested); + } + } +} diff --git a/OpenSim/Tests/ConfigurationLoaderTest.cs b/OpenSim/Tests/ConfigurationLoaderTest.cs new file mode 100644 index 0000000000..a409a1349b --- /dev/null +++ b/OpenSim/Tests/ConfigurationLoaderTest.cs @@ -0,0 +1,148 @@ +/* + * 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 Nini.Config; +using NUnit.Framework; +using OpenSim.Framework; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests +{ + [TestFixture] + public class ConfigurationLoaderTests : OpenSimTestCase + { + private const string m_testSubdirectory = "test"; + private string m_basePath; + private string m_workingDirectory; + private IConfigSource m_config; + + /// + /// Set up a test directory. + /// + [SetUp] + public override void SetUp() + { + base.SetUp(); + + m_basePath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + string path = Path.Combine(m_basePath, m_testSubdirectory); + Directory.CreateDirectory(path); + m_workingDirectory = Directory.GetCurrentDirectory(); + Directory.SetCurrentDirectory(path); + } + + /// + /// Remove the test directory. + /// + [TearDown] + public void TearDown() + { + Directory.SetCurrentDirectory(m_workingDirectory); + Directory.Delete(m_basePath, true); + } + + /// + /// Test the including of ini files with absolute and relative paths. + /// + [Test] + public void IncludeTests() + { + const string mainIniFile = "OpenSimDefaults.ini"; + m_config = new IniConfigSource(); + + // Create ini files in a directory structure + IniConfigSource ini; + IConfig config; + + ini = new IniConfigSource(); + config = ini.AddConfig("IncludeTest"); + config.Set("Include-absolute", "absolute/*/config/*.ini"); + config.Set("Include-relative", "../" + m_testSubdirectory + "/relative/*/config/*.ini"); + CreateIni(mainIniFile, ini); + + ini = new IniConfigSource(); + ini.AddConfig("Absolute1").Set("name1", "value1"); + CreateIni("absolute/one/config/setting.ini", ini); + + ini = new IniConfigSource(); + ini.AddConfig("Absolute2").Set("name2", 2.3); + CreateIni("absolute/two/config/setting1.ini", ini); + + ini = new IniConfigSource(); + ini.AddConfig("Absolute2").Set("name3", "value3"); + CreateIni("absolute/two/config/setting2.ini", ini); + + ini = new IniConfigSource(); + ini.AddConfig("Relative1").Set("name4", "value4"); + CreateIni("relative/one/config/setting.ini", ini); + + ini = new IniConfigSource(); + ini.AddConfig("Relative2").Set("name5", true); + CreateIni("relative/two/config/setting1.ini", ini); + + ini = new IniConfigSource(); + ini.AddConfig("Relative2").Set("name6", 6); + CreateIni("relative/two/config/setting2.ini", ini); + + // Prepare call to ConfigurationLoader.LoadConfigSettings() + ConfigurationLoader cl = new ConfigurationLoader(); + IConfigSource argvSource = new IniConfigSource(); + EnvConfigSource envConfigSource = new EnvConfigSource(); + argvSource.AddConfig("Startup").Set("inifile", mainIniFile); + ConfigSettings configSettings; + NetworkServersInfo networkInfo; + + OpenSimConfigSource source = cl.LoadConfigSettings(argvSource, envConfigSource, + out configSettings, out networkInfo); + + // Remove default config + config = source.Source.Configs["Startup"]; + source.Source.Configs.Remove(config); + config = source.Source.Configs["Network"]; + source.Source.Configs.Remove(config); + + // Finally, we are able to check the result + Assert.AreEqual(m_config.ToString(), source.Source.ToString(), + "Configuration with includes does not contain all settings."); + // The following would be preferable but fails due to a type mismatch which I am not able to resolve + //CollectionAssert.AreEquivalent(m_config.Configs, source.Source.Configs, + // String.Format("Configuration with includes does not contain all settings.\nAll settings:\n{0}\nSettings read:\n{1}", m_config, source.Source)); + } + + private void CreateIni(string filepath, IniConfigSource source) + { + string path = Path.GetDirectoryName(filepath); + if (path != string.Empty) + { + Directory.CreateDirectory(path); + } + source.Save(filepath); + m_config.Merge(source); + } + } +} diff --git a/OpenSim/Tests/Performance/NPCPerformanceTests.cs b/OpenSim/Tests/Performance/NPCPerformanceTests.cs new file mode 100644 index 0000000000..e222dc2dfe --- /dev/null +++ b/OpenSim/Tests/Performance/NPCPerformanceTests.cs @@ -0,0 +1,191 @@ +/* + * 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.Diagnostics; +using System.Reflection; +using log4net; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Communications; +using OpenSim.Region.CoreModules.Avatar.Attachments; +using OpenSim.Region.CoreModules.Avatar.AvatarFactory; +using OpenSim.Region.CoreModules.Framework.InventoryAccess; +using OpenSim.Region.CoreModules.Framework.UserManagement; +using OpenSim.Region.CoreModules.ServiceConnectorsOut.Avatar; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.OptionalModules.World.NPC; +using OpenSim.Services.AvatarService; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests.Performance +{ + /// + /// NPC performance tests + /// + /// + /// Don't rely on the numbers given by these tests - they will vary a lot depending on what is already cached, + /// how much memory is free, etc. In some cases, later larger tests will apparently take less time than smaller + /// earlier tests. + /// + [TestFixture] + public class NPCPerformanceTests : OpenSimTestCase + { + private TestScene scene; + private AvatarFactoryModule afm; + private UserManagementModule umm; + private AttachmentsModule am; + + [TestFixtureSetUp] + public void FixtureInit() + { + // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. + Util.FireAndForgetMethod = FireAndForgetMethod.None; + } + + [TestFixtureTearDown] + public void TearDown() + { + scene.Close(); + scene = null; + GC.Collect(); + GC.WaitForPendingFinalizers(); + + // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple + // threads. Possibly, later tests should be rewritten not to worry about such things. + Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; + } + + [SetUp] + public void Init() + { + IConfigSource config = new IniConfigSource(); + config.AddConfig("NPC"); + config.Configs["NPC"].Set("Enabled", "true"); + config.AddConfig("Modules"); + config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule"); + + afm = new AvatarFactoryModule(); + umm = new UserManagementModule(); + am = new AttachmentsModule(); + + scene = new SceneHelpers().SetupScene(); + SceneHelpers.SetupSceneModules(scene, config, afm, umm, am, new BasicInventoryAccessModule(), new NPCModule()); + } + + [Test] + public void Test_0001_AddRemove100NPCs() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddRemoveNPCs(100); + } + + [Test] + public void Test_0002_AddRemove1000NPCs() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddRemoveNPCs(1000); + } + + [Test] + public void Test_0003_AddRemove2000NPCs() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddRemoveNPCs(2000); + } + + private void TestAddRemoveNPCs(int numberOfNpcs) + { + ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1)); +// ScenePresence originalAvatar = scene.GetScenePresence(originalClient.AgentId); + + // 8 is the index of the first baked texture in AvatarAppearance + UUID originalFace8TextureId = TestHelpers.ParseTail(0x10); + Primitive.TextureEntry originalTe = new Primitive.TextureEntry(UUID.Zero); + Primitive.TextureEntryFace originalTef = originalTe.CreateFace(8); + originalTef.TextureID = originalFace8TextureId; + + // We also need to add the texture to the asset service, otherwise the AvatarFactoryModule will tell + // ScenePresence.SendInitialData() to reset our entire appearance. + scene.AssetService.Store(AssetHelpers.CreateNotecardAsset(originalFace8TextureId)); + +/* + afm.SetAppearance(sp, originalTe, null); + + INPCModule npcModule = scene.RequestModuleInterface(); + + List npcs = new List(); + + long startGcMemory = GC.GetTotalMemory(true); + Stopwatch sw = new Stopwatch(); + sw.Start(); + + for (int i = 0; i < numberOfNpcs; i++) + { + npcs.Add( + npcModule.CreateNPC("John", "Smith", new Vector3(128, 128, 30), UUID.Zero, true, scene, sp.Appearance)); + } + + for (int i = 0; i < numberOfNpcs; i++) + { + Assert.That(npcs[i], Is.Not.Null); + + ScenePresence npc = scene.GetScenePresence(npcs[i]); + Assert.That(npc, Is.Not.Null); + } + + for (int i = 0; i < numberOfNpcs; i++) + { + Assert.That(npcModule.DeleteNPC(npcs[i], scene), Is.True); + ScenePresence npc = scene.GetScenePresence(npcs[i]); + Assert.That(npc, Is.Null); + } + + sw.Stop(); + + long endGcMemory = GC.GetTotalMemory(true); + + Console.WriteLine("Took {0} ms", sw.ElapsedMilliseconds); + Console.WriteLine( + "End {0} MB, Start {1} MB, Diff {2} MB", + endGcMemory / 1024 / 1024, + startGcMemory / 1024 / 1024, + (endGcMemory - startGcMemory) / 1024 / 1024); +*/ + } + } +} diff --git a/OpenSim/Tests/Performance/ObjectPerformanceTests.cs b/OpenSim/Tests/Performance/ObjectPerformanceTests.cs new file mode 100644 index 0000000000..9dad423a5f --- /dev/null +++ b/OpenSim/Tests/Performance/ObjectPerformanceTests.cs @@ -0,0 +1,174 @@ +/* + * 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.Diagnostics; +using System.Reflection; +using log4net; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests.Performance +{ + /// + /// Object performance tests + /// + /// + /// Don't rely on the numbers given by these tests - they will vary a lot depending on what is already cached, + /// how much memory is free, etc. In some cases, later larger tests will apparently take less time than smaller + /// earlier tests. + /// + [TestFixture] + public class ObjectPerformanceTests : OpenSimTestCase + { + [TearDown] + public void TearDown() + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + +// [Test] +// public void Test0000Clean() +// { +// TestHelpers.InMethod(); +//// log4net.Config.XmlConfigurator.Configure(); +// +// TestAddObjects(200000); +// } + + [Test] + public void Test_0001_10K_1PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(1, 10000); + } + + [Test] + public void Test_0002_100K_1PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(1, 100000); + } + + [Test] + public void Test_0003_200K_1PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(1, 200000); + } + + [Test] + public void Test_0011_100_100PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(100, 100); + } + + [Test] + public void Test_0012_1K_100PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(100, 1000); + } + + [Test] + public void Test_0013_2K_100PrimObjects() + { + TestHelpers.InMethod(); +// log4net.Config.XmlConfigurator.Configure(); + + TestAddObjects(100, 2000); + } + + private void TestAddObjects(int primsInEachObject, int objectsToAdd) + { + UUID ownerId = new UUID("F0000000-0000-0000-0000-000000000000"); + + // Using a local variable for scene, at least on mono 2.6.7, means that it's much more likely to be garbage + // collected when we teardown this test. If it's done in a member variable, even if that is subsequently + // nulled out, the garbage collect can be delayed. + TestScene scene = new SceneHelpers().SetupScene(); + +// Process process = Process.GetCurrentProcess(); +// long startProcessMemory = process.PrivateMemorySize64; + long startGcMemory = GC.GetTotalMemory(true); + DateTime start = DateTime.Now; + + for (int i = 1; i <= objectsToAdd; i++) + { + SceneObjectGroup so = SceneHelpers.CreateSceneObject(primsInEachObject, ownerId, "part_", i); + Assert.That(scene.AddNewSceneObject(so, false), Is.True, string.Format("Object {0} was not created", i)); + } + + TimeSpan elapsed = DateTime.Now - start; +// long processMemoryAlloc = process.PrivateMemorySize64 - startProcessMemory; + long endGcMemory = GC.GetTotalMemory(false); + + for (int i = 1; i <= objectsToAdd; i++) + { + Assert.That( + scene.GetSceneObjectGroup(TestHelpers.ParseTail(i)), + Is.Not.Null, + string.Format("Object {0} could not be retrieved", i)); + } + + // When a scene object is added to a scene, it is placed in the update list for sending to viewers + // (though in this case we have none). When it is deleted, it is not removed from the update which is + // fine since it will later be ignored. + // + // However, that means that we need to manually run an update here to clear out that list so that deleted + // objects will be clean up by the garbage collector before the next stress test is run. + scene.Update(1); + + Console.WriteLine( + "Took {0}ms, {1}MB ({2} - {3}) to create {4} objects each containing {5} prim(s)", + Math.Round(elapsed.TotalMilliseconds), + (endGcMemory - startGcMemory) / 1024 / 1024, + endGcMemory / 1024 / 1024, + startGcMemory / 1024 / 1024, + objectsToAdd, + primsInEachObject); + + scene.Close(); +// scene = null; + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Performance/ScriptPerformanceTests.cs b/OpenSim/Tests/Performance/ScriptPerformanceTests.cs new file mode 100644 index 0000000000..028f4b00d5 --- /dev/null +++ b/OpenSim/Tests/Performance/ScriptPerformanceTests.cs @@ -0,0 +1,167 @@ +/* + * 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.Diagnostics; +using System.Reflection; +using System.Threading; +using log4net; +using Nini.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Scripting.WorldComm; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.ScriptEngine.XEngine; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests.Performance +{ + /// + /// Script performance tests + /// + /// + /// Don't rely on the numbers given by these tests - they will vary a lot depending on what is already cached, + /// how much memory is free, etc. In some cases, later larger tests will apparently take less time than smaller + /// earlier tests. + /// + [TestFixture] + public class ScriptPerformanceTests : OpenSimTestCase + { + private TestScene m_scene; + private XEngine m_xEngine; + private AutoResetEvent m_chatEvent = new AutoResetEvent(false); + + private int m_expectedChatMessages; + private List m_osChatMessagesReceived = new List(); + + [SetUp] + public void Init() + { + //AppDomain.CurrentDomain.SetData("APPBASE", Environment.CurrentDirectory + "/bin"); +// Console.WriteLine(AppDomain.CurrentDomain.BaseDirectory); + m_xEngine = new XEngine(); + + // Necessary to stop serialization complaining + WorldCommModule wcModule = new WorldCommModule(); + + IniConfigSource configSource = new IniConfigSource(); + + IConfig startupConfig = configSource.AddConfig("Startup"); + startupConfig.Set("DefaultScriptEngine", "XEngine"); + + IConfig xEngineConfig = configSource.AddConfig("XEngine"); + xEngineConfig.Set("Enabled", "true"); + + // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call + // to AssemblyResolver.OnAssemblyResolve fails. + xEngineConfig.Set("AppDomainLoading", "false"); + + m_scene = new SceneHelpers().SetupScene("My Test", UUID.Random(), 1000, 1000, configSource); + SceneHelpers.SetupSceneModules(m_scene, configSource, m_xEngine, wcModule); + + m_scene.EventManager.OnChatFromWorld += OnChatFromWorld; + m_scene.StartScripts(); + } + + [TearDown] + public void TearDown() + { + m_scene.Close(); + m_scene = null; + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + [Test] + public void TestCompileAndStart100Scripts() + { + TestHelpers.InMethod(); + log4net.Config.XmlConfigurator.Configure(); + + TestCompileAndStartScripts(100); + } + + private void TestCompileAndStartScripts(int scriptsToCreate) + { + UUID userId = TestHelpers.ParseTail(0x1); + + m_expectedChatMessages = scriptsToCreate; + int startingObjectIdTail = 0x100; + + GC.Collect(); + + for (int idTail = startingObjectIdTail;idTail < startingObjectIdTail + scriptsToCreate; idTail++) + { + AddObjectAndScript(idTail, userId); + } + + m_chatEvent.WaitOne(40000 + scriptsToCreate * 1000); + + Assert.That(m_osChatMessagesReceived.Count, Is.EqualTo(m_expectedChatMessages)); + + foreach (OSChatMessage msg in m_osChatMessagesReceived) + Assert.That( + msg.Message, + Is.EqualTo("Script running"), + string.Format( + "Message from {0} was {1} rather than {2}", msg.SenderUUID, msg.Message, "Script running")); + } + + private void AddObjectAndScript(int objectIdTail, UUID userId) + { +// UUID itemId = TestHelpers.ParseTail(0x3); + string itemName = string.Format("AddObjectAndScript() Item for object {0}", objectIdTail); + + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, "AddObjectAndScriptPart_", objectIdTail); + m_scene.AddNewSceneObject(so, true); + + InventoryItemBase itemTemplate = new InventoryItemBase(); +// itemTemplate.ID = itemId; + itemTemplate.Name = itemName; + itemTemplate.Folder = so.UUID; + itemTemplate.InvType = (int)InventoryType.LSL; + + m_scene.RezNewScript(userId, itemTemplate); + } + + private void OnChatFromWorld(object sender, OSChatMessage oscm) + { +// Console.WriteLine("Got chat [{0}]", oscm.Message); + + lock (m_osChatMessagesReceived) + { + m_osChatMessagesReceived.Add(oscm); + + if (m_osChatMessagesReceived.Count == m_expectedChatMessages) + m_chatEvent.Set(); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Robust/Clients/Grid/GridClient.cs b/OpenSim/Tests/Robust/Clients/Grid/GridClient.cs new file mode 100644 index 0000000000..671aca7c0b --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/Grid/GridClient.cs @@ -0,0 +1,133 @@ +/* + * 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.Text; +using System.Reflection; + +using OpenMetaverse; +using NUnit.Framework; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using GridRegion = OpenSim.Services.Interfaces.GridRegion; +using OpenSim.Services.Connectors; + +namespace Robust.Tests +{ + [TestFixture] + public class GridClient + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + [Test] + public void Grid_001() + { + GridServicesConnector m_Connector = new GridServicesConnector(DemonServer.Address); + + GridRegion r1 = CreateRegion("Test Region 1", 1000, 1000); + GridRegion r2 = CreateRegion("Test Region 2", 1001, 1000); + GridRegion r3 = CreateRegion("Test Region 3", 1005, 1000); + + string msg = m_Connector.RegisterRegion(UUID.Zero, r1); + Assert.AreEqual(msg, string.Empty, "Region 1 failed to register"); + + msg = m_Connector.RegisterRegion(UUID.Zero, r2); + Assert.AreEqual(msg, string.Empty, "Region 2 failed to register"); + + msg = m_Connector.RegisterRegion(UUID.Zero, r3); + Assert.AreEqual(msg, string.Empty, "Region 3 failed to register"); + + bool success; + success = m_Connector.DeregisterRegion(r3.RegionID); + Assert.AreEqual(success, true, "Region 3 failed to deregister"); + + msg = m_Connector.RegisterRegion(UUID.Zero, r3); + Assert.AreEqual(msg, string.Empty, "Region 3 failed to re-register"); + + List regions = m_Connector.GetNeighbours(UUID.Zero, r1.RegionID); + Assert.AreNotEqual(regions, null, "GetNeighbours of region 1 failed"); + Assert.AreEqual(regions.Count, 1, "Region 1 should have 1 neighbor"); + Assert.AreEqual(regions[0].RegionName, "Test Region 2", "Region 1 has the wrong neighbor"); + + GridRegion region = m_Connector.GetRegionByUUID(UUID.Zero, r2.RegionID); + Assert.AreNotEqual(region, null, "GetRegionByUUID for region 2 failed"); + Assert.AreEqual(region.RegionName, "Test Region 2", "GetRegionByUUID of region 2 returned wrong region"); + + region = m_Connector.GetRegionByUUID(UUID.Zero, UUID.Random()); + Assert.AreEqual(region, null, "Region with randon id should not exist"); + + region = m_Connector.GetRegionByName(UUID.Zero, r3.RegionName); + Assert.AreNotEqual(region, null, "GetRegionByUUID for region 3 failed"); + Assert.AreEqual(region.RegionName, "Test Region 3", "GetRegionByUUID of region 3 returned wrong region"); + + region = m_Connector.GetRegionByName(UUID.Zero, "Foo"); + Assert.AreEqual(region, null, "Region Foo should not exist"); + + regions = m_Connector.GetRegionsByName(UUID.Zero, "Test", 10); + Assert.AreNotEqual(regions, null, "GetRegionsByName failed"); + Assert.AreEqual(regions.Count, 3, "GetRegionsByName should return 3"); + + regions = m_Connector.GetRegionRange(UUID.Zero, + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(1002), + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(1002) ); + Assert.AreNotEqual(regions, null, "GetRegionRange failed"); + Assert.AreEqual(regions.Count, 2, "GetRegionRange should return 2"); + + regions = m_Connector.GetRegionRange(UUID.Zero, + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(950), + (int)Util.RegionToWorldLoc(900), (int)Util.RegionToWorldLoc(950) ); + Assert.AreNotEqual(regions, null, "GetRegionRange (bis) failed"); + Assert.AreEqual(regions.Count, 0, "GetRegionRange (bis) should return 0"); + + // Deregister them all + success = m_Connector.DeregisterRegion(r1.RegionID); + Assert.AreEqual(success, true, "Region 1 failed to deregister"); + + success = m_Connector.DeregisterRegion(r2.RegionID); + Assert.AreEqual(success, true, "Region 2 failed to deregister"); + + success = m_Connector.DeregisterRegion(r3.RegionID); + Assert.AreEqual(success, true, "Region 3 failed to deregister"); + } + + private static GridRegion CreateRegion(string name, uint xcell, uint ycell) + { + GridRegion region = new GridRegion(xcell, ycell); + region.RegionName = name; + region.RegionID = UUID.Random(); + region.ExternalHostName = "127.0.0.1"; + region.HttpPort = 9000; + region.InternalEndPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse("0.0.0.0"), 9000); + + return region; + } + } +} diff --git a/OpenSim/Tests/Robust/Clients/Grid/GridForm.html b/OpenSim/Tests/Robust/Clients/Grid/GridForm.html new file mode 100644 index 0000000000..252920f38d --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/Grid/GridForm.html @@ -0,0 +1,11 @@ + + +
+xmin: +xmax: +ymin: +ymax: + + + + diff --git a/OpenSim/Tests/Robust/Clients/InstantMessage/IMClient.cs b/OpenSim/Tests/Robust/Clients/InstantMessage/IMClient.cs new file mode 100644 index 0000000000..8f312ebe10 --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/InstantMessage/IMClient.cs @@ -0,0 +1,58 @@ +/* + * 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.Text; + +using OpenMetaverse; +using NUnit.Framework; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors.InstantMessage; + +namespace Robust.Tests +{ + [TestFixture] + public class IMClient + { + [Test] + public void HGIM_001() + { + GridInstantMessage im = new GridInstantMessage(); + im.fromAgentID = new Guid(); + im.toAgentID = new Guid(); + im.message = "Hello"; + im.imSessionID = new Guid(); + + bool success = InstantMessageServiceConnector.SendInstantMessage(DemonServer.Address, im); + Assert.IsFalse(success, "Sending of IM succeeded, but it should have failed"); + } + + } +} diff --git a/OpenSim/Tests/Robust/Clients/Inventory/InventoryClient.cs b/OpenSim/Tests/Robust/Clients/Inventory/InventoryClient.cs new file mode 100644 index 0000000000..0280b7377e --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/Inventory/InventoryClient.cs @@ -0,0 +1,206 @@ +/* + * 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.Text; +using System.Reflection; + +using OpenMetaverse; +using NUnit.Framework; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; + +using OpenSim.Tests.Common; + +namespace Robust.Tests +{ + [TestFixture] + public class InventoryClient + { +// private static readonly ILog m_log = +// LogManager.GetLogger( +// MethodBase.GetCurrentMethod().DeclaringType); + + private UUID m_userID = new UUID("00000000-0000-0000-0000-333333333333"); + private UUID m_rootFolderID; + private UUID m_notecardsFolder; + private UUID m_objectsFolder; + + [Test] + public void Inventory_001_CreateInventory() + { + TestHelpers.InMethod(); + XInventoryServicesConnector m_Connector = new XInventoryServicesConnector(DemonServer.Address); + + // Create an inventory that looks like this: + // + // /My Inventory + // + // /Objects + // Some Object + // /Notecards + // Notecard 1 + // Notecard 2 + // /Test Folder + // Link to notecard -> /Notecards/Notecard 2 + // Link to Objects folder -> /Objects + + bool success = m_Connector.CreateUserInventory(m_userID); + Assert.IsTrue(success, "Failed to create user inventory"); + + m_rootFolderID = m_Connector.GetRootFolder(m_userID).ID; + Assert.AreNotEqual(m_rootFolderID, UUID.Zero, "Root folder ID must not be UUID.Zero"); + + InventoryFolderBase of = m_Connector.GetFolderForType(m_userID, FolderType.Object); + Assert.IsNotNull(of, "Failed to retrieve Objects folder"); + m_objectsFolder = of.ID; + Assert.AreNotEqual(m_objectsFolder, UUID.Zero, "Objects folder ID must not be UUID.Zero"); + + // 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"; + item.Description = string.Empty; + success = m_Connector.AddItem(item); + Assert.IsTrue(success, "Failed to add object to inventory"); + + InventoryFolderBase ncf = m_Connector.GetFolderForType(m_userID, FolderType.Notecard); + Assert.IsNotNull(of, "Failed to retrieve Notecards folder"); + m_notecardsFolder = ncf.ID; + Assert.AreNotEqual(m_notecardsFolder, UUID.Zero, "Notecards folder ID must not be UUID.Zero"); + 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"; + item.Description = string.Empty; + success = m_Connector.AddItem(item); + Assert.IsTrue(success, "Failed to add Notecard 1 to inventory"); + // 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"; + item.Description = string.Empty; + success = m_Connector.AddItem(item); + Assert.IsTrue(success, "Failed to add Notecard 2 to inventory"); + + // Add a folder + InventoryFolderBase folder = new InventoryFolderBase(new UUID("f0000000-0000-0000-0000-00000000000f"), "Test Folder", m_userID, m_rootFolderID); + folder.Type = (int)FolderType.None; + success = m_Connector.AddFolder(folder); + Assert.IsTrue(success, "Failed to add Test Folder to inventory"); + + // 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"; + item.Description = string.Empty; + success = m_Connector.AddItem(item); + Assert.IsTrue(success, "Failed to add link to notecard to inventory"); + + // Add a link to the Objects folder in Test Folder + item.AssetID = m_Connector.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"; + item.Description = string.Empty; + success = m_Connector.AddItem(item); + Assert.IsTrue(success, "Failed to add link to objects folder to inventory"); + + InventoryCollection coll = m_Connector.GetFolderContent(m_userID, m_rootFolderID); + Assert.IsNotNull(coll, "Failed to retrieve contents of root folder"); + Assert.Greater(coll.Folders.Count, 0, "Root folder does not have any subfolders"); + + coll = m_Connector.GetFolderContent(m_userID, folder.ID); + Assert.IsNotNull(coll, "Failed to retrieve contents of Test Folder"); + Assert.AreEqual(coll.Items.Count + coll.Folders.Count, 2, "Test Folder is expected to have exactly 2 things inside"); + + } + + [Test] + public void Inventory_002_MultipleItemsRequest() + { + TestHelpers.InMethod(); + XInventoryServicesConnector m_Connector = new XInventoryServicesConnector(DemonServer.Address); + + // Prefetch Notecard 1, will be cached from here on + InventoryItemBase item = new InventoryItemBase(new UUID("10000000-0000-0000-0000-000000000001"), m_userID); + item = m_Connector.GetItem(item); + Assert.NotNull(item, "Failed to get Notecard 1"); + Assert.AreEqual("Test Notecard 1", item.Name, "Wrong name for Notecard 1"); + + UUID[] uuids = new UUID[2]; + uuids[0] = item.ID; + uuids[1] = new UUID("20000000-0000-0000-0000-000000000002"); + + InventoryItemBase[] items = m_Connector.GetMultipleItems(m_userID, uuids); + Assert.NotNull(items, "Failed to get multiple items"); + Assert.IsTrue(items.Length == 2, "Requested 2 items, but didn't receive 2 items"); + + // Now they should both be cached + items = m_Connector.GetMultipleItems(m_userID, uuids); + Assert.NotNull(items, "(Repeat) Failed to get multiple items"); + Assert.IsTrue(items.Length == 2, "(Repeat) Requested 2 items, but didn't receive 2 items"); + + // This item doesn't exist, but [0] does, and it's cached. + uuids[1] = new UUID("bb000000-0000-0000-0000-0000000000bb"); + // Fetching should return 2 items, but [1] should be null + items = m_Connector.GetMultipleItems(m_userID, uuids); + Assert.NotNull(items, "(Three times) Failed to get multiple items"); + Assert.IsTrue(items.Length == 2, "(Three times) Requested 2 items, but didn't receive 2 items"); + Assert.AreEqual("Test Notecard 1", items[0].Name, "(Three times) Wrong name for Notecard 1"); + Assert.IsNull(items[1], "(Three times) Expecting 2nd item to be null"); + + // Now both don't exist + uuids[0] = new UUID("aa000000-0000-0000-0000-0000000000aa"); + items = m_Connector.GetMultipleItems(m_userID, uuids); + Assert.Null(items[0], "Request to multiple non-existent items is supposed to return null [0]"); + Assert.Null(items[1], "Request to multiple non-existent items is supposed to return null [1]"); + + // This item exists, and it's not cached + uuids[1] = new UUID("b0000000-0000-0000-0000-00000000000b"); + // Fetching should return 2 items, but [0] should be null + items = m_Connector.GetMultipleItems(m_userID, uuids); + Assert.NotNull(items, "(Four times) Failed to get multiple items"); + Assert.IsTrue(items.Length == 2, "(Four times) Requested 2 items, but didn't receive 2 items"); + Assert.AreEqual("Some Object", items[1].Name, "(Four times) Wrong name for Some Object"); + Assert.IsNull(items[0], "(Four times) Expecting 1st item to be null"); + + } + } +} diff --git a/OpenSim/Tests/Robust/Clients/Presence/PresenceClient.cs b/OpenSim/Tests/Robust/Clients/Presence/PresenceClient.cs new file mode 100644 index 0000000000..31c8ee96ef --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/Presence/PresenceClient.cs @@ -0,0 +1,81 @@ +/* + * 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.Text; +using System.Reflection; + +using OpenMetaverse; +using NUnit.Framework; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; + +namespace Robust.Tests +{ + [TestFixture] + public class PresenceClient + { + [Test] + public void Presence_001() + { + PresenceServicesConnector m_Connector = new PresenceServicesConnector(DemonServer.Address); + + UUID user1 = UUID.Random(); + UUID session1 = UUID.Random(); + UUID region1 = UUID.Random(); + + bool success = m_Connector.LoginAgent(user1.ToString(), session1, UUID.Zero); + Assert.AreEqual(success, true, "Failed to add user session"); + + PresenceInfo pinfo = m_Connector.GetAgent(session1); + Assert.AreNotEqual(pinfo, null, "Unable to retrieve session"); + Assert.AreEqual(pinfo.UserID, user1.ToString(), "Retrieved session does not match expected userID"); + Assert.AreNotEqual(pinfo.RegionID, region1, "Retrieved session is unexpectedly in region"); + + success = m_Connector.ReportAgent(session1, region1); + Assert.AreEqual(success, true, "Failed to report session in region 1"); + + pinfo = m_Connector.GetAgent(session1); + Assert.AreNotEqual(pinfo, null, "Unable to session presence"); + Assert.AreEqual(pinfo.UserID, user1.ToString(), "Retrieved session does not match expected userID"); + Assert.AreEqual(pinfo.RegionID, region1, "Retrieved session is not in expected region"); + + success = m_Connector.LogoutAgent(session1); + Assert.AreEqual(success, true, "Failed to remove session"); + + pinfo = m_Connector.GetAgent(session1); + Assert.AreEqual(pinfo, null, "Session is still there, even though it shouldn't"); + + success = m_Connector.ReportAgent(session1, UUID.Random()); + Assert.AreEqual(success, false, "Remove non-existing session should fail"); + } + + } +} diff --git a/OpenSim/Tests/Robust/Clients/UserAccounts/UserAccountsClient.cs b/OpenSim/Tests/Robust/Clients/UserAccounts/UserAccountsClient.cs new file mode 100644 index 0000000000..3238dc9a31 --- /dev/null +++ b/OpenSim/Tests/Robust/Clients/UserAccounts/UserAccountsClient.cs @@ -0,0 +1,86 @@ +/* + * 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.Text; +using System.Reflection; + +using OpenMetaverse; +using NUnit.Framework; + +using OpenSim.Framework; +using OpenSim.Services.Interfaces; +using OpenSim.Services.Connectors; + +namespace Robust.Tests +{ + [TestFixture] + public class UserAccountsClient + { + [Test] + public void UserAccounts_001() + { + UserAccountServicesConnector m_Connector = new UserAccountServicesConnector(DemonServer.Address); + + string first = "Completely"; + string last = "Clueless"; + string email = "foo@bar.com"; + + UserAccount account = m_Connector.CreateUser(first, last, "123", email, UUID.Zero); + Assert.IsNotNull(account, "Failed to create account " + first + " " + last); + UUID user1 = account.PrincipalID; + + account = m_Connector.GetUserAccount(UUID.Zero, user1); + Assert.NotNull(account, "Failed to retrieve account for user id " + user1); + Assert.AreEqual(account.FirstName, first, "First name does not match"); + Assert.AreEqual(account.LastName, last, "Last name does not match"); + + account = m_Connector.GetUserAccount(UUID.Zero, first, last); + Assert.IsNotNull(account, "Failed to retrieve account for user " + first + " " + last); + Assert.AreEqual(account.FirstName, first, "First name does not match (bis)"); + Assert.AreEqual(account.LastName, last, "Last name does not match (bis)"); + + account.Email = "user@example.com"; + bool success = m_Connector.StoreUserAccount(account); + Assert.IsTrue(success, "Failed to store existing account"); + + account = m_Connector.GetUserAccount(UUID.Zero, user1); + Assert.NotNull(account, "Failed to retrieve account for user id " + user1); + Assert.AreEqual(account.Email, "user@example.com", "Incorrect email"); + + account = new UserAccount(UUID.Zero, "DoesNot", "Exist", "xxx@xxx.com"); + success = m_Connector.StoreUserAccount(account); + Assert.IsFalse(success, "Storing a non-existing account must fail"); + + account = m_Connector.GetUserAccount(UUID.Zero, "DoesNot", "Exist"); + Assert.IsNull(account, "Account DoesNot Exist must not be there"); + + } + + } +} diff --git a/OpenSim/Tests/Robust/Server/DemonServer.cs b/OpenSim/Tests/Robust/Server/DemonServer.cs new file mode 100644 index 0000000000..1e0797e1ac --- /dev/null +++ b/OpenSim/Tests/Robust/Server/DemonServer.cs @@ -0,0 +1,69 @@ +/* + * 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.Threading; + +using Nini.Config; +using log4net; +using NUnit.Framework; + +using OpenSim.Server; + +namespace Robust.Tests +{ + [SetUpFixture] + public class DemonServer : OpenSimServer + { + private Thread m_demon; + + public static string Address = "http://localhost:8888"; + + [SetUp] + public void StartDemon() + { + if (File.Exists("Robust.Tests.log")) + File.Delete("Robust.Tests.log"); + + Console.WriteLine("**** Starting demon Robust server ****"); + m_demon = new Thread( () => Main(new string[] {"-inifile=Robust.Tests.ini"})); + m_demon.Start(); + // Give some time for the server to instantiate all services + Thread.Sleep(3000); + Console.WriteLine("**** Setup Finished ****"); + } + + [TearDown] + public void StopDemon() + { + Console.WriteLine("**** Killing demon Robust Server ****"); + m_Server.Shutdown(); + } + } +} diff --git a/OpenSim/Tests/Stress/VectorRenderModuleStressTests.cs b/OpenSim/Tests/Stress/VectorRenderModuleStressTests.cs new file mode 100644 index 0000000000..0ab407e224 --- /dev/null +++ b/OpenSim/Tests/Stress/VectorRenderModuleStressTests.cs @@ -0,0 +1,131 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Threading; +using log4net.Config; +using NUnit.Framework; +using OpenMetaverse; +using OpenMetaverse.Assets; +using OpenSim.Framework; +using OpenSim.Region.CoreModules.Scripting.DynamicTexture; +using OpenSim.Region.CoreModules.Scripting.VectorRender; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Serialization; +using OpenSim.Tests.Common; + +namespace OpenSim.Tests.Stress +{ + [TestFixture] + public class VectorRenderModuleStressTests : OpenSimTestCase + { + public Scene Scene { get; private set; } + public DynamicTextureModule Dtm { get; private set; } + public VectorRenderModule Vrm { get; private set; } + + private void SetupScene(bool reuseTextures) + { + Scene = new SceneHelpers().SetupScene(); + + Dtm = new DynamicTextureModule(); + Dtm.ReuseTextures = reuseTextures; + + Vrm = new VectorRenderModule(); + + SceneHelpers.SetupSceneModules(Scene, Dtm, Vrm); + } + + [Test] + public void TestConcurrentRepeatedDraw() + { + int threads = 4; + TestHelpers.InMethod(); + + SetupScene(false); + + List drawers = new List(); + + for (int i = 0; i < threads; i++) + { + Drawer d = new Drawer(this, i); + drawers.Add(d); + Console.WriteLine("Starting drawer {0}", i); + Util.FireAndForget(o => d.Draw(), null, "VectorRenderModuleStressTests.TestConcurrentRepeatedDraw"); + } + + Thread.Sleep(10 * 60 * 1000); + + drawers.ForEach(d => d.Ready = false); + drawers.ForEach(d => Console.WriteLine("Drawer {0} drew {1} textures", d.Number, d.Pass + 1)); + } + + class Drawer + { + public int Number { get; private set; } + public int Pass { get; private set; } + public bool Ready { get; set; } + + private VectorRenderModuleStressTests m_tests; + + public Drawer(VectorRenderModuleStressTests tests, int number) + { + m_tests = tests; + Number = number; + Ready = true; + } + + public void Draw() + { + SceneObjectGroup so = SceneHelpers.AddSceneObject(m_tests.Scene); + + while (Ready) + { + UUID originalTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID; + + // Ensure unique text + string text = string.Format("{0:D2}{1}", Number, Pass); + + m_tests.Dtm.AddDynamicTextureData( + m_tests.Scene.RegionInfo.RegionID, + so.UUID, + m_tests.Vrm.GetContentType(), + string.Format("PenColour BLACK; MoveTo 40,220; FontSize 32; Text {0};", text), + "", + 0); + + Assert.That(originalTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID)); + + Pass++; + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/Compiler/Program.cs b/OpenSim/Tools/Compiler/Program.cs new file mode 100644 index 0000000000..b010eaf7fe --- /dev/null +++ b/OpenSim/Tools/Compiler/Program.cs @@ -0,0 +1,325 @@ +/* + * 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.Text; +using Microsoft.CSharp; +using OpenSim.Region.ScriptEngine.Shared.CodeTools; +using System.CodeDom.Compiler; + +namespace OpenSim.Tools.LSL.Compiler +{ + class Program + { +// Commented out because generated warning since m_positionMap could never be anything other than null +// private static Dictionary, KeyValuePair> m_positionMap; + private static CSharpCodeProvider CScodeProvider = new CSharpCodeProvider(); + + static void Main(string[] args) + { + string source = null; + + if (args.Length == 0) + { + Console.WriteLine("No input file specified"); + Environment.Exit(1); + } + + if (!File.Exists(args[0])) + { + Console.WriteLine("Input file does not exist"); + Environment.Exit(1); + } + + try + { + ICodeConverter cvt = (ICodeConverter) new CSCodeGenerator(); + source = cvt.Convert(File.ReadAllText(args[0])); + } + catch(Exception e) + { + Console.WriteLine("Conversion failed:\n"+e.Message); + Environment.Exit(1); + } + + source = CreateCSCompilerScript(source); + + try + { + Console.WriteLine(CompileFromDotNetText(source,"a.out")); + } + catch(Exception e) + { + Console.WriteLine("Conversion failed: "+e.Message); + Environment.Exit(1); + } + + Environment.Exit(0); + } + + private static string CreateCSCompilerScript(string compileScript) + { + compileScript = String.Empty + + "using OpenSim.Region.ScriptEngine.Shared; using System.Collections.Generic;\r\n" + + String.Empty + "namespace SecondLife { " + + String.Empty + "public class Script : OpenSim.Region.ScriptEngine.Shared.ScriptBase.ScriptBaseClass { \r\n" + + @"public Script() { } " + + compileScript + + "} }\r\n"; + return compileScript; + } + + private static string CompileFromDotNetText(string Script, string asset) + { + + string OutFile = asset; + string disp ="OK"; + + try + { + File.Delete(OutFile); + } + catch (Exception e) // NOTLEGIT - Should be just FileIOException + { + throw new Exception("Unable to delete old existing "+ + "script-file before writing new. Compile aborted: " + + e.ToString()); + } + + // Do actual compile + CompilerParameters parameters = new CompilerParameters(); + + parameters.IncludeDebugInformation = true; + + string rootPath = + Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); + + parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, + "OpenSim.Region.ScriptEngine.Shared.dll")); + parameters.ReferencedAssemblies.Add(Path.Combine(rootPath, + "OpenSim.Region.ScriptEngine.Shared.Api.Runtime.dll")); + + parameters.GenerateExecutable = false; + parameters.OutputAssembly = OutFile; + parameters.IncludeDebugInformation = true; + parameters.WarningLevel = 1; + parameters.TreatWarningsAsErrors = false; + + CompilerResults results = CScodeProvider.CompileAssemblyFromSource(parameters, Script); + + if (results.Errors.Count > 0) + { + string errtext = String.Empty; + foreach (CompilerError CompErr in results.Errors) + { + string severity = CompErr.IsWarning ? "Warning" : "Error"; + + KeyValuePair lslPos; + + lslPos = FindErrorPosition(CompErr.Line, CompErr.Column); + + string text = CompErr.ErrorText; + + text = ReplaceTypes(CompErr.ErrorText); + + // The Second Life viewer's script editor begins + // countingn lines and columns at 0, so we subtract 1. + errtext += String.Format("Line ({0},{1}): {4} {2}: {3}\n", + lslPos.Key - 1, lslPos.Value - 1, + CompErr.ErrorNumber, text, severity); + } + + disp = "Completed with errors"; + + if (!File.Exists(OutFile)) + { + throw new Exception(errtext); + } + } + + if (!File.Exists(OutFile)) + { + string errtext = String.Empty; + errtext += "No compile error. But not able to locate compiled file."; + throw new Exception(errtext); + } + + // Because windows likes to perform exclusive locks, we simply + // write out a textual representation of the file here + // + // Read the binary file into a buffer + // + FileInfo fi = new FileInfo(OutFile); + + if (fi == null) + { + string errtext = String.Empty; + errtext += "No compile error. But not able to stat file."; + throw new Exception(errtext); + } + + Byte[] data = new Byte[fi.Length]; + + try + { + FileStream fs = File.Open(OutFile, FileMode.Open, FileAccess.Read); + fs.Read(data, 0, data.Length); + fs.Close(); + } + catch (Exception) + { + string errtext = String.Empty; + errtext += "No compile error. But not able to open file."; + throw new Exception(errtext); + } + + // Convert to base64 + // + string filetext = System.Convert.ToBase64String(data); + Byte[] buf = Encoding.ASCII.GetBytes(filetext); + FileStream sfs = File.Create(OutFile + ".text"); + sfs.Write(buf, 0, buf.Length); + sfs.Close(); + + string posmap = String.Empty; +// if (m_positionMap != null) +// { +// foreach (KeyValuePair, KeyValuePair> kvp in m_positionMap) +// { +// KeyValuePair k = kvp.Key; +// KeyValuePair v = kvp.Value; +// posmap += String.Format("{0},{1},{2},{3}\n", +// k.Key, k.Value, v.Key, v.Value); +// } +// } + + buf = Encoding.ASCII.GetBytes(posmap); + + FileStream mfs = File.Create(OutFile + ".map"); + mfs.Write(buf, 0, buf.Length); + mfs.Close(); + + return disp; + } + + private static string ReplaceTypes(string message) + { + message = message.Replace( + "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString", + "string"); + + message = message.Replace( + "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger", + "integer"); + + message = message.Replace( + "OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat", + "float"); + + message = message.Replace( + "OpenSim.Region.ScriptEngine.Shared.LSL_Types.list", + "list"); + + return message; + } + + private static KeyValuePair FindErrorPosition(int line, int col) + { + //return FindErrorPosition(line, col, m_positionMap); + return FindErrorPosition(line, col, null); + } + + private class kvpSorter : IComparer, KeyValuePair>> + { + public int Compare(KeyValuePair, KeyValuePair> a, + KeyValuePair, KeyValuePair> b) + { + int kc = a.Key.Key.CompareTo(b.Key.Key); + return (kc != 0) ? kc : a.Key.Value.CompareTo(b.Key.Value); + } + } + + public static KeyValuePair FindErrorPosition(int line, + int col, Dictionary, + KeyValuePair> positionMap) + { + if (positionMap == null || positionMap.Count == 0) + return new KeyValuePair(line, col); + + KeyValuePair ret = new KeyValuePair(); + + if (positionMap.TryGetValue(new KeyValuePair(line, col), + out ret)) + return ret; + + var sorted = new List, KeyValuePair>>(positionMap); + + sorted.Sort(new kvpSorter()); + + int l = 1; + int c = 1; + int pl = 1; + + foreach (KeyValuePair, KeyValuePair> posmap in sorted) + { + //m_log.DebugFormat("[Compiler]: Scanning line map {0},{1} --> {2},{3}", posmap.Key.Key, posmap.Key.Value, posmap.Value.Key, posmap.Value.Value); + int nl = posmap.Value.Key + line - posmap.Key.Key; // New, translated LSL line and column. + int nc = posmap.Value.Value + col - posmap.Key.Value; + // Keep going until we find the first point passed line,col. + if (posmap.Key.Key > line) + { + //m_log.DebugFormat("[Compiler]: Line is larger than requested {0},{1}, returning {2},{3}", line, col, l, c); + if (pl < line) + { + //m_log.DebugFormat("[Compiler]: Previous line ({0}) is less than requested line ({1}), setting column to 1.", pl, line); + c = 1; + } + break; + } + if (posmap.Key.Key == line && posmap.Key.Value > col) + { + // Never move l,c backwards. + if (nl > l || (nl == l && nc > c)) + { + //m_log.DebugFormat("[Compiler]: Using offset relative to this: {0} + {1} - {2}, {3} + {4} - {5} = {6}, {7}", + // posmap.Value.Key, line, posmap.Key.Key, posmap.Value.Value, col, posmap.Key.Value, nl, nc); + l = nl; + c = nc; + } + //m_log.DebugFormat("[Compiler]: Column is larger than requested {0},{1}, returning {2},{3}", line, col, l, c); + break; + } + pl = posmap.Key.Key; + l = posmap.Value.Key; + c = posmap.Value.Value; + } + return new KeyValuePair(l, c); + } + } +} diff --git a/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs b/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b97c9773c3 --- /dev/null +++ b/OpenSim/Tools/Compiler/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Tools.lslc")] +[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("002864e7-b2a2-41d2-add8-82f653663160")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Tools/Configger/ConfigurationLoader.cs b/OpenSim/Tools/Configger/ConfigurationLoader.cs new file mode 100644 index 0000000000..f1d3649258 --- /dev/null +++ b/OpenSim/Tools/Configger/ConfigurationLoader.cs @@ -0,0 +1,254 @@ +/* + * 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.Threading; +using System.Xml; +using log4net; +using Nini.Config; + +namespace OpenSim.Tools.Configger +{ + /// + /// Loads the Configuration files into nIni + /// + public class ConfigurationLoader + { + /// + /// A source of Configuration data + /// + protected IConfigSource m_config; + + /// + /// Console logger + /// + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + public ConfigurationLoader() + { + } + + /// + /// Loads the region configuration + /// + /// Parameters passed into the process when started + /// + /// + /// A configuration that gets passed to modules + public IConfigSource LoadConfigSettings(IConfig startupConfig) + { + bool iniFileExists = false; + + List sources = new List(); + + string iniFileName = startupConfig.GetString("inifile", Path.Combine(".", "OpenSim.ini")); + + if (IsUri(iniFileName)) + { + if (!sources.Contains(iniFileName)) + sources.Add(iniFileName); + } + else + { + if (File.Exists(iniFileName)) + { + if (!sources.Contains(iniFileName)) + sources.Add(iniFileName); + } + } + + m_config = new IniConfigSource(); + m_config.Merge(DefaultConfig()); + + m_log.Info("[CONFIG] Reading configuration settings"); + + if (sources.Count == 0) + { + m_log.FatalFormat("[CONFIG] Could not load any configuration"); + m_log.FatalFormat("[CONFIG] Did you copy the OpenSim.ini.example file to OpenSim.ini?"); + Environment.Exit(1); + } + + for (int i = 0 ; i < sources.Count ; i++) + { + if (ReadConfig(sources[i])) + iniFileExists = true; + AddIncludes(sources); + } + + if (!iniFileExists) + { + m_log.FatalFormat("[CONFIG] Could not load any configuration"); + m_log.FatalFormat("[CONFIG] Configuration exists, but there was an error loading it!"); + Environment.Exit(1); + } + + return m_config; + } + + /// + /// Adds the included files as ini configuration files + /// + /// List of URL strings or filename strings + private void AddIncludes(List sources) + { + //loop over config sources + foreach (IConfig config in m_config.Configs) + { + // Look for Include-* in the key name + string[] keys = config.GetKeys(); + foreach (string k in keys) + { + if (k.StartsWith("Include-")) + { + // read the config file to be included. + string file = config.GetString(k); + if (IsUri(file)) + { + if (!sources.Contains(file)) + sources.Add(file); + } + else + { + string basepath = Path.GetFullPath("."); + // Resolve relative paths with wildcards + string chunkWithoutWildcards = file; + string chunkWithWildcards = string.Empty; + int wildcardIndex = file.IndexOfAny(new char[] { '*', '?' }); + if (wildcardIndex != -1) + { + chunkWithoutWildcards = file.Substring(0, wildcardIndex); + chunkWithWildcards = file.Substring(wildcardIndex); + } + string path = Path.Combine(basepath, chunkWithoutWildcards); + path = Path.GetFullPath(path) + chunkWithWildcards; + string[] paths = Util.Glob(path); + foreach (string p in paths) + { + if (!sources.Contains(p)) + sources.Add(p); + } + } + } + } + } + } + /// + /// Check if we can convert the string to a URI + /// + /// String uri to the remote resource + /// true if we can convert the string to a Uri object + bool IsUri(string file) + { + Uri configUri; + + return Uri.TryCreate(file, UriKind.Absolute, + out configUri) && configUri.Scheme == Uri.UriSchemeHttp; + } + + /// + /// Provide same ini loader functionality for standard ini and master ini - file system or XML over http + /// + /// Full path to the ini + /// + private bool ReadConfig(string iniPath) + { + bool success = false; + + if (!IsUri(iniPath)) + { + m_log.InfoFormat("[CONFIG] Reading configuration file {0}", + Path.GetFullPath(iniPath)); + + m_config.Merge(new IniConfigSource(iniPath)); + success = true; + } + else + { + m_log.InfoFormat("[CONFIG] {0} is a http:// URI, fetching ...", + iniPath); + + // The ini file path is a http URI + // Try to read it + // + try + { + XmlReader r = XmlReader.Create(iniPath); + XmlConfigSource cs = new XmlConfigSource(r); + m_config.Merge(cs); + + success = true; + } + catch (Exception e) + { + m_log.FatalFormat("[CONFIG] Exception reading config from URI {0}\n" + e.ToString(), iniPath); + Environment.Exit(1); + } + } + return success; + } + + /// + /// Setup a default config values in case they aren't present in the ini file + /// + /// A Configuration source containing the default configuration + private static IConfigSource DefaultConfig() + { + IConfigSource defaultConfig = new IniConfigSource(); + + { + IConfig config = defaultConfig.Configs["Startup"]; + + if (null == config) + config = defaultConfig.AddConfig("Startup"); + + config.Set("region_info_source", "filesystem"); + config.Set("allow_regionless", false); + + config.Set("gridmode", false); + config.Set("physics", "OpenDynamicsEngine"); + config.Set("meshing", "Meshmerizer"); + config.Set("physical_prim", true); + config.Set("serverside_object_permissions", true); + config.Set("storage_prim_inventories", true); + config.Set("startup_console_commands_file", String.Empty); + config.Set("shutdown_console_commands_file", String.Empty); + config.Set("DefaultScriptEngine", "XEngine"); + config.Set("clientstack_plugin", "OpenSim.Region.ClientStack.LindenUDP.dll"); + // life doesn't really work without this + config.Set("EventQueue", true); + } + + return defaultConfig; + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/Configger/Main.cs b/OpenSim/Tools/Configger/Main.cs new file mode 100644 index 0000000000..d7d918b29c --- /dev/null +++ b/OpenSim/Tools/Configger/Main.cs @@ -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 Nini.Config; +using System; + +namespace OpenSim.Tools.Configger +{ + public class Configger + { + public static int Main(string[] args) + { + ArgvConfigSource argvConfig = new ArgvConfigSource(args); + + argvConfig.AddSwitch("Startup", "format", "f"); + argvConfig.AddSwitch("Startup", "inifile"); + + IConfig startupConfig = argvConfig.Configs["Startup"]; + + string format = startupConfig.GetString("format", "ini"); + + ConfigurationLoader loader = new ConfigurationLoader(); + IConfigSource s = loader.LoadConfigSettings(startupConfig); + + if (format == "mysql") + { + foreach (IConfig c in s.Configs) + { + foreach (string k in c.GetKeys()) + { + string v = c.GetString(k); + + if (k.StartsWith("Include-")) + continue; + Console.WriteLine("insert ignore into config (section, name, value) values ('{0}', '{1}', '{2}');", c.Name, k, v); + } + } + } + else if (format == "xml") + { + Console.WriteLine(""); + + foreach (IConfig c in s.Configs) + { + int count = 0; + + foreach (string k in c.GetKeys()) + { + if (k.StartsWith("Include-")) + continue; + + count++; + } + + if (count > 0) + { + Console.WriteLine("
", c.Name); + + foreach (string k in c.GetKeys()) + { + string v = c.GetString(k); + + if (k.StartsWith("Include-")) + continue; + Console.WriteLine(" ", k, v); + } + + Console.WriteLine("
"); + } + } + Console.WriteLine("
"); + } + else if (format == "ini") + { + foreach (IConfig c in s.Configs) + { + int count = 0; + + foreach (string k in c.GetKeys()) + { + if (k.StartsWith("Include-")) + continue; + + count++; + } + + if (count > 0) + { + Console.WriteLine("[{0}]", c.Name); + + foreach (string k in c.GetKeys()) + { + string v = c.GetString(k); + + if (k.StartsWith("Include-")) + continue; + Console.WriteLine("{0} = \"{1}\"", k, v); + } + + Console.WriteLine(); + } + } + } + else + { + Console.WriteLine("Error: unknown format: {0}", format); + } + + return 0; + } + } +} diff --git a/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs b/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e88f0f59fc --- /dev/null +++ b/OpenSim/Tools/Configger/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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.Tools.Configger")] +[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("67d7fdf2-554c-40f0-8f9d-f71373c20926")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Tools/Configger/Util.cs b/OpenSim/Tools/Configger/Util.cs new file mode 100644 index 0000000000..fe7744d0b6 --- /dev/null +++ b/OpenSim/Tools/Configger/Util.cs @@ -0,0 +1,106 @@ +/* + * 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.Globalization; +using System.IO; +using System.IO.Compression; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using log4net; +using Nini.Config; + +namespace OpenSim.Tools.Configger +{ + public static class Util + { + public static string[] Glob(string path) + { + string vol=String.Empty; + + if (Path.VolumeSeparatorChar != Path.DirectorySeparatorChar) + { + string[] vcomps = path.Split(new char[] {Path.VolumeSeparatorChar}, 2, StringSplitOptions.RemoveEmptyEntries); + + if (vcomps.Length > 1) + { + path = vcomps[1]; + vol = vcomps[0]; + } + } + + string[] comps = path.Split(new char[] {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar}, StringSplitOptions.RemoveEmptyEntries); + + // Glob + + path = vol; + if (vol != String.Empty) + path += new String(new char[] {Path.VolumeSeparatorChar, Path.DirectorySeparatorChar}); + else + path = new String(new char[] {Path.DirectorySeparatorChar}); + + List paths = new List(); + List found = new List(); + paths.Add(path); + + int compIndex = -1; + foreach (string c in comps) + { + compIndex++; + + List addpaths = new List(); + foreach (string p in paths) + { + string[] dirs = Directory.GetDirectories(p, c); + + if (dirs.Length != 0) + { + foreach (string dir in dirs) + addpaths.Add(Path.Combine(path, dir)); + } + + // Only add files if that is the last path component + if (compIndex == comps.Length - 1) + { + string[] files = Directory.GetFiles(p, c); + foreach (string f in files) + found.Add(f); + } + } + paths = addpaths; + } + + return found.ToArray(); + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/Info.plist b/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/Info.plist new file mode 100644 index 0000000000..71393507c5 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/Info.plist @@ -0,0 +1,12 @@ + + + + + CFBundleIdentifier + LaunchSLClient + CFBundleExecutable + LaunchSLClient + CFBundleIconFile + + + diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/MacOS/LaunchSLClient b/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/MacOS/LaunchSLClient new file mode 100755 index 0000000000..22acaded74 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient.app.skel/Contents/MacOS/LaunchSLClient @@ -0,0 +1,13 @@ +#!/bin/sh + +PWD=`pwd` + +# Fetch the path relative to the launch point where this shell script exists. +APP_PATH=`echo $0 | awk '{split($0,patharr,"/"); idx=1; while(patharr[idx+3] != "") { if (patharr[idx] != "/") {printf("%s/", patharr[idx]); idx++ }} }'` + +# Fetch the app name (its our own name) +APP_NAME=`echo $0 | awk '{split($0,patharr,"/"); idx=1; while(patharr[idx+1] != "") {idx++} printf("%s", patharr[idx]); }'` + +cd "$APP_PATH/Contents/Resources" + +./$APP_NAME diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.Designer.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.Designer.cs new file mode 100644 index 0000000000..8d07724b19 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.Designer.cs @@ -0,0 +1,95 @@ +/* + * 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 LaunchSLClient +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.comboBox1 = new System.Windows.Forms.ComboBox(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.SuspendLayout(); + + this.comboBox1.FormattingEnabled = true; + this.comboBox1.Location = new System.Drawing.Point(37, 83); + this.comboBox1.Name = "comboBox1"; + this.comboBox1.Size = new System.Drawing.Size(348, 21); + this.comboBox1.TabIndex = 0; + this.comboBox1.Text = "Choose from list"; + this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged); + + this.textBox1.BorderStyle = System.Windows.Forms.BorderStyle.None; + this.textBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.textBox1.Location = new System.Drawing.Point(37, 32); + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(292, 19); + this.textBox1.TabIndex = 1; + this.textBox1.Text = "Grid to connect to:"; + + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(501, 339); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.comboBox1); + this.Name = "Form1"; + this.Text = "OpenSim Client Launcher"; + this.ResumeLayout(false); + this.PerformLayout(); + } + + #endregion + + private System.Windows.Forms.ComboBox comboBox1; + private System.Windows.Forms.TextBox textBox1; + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.cs new file mode 100644 index 0000000000..2a5d2a6aad --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.cs @@ -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.IO; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Nini.Config; + +namespace LaunchSLClient +{ + public partial class Form1 : Form + { + string gridUrl = ""; + string sandboxUrl = ""; + string runUrl = ""; + string runLine = ""; + string exeFlags = ""; + string exePath = ""; + + private MachineConfig m_machineConfig; + private List m_grids = new List(); + + public Form1() + { + InitializeComponent(); + + m_machineConfig = getMachineConfig(); + m_machineConfig.GetClient(ref exePath, ref runLine, ref exeFlags); + + initializeGrids(); + + ArrayList menuItems = new ArrayList(); + menuItems.Add(string.Empty); + + addLocalSims(ref menuItems); + + foreach (Grid grid in m_grids) + { + menuItems.Add(grid.Name + " - " + grid.URL); + } + + comboBox1.DataSource = menuItems; + } + + private MachineConfig getMachineConfig() + { + if (Environment.OSVersion.Platform == PlatformID.Unix) + { + if (File.Exists("/System/Library/Frameworks/Cocoa.framework/Cocoa")) + { + return new OSXConfig(); + } + else + { + return new UnixConfig(); + } + } + else + { + return new WindowsConfig(); + } + } + + private void initializeGrids() + { + string iniFile = "LaunchSLClient.ini"; + + if (!File.Exists(iniFile)) + return; + + IniConfigSource configSource = new IniConfigSource(iniFile); + + foreach (IConfig config in configSource.Configs) + { + Grid grid = new Grid(); + + grid.Name = config.Name; + grid.LoginURI = config.GetString("loginURI", ""); + grid.URL = config.GetString("URL", ""); + + m_grids.Add(grid); + } + } + + private void addLocalSandbox(ref ArrayList menuItems) + { + // build sandbox URL from Regions/default.xml + // this is highly dependant on a standard default.xml + if (File.Exists("Regions/default.xml")) + { + string sandboxHostName = ""; + string sandboxPort = ""; + string text; + + Regex myRegex = new Regex(".*internal_ip_port=\\\"(?.*?)\\\".*external_host_name=\\\"(?.*?)\\\".*"); + + FileInfo defaultFile = new FileInfo("Regions/default.xml"); + StreamReader stream = defaultFile.OpenText(); + do + { + text = stream.ReadLine(); + if (text == null) + { + break; + } + MatchCollection theMatches = myRegex.Matches(text); + foreach (Match theMatch in theMatches) + { + if (theMatch.Length != 0) + { + sandboxHostName = theMatch.Groups["name"].ToString(); + sandboxPort = theMatch.Groups["port"].ToString(); + } + } + } while (text != null); + + stream.Close(); + sandboxUrl = "http:\\" + sandboxHostName + ":" + sandboxPort; + menuItems.Add("Local Sandbox"); + } + } + + private void addLocalGrid(ref ArrayList menuItems) + { + //build local grid URL from network_servers_information.xml + if (File.Exists("network_servers_information.xml")) + { + string text; + FileInfo defaultFile = new FileInfo("network_servers_information.xml"); + Regex myRegex = new Regex(".*UserServerURL=\\\"(?.*?)\\\".*"); + StreamReader stream = defaultFile.OpenText(); + + do + { + text = stream.ReadLine(); + if (text == null) + { + break; + } + foreach (Match theMatch in myRegex.Matches(text)) + { + if (theMatch.Length != 0) + { + gridUrl = theMatch.Groups["url"].ToString(); + } + } + } while (text != null); + stream.Close(); + if (gridUrl != null) + { + menuItems.Add("Local Grid Server"); + } + } + } + + private void addLocalSims(ref ArrayList menuItems) + { + string configDir = m_machineConfig.GetConfigDir(); + + if (!string.IsNullOrEmpty(configDir)) + { + Directory.SetCurrentDirectory(configDir); + + addLocalSandbox(ref menuItems); + addLocalGrid(ref menuItems); + } + } + + private void comboBox1_SelectedIndexChanged(object sender, EventArgs e) + { + if (comboBox1.Text == string.Empty) + { + return; + } + else if (comboBox1.Text == "Local Sandbox") + { + runUrl=" -loginuri " + sandboxUrl; + } + else if (comboBox1.Text == "Local Grid Server") + { + runUrl = " -loginuri " + gridUrl; + } + else + { + foreach (Grid grid in m_grids) + { + if (comboBox1.Text == grid.Name + " - " + grid.URL) + { + if (grid.LoginURI != string.Empty) + runUrl = " -loginuri " + grid.LoginURI; + else + runUrl = ""; + + break; + } + } + } + + comboBox1.DroppedDown = false; + Hide(); + + System.Diagnostics.Process proc = new System.Diagnostics.Process(); + proc.StartInfo.FileName = runLine; + proc.StartInfo.Arguments = exeFlags + " " + runUrl; + proc.StartInfo.UseShellExecute = false; + proc.StartInfo.RedirectStandardOutput = false; + proc.StartInfo.WorkingDirectory = exePath; + proc.Start(); + proc.WaitForExit(); + + Application.Exit(); + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.resx b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.resx new file mode 100644 index 0000000000..19dc0dd8b3 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Form1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Grid.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Grid.cs new file mode 100644 index 0000000000..58ebbf0471 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Grid.cs @@ -0,0 +1,38 @@ +/* + * 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; + +namespace LaunchSLClient +{ + public struct Grid + { + public string Name; + public string LoginURI; + public string URL; + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/LauncherException.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/LauncherException.cs new file mode 100644 index 0000000000..a28eb20bbe --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/LauncherException.cs @@ -0,0 +1,52 @@ +/* + * 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.Text; + +namespace LaunchSLClient +{ + class LauncherException : ApplicationException + { + private const string CUSTOMMESSAGE = "The SL Client Launcher has failed with the following error: "; + + private LauncherException() { } + + public LauncherException(string errorMesssage, string source) + : base (CUSTOMMESSAGE + errorMesssage) + { + base.Source = source; + } + + public LauncherException(string errorMessage, string source, Exception innerException) + : base(CUSTOMMESSAGE + errorMessage, innerException) + { + base.Source = source; + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/MachineConfig.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/MachineConfig.cs new file mode 100644 index 0000000000..6020347683 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/MachineConfig.cs @@ -0,0 +1,38 @@ +/* + * 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; + +namespace LaunchSLClient +{ + public abstract class MachineConfig + { + public abstract void GetClient(ref string exePath, ref string runLine, ref string exeFlags); + public abstract string GetConfigDir(); + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/OSXConfig.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/OSXConfig.cs new file mode 100644 index 0000000000..19a2e54aac --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/OSXConfig.cs @@ -0,0 +1,55 @@ +/* + * 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; + +namespace LaunchSLClient +{ + public class OSXConfig : UnixConfig + { + public override void GetClient(ref string exePath, ref string runLine, ref string exeFlags) + { + if (Directory.Exists("/Applications/Second Life.app")) + { + exePath = "/Applications/Second Life.app/Contents/MacOS"; + } + else if (Directory.Exists("/Applications/Second Life Release Candidate.app")) + { + exePath = "/Applications/Second Life Release Candidate.app/Contents/MacOS"; + } + + runLine = Path.Combine(exePath, "Second Life"); + exeFlags = string.Empty; + } + + public override string GetConfigDir() + { + return string.Empty; + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Program.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Program.cs new file mode 100644 index 0000000000..bfd43cf8ff --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Program.cs @@ -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; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace LaunchSLClient +{ + static class Program + { + [STAThread] + static void Main() + { + try + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new Form1()); + } + catch (Exception ex) + { + MessageBox.Show(ex.ToString(), "Error"); + } + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/AssemblyInfo.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..f6a0c64e5d --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/AssemblyInfo.cs @@ -0,0 +1,60 @@ +/* + * 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.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("LaunchSLClient")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("LaunchSLClient")] +[assembly: AssemblyCopyright("Copyright (c) 2007")] +[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("b08c6904-e6cc-4d9c-8d24-feb0464b1648")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.6.3.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.Designer.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..6b9a8a169b --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.Designer.cs @@ -0,0 +1,95 @@ +/* + * 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. + */ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.832 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LaunchSLClient.Properties +{ + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("LaunchSLClient.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.resx b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.resx new file mode 100644 index 0000000000..af7dbebbac --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.Designer.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.Designer.cs new file mode 100644 index 0000000000..5ae7be2bb5 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.Designer.cs @@ -0,0 +1,54 @@ +/* + * 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. + */ + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.832 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace LaunchSLClient.Properties +{ + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.settings b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.settings new file mode 100644 index 0000000000..39645652af --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/UnixConfig.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/UnixConfig.cs new file mode 100644 index 0000000000..5895dad01f --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/UnixConfig.cs @@ -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. + */ + +using System; +using System.IO; + +namespace LaunchSLClient +{ + public class UnixConfig : MachineConfig + { + public override void GetClient(ref string exePath, ref string runLine, ref string exeFlags) + { + exePath = string.Empty; + exeFlags = string.Empty; + + foreach (string path in Environment.GetEnvironmentVariable("PATH").Split(':')) + { + if (File.Exists(Path.Combine(path, "secondlife"))) + { + exePath = path; + break; + } + } + + runLine = Path.Combine(exePath, "secondlife"); + } + + public override string GetConfigDir() + { + return string.Empty; + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/LaunchSLClient/WindowsConfig.cs b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/WindowsConfig.cs new file mode 100644 index 0000000000..ac0186d27e --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/LaunchSLClient/WindowsConfig.cs @@ -0,0 +1,69 @@ +/* + * 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 Microsoft.Win32; + +namespace LaunchSLClient +{ + public class WindowsConfig : MachineConfig + { + public override void GetClient(ref string exePath, ref string runLine, ref string exeFlags) + { + RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Linden Research, Inc.\SecondLife"); + if (regKey == null) + { + regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Wow6432Node\Linden Research, Inc.\SecondLife"); + if (regKey == null) + { + throw new LauncherException("Can't find Second Life. Are you sure it is installed?", "LauncherException.Form1"); + } + } + string exe = regKey.GetValue("Exe").ToString(); + exeFlags = regKey.GetValue("Flags").ToString(); + exePath = regKey.GetValue("").ToString(); + runLine = Path.Combine(exePath, exe); + Registry.LocalMachine.Flush(); + Registry.LocalMachine.Close(); + } + + public override string GetConfigDir() + { + RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\OpenSim\OpenSim"); + + if (key == null) + { + return string.Empty; + } + else + { + return key.GetValue("Path").ToString(); + } + } + } +} diff --git a/OpenSim/Tools/LaunchSLClient/make-OSX-app.sh b/OpenSim/Tools/LaunchSLClient/make-OSX-app.sh new file mode 100755 index 0000000000..aa37cb6fbe --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/make-OSX-app.sh @@ -0,0 +1,40 @@ +#!/bin/sh + +# This script will build LaunchSLClient.app from the .exe, .dll's, and +# other necessary files. +# +# This should be run from the bin directory. + +APP_NAME="LaunchSLClient" +SOURCE_PATH="../OpenSim/Tools/${APP_NAME}" + +ASSEMBLIES="mscorlib.dll \ + System.Windows.Forms.dll \ + System.Drawing.dll \ + System.Configuration.dll \ + System.Xml.dll \ + System.Security.dll \ + Mono.Security.dll \ + System.Data.dll \ + Mono.Data.Tds.dll \ + System.Transactions.dll \ + System.EnterpriseServices.dll \ + Mono.Mozilla.dll \ + Mono.Posix.dll \ + Accessibility.dll" + +if [ ! -e ${APP_NAME}.exe ]; then + echo "Error: Could not find ${APP_NAME}.exe." >& 2 + echo "Have you built it, and are you currently in the bin directory?" >& 2 + exit 1 +fi + +mkbundle2 -z -o ${APP_NAME} ${APP_NAME}.exe ${ASSEMBLIES} || exit 1 + +if [ -d ${APP_NAME}.app ]; then rm -rf ${APP_NAME}.app; fi +cp -r ${SOURCE_PATH}/${APP_NAME}.app.skel ${APP_NAME}.app + +# mkbundle doesn't seem to recognize the -L option, so we can't include Nini.dll in the bundling +cp Nini.dll ${APP_NAME}.app/Contents/Resources + +cp ${APP_NAME} ${APP_NAME}.ini ${APP_NAME}.app/Contents/Resources diff --git a/OpenSim/Tools/LaunchSLClient/prebuild.xml b/OpenSim/Tools/LaunchSLClient/prebuild.xml new file mode 100644 index 0000000000..4a13365c3e --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/prebuild.xml @@ -0,0 +1,67 @@ + + + + + + TRACE;DEBUG + false + false + false + 4 + false + + ../../../bin + true + true + false + + + + + TRACE + true + false + false + 4 + false + + ../../../bin + false + true + false + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + diff --git a/OpenSim/Tools/LaunchSLClient/runprebuild.bat b/OpenSim/Tools/LaunchSLClient/runprebuild.bat new file mode 100755 index 0000000000..41e2714a50 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/runprebuild.bat @@ -0,0 +1,4 @@ +..\..\..\bin\Prebuild.exe /target nant +..\..\..\bin\Prebuild.exe /target vs2008 +echo C:\WINDOWS\Microsoft.NET\Framework\v3.5\msbuild LaunchSLClient.sln > compile.bat + diff --git a/OpenSim/Tools/LaunchSLClient/runprebuild.sh b/OpenSim/Tools/LaunchSLClient/runprebuild.sh new file mode 100755 index 0000000000..25ea4cd284 --- /dev/null +++ b/OpenSim/Tools/LaunchSLClient/runprebuild.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +mono ../../../bin/Prebuild.exe /target nant +mono ../../../bin/Prebuild.exe /target monodev +mono ../../../bin/Prebuild.exe /target vs2008 diff --git a/OpenSim/Tools/classaudit.pl b/OpenSim/Tools/classaudit.pl new file mode 100755 index 0000000000..d5d28c763c --- /dev/null +++ b/OpenSim/Tools/classaudit.pl @@ -0,0 +1,133 @@ +#!/usr/bin/perl +# +# Audit tool for OpenSim class and namespace definitions. +# +# Copyright 2007 IBM +# +# Authors: Sean Dague +# +# 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 OpenSim 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. + +use strict; +use File::Find; +use Data::Dumper; +use constant YELLOW => "\033[33m"; +use constant RED => "\033[31m"; +use constant CLEAR => "\033[0m"; +our %totals; + + +find(\&test, "../../OpenSim"); +print Dumper(\%totals); + +sub test { + my $file = $File::Find::name; + my $dir = $File::Find::dir; + $file =~ s{^../../}{}; #strip off prefix + $dir =~ s{^../../}{}; #strip off prefix + + return if ($file !~ /\.cs$/); + return if ($file =~ /AssemblyInfo\.cs$/); + + print "Processing File: $file\n"; + + my $namespace = find_namespace($_); + my $class = find_class($_); + + + + if(cmp_namespace($namespace, $dir) == 1) { + $totals{goodns}++; + } else { + $totals{badns}++; + } + + + if(cmp_class($namespace, $class, $file) == 1) { + $totals{goodclass}++; + } else { + $totals{badclass}++; + } + print "\n"; +} + +sub find_class { + my $file = shift; + my $content = slurp($file); + if ($content =~ /\n\s*(public|private|protected)?\s*(class|interface)\s+(\S+)/) { + return $3; + } + return ""; +} + +sub find_namespace { + my $file = shift; + my $content = slurp($file); + + if ($content =~ /\bnamespace\s+(\S+)/s) { + return $1; + } + return ""; +} + +sub slurp { + my $file = shift; + local(*IN); + local $/ = undef; + + open(IN, "$file") or die "Can't open '$file': $!"; + my $content = ; + close(IN); + + return $content; +} + +sub cmp_class { + my ($ns, $class, $file) = @_; + $class = "$ns.$class"; + my $classtrans = $class; + $classtrans =~ s{\.}{/}g; + $classtrans .= ".cs"; + + if($classtrans ne $file) { + error(YELLOW, "CLASS: $class != $file"); + return -1; + } + return 1; +} + +sub cmp_namespace { + my ($ns, $dir) = @_; + my $nstrans = $ns; + $nstrans =~ s{\.}{/}g; + + if($nstrans ne $dir) { + error(RED, "NS: $ns != $dir"); + return -1; + } + return 1; +} + +sub error { + print @_, CLEAR, "\n"; +} diff --git a/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs new file mode 100644 index 0000000000..c1ba36b94a --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/AbstractBehaviour.cs @@ -0,0 +1,64 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using pCampBot.Interfaces; + +namespace pCampBot +{ + public abstract class AbstractBehaviour : IBehaviour + { + /// + /// Abbreviated name of this behaviour. + /// + public string AbbreviatedName { get; protected set; } + + public string Name { get; protected set; } + + public Bot Bot { get; protected set; } + + public abstract void Action(); + + public virtual void Interrupt() {} + + protected AutoResetEvent m_interruptEvent = new AutoResetEvent(false); + + public virtual void Initialize(Bot bot) + { + Bot = bot; + } + + public virtual void Close() + { + Interrupt(); + } + } +} diff --git a/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs new file mode 100644 index 0000000000..4d806fcc67 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/CrossBehaviour.cs @@ -0,0 +1,173 @@ +/* + * 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 log4net; +using OpenMetaverse; +using OpenSim.Framework; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Get the bot to make a region crossing. + /// + public class CrossBehaviour : AbstractBehaviour + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public AutoResetEvent m_regionCrossedMutex = new AutoResetEvent(false); + + public const int m_regionCrossingTimeout = 1000 * 60; + + public CrossBehaviour() + { + AbbreviatedName = "c"; + Name = "Cross"; + } + + public override void Action() + { + GridClient client = Bot.Client; + +// // Fly to make the border cross easier. +// client.Self.Movement.Fly = true; +// client.Self.Movement.Fly = false; + + // Seek out neighbouring region + Simulator currentSim = client.Network.CurrentSim; + ulong currentHandle = currentSim.Handle; + uint currentX, currentY; + Utils.LongToUInts(currentHandle, out currentX, out currentY); + + List candidateRegions = new List(); + TryAddRegion(Utils.UIntsToLong(Math.Max(0, currentX - Constants.RegionSize), currentY), candidateRegions); // West + TryAddRegion(Utils.UIntsToLong(currentX + Constants.RegionSize, currentY), candidateRegions); // East + TryAddRegion(Utils.UIntsToLong(currentX, Math.Max(0, currentY - Constants.RegionSize)), candidateRegions); // South + TryAddRegion(Utils.UIntsToLong(currentX, currentY + Constants.RegionSize), candidateRegions); // North + + if (candidateRegions.Count != 0) + { + GridRegion destRegion = candidateRegions[Bot.Manager.Rng.Next(candidateRegions.Count)]; + + uint targetX, targetY; + Utils.LongToUInts(destRegion.RegionHandle, out targetX, out targetY); + + Vector3 pos = client.Self.SimPosition; + if (targetX < currentX) + pos.X = -1; + else if (targetX > currentX) + pos.X = Constants.RegionSize + 1; + + if (targetY < currentY) + pos.Y = -1; + else if (targetY > currentY) + pos.Y = Constants.RegionSize + 1; + + m_log.DebugFormat( + "[CROSS BEHAVIOUR]: {0} moving to cross from {1} into {2}, target {3}", + Bot.Name, currentSim.Name, destRegion.Name, pos); + + // Face in the direction of the candidate region + client.Self.Movement.TurnToward(pos); + + // Listen for event so that we know when we've crossed the region boundary + Bot.Client.Self.RegionCrossed += Self_RegionCrossed; + + // Start moving + Bot.Client.Self.Movement.AtPos = true; + + // Stop when reach region target or border cross detected + if (!m_regionCrossedMutex.WaitOne(m_regionCrossingTimeout)) + { + m_log.ErrorFormat( + "[CROSS BEHAVIOUR]: {0} failed to cross from {1} into {2} with {3}ms", + Bot.Name, currentSim.Name, destRegion.Name, m_regionCrossingTimeout); + } + else + { + m_log.DebugFormat( + "[CROSS BEHAVIOUR]: {0} crossed from {1} into {2}", + Bot.Name, currentSim.Name, destRegion.Name); + } + + Bot.Client.Self.RegionCrossed -= Self_RegionCrossed; + + // We will hackishly carry on travelling into the region for a little bit. + Thread.Sleep(6000); + + m_log.DebugFormat( + "[CROSS BEHAVIOUR]: {0} stopped moving after cross from {1} into {2}", + Bot.Name, currentSim.Name, destRegion.Name); + + Bot.Client.Self.Movement.AtPos = false; + } + else + { + m_log.DebugFormat( + "[CROSS BEHAVIOUR]: No candidate region for {0} to cross into from {1}. Ignoring.", + Bot.Name, currentSim.Name); + } + } + + private bool TryAddRegion(ulong handle, List regions) + { + Dictionary knownRegions = Bot.Manager.RegionsKnown; + + lock (knownRegions) + { + if (knownRegions.Count == 0) + return false; + + m_log.DebugFormat("[CROSS BEHAVIOUR]: Looking for region with handle {0} in known regions", handle); + + if (knownRegions.ContainsKey(handle)) + { + GridRegion region = knownRegions[handle]; + m_log.DebugFormat( + "[CROSS BEHAVIOUR]: Adding region {0} to crossing candidates for {1}", region.Name, Bot.Name); + + regions.Add(region); + + return true; + } + else + { + return false; + } + } + } + + internal void Self_RegionCrossed(object o, RegionCrossedEventArgs args) + { + m_regionCrossedMutex.Set(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs new file mode 100644 index 0000000000..59f6244052 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs @@ -0,0 +1,68 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Click (grab) on random objects in the scene. + /// + /// + /// The viewer itself does not give the option of grabbing objects that haven't been signalled as grabbable. + /// + public class GrabbingBehaviour : AbstractBehaviour + { + public GrabbingBehaviour() + { + AbbreviatedName = "g"; + Name = "Grabbing"; + } + + public override void Action() + { + Dictionary objects = Bot.Objects; + + if (objects.Count <= 0) + return; + + Primitive prim = objects.ElementAt(Bot.Random.Next(0, objects.Count - 1)).Value; + + // This appears to be a typical message sent when a viewer user clicks a clickable object + Bot.Client.Self.Grab(prim.LocalID); + Bot.Client.Self.GrabUpdate(prim.ID, Vector3.Zero); + Bot.Client.Self.DeGrab(prim.LocalID); + + Thread.Sleep(1000); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs new file mode 100644 index 0000000000..521415cca9 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/InventoryDownloadBehaviour.cs @@ -0,0 +1,121 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Linq; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Do nothing + /// + public class InventoryDownloadBehaviour : AbstractBehaviour + { + private bool m_initialized; + private int m_Requests = 2; + private Stopwatch m_StopWatch = new Stopwatch(); + private List m_processed = new List(); + + public InventoryDownloadBehaviour() + { + AbbreviatedName = "inv"; + Name = "Inventory"; + } + + public override void Action() + { + if (!m_initialized) + { + m_initialized = true; + Bot.Client.Settings.HTTP_INVENTORY = true; + Bot.Client.Settings.FETCH_MISSING_INVENTORY = true; + Bot.Client.Inventory.FolderUpdated += Inventory_FolderUpdated; + Console.WriteLine("Lib owner is " + Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID); + m_StopWatch.Start(); + Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.RootFolder.UUID, Bot.Client.Self.AgentID, true, true, InventorySortOrder.ByDate); + Bot.Client.Inventory.RequestFolderContents(Bot.Client.Inventory.Store.LibraryRootNode.Data.UUID, Bot.Client.Inventory.Store.LibraryRootNode.Data.OwnerID, true, true, InventorySortOrder.ByDate); + } + + Thread.Sleep(1000); + Console.WriteLine("Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed); + + } + + void Inventory_FolderUpdated(object sender, FolderUpdatedEventArgs e) + { + if (e.Success) + { + //Console.WriteLine("Folder " + e.FolderID + " updated"); + bool fetch = false; + lock (m_processed) + { + if (!m_processed.Contains(e.FolderID)) + { + m_processed.Add(e.FolderID); + fetch = true; + } + } + + if (fetch) + { + List m_foldersToFetch = new List(); + foreach (InventoryBase item in Bot.Client.Inventory.Store.GetContents(e.FolderID)) + { + if (item is InventoryFolder) + { + InventoryFolder f = new InventoryFolder(item.UUID); + f.OwnerID = item.OwnerID; + m_foldersToFetch.Add(f); + } + } + if (m_foldersToFetch.Count > 0) + { + m_Requests += 1; + Bot.Client.Inventory.RequestFolderContentsCap(m_foldersToFetch, Bot.Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventoryDescendents2"), true, true, InventorySortOrder.ByDate); + } + } + + if (Bot.Client.Inventory.Store.Items.Count >= 15739) + { + m_StopWatch.Stop(); + Console.WriteLine("Stop! Total items: " + Bot.Client.Inventory.Store.Items.Count + "; Total requests: " + m_Requests + "; Time: " + m_StopWatch.Elapsed); + } + } + + } + + public override void Interrupt() + { + m_interruptEvent.Set(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs new file mode 100644 index 0000000000..0d4378194f --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/NoneBehaviour.cs @@ -0,0 +1,60 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Linq; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Do nothing + /// + public class NoneBehaviour : AbstractBehaviour + { + public NoneBehaviour() + { + AbbreviatedName = "n"; + Name = "None"; + } + + public override void Action() + { + Bot.Client.Self.Jump(false); + Bot.Client.Self.Movement.Stop = true; + m_interruptEvent.WaitOne(); + Bot.Client.Self.Movement.Stop = false; + } + + public override void Interrupt() + { + m_interruptEvent.Set(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs new file mode 100644 index 0000000000..98ab931441 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour.cs @@ -0,0 +1,104 @@ +/* + * 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.Threading; +using OpenMetaverse; +using OpenSim.Framework; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Stress physics by moving and bouncing around bots a whole lot. + /// + /// + /// TODO: talkarray should be in a separate behaviour. + /// + public class PhysicsBehaviour : AbstractBehaviour + { + private string[] talkarray; + + public PhysicsBehaviour() + { + AbbreviatedName = "p"; + Name = "Physics"; + talkarray = readexcuses(); + } + + public override void Action() + { + int walkorrun = Bot.Random.Next(4); // Randomize between walking and running. The greater this number, + // the greater the bot's chances to walk instead of run. + Bot.Client.Self.Jump(false); + if (walkorrun == 0) + { + Bot.Client.Self.Movement.AlwaysRun = true; + } + else + { + Bot.Client.Self.Movement.AlwaysRun = false; + } + + // TODO: unused: Vector3 pos = client.Self.SimPosition; + Vector3 newpos = new Vector3(Bot.Random.Next(1, 254), Bot.Random.Next(1, 254), Bot.Random.Next(1, 254)); + Bot.Client.Self.Movement.TurnToward(newpos); + + Bot.Client.Self.Movement.AtPos = true; + Thread.Sleep(Bot.Random.Next(3000, 13000)); + Bot.Client.Self.Movement.AtPos = false; + Bot.Client.Self.Jump(true); + string randomf = talkarray[Bot.Random.Next(talkarray.Length)]; + if (talkarray.Length > 1 && randomf.Length > 1) + Bot.Client.Self.Chat(randomf, 0, ChatType.Normal); + } + + public override void Close() + { + if (Bot.ConnectionState == ConnectionState.Connected) + Bot.Client.Self.Jump(false); + + base.Close(); + } + + private string[] readexcuses() + { + string allexcuses = ""; + + string file = Path.Combine(Util.configDir(), "pCampBotSentences.txt"); + if (File.Exists(file)) + { + StreamReader csr = File.OpenText(file); + allexcuses = csr.ReadToEnd(); + csr.Close(); + } + + return allexcuses.Split(Environment.NewLine.ToCharArray()); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs new file mode 100644 index 0000000000..1ec2046e1e --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/PhysicsBehaviour2.cs @@ -0,0 +1,86 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// This behavior is for the systematic study of some performance improvements made + /// for OSCC'13. + /// Walk around, sending AgentUpdate packets all the time. + /// + public class PhysicsBehaviour2 : AbstractBehaviour + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public PhysicsBehaviour2() + { + AbbreviatedName = "ph2"; + Name = "Physics2"; + } + + private const int TIME_WALKING = 5 * 10; // 5 seconds + private int counter = 0; + + public override void Action() + { + + if (counter >= TIME_WALKING) + { + counter = 0; + + Vector3 target = new Vector3(Bot.Random.Next(1, 254), Bot.Random.Next(1, 254), Bot.Client.Self.SimPosition.Z); + MyTurnToward(target); + + Bot.Client.Self.Movement.AtPos = true; + + } + else + counter++; + // In any case, send an update + Bot.Client.Self.Movement.SendUpdate(); + } + + private void MyTurnToward(Vector3 target) + { + Quaternion between = Vector3.RotationBetween(Vector3.UnitX, Vector3.Normalize(target - Bot.Client.Self.SimPosition)); + Quaternion rot = between ; + + Bot.Client.Self.Movement.BodyRotation = rot; + Bot.Client.Self.Movement.HeadRotation = rot; + Bot.Client.Self.Movement.Camera.LookAt(Bot.Client.Self.SimPosition, target); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs new file mode 100644 index 0000000000..81f250d338 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/TeleportBehaviour.cs @@ -0,0 +1,82 @@ +/* + * 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 log4net; +using OpenMetaverse; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// Teleport to a random region on the grid. + /// + public class TeleportBehaviour : AbstractBehaviour + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public TeleportBehaviour() + { + AbbreviatedName = "t"; + Name = "Teleport"; + } + + public override void Action() + { + Random rng = Bot.Manager.Rng; + GridRegion[] knownRegions; + + lock (Bot.Manager.RegionsKnown) + { + if (Bot.Manager.RegionsKnown.Count == 0) + { + m_log.DebugFormat( + "[TELEPORT BEHAVIOUR]: Ignoring teleport action for {0} since no regions are known yet", Bot.Name); + return; + } + + knownRegions = Bot.Manager.RegionsKnown.Values.ToArray(); + } + + Simulator sourceRegion = Bot.Client.Network.CurrentSim; + GridRegion destRegion = knownRegions[rng.Next(knownRegions.Length)]; + Vector3 destPosition = new Vector3(rng.Next(255), rng.Next(255), 50); + + m_log.DebugFormat( + "[TELEPORT BEHAVIOUR]: Teleporting {0} from {1} {2} to {3} {4}", + Bot.Name, sourceRegion.Name, Bot.Client.Self.SimPosition, destRegion.Name, destPosition); + + Bot.Client.Self.Teleport(destRegion.RegionHandle, destPosition); + + Thread.Sleep(Bot.Random.Next(3000, 10000)); + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs new file mode 100644 index 0000000000..7b4639d72a --- /dev/null +++ b/OpenSim/Tools/pCampBot/Behaviours/TwitchyBehaviour.cs @@ -0,0 +1,73 @@ +/* + * 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; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using log4net; +using pCampBot.Interfaces; + +namespace pCampBot +{ + /// + /// This behavior is for the systematic study of some performance improvements made + /// for OSCC'13. + /// Do nothing, but send AgentUpdate packets all the time that have only slightly + /// different state. The delta of difference will be filtered by OpenSim early on + /// in the packet processing pipeline. These filters did not exist before OSCC'13. + /// + public class TwitchyBehaviour : AbstractBehaviour + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public TwitchyBehaviour() + { + AbbreviatedName = "tw"; + Name = "Twitchy"; + } + + private const float TWITCH = 0.0001f; + private int direction = 1; + + public override void Action() + { + Bot.Client.Self.Movement.BodyRotation = new Quaternion(Bot.Client.Self.Movement.BodyRotation.X + direction * TWITCH, + Bot.Client.Self.Movement.BodyRotation.Y, + Bot.Client.Self.Movement.BodyRotation.Z, + Bot.Client.Self.Movement.BodyRotation.W); + + //m_log.DebugFormat("[TWITCH]: BodyRot {0}", Bot.Client.Self.Movement.BodyRotation); + direction = -direction; + + Bot.Client.Self.Movement.SendUpdate(); + + } + + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Bot.cs b/OpenSim/Tools/pCampBot/Bot.cs new file mode 100644 index 0000000000..4f287339fd --- /dev/null +++ b/OpenSim/Tools/pCampBot/Bot.cs @@ -0,0 +1,748 @@ +/* + * 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.Text; +using System.IO; +using System.Reflection; +using System.Threading; +using System.Timers; +using log4net; +using OpenMetaverse; +using OpenMetaverse.Assets; +using OpenMetaverse.Packets; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using pCampBot.Interfaces; +using Timer = System.Timers.Timer; +using PermissionMask = OpenSim.Framework.PermissionMask; + +namespace pCampBot +{ + public enum ConnectionState + { + Disconnected, + Connecting, + Connected, + Disconnecting + } + + public class Bot + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public int PacketDebugLevel + { + get { return m_packetDebugLevel; } + set + { + if (value == m_packetDebugLevel) + return; + + m_packetDebugLevel = value; + + if (Client != null) + { + if (m_packetDebugLevel <= 0) + Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler); + else + Client.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler, false); + } + } + } + private int m_packetDebugLevel; + + public delegate void AnEvent(Bot callbot, EventType someevent); // event delegate for bot events + + /// + /// Controls whether bots request textures for the object information they receive + /// + public bool RequestObjectTextures { get; set; } + + /// + /// Bot manager. + /// + public BotManager Manager { get; private set; } + + /// + /// Behaviours implemented by this bot. + /// + /// + /// Indexed by abbreviated name. There can only be one instance of a particular behaviour. + /// Lock this structure before manipulating it. + /// + public Dictionary Behaviours { get; private set; } + + /// + /// Objects that the bot has discovered. + /// + /// + /// Returns a list copy. Inserting new objects manually will have no effect. + /// + public Dictionary Objects + { + get + { + lock (m_objects) + return new Dictionary(m_objects); + } + } + private Dictionary m_objects = new Dictionary(); + + /// + /// Is this bot connected to the grid? + /// + public ConnectionState ConnectionState { get; private set; } + + public List Simulators + { + get + { + lock (Client.Network.Simulators) + return new List(Client.Network.Simulators); + } + } + + /// + /// The number of connections that this bot has to different simulators. + /// + /// Includes both root and child connections. + public int SimulatorsCount + { + get + { + lock (Client.Network.Simulators) + return Client.Network.Simulators.Count; + } + } + + public string FirstName { get; private set; } + public string LastName { get; private set; } + public string Name { get; private set; } + public string Password { get; private set; } + public string LoginUri { get; private set; } + public string StartLocation { get; private set; } + + public string saveDir; + public string wear; + + public event AnEvent OnConnected; + public event AnEvent OnDisconnected; + + /// + /// Keep a track of the continuously acting thread so that we can abort it. + /// + private Thread m_actionThread; + + protected List objectIDs = new List(); + + /// + /// Random number generator. + /// + public Random Random { get; private set; } + + /// + /// New instance of a SecondLife client + /// + public GridClient Client { get; private set; } + + /// + /// Constructor + /// + /// + /// Behaviours for this bot to perform + /// + /// + /// + /// + /// + public Bot( + BotManager bm, List behaviours, + string firstName, string lastName, string password, string startLocation, string loginUri) + { + ConnectionState = ConnectionState.Disconnected; + + Random = new Random(bm.Rng.Next()); + FirstName = firstName; + LastName = lastName; + Name = string.Format("{0} {1}", FirstName, LastName); + Password = password; + LoginUri = loginUri; + StartLocation = startLocation; + + Manager = bm; + + Behaviours = new Dictionary(); + foreach (IBehaviour behaviour in behaviours) + AddBehaviour(behaviour); + + // Only calling for use as a template. + CreateLibOmvClient(); + } + + public bool TryGetBehaviour(string abbreviatedName, out IBehaviour behaviour) + { + lock (Behaviours) + return Behaviours.TryGetValue(abbreviatedName, out behaviour); + } + + public bool AddBehaviour(IBehaviour behaviour) + { + Dictionary updatedBehaviours = new Dictionary(Behaviours); + + if (!updatedBehaviours.ContainsKey(behaviour.AbbreviatedName)) + { + behaviour.Initialize(this); + updatedBehaviours.Add(behaviour.AbbreviatedName, behaviour); + Behaviours = updatedBehaviours; + + return true; + } + + return false; + } + + public bool RemoveBehaviour(string abbreviatedName) + { + if (Behaviours.Count <= 0) + return false; + + Dictionary updatedBehaviours = new Dictionary(Behaviours); + IBehaviour behaviour; + + if (!updatedBehaviours.TryGetValue(abbreviatedName, out behaviour)) + return false; + + updatedBehaviours.Remove(abbreviatedName); + Behaviours = updatedBehaviours; + + behaviour.Close(); + + return true; + } + + private void CreateLibOmvClient() + { + GridClient newClient = new GridClient(); + + if (Client != null) + { + // Remove any registered debug handlers + Client.Network.UnregisterCallback(PacketType.Default, PacketReceivedDebugHandler); + + newClient.Settings.LOGIN_SERVER = Client.Settings.LOGIN_SERVER; + newClient.Settings.ALWAYS_DECODE_OBJECTS = Client.Settings.ALWAYS_DECODE_OBJECTS; + newClient.Settings.AVATAR_TRACKING = Client.Settings.AVATAR_TRACKING; + newClient.Settings.OBJECT_TRACKING = Client.Settings.OBJECT_TRACKING; + newClient.Settings.SEND_AGENT_THROTTLE = Client.Settings.SEND_AGENT_THROTTLE; + newClient.Settings.SEND_AGENT_UPDATES = Client.Settings.SEND_AGENT_UPDATES; + newClient.Settings.SEND_PINGS = Client.Settings.SEND_PINGS; + newClient.Settings.STORE_LAND_PATCHES = Client.Settings.STORE_LAND_PATCHES; + newClient.Settings.USE_ASSET_CACHE = Client.Settings.USE_ASSET_CACHE; + newClient.Settings.MULTIPLE_SIMS = Client.Settings.MULTIPLE_SIMS; + newClient.Throttle.Asset = Client.Throttle.Asset; + newClient.Throttle.Land = Client.Throttle.Land; + newClient.Throttle.Task = Client.Throttle.Task; + newClient.Throttle.Texture = Client.Throttle.Texture; + newClient.Throttle.Wind = Client.Throttle.Wind; + newClient.Throttle.Total = Client.Throttle.Total; + } + else + { + newClient.Settings.LOGIN_SERVER = LoginUri; + newClient.Settings.ALWAYS_DECODE_OBJECTS = false; + newClient.Settings.AVATAR_TRACKING = false; + newClient.Settings.OBJECT_TRACKING = false; + newClient.Settings.SEND_AGENT_THROTTLE = true; + newClient.Settings.SEND_PINGS = true; + newClient.Settings.STORE_LAND_PATCHES = false; + newClient.Settings.USE_ASSET_CACHE = false; + newClient.Settings.MULTIPLE_SIMS = true; + newClient.Throttle.Asset = 100000; + newClient.Throttle.Land = 100000; + newClient.Throttle.Task = 100000; + newClient.Throttle.Texture = 100000; + newClient.Throttle.Wind = 100000; + newClient.Throttle.Total = 400000; + } + + newClient.Network.LoginProgress += Network_LoginProgress; + newClient.Network.SimConnected += Network_SimConnected; + newClient.Network.SimDisconnected += Network_SimDisconnected; + newClient.Network.Disconnected += Network_OnDisconnected; + newClient.Objects.ObjectUpdate += Objects_NewPrim; + + if (m_packetDebugLevel > 0) + newClient.Network.RegisterCallback(PacketType.Default, PacketReceivedDebugHandler); + + Client = newClient; + } + + //We do our actions here. This is where one would + //add additional steps and/or things the bot should do + private void Action() + { + while (ConnectionState == ConnectionState.Connected) + { + foreach (IBehaviour behaviour in Behaviours.Values) + { +// Thread.Sleep(Random.Next(3000, 10000)); + + // m_log.DebugFormat("[pCAMPBOT]: For {0} performing action {1}", Name, b.GetType()); + behaviour.Action(); + } + } + + foreach (IBehaviour b in Behaviours.Values) + b.Close(); + } + + /// + /// Tells LibSecondLife to logout and disconnect. Raises the disconnect events once it finishes. + /// + public void Disconnect() + { + ConnectionState = ConnectionState.Disconnecting; + + foreach (IBehaviour behaviour in Behaviours.Values) + behaviour.Close(); + + Client.Network.Logout(); + } + + public void Connect() + { + Thread connectThread = new Thread(ConnectInternal); + connectThread.Name = Name; + connectThread.IsBackground = true; + + connectThread.Start(); + } + + /// + /// This is the bot startup loop. + /// + private void ConnectInternal() + { + ConnectionState = ConnectionState.Connecting; + + // Current create a new client on each connect. libomv doesn't seem to process new sim + // information (e.g. EstablishAgentCommunication events) if connecting after a disceonnect with the same + // client + CreateLibOmvClient(); + + if (Client.Network.Login(FirstName, LastName, Password, "pCampBot", StartLocation, "pCampBot")) + { + ConnectionState = ConnectionState.Connected; + + Thread.Sleep(Random.Next(1000, 10000)); + m_actionThread = new Thread(Action); + m_actionThread.Start(); + +// OnConnected(this, EventType.CONNECTED); + if (wear == "save") + { + SaveDefaultAppearance(); + } + else if (wear != "no") + { + MakeDefaultAppearance(wear); + } + + // Extract nearby region information. + Client.Grid.GridRegion += Manager.Grid_GridRegion; + uint xUint, yUint; + Utils.LongToUInts(Client.Network.CurrentSim.Handle, out xUint, out yUint); + ushort minX, minY, maxX, maxY; + minX = (ushort)Math.Min(0, xUint - 5); + minY = (ushort)Math.Min(0, yUint - 5); + maxX = (ushort)(xUint + 5); + maxY = (ushort)(yUint + 5); + Client.Grid.RequestMapBlocks(GridLayerType.Terrain, minX, minY, maxX, maxY, false); + } + else + { + ConnectionState = ConnectionState.Disconnected; + + m_log.ErrorFormat( + "{0} {1} cannot login: {2}", FirstName, LastName, Client.Network.LoginMessage); + + if (OnDisconnected != null) + { + OnDisconnected(this, EventType.DISCONNECTED); + } + } + } + + /// + /// Sit this bot on the ground. + /// + public void SitOnGround() + { + if (ConnectionState == ConnectionState.Connected) + Client.Self.SitOnGround(); + } + + /// + /// Stand this bot + /// + public void Stand() + { + if (ConnectionState == ConnectionState.Connected) + { + // Unlike sit on ground, here libomv checks whether we have SEND_AGENT_UPDATES enabled. + bool prevUpdatesSetting = Client.Settings.SEND_AGENT_UPDATES; + Client.Settings.SEND_AGENT_UPDATES = true; + Client.Self.Stand(); + Client.Settings.SEND_AGENT_UPDATES = prevUpdatesSetting; + } + } + + public void SaveDefaultAppearance() + { + saveDir = "MyAppearance/" + FirstName + "_" + LastName; + if (!Directory.Exists(saveDir)) + { + Directory.CreateDirectory(saveDir); + } + + Array wtypes = Enum.GetValues(typeof(WearableType)); + foreach (WearableType wtype in wtypes) + { + UUID wearable = Client.Appearance.GetWearableAsset(wtype); + if (wearable != UUID.Zero) + { + Client.Assets.RequestAsset(wearable, AssetType.Clothing, false, Asset_ReceivedCallback); + Client.Assets.RequestAsset(wearable, AssetType.Bodypart, false, Asset_ReceivedCallback); + } + } + } + + public void SaveAsset(AssetWearable asset) + { + if (asset != null) + { + try + { + if (asset.Decode()) + { + File.WriteAllBytes(Path.Combine(saveDir, String.Format("{1}.{0}", + asset.AssetType.ToString().ToLower(), + asset.WearableType)), asset.AssetData); + } + else + { + m_log.WarnFormat("Failed to decode {0} asset {1}", asset.AssetType, asset.AssetID); + } + } + catch (Exception e) + { + m_log.ErrorFormat("Exception: {0}{1}", e.Message, e.StackTrace); + } + } + } + + public WearableType GetWearableType(string path) + { + string type = ((((path.Split('/'))[2]).Split('.'))[0]).Trim(); + switch (type) + { + case "Eyes": + return WearableType.Eyes; + case "Hair": + return WearableType.Hair; + case "Pants": + return WearableType.Pants; + case "Shape": + return WearableType.Shape; + case "Shirt": + return WearableType.Shirt; + case "Skin": + return WearableType.Skin; + default: + return WearableType.Shape; + } + } + + public void MakeDefaultAppearance(string wear) + { + try + { + if (wear == "yes") + { + //TODO: Implement random outfit picking + m_log.DebugFormat("Picks a random outfit. Not yet implemented."); + } + else if (wear != "save") + saveDir = "MyAppearance/" + wear; + saveDir = saveDir + "/"; + + string[] clothing = Directory.GetFiles(saveDir, "*.clothing", SearchOption.TopDirectoryOnly); + string[] bodyparts = Directory.GetFiles(saveDir, "*.bodypart", SearchOption.TopDirectoryOnly); + InventoryFolder clothfolder = FindClothingFolder(); + UUID transid = UUID.Random(); + List listwearables = new List(); + + for (int i = 0; i < clothing.Length; i++) + { + UUID assetID = UUID.Random(); + AssetClothing asset = new AssetClothing(assetID, File.ReadAllBytes(clothing[i])); + asset.Decode(); + asset.Owner = Client.Self.AgentID; + asset.WearableType = GetWearableType(clothing[i]); + asset.Encode(); + transid = Client.Assets.RequestUpload(asset,true); + Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyClothing" + i.ToString(), "MyClothing", AssetType.Clothing, + transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item) + { + if (success) + { + listwearables.Add(item); + } + else + { + m_log.WarnFormat("Failed to create item {0}", item.Name); + } + } + ); + } + + for (int i = 0; i < bodyparts.Length; i++) + { + UUID assetID = UUID.Random(); + AssetBodypart asset = new AssetBodypart(assetID, File.ReadAllBytes(bodyparts[i])); + asset.Decode(); + asset.Owner = Client.Self.AgentID; + asset.WearableType = GetWearableType(bodyparts[i]); + asset.Encode(); + transid = Client.Assets.RequestUpload(asset,true); + Client.Inventory.RequestCreateItem(clothfolder.UUID, "MyBodyPart" + i.ToString(), "MyBodyPart", AssetType.Bodypart, + transid, InventoryType.Wearable, asset.WearableType, (OpenMetaverse.PermissionMask)PermissionMask.All, delegate(bool success, InventoryItem item) + { + if (success) + { + listwearables.Add(item); + } + else + { + m_log.WarnFormat("Failed to create item {0}", item.Name); + } + } + ); + } + + Thread.Sleep(1000); + + if (listwearables == null || listwearables.Count == 0) + { + m_log.DebugFormat("Nothing to send on this folder!"); + } + else + { + m_log.DebugFormat("Sending {0} wearables...", listwearables.Count); + Client.Appearance.WearOutfit(listwearables, false); + } + } + catch (Exception ex) + { + Console.WriteLine(ex.ToString()); + } + } + + public InventoryFolder FindClothingFolder() + { + UUID rootfolder = Client.Inventory.Store.RootFolder.UUID; + List listfolders = Client.Inventory.Store.GetContents(rootfolder); + InventoryFolder clothfolder = new InventoryFolder(UUID.Random()); + foreach (InventoryBase folder in listfolders) + { + if (folder.Name == "Clothing") + { + clothfolder = (InventoryFolder)folder; + break; + } + } + return clothfolder; + } + + public void Network_LoginProgress(object sender, LoginProgressEventArgs args) + { + m_log.DebugFormat("[BOT]: Bot {0} {1} in Network_LoginProcess", Name, args.Status); + + if (args.Status == LoginStatus.Success) + { + if (OnConnected != null) + { + OnConnected(this, EventType.CONNECTED); + } + } + } + + public void Network_SimConnected(object sender, SimConnectedEventArgs args) + { + m_log.DebugFormat( + "[BOT]: Bot {0} connected to region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); + } + + public void Network_SimDisconnected(object sender, SimDisconnectedEventArgs args) + { + m_log.DebugFormat( + "[BOT]: Bot {0} disconnected from region {1} at {2}", Name, args.Simulator.Name, args.Simulator.IPEndPoint); + } + + public void Network_OnDisconnected(object sender, DisconnectedEventArgs args) + { + ConnectionState = ConnectionState.Disconnected; + + m_log.DebugFormat( + "[BOT]: Bot {0} disconnected from grid, reason {1}, message {2}", Name, args.Reason, args.Message); + +// m_log.ErrorFormat("Fired Network_OnDisconnected"); + +// if ( +// (args.Reason == NetworkManager.DisconnectType.SimShutdown +// || args.Reason == NetworkManager.DisconnectType.NetworkTimeout) +// && OnDisconnected != null) + + + + if ( + (args.Reason == NetworkManager.DisconnectType.ClientInitiated + || args.Reason == NetworkManager.DisconnectType.ServerInitiated + || args.Reason == NetworkManager.DisconnectType.NetworkTimeout) + && OnDisconnected != null) +// if (OnDisconnected != null) + { + OnDisconnected(this, EventType.DISCONNECTED); + } + } + + public void Objects_NewPrim(object sender, PrimEventArgs args) + { + if (!RequestObjectTextures) + return; + + Primitive prim = args.Prim; + + if (prim != null) + { + lock (m_objects) + m_objects[prim.ID] = prim; + + if (prim.Textures != null) + { + if (prim.Textures.DefaultTexture.TextureID != UUID.Zero) + { + GetTextureOrMesh(prim.Textures.DefaultTexture.TextureID, true); + } + + for (int i = 0; i < prim.Textures.FaceTextures.Length; i++) + { + Primitive.TextureEntryFace face = prim.Textures.FaceTextures[i]; + + if (face != null) + { + UUID textureID = prim.Textures.FaceTextures[i].TextureID; + + if (textureID != UUID.Zero) + GetTextureOrMesh(textureID, true); + } + } + } + + if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero) + { + bool mesh = (prim.Sculpt.Type == SculptType.Mesh); + GetTextureOrMesh(prim.Sculpt.SculptTexture, !mesh); + } + } + } + + private void GetTextureOrMesh(UUID assetID, bool texture) + { + lock (Manager.AssetsReceived) + { + // Don't request assets more than once. + if (Manager.AssetsReceived.ContainsKey(assetID)) + return; + + Manager.AssetsReceived[assetID] = false; + } + + try + { + if (texture) + Client.Assets.RequestImage(assetID, ImageType.Normal, Asset_TextureCallback_Texture); + else + Client.Assets.RequestMesh(assetID, Asset_MeshCallback); + } + catch (Exception e) + { + m_log.Warn(string.Format("Error requesting {0} {1}", texture ? "texture" : "mesh", assetID), e); + } + } + + public void Asset_TextureCallback_Texture(TextureRequestState state, AssetTexture assetTexture) + { + if (state == TextureRequestState.Finished) + { + lock (Manager.AssetsReceived) + Manager.AssetsReceived[assetTexture.AssetID] = true; + } + } + + private void Asset_MeshCallback(bool success, AssetMesh assetMesh) + { + lock (Manager.AssetsReceived) + Manager.AssetsReceived[assetMesh.AssetID] = success; + } + + public void Asset_ReceivedCallback(AssetDownload transfer, Asset asset) + { + lock (Manager.AssetsReceived) + Manager.AssetsReceived[asset.AssetID] = true; + +// if (wear == "save") +// { +// SaveAsset((AssetWearable) asset); +// } + } + + private void PacketReceivedDebugHandler(object o, PacketReceivedEventArgs args) + { + Packet p = args.Packet; + Header h = p.Header; + Simulator s = args.Simulator; + + m_log.DebugFormat( + "[BOT]: Bot {0} received from {1} packet {2} #{3}, rel {4}, res {5}", + Name, s.Name, p.Type, h.Sequence, h.Reliable, h.Resent); + } + } +} diff --git a/OpenSim/Tools/pCampBot/BotManager.cs b/OpenSim/Tools/pCampBot/BotManager.cs new file mode 100644 index 0000000000..0af95929f5 --- /dev/null +++ b/OpenSim/Tools/pCampBot/BotManager.cs @@ -0,0 +1,983 @@ +/* + * 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 OpenMetaverse; +using log4net; +using log4net.Appender; +using log4net.Core; +using log4net.Repository; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; +using pCampBot.Interfaces; + +namespace pCampBot +{ + public enum BotManagerBotConnectingState + { + Initializing, + Ready, + Connecting, + Disconnecting + } + + /// + /// Thread/Bot manager for the application + /// + public class BotManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const int DefaultLoginDelay = 5000; + + /// + /// Is pCampbot ready to connect or currently in the process of connecting or disconnecting bots? + /// + public BotManagerBotConnectingState BotConnectingState { get; private set; } + + /// + /// Used to control locking as we can't lock an enum. + /// + private object BotConnectingStateChangeObject = new object(); + + /// + /// Delay between logins of multiple bots. + /// + /// TODO: This value needs to be configurable by a command line argument. + public int LoginDelay { get; set; } + + /// + /// Command console + /// + protected CommandConsole m_console; + + /// + /// Controls whether bots start out sending agent updates on connection. + /// + public bool InitBotSendAgentUpdates { get; set; } + + /// + /// Controls whether bots request textures for the object information they receive + /// + public bool InitBotRequestObjectTextures { get; set; } + + /// + /// Created bots, whether active or inactive. + /// + protected List m_bots; + + /// + /// Random number generator. + /// + public Random Rng { get; private set; } + + /// + /// Track the assets we have and have not received so we don't endlessly repeat requests. + /// + public Dictionary AssetsReceived { get; private set; } + + /// + /// The regions that we know about. + /// + public Dictionary RegionsKnown { get; private set; } + + /// + /// First name for bots + /// + private string m_firstName; + + /// + /// Last name stem for bots + /// + private string m_lastNameStem; + + /// + /// Password for bots + /// + private string m_password; + + /// + /// Login URI for bots. + /// + private string m_loginUri; + + /// + /// Start location for bots. + /// + private string m_startUri; + + /// + /// Postfix bot number at which bot sequence starts. + /// + private int m_fromBotNumber; + + /// + /// Wear setting for bots. + /// + private string m_wearSetting; + + /// + /// Behaviour switches for bots. + /// + private HashSet m_defaultBehaviourSwitches = new HashSet(); + + /// + /// Collects general information on this server (which reveals this to be a misnamed class). + /// + private ServerStatsCollector m_serverStatsCollector; + + /// + /// Constructor Creates MainConsole.Instance to take commands and provide the place to write data + /// + public BotManager() + { + // We set this to avoid issues with bots running out of HTTP connections if many are run from a single machine + // to multiple regions. + Settings.MAX_HTTP_CONNECTIONS = int.MaxValue; + +// System.Threading.ThreadPool.SetMaxThreads(600, 240); +// +// int workerThreads, iocpThreads; +// System.Threading.ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); +// Console.WriteLine("ThreadPool.GetMaxThreads {0} {1}", workerThreads, iocpThreads); + + InitBotSendAgentUpdates = true; + InitBotRequestObjectTextures = true; + + LoginDelay = DefaultLoginDelay; + + Rng = new Random(Environment.TickCount); + AssetsReceived = new Dictionary(); + RegionsKnown = new Dictionary(); + + m_console = CreateConsole(); + MainConsole.Instance = m_console; + + // Make log4net see the console + // + ILoggerRepository repository = LogManager.GetRepository(); + IAppender[] appenders = repository.GetAppenders(); + OpenSimAppender consoleAppender = null; + + foreach (IAppender appender in appenders) + { + if (appender.Name == "Console") + { + consoleAppender = (OpenSimAppender)appender; + consoleAppender.Console = m_console; + break; + } + } + + m_console.Commands.AddCommand( + "Bots", false, "shutdown", "shutdown", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "quit", "quit", "Shutdown bots and exit", HandleShutdown); + + m_console.Commands.AddCommand( + "Bots", false, "connect", "connect []", "Connect bots", + "If an is given, then the first disconnected bots by postfix number are connected.\n" + + "If no is given, then all currently disconnected bots are connected.", + HandleConnect); + + m_console.Commands.AddCommand( + "Bots", false, "disconnect", "disconnect []", "Disconnect bots", + "Disconnecting bots will interupt any bot connection process, including connection on startup.\n" + + "If an is given, then the last connected bots by postfix number are disconnected.\n" + + "If no is given, then all currently connected bots are disconnected.", + HandleDisconnect); + + m_console.Commands.AddCommand( + "Bots", false, "add behaviour", "add behaviour []", + "Add a behaviour to a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleAddBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "remove behaviour", "remove behaviour []", + "Remove a behaviour from a bot", + "If no bot number is specified then behaviour is added to all bots.\n" + + "Can be performed on connected or disconnected bots.", + HandleRemoveBehaviour); + + m_console.Commands.AddCommand( + "Bots", false, "sit", "sit", "Sit all bots on the ground.", + HandleSit); + + m_console.Commands.AddCommand( + "Bots", false, "stand", "stand", "Stand all bots.", + HandleStand); + + m_console.Commands.AddCommand( + "Bots", false, "set bots", "set bots ", "Set a setting for all bots.", HandleSetBots); + + m_console.Commands.AddCommand( + "Bots", false, "show regions", "show regions", "Show regions known to bots", HandleShowRegions); + + m_console.Commands.AddCommand( + "Bots", false, "show bots", "show bots", "Shows the status of all bots.", HandleShowBotsStatus); + + m_console.Commands.AddCommand( + "Bots", false, "show bot", "show bot ", + "Shows the detailed status and settings of a particular bot.", HandleShowBotStatus); + + m_console.Commands.AddCommand( + "Debug", + false, + "debug lludp packet", + "debug lludp packet ", + "Turn on received packet logging.", + "If level > 0 then all received packets that are not duplicates are logged.\n" + + "If level <= 0 then no received packets are logged.", + HandleDebugLludpPacketCommand); + + m_console.Commands.AddCommand( + "Bots", false, "show status", "show status", "Shows pCampbot status.", HandleShowStatus); + + m_bots = new List(); + + Watchdog.Enabled = true; + StatsManager.RegisterConsoleCommands(m_console); + + m_serverStatsCollector = new ServerStatsCollector(); + m_serverStatsCollector.Initialise(null); + m_serverStatsCollector.Enabled = true; + m_serverStatsCollector.Start(); + + BotConnectingState = BotManagerBotConnectingState.Ready; + } + + /// + /// Startup number of bots specified in the starting arguments + /// + /// How many bots to start up + /// The configuration for the bots to use + public void CreateBots(int botcount, IConfig startupConfig) + { + m_firstName = startupConfig.GetString("firstname"); + m_lastNameStem = startupConfig.GetString("lastname"); + m_password = startupConfig.GetString("password"); + m_loginUri = startupConfig.GetString("loginuri"); + m_fromBotNumber = startupConfig.GetInt("from", 0); + m_wearSetting = startupConfig.GetString("wear", "no"); + + m_startUri = ParseInputStartLocationToUri(startupConfig.GetString("start", "last")); + + Array.ForEach( + startupConfig.GetString("behaviours", "p").Split(new char[] { ',' }), b => m_defaultBehaviourSwitches.Add(b)); + + for (int i = 0; i < botcount; i++) + { + lock (m_bots) + { + string lastName = string.Format("{0}_{1}", m_lastNameStem, i + m_fromBotNumber); + + CreateBot( + this, + CreateBehavioursFromAbbreviatedNames(m_defaultBehaviourSwitches), + m_firstName, lastName, m_password, m_loginUri, m_startUri, m_wearSetting); + } + } + } + + private List CreateBehavioursFromAbbreviatedNames(HashSet abbreviatedNames) + { + // We must give each bot its own list of instantiated behaviours since they store state. + List behaviours = new List(); + + // Hard-coded for now + foreach (string abName in abbreviatedNames) + { + IBehaviour newBehaviour = null; + + if (abName == "c") + newBehaviour = new CrossBehaviour(); + + if (abName == "g") + newBehaviour = new GrabbingBehaviour(); + + if (abName == "n") + newBehaviour = new NoneBehaviour(); + + if (abName == "p") + newBehaviour = new PhysicsBehaviour(); + + if (abName == "t") + newBehaviour = new TeleportBehaviour(); + + if (abName == "tw") + newBehaviour = new TwitchyBehaviour(); + + if (abName == "ph2") + newBehaviour = new PhysicsBehaviour2(); + + if (abName == "inv") + newBehaviour = new InventoryDownloadBehaviour(); + + if (newBehaviour != null) + { + behaviours.Add(newBehaviour); + } + else + { + MainConsole.Instance.OutputFormat("No behaviour with abbreviated name {0} found", abName); + } + } + + return behaviours; + } + + public void ConnectBots(int botcount) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Ready) + { + MainConsole.Instance.OutputFormat( + "Bot connecting status is {0}. Please wait for previous process to complete.", BotConnectingState); + return; + } + + BotConnectingState = BotManagerBotConnectingState.Connecting; + } + + Thread connectBotThread = new Thread(o => ConnectBotsInternal(botcount)); + + connectBotThread.Name = "Bots connection thread"; + connectBotThread.Start(); + } + + private void ConnectBotsInternal(int botCount) + { + m_log.InfoFormat( + "[BOT MANAGER]: Starting {0} bots connecting to {1}, location {2}, named {3} {4}_", + botCount, + m_loginUri, + m_startUri, + m_firstName, + m_lastNameStem); + + m_log.DebugFormat("[BOT MANAGER]: Delay between logins is {0}ms", LoginDelay); + m_log.DebugFormat("[BOT MANAGER]: BotsSendAgentUpdates is {0}", InitBotSendAgentUpdates); + m_log.DebugFormat("[BOT MANAGER]: InitBotRequestObjectTextures is {0}", InitBotRequestObjectTextures); + + List botsToConnect = new List(); + + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Disconnected) + botsToConnect.Add(bot); + + if (botsToConnect.Count >= botCount) + break; + } + } + + foreach (Bot bot in botsToConnect) + { + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState != BotManagerBotConnectingState.Connecting) + { + MainConsole.Instance.Output( + "[BOT MANAGER]: Aborting bot connection due to user-initiated disconnection"); + return; + } + } + + bot.Connect(); + + // Stagger logins + Thread.Sleep(LoginDelay); + } + + lock (BotConnectingStateChangeObject) + { + if (BotConnectingState == BotManagerBotConnectingState.Connecting) + BotConnectingState = BotManagerBotConnectingState.Ready; + } + } + + /// + /// Parses the command line start location to a start string/uri that the login mechanism will recognize. + /// + /// + /// The input start location to URI. + /// + /// + /// Start location. + /// + private string ParseInputStartLocationToUri(string startLocation) + { + if (startLocation == "home" || startLocation == "last") + return startLocation; + + string regionName; + + // Just a region name or only one (!) extra component. Like a viewer, we will stick 128/128/0 on the end + Vector3 startPos = new Vector3(128, 128, 0); + + string[] startLocationComponents = startLocation.Split('/'); + + regionName = startLocationComponents[0]; + + if (startLocationComponents.Length >= 2) + { + float.TryParse(startLocationComponents[1], out startPos.X); + + if (startLocationComponents.Length >= 3) + { + float.TryParse(startLocationComponents[2], out startPos.Y); + + if (startLocationComponents.Length >= 4) + float.TryParse(startLocationComponents[3], out startPos.Z); + } + } + + return string.Format("uri:{0}&{1}&{2}&{3}", regionName, startPos.X, startPos.Y, startPos.Z); + } + + /// + /// This creates a bot but does not start it. + /// + /// + /// Behaviours for this bot to perform. + /// First name + /// Last name + /// Password + /// Login URI + /// Location to start the bot. Can be "last", "home" or a specific sim name. + /// + public void CreateBot( + BotManager bm, List behaviours, + string firstName, string lastName, string password, string loginUri, string startLocation, string wearSetting) + { + MainConsole.Instance.OutputFormat( + "[BOT MANAGER]: Creating bot {0} {1}, behaviours are {2}", + firstName, lastName, string.Join(",", behaviours.ConvertAll(b => b.Name).ToArray())); + + Bot pb = new Bot(bm, behaviours, firstName, lastName, password, startLocation, loginUri); + pb.wear = wearSetting; + pb.Client.Settings.SEND_AGENT_UPDATES = InitBotSendAgentUpdates; + pb.RequestObjectTextures = InitBotRequestObjectTextures; + + pb.OnConnected += handlebotEvent; + pb.OnDisconnected += handlebotEvent; + + m_bots.Add(pb); + } + + /// + /// High level connnected/disconnected events so we can keep track of our threads by proxy + /// + /// + /// + private void handlebotEvent(Bot callbot, EventType eventt) + { + switch (eventt) + { + case EventType.CONNECTED: + { + m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Connected"); + break; + } + + case EventType.DISCONNECTED: + { + m_log.Info("[" + callbot.FirstName + " " + callbot.LastName + "]: Disconnected"); + break; + } + } + } + + /// + /// Standard CreateConsole routine + /// + /// + protected CommandConsole CreateConsole() + { + return new LocalConsole("pCampbot"); + } + + private void HandleConnect(string module, string[] cmd) + { + lock (m_bots) + { + int botsToConnect; + int disconnectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Disconnected); + + if (cmd.Length == 1) + { + botsToConnect = disconnectedBots; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToConnect)) + return; + + botsToConnect = Math.Min(botsToConnect, disconnectedBots); + } + + MainConsole.Instance.OutputFormat("Connecting {0} bots", botsToConnect); + + ConnectBots(botsToConnect); + } + } + + private void HandleAddBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: add behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + + HashSet rawAbbreviatedSwitchesToAdd = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => rawAbbreviatedSwitchesToAdd.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursAdded = new List(); + + foreach (IBehaviour behaviour in CreateBehavioursFromAbbreviatedNames(rawAbbreviatedSwitchesToAdd)) + { + if (bot.AddBehaviour(behaviour)) + behavioursAdded.Add(behaviour); + } + + MainConsole.Instance.OutputFormat( + "Added behaviours {0} to bot {1}", + string.Join(", ", behavioursAdded.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + + private void HandleRemoveBehaviour(string module, string[] cmd) + { + if (cmd.Length < 3 || cmd.Length > 4) + { + MainConsole.Instance.OutputFormat("Usage: remove behaviour []"); + return; + } + + string rawBehaviours = cmd[2]; + + List botsToEffect = new List(); + + if (cmd.Length == 3) + { + lock (m_bots) + botsToEffect.AddRange(m_bots); + } + else + { + int botNumber; + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[3], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + botsToEffect.Add(bot); + } + + HashSet abbreviatedBehavioursToRemove = new HashSet(); + Array.ForEach(rawBehaviours.Split(new char[] { ',' }), b => abbreviatedBehavioursToRemove.Add(b)); + + foreach (Bot bot in botsToEffect) + { + List behavioursRemoved = new List(); + + foreach (string b in abbreviatedBehavioursToRemove) + { + IBehaviour behaviour; + + if (bot.TryGetBehaviour(b, out behaviour)) + { + bot.RemoveBehaviour(b); + behavioursRemoved.Add(behaviour); + } + } + + MainConsole.Instance.OutputFormat( + "Removed behaviours {0} from bot {1}", + string.Join(", ", behavioursRemoved.ConvertAll(b => b.Name).ToArray()), bot.Name); + } + } + + private void HandleDisconnect(string module, string[] cmd) + { + List connectedBots; + int botsToDisconnectCount; + + lock (m_bots) + connectedBots = m_bots.FindAll(b => b.ConnectionState == ConnectionState.Connected); + + if (cmd.Length == 1) + { + botsToDisconnectCount = connectedBots.Count; + } + else + { + if (!ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, cmd[1], out botsToDisconnectCount)) + return; + + botsToDisconnectCount = Math.Min(botsToDisconnectCount, connectedBots.Count); + } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Disconnecting; + + Thread disconnectBotThread = new Thread(o => DisconnectBotsInternal(connectedBots, botsToDisconnectCount)); + + disconnectBotThread.Name = "Bots disconnection thread"; + disconnectBotThread.Start(); + } + + private void DisconnectBotsInternal(List connectedBots, int disconnectCount) + { + MainConsole.Instance.OutputFormat("Disconnecting {0} bots", disconnectCount); + + int disconnectedBots = 0; + + for (int i = connectedBots.Count - 1; i >= 0; i--) + { + if (disconnectedBots >= disconnectCount) + break; + + Bot thisBot = connectedBots[i]; + + if (thisBot.ConnectionState == ConnectionState.Connected) + { + ThreadPool.QueueUserWorkItem(o => thisBot.Disconnect()); + disconnectedBots++; + } + } + + lock (BotConnectingStateChangeObject) + BotConnectingState = BotManagerBotConnectingState.Ready; + } + + private void HandleSit(string module, string[] cmd) + { + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Sitting bot {0} on ground.", bot.Name); + bot.SitOnGround(); + } + } + } + } + + private void HandleStand(string module, string[] cmd) + { + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + if (bot.ConnectionState == ConnectionState.Connected) + { + MainConsole.Instance.OutputFormat("Standing bot {0} from ground.", bot.Name); + bot.Stand(); + } + } + } + } + + private void HandleShutdown(string module, string[] cmd) + { + lock (m_bots) + { + int connectedBots = m_bots.Count(b => b.ConnectionState == ConnectionState.Connected); + + if (connectedBots > 0) + { + MainConsole.Instance.OutputFormat("Please disconnect {0} connected bots first", connectedBots); + return; + } + } + + MainConsole.Instance.Output("Shutting down"); + + m_serverStatsCollector.Close(); + + Environment.Exit(0); + } + + private void HandleSetBots(string module, string[] cmd) + { + string key = cmd[2]; + string rawValue = cmd[3]; + + if (key == "SEND_AGENT_UPDATES") + { + bool newSendAgentUpdatesSetting; + + if (!ConsoleUtil.TryParseConsoleBool(MainConsole.Instance, rawValue, out newSendAgentUpdatesSetting)) + return; + + MainConsole.Instance.OutputFormat( + "Setting SEND_AGENT_UPDATES to {0} for all bots", newSendAgentUpdatesSetting); + + lock (m_bots) + m_bots.ForEach(b => b.Client.Settings.SEND_AGENT_UPDATES = newSendAgentUpdatesSetting); + } + else + { + MainConsole.Instance.Output("Error: Only setting currently available is SEND_AGENT_UPDATES"); + } + } + + private void HandleDebugLludpPacketCommand(string module, string[] args) + { + if (args.Length != 6) + { + MainConsole.Instance.OutputFormat("Usage: debug lludp packet "); + return; + } + + int level; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[3], out level)) + return; + + string botFirstName = args[4]; + string botLastName = args[5]; + + Bot bot; + + lock (m_bots) + bot = m_bots.FirstOrDefault(b => b.FirstName == botFirstName && b.LastName == botLastName); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("No bot named {0} {1}", botFirstName, botLastName); + return; + } + + bot.PacketDebugLevel = level; + + MainConsole.Instance.OutputFormat("Set debug level of {0} to {1}", bot.Name, bot.PacketDebugLevel); + } + + private void HandleShowRegions(string module, string[] cmd) + { + string outputFormat = "{0,-30} {1, -20} {2, -5} {3, -5}"; + MainConsole.Instance.OutputFormat(outputFormat, "Name", "Handle", "X", "Y"); + + lock (RegionsKnown) + { + foreach (GridRegion region in RegionsKnown.Values) + { + MainConsole.Instance.OutputFormat( + outputFormat, region.Name, region.RegionHandle, region.X, region.Y); + } + } + } + + private void HandleShowStatus(string module, string[] cmd) + { + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Bot connecting state", BotConnectingState); + + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleShowBotsStatus(string module, string[] cmd) + { + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.AddColumn("Name", 24); + cdt.AddColumn("Region", 24); + cdt.AddColumn("Status", 13); + cdt.AddColumn("Conns", 5); + cdt.AddColumn("Behaviours", 20); + + Dictionary totals = new Dictionary(); + foreach (object o in Enum.GetValues(typeof(ConnectionState))) + totals[(ConnectionState)o] = 0; + + lock (m_bots) + { + foreach (Bot bot in m_bots) + { + Simulator currentSim = bot.Client.Network.CurrentSim; + totals[bot.ConnectionState]++; + + cdt.AddRow( + bot.Name, + currentSim != null ? currentSim.Name : "(none)", + bot.ConnectionState, + bot.SimulatorsCount, + string.Join(",", bot.Behaviours.Keys.ToArray())); + } + } + + MainConsole.Instance.Output(cdt.ToString()); + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + + foreach (KeyValuePair kvp in totals) + cdl.AddRow(kvp.Key, kvp.Value); + + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleShowBotStatus(string module, string[] cmd) + { + if (cmd.Length != 3) + { + MainConsole.Instance.Output("Usage: show bot "); + return; + } + + int botNumber; + + if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, cmd[2], out botNumber)) + return; + + Bot bot = GetBotFromNumber(botNumber); + + if (bot == null) + { + MainConsole.Instance.OutputFormat("Error: No bot found with number {0}", botNumber); + return; + } + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Name", bot.Name); + cdl.AddRow("Status", bot.ConnectionState); + + Simulator currentSim = bot.Client.Network.CurrentSim; + cdl.AddRow("Region", currentSim != null ? currentSim.Name : "(none)"); + + List connectedSimulators = bot.Simulators; + List simulatorNames = connectedSimulators.ConvertAll(cs => cs.Name); + cdl.AddRow("Connections", string.Join(", ", simulatorNames.ToArray())); + + MainConsole.Instance.Output(cdl.ToString()); + + MainConsole.Instance.Output("Settings"); + + ConsoleDisplayList statusCdl = new ConsoleDisplayList(); + + statusCdl.AddRow( + "Behaviours", + string.Join(", ", bot.Behaviours.Values.ToList().ConvertAll(b => b.Name).ToArray())); + + GridClient botClient = bot.Client; + statusCdl.AddRow("SEND_AGENT_UPDATES", botClient.Settings.SEND_AGENT_UPDATES); + + MainConsole.Instance.Output(statusCdl.ToString()); + } + + /// + /// Get a specific bot from its number. + /// + /// null if no bot was found + /// + private Bot GetBotFromNumber(int botNumber) + { + string name = GenerateBotNameFromNumber(botNumber); + + Bot bot; + + lock (m_bots) + bot = m_bots.Find(b => b.Name == name); + + return bot; + } + + private string GenerateBotNameFromNumber(int botNumber) + { + return string.Format("{0} {1}_{2}", m_firstName, m_lastNameStem, botNumber); + } + + internal void Grid_GridRegion(object o, GridRegionEventArgs args) + { + lock (RegionsKnown) + { + GridRegion newRegion = args.Region; + + if (RegionsKnown.ContainsKey(newRegion.RegionHandle)) + { + return; + } + else + { + m_log.DebugFormat( + "[BOT MANAGER]: Adding {0} {1} to known regions", newRegion.Name, newRegion.RegionHandle); + RegionsKnown[newRegion.RegionHandle] = newRegion; + } + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs new file mode 100644 index 0000000000..660c630468 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Interfaces/IBehaviour.cs @@ -0,0 +1,75 @@ +/* + * 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; + +namespace pCampBot.Interfaces +{ + public interface IBehaviour + { + /// + /// Abbreviated name of this behaviour. + /// + string AbbreviatedName { get; } + + /// + /// Name of this behaviour. + /// + string Name { get; } + + /// + /// Initialize the behaviour for this bot. + /// + /// + /// This must be invoked before Action() is called. + /// + /// + void Initialize(Bot bot); + + /// + /// Interrupt the behaviour. + /// + /// + /// This should cause the current Action call() to terminate if this is active. + /// + void Interrupt(); + + /// + /// Close down this behaviour. + /// + /// + /// This is triggered if a behaviour is removed via explicit command and when a bot is disconnected + /// + void Close(); + + /// + /// Action to take when this behaviour is invoked. + /// + /// + void Action(); + } +} \ No newline at end of file diff --git a/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..87af19aff6 --- /dev/null +++ b/OpenSim/Tools/pCampBot/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +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("pCampBot")] +[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("84a69c60-76d3-4846-bd5b-0e1083774039")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("0.8.2.*")] + diff --git a/OpenSim/Tools/pCampBot/README.txt b/OpenSim/Tools/pCampBot/README.txt new file mode 100644 index 0000000000..c4fcf3393a --- /dev/null +++ b/OpenSim/Tools/pCampBot/README.txt @@ -0,0 +1,35 @@ +This is the PhysicsCamperbot libslBot tester. + +This is designed to stress test the simulator. It creates +clients that log in, randomly jump/walk around, and can say excuses from +the BOFH. + +Bots must have accounts already created. Each bot will have the same firstname and password +but their lastname will be appended with _ starting from 0. So if you have two bots called ima bot, their +first names will be ima_bot_0 and ima_bot_1. + +*** WARNING *** +Using this bot on a public grid could get you banned permanently, so +just say No! to griefing! + +----- Setup ----- +Linux: To build, in the main opensim directory, run: + ./runprebuild.sh + nant + +Windows: Run the prebuild.bat in the main opensim directory and then +open the created solution and compile it. + +pCampBot.exe will end up in the regular opensim/bin folder + +----- Running the bot ----- + +windows: pCampBot.exe -botcount -loginuri -firstname -lastname -password +*nix: mono pCampBot.exe -botcount -loginuri -firstname -lastname -password + +----- Commands ----- + +The bot has console commands: + help - lists the console commands and what they do + shutdown - gracefully shuts down the bots + quit - forcefully shuts things down leaving stuff unclean diff --git a/OpenSim/Tools/pCampBot/pCampBot.cs b/OpenSim/Tools/pCampBot/pCampBot.cs new file mode 100644 index 0000000000..1fb0e03fba --- /dev/null +++ b/OpenSim/Tools/pCampBot/pCampBot.cs @@ -0,0 +1,175 @@ +/* + * 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.Reflection; +using System.Threading; +using log4net; +using log4net.Config; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; + +namespace pCampBot +{ + /// + /// Event Types from the BOT. Add new events here + /// + public enum EventType:int + { + NONE = 0, + CONNECTED = 1, + DISCONNECTED = 2 + } + + public class pCampBot + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const string ConfigFileName = "pCampBot.ini"; + + [STAThread] + public static void Main(string[] args) + { + XmlConfigurator.Configure(); + + IConfig commandLineConfig = ParseConfig(args); + if (commandLineConfig.Get("help") != null || commandLineConfig.Get("loginuri") == null) + { + Help(); + } + else if ( + commandLineConfig.Get("firstname") == null + || commandLineConfig.Get("lastname") == null + || commandLineConfig.Get("password") == null) + { + Console.WriteLine("ERROR: You must supply a firstname, lastname and password for the bots."); + } + else + { + BotManager bm = new BotManager(); + + string iniFilePath = Path.GetFullPath(Path.Combine(Util.configDir(), ConfigFileName)); + + if (File.Exists(iniFilePath)) + { + m_log.InfoFormat("[PCAMPBOT]: Reading configuration settings from {0}", iniFilePath); + + IConfigSource configSource = new IniConfigSource(iniFilePath); + + IConfig botManagerConfig = configSource.Configs["BotManager"]; + + if (botManagerConfig != null) + { + bm.LoginDelay = botManagerConfig.GetInt("LoginDelay", bm.LoginDelay); + } + + IConfig botConfig = configSource.Configs["Bot"]; + + if (botConfig != null) + { + bm.InitBotSendAgentUpdates + = botConfig.GetBoolean("SendAgentUpdates", bm.InitBotSendAgentUpdates); + bm.InitBotRequestObjectTextures + = botConfig.GetBoolean("RequestObjectTextures", bm.InitBotRequestObjectTextures); + } + } + + int botcount = commandLineConfig.GetInt("botcount", 1); + bool startConnected = commandLineConfig.Get("connect") != null; + + bm.CreateBots(botcount, commandLineConfig); + + if (startConnected) + bm.ConnectBots(botcount); + + while (true) + { + try + { + MainConsole.Instance.Prompt(); + } + catch (Exception e) + { + m_log.ErrorFormat("Command error: {0}", e); + } + } + } + } + + private static IConfig ParseConfig(String[] args) + { + //Set up our nifty config.. thanks to nini + ArgvConfigSource cs = new ArgvConfigSource(args); + + cs.AddSwitch("Startup", "connect", "c"); + cs.AddSwitch("Startup", "botcount", "n"); + cs.AddSwitch("Startup", "from", "f"); + cs.AddSwitch("Startup", "loginuri", "l"); + cs.AddSwitch("Startup", "start", "s"); + cs.AddSwitch("Startup", "firstname"); + cs.AddSwitch("Startup", "lastname"); + cs.AddSwitch("Startup", "password"); + cs.AddSwitch("Startup", "behaviours", "b"); + cs.AddSwitch("Startup", "help", "h"); + cs.AddSwitch("Startup", "wear"); + + IConfig ol = cs.Configs["Startup"]; + return ol; + } + + private static void Help() + { + // Added the wear command. This allows the bot to wear real clothes instead of default locked ones. + // You can either say no, to not load anything, yes, to load one of the default wearables, a folder + // name, to load an specific folder, or save, to save an avatar with some already existing wearables + // worn to the folder MyAppearance/FirstName_LastName, and the load it. + + Console.WriteLine( + "Usage: pCampBot -loginuri -firstname -lastname -password [OPTIONS]\n" + + "Spawns a set of bots to test an OpenSim region\n\n" + + " -l, -loginuri loginuri for grid/standalone (required)\n" + + " -s, -start start location for bots (default: last) (optional). Can be \"last\", \"home\" or a specific location with or without co-ords (e.g. \"region1\" or \"region2/50/30/90\"\n" + + " -firstname first name for the bots (required)\n" + + " -lastname lastname for the bots (required). Each lastname will have _ appended, e.g. Ima Bot_0\n" + + " -password password for the bots (required)\n" + + " -n, -botcount number of bots to start (default: 1) (optional)\n" + + " -f, -from starting number for login bot names, e.g. 25 will login Ima Bot_25, Ima Bot_26, etc. (default: 0) (optional)\n" + + " -c, -connect connect all bots at startup (optional)\n" + + " -b, behaviours behaviours for bots. Comma separated, e.g. p,g (default: p) (optional)\n" + + " current options are:\n" + + " p (physics - bots constantly move and jump around)\n" + + " g (grab - bots randomly click prims whether set clickable or not)\n" + + " n (none - bots do nothing)\n" + + " t (teleport - bots regularly teleport between regions on the grid)\n" +// " c (cross)\n" + + + " -wear folder from which to load appearance data, \"no\" if there is no such folder (default: no) (optional)\n" + + " -h, -help show this message.\n"); + } + } +} diff --git a/Prebuild/AUTHORS b/Prebuild/AUTHORS new file mode 100644 index 0000000000..9e86ac018f --- /dev/null +++ b/Prebuild/AUTHORS @@ -0,0 +1,10 @@ +Dave Hudson (jendave@yahoo.com), +Matthew Holmes (matthew@wildfiregames.com) +Dan Moorehead (dan05a@gmail.com) +Rob Loach (http://www.robloach.net) +C.J. Adams-Collier (cjac@colliertech.org) + +Patch Contributers +lbsa71 +chi11ken +sdague diff --git a/Prebuild/COPYING b/Prebuild/COPYING new file mode 100644 index 0000000000..d3cdf7e38e --- /dev/null +++ b/Prebuild/COPYING @@ -0,0 +1,65 @@ +BSD License +Copyright (c)2004-2008 + +See AUTHORS file for list of copyright holders + +Dave Hudson (jendave@yahoo.com), +Matthew Holmes (matthew@wildfiregames.com) +Dan Moorehead (dan05a@gmail.com) +Rob Loach (http://www.robloach.net) +C.J. Adams-Collier (cjac@colliertech.org) + +http://dnpb.sourceforge.net +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. 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. + +3. The names of the authors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "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 + COPYRIGHT OWNER OR 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. + +--- + +Portions of src/Core/Targets/AutotoolsTarget.cs +// Copyright (C) 2006 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Prebuild/ChangeLog b/Prebuild/ChangeLog new file mode 100644 index 0000000000..bb8e7b09d8 --- /dev/null +++ b/Prebuild/ChangeLog @@ -0,0 +1,461 @@ +2008-12-09T02:15 D. Moonfire + * src/Core/Kernel.cs + - Added a /ppi target to get the results of processing but before + processing the actual results. + * src/Core/Preprocessor.cs + - Applied the patch from kanato with formatting changes. + - Uses the format. + * tests/ + - Added some lightweight tests to test the functionality of the + include patch. + +2008-06-19T09:37 John Anderson + * src/Core/Kernel.cs + - Only Loop through targets that are not abstract. + * src/Core/Targets/VSGenericTarget.cs + - Marked abstract and removed the Target attribute. + +2008-06-16T17:37 John Anderson + * src/Core/Nodes/ProjectNode.cs,data/prebuild-1.7.xsd + - Added the ability to hardcode guid's in the projects + +2008-05-21T0737 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - catch exception when unable to compile AssemblyInfo.cs + +2008-05-07T17:29 John Anderson + * src/Core/Targets/VSGenericTarget.cs + - Generate asp.net output in bin\ folder (asp.net requires it there) + +2008-04-30T17:29 John Anderson + * src/Core/Nodes/DatabaseReferenceNode.cs, + src/Core/Nodes/Datanode.cs, + src/Core/Nodes/FileNode.cs, + src/Core/Nodes/FileNodes.cs, + src/Core/Nodes/MatchNode.cs, + src/Core/Targets/VS2008Target.cs, + src/data/prebuild-1.7.xsd + - Refactored the project generation code to handle web projects and more + logically handle embedded resources and designer files. + +2008-04-30T17:29 Joseph Lombrozo + * src/Core/Nodes/SolutionNode.cs + - Had solutions inherit Configurations in the same way that Projects do. + +2008-04-29T06:35 Joseph Lombrozo + * src/Core/Targets/VS2008Target.cs, + src/Core/Nodes/DatabaseProjectNode.cs, + src/Core/Nodes/DatabaseReferenceNode.cs, + src/data/prebuild-1.7.xsd + - Added database references to database projects. + - Prevented nested solutions from being written to disk. + +2008-04-29T05:43 Joseph Lombrozo + * src/Core/Targets/VS2008Target.cs + - Enabled embedded solutions to contain Files. + +2008-04-29T04:13 Joseph Lombrozo + * src/Core/VSVersion.cs + - Fixed spelling mistake in enum comment. + * src/Core/Attributes/DataNodeAttribute.cs + - Allowed the DataNodeAttribute to be attached to a single class + more than once, allowing one class to be used to parse more than + one node at a time. + * src/Core/Kernel.cs + - Changed CacheNodeTypes() to allow for multiple DataNodeAttribute + instances in one class. Refactored ProcessFile(...) to return Solutions, + rather than adding them to the Kernel. + * src/Core/Nodes/SolutionNode.cs + - Added Guid (for embedded folders) + - Added DatabaseProjects, Solutions and Processes to the SolutionNode + when parsing. + * src/Core/Nodes/ProjectNode.cs + - Added FrameworkVersion property to allow for 2.0/3.0/3.5 differentiation. + * src/Core/Targets/VS2008Target.cs, src/data/prebuild-1.7.xsd + - Added ability to have embedded solutions, and externally referenced + prebuild scripts. + +2008-04-24T04:33 John M. Anderson + * src/Core/Targets/VS2003Target.cs, src/Core/Targets/VSVersion.cs + - Moved the VSVersion enum into its own file. + * src/Core/Targets/VS2008Target.cs + - added support for VS2008 + * src/Core/Nodes/ProjectNode.cs + - Added initial support for ASP.NET projects + * src/Core/Nodes/DatabaseProjectNode.cs + - Added support for Visual Studio database projects + +2008-02-19T07:08 C.J. Adams-Collier + * TODO + - added some tasks from Sam Hocevar + * src/Core/Targets/AutotoolsTarget.cs + - added a missing end paren + * COPYING + - Removed Randy Ridge's name from the copyright. Looks to me like + his name was present only because the file was nabbed from Tao + +2008-02-09T20:29 C.J. Adams-Collier + * COPYING + - added MIT/X11 license due to inclusion of code from Monodevelop + * THANKS + - added Lluis Sanchez Gual and Todd Berman - I yoinked code from + their pkg-config .pc file parser to build AutotoolsTarget.cs. + Sorry it took me so long to remember to add mention of you guys! + * src/Core/Targets/AutotoolsTarget.cs + - added MIT/X11 license. see above. + +2008-02-07T08:27 C.J. Adams-Collier + * AUTHORS + - consolidated names and contact info found laying around the + source + * src/Core/Kernel.cs + - updated copyright date + - re-formatted license for 80-column editor + - updated log banner to indicate new date, new authors + * src/Core/Targets/AutotoolsTarget.cs + - clarified reasoning behind use of constants in + AutotoolsTarget.ParsePCFile + - reduced length of some long lines using newline/indent + - added log messages for parsing .pc files, emitting solutions, + projects + - robustified the inter-package dependency resolution target + - log warning when we can't find assembly for + - clarified code for case of inability to find embedded + autotools.xml + * src/data/autotools.xml + - adding system lookup of resgen2 to configure.ac + - fixed .resource -> .resources typo + - added a rule to create .response file containing all sources + - using @.response on $(CSC) command line instead of listing + all source files + * src/Properties/AssemblyInfo.cs + - re-formatted license for an 80-column editor + - added more authors to the AssemblyCopyright attribute + - bumped version to 2.0.3 + * prebuild.xml + - bumped version to 2.0.3 + * scripts/autotools.sh + - if 'mono' is in the path, run Prebuild.exe with it + - using dirname to capture correct path to prebuild.xml &c + +2008-02-06T17:18 C.J. Adams-Collier + * src/Core/Targets/NAntTarget.cs + - re-formatted the license for an 80-column editor + - added myself to the copyright + - added a fix submitted by Gryc Ueusp + * src/Core/Targets/AutotoolsTarget.cs + - updated copyright to include 2008 + * THANKS + - created file, added Gryc Ueusp + +2008-01-01T14:50 C.J. Adams-Collier + * src/data/autotools.xml + - fixed .resx -> .resource compilation + - fixed failing edge case where Project is an unsigned Library + - added $(RESOURCE_SRC) to list of extra dist files + * src/Core/Targets/AutotoolsTarget.cs + - removed embeddedResources from extraDistFiles list + +2007-04-18T07:49 C.J. Adams-Collier + * src/data/prebuild-1.7.xsd + - removed default version from references + +2007-04-06T12:42 C.J. Adams-Collier + * src/data/autotools.xml + - added support for /doc: output when XmlDocFile is not empty + - not printing \t \\n on lines that have no content + - gacutil now installs the root assembly instead of the one under + bin/Debug or whatever + +2007-04-04T22:12 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - removed debugging Console.WriteLine() + * src/data/autotools.xml + - ensuring that install-sh and missing get distributed + - explicitly stating that the sources, snk, resources and binary + references live under $(srcdir) + - corrected uninstall target + - verified distcheck completes successfully + +2007-04-03T21:56 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - added a using for System.Diagnostics + - added enum ClrVersion for use with the pkg-config parser + - added class SystemPackage for use with the pkg-config parser + - removed explicit "private" property of members since it is implied + - flushing the stream-writer before it's closed + - removed excess braces around an if statement + ! NormalizeAsmName(), AddAssembly(), GetAssembliesWithLibInfo(), + GetAssembliesWithoutLibInfo(), ProcessPiece(), + GetVariableFromPkgConfig(), ParsePCFile(), + RegisterSystemAssemblies(), RunInitialization() + - pulled the above from MonoDevelop to parse the system pkgconfig + files and determine /pkg: arguments. Original sources are here: + http://svn.myrealbox.com/source/trunk/monodevelop/Core/src/MonoDevelop.Core/MonoDevelop.Core/SystemAssemblyService.cs + http://svn.myrealbox.com/source/trunk/monodevelop/Core/src/MonoDevelop.Core/MonoDevelop.Core/ClrVersion.cs + ! WriteProject() + - now gathering project version from AssemblyInfo.cs if it is part + of the project + - changed the declaration of the ArrayList's in the method + - now copying assembly .config files to the project, distributing, + installing + - making sure all needed files live under the Project directory + - copying strongname keys to project directory + - parsing AssemblyInfo.cs to determine assembly version + - removing all references to ".." + - removed superfluous if(project.References.Count > 0) around + for(int refNum = 0; refNum < project.References.Count; refNum++) + - removed use of runtimeLibs + - adding hook to copy sibling project's generated assemblies to + this project during Make time + - added extra dist target to ensure all files required to build + get distributed during "make dist" + - added new xslt processing args: + -- assemblyName + -- extraDistFiles + -- pkgLibs (/pkg:foo) + -- localCopyTargets (to copy sibling assemblies at Make time) + -- projectVersion (if determined from AssemblyInfo.cs) + -- hasAssemblyConfig (if there's a assembly.exe.config present) + ! Write() + - calling RunInitialization() to gather pkg-config data + * src/data/autotools.xml + - accepting new args passed from AutotoolsTarget.cs + - modernized configure.ac a bit + - using a version of tar that allows >99-char filenames + - added ASSEMBLY_NAME variable + - using assembly name rather than project name for gac installation + - generated assembly is now assembly name and not project name + - accepting /pkg: flags gathered from AutotoolsTarget.cs + - adding Makefile targets to copy sibling project assemblies to . + - fixed Debug, Release targets + - adding support for strongname key signing + - adding /unsafe support + - adding a clean make target + - only running gacutil /u if the assembly being uninstalled is in gac + - added some templates to determine each Configuration's .snk + - added explanation as to why .exe assemblies live in $prefix/lib + * src/Properties/AssemblyInfo.cs + - bumped assembly version + * prebuild.xml + - bumped assembly version + +2007-03-29T18:03 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + ! WriteProject() + - re-named incorrectly-named variable gacLibs to systemLibs + - added another reference list, runtimeLibs which contains the + libs we will need at runtime. we use this to build a MONO_PATH + - added monoPath to the xslt args list + * src/data/autotools.xml + ! + - renamed gacLibs to systemLibs + - added the sources to the dist list + - added logic to install libs that aren't strongnamed + ! + - accepting a param to update the MONO_PATH + +2007-03-28T19:46 C.J. Adams-Collier + * src/Core/Targets/MonoDevelopTarget.cs + ! CleanProject() + - using Assembly.LoadWithPartialName to locate the assembly + * src/Core/Targets/AutotoolsTarget.cs + ! WriteCombine() + - no longer using $PWD to determine a project's source dir; this + doesn't work with elements + - passing the solution name to all templates - fixes + multi-solution prebuild systems + ! WriteProject() + - no longer using $PWD to determine a project's source dir; this + doesn't work with elements + - passing the solution name to all templates - fixes + multi-solution prebuild systems + - copying strongname key to the autotools directory + - using Assembly.LoadWithPartialName to locate assemblies + * src/data/autotools.xml + ! + - fixed the .pc AC_CONFIG_FILES macro + ! + - added solution name param + - wrapping if type=exe check around script install macro + - added type=lib check and .pc file install macro + - added support for Configuration-specific builds (Debug, Release, etc) + - added strongname keyfile code + - TODO: support non-strongnamed library installation + ! + - added space preservation attribute to stylesheet element + - added a lower-case project name variable + - added solution name param + - made for-each template more specific + ! + - added solution name param + ! + - added solution name param + ! + - added solution name param + ! + - added solution name param + +2007-03-27T09:33 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - now processing the wrapper script if type is "Exe" or "WinExe" + * src/data/autotools.xml + ! + - being more exact about where text escaping is used + - using the correct variable name for the GACUTIL_FLAGS template + - using correct test="" for the AC_CONFIG_FILES macros + ! + - uncommented the bin_SCRIPTS section now that the script is being + generated correctly + ! + - fixed whitespace at beginning of file, before #! + - using lower-case packageName to indicate installation location + +2007-03-27T09:33 C.J. Adams-Collier + * src/data/autotools.xml + ! + * added a lcProjectName which is $projectName lower-cased + * moved autoconf template specifier near AC_OUTPUT + * AC_OUTPUT with args is deprecated. now using AC_CONFIG_FILES + * placed AC_CONFIG_FILES() calls for wrapper script or pkg-config + file in xslt project type checks + ! + * commented out bin_SCRIPTS + * added a lcProjectName which is $projectName lower-cased + * using $lcProjectName instead of the longer version + +2007-03-27T08:39 C.J. Adams-Collier + * src/data/autotools.xml + ! + - added whitespace-preservation + - added the missing projectName param + - replaced bin_SCRIPTS with something that worked + +2007-03-27T07:56 C.J. Adams-Collier + * src/data/autotools.xml + ! + - cleaned up duplicate checks + - placed initialization macros above system check macros + - added some more messages about what's going on + - added GACUTIL_FLAGS variable including /package option + ! + - added an incomplete bin_SCRIPTS + - RCS check says "building" instead of "compiling" + ! + - removed macros that are useful only for projects + ! + - created this element on this revision + - this is a wrapper shell script that lives in the $PATH and runs + the CIL assembly + +2007-03-26T20:18 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - creating new template arguments to contain the list of libs to + reference: source, binary & GAC + - source libs are included as part of this solution (untested) + - binary libs are distributed with the source (untested) + - GAC libs are assumed to be in the GAC or other lib path (tested) + * src/data/autotools.xml + - created new params through which to accept reference info + - created a working $(CSC) line + - added a TODO item for ordering project dependency for + AC_CONFIG_SUBDIRS code + +2007-03-26T08:41 C.J. Adams-Collier + * src/Core/Targets/AutotoolsTarget.cs + - now creating list of source files in managed code and passing + them to the template via s + * src/data/prebuild-1.7.xsd + - updated the header comment to 2007 + * src/data/autotools.xml + ! + - copied checks from Solution-level configure.ac + - copied solution-level config status + ! + - added elements for file list to be passed through + - made a temporary target for the assembly we're building + - added this target to the deps of "all:" + ! + - changed status header/footer from "- - -" to "===" + +2007-03-23T08:33 C.J. Adams-Collier + Added version attribute handling code for Property element + Added description element handling code + * prebuild.xml + - added /Prebuild/Solution/Property/@version attribute + - added /Prebuild/Solution/Property/Description element + * src/Core/Nodes/ProjectNode.cs + - added some docs where they were missing and obvious + - added code to handle @version + * src/Core/Nodes/DescriptionNode.cs + - new file. Used to handle /Prebuild/Solution/Property/Description + * src/Core/Targets/AutotoolsTarget.cs + - added mkdirDashP(), a recursive directory creation method + - WriteProject() now copies the files to autotools/ + * src/data/prebuild-1.7.xsd + - added /Prebuild/Solution/Property/Description element + - added /Prebuild/Solution/Property/@version attribute + * src/data/autotools.xml + - removed excess + - explicitly using dnpb: prefix + +2007-03-23T04:31 C.J. Adams-Collier + Merged code from my stripped-down test + Adding support for the /Prebuild/Solution/Project/Author element + * prebuild.xml + - added Author elements + - cleaned up the really long Project element + * src/Core/Nodes/ProjectNode.cs + - added Author tag processing code + * src/Core/Nodes/AuthorNode.cs + - Created to process Author elements + - based off of ReferencePathNode.cs + * src/Core/Targets/AutotoolsTarget.cs + - merged code from https://svn.colliertech.org/mono/dnpbAutotools/dnpbAutotools/test.cs + - renamed old WriteCombine to WriteCombineOld + - renamed old WriteProject to WriteProjectOld + * src/data/prebuild-1.7.xsd + - added Author element to Project + * src/data/autotools.xml + - lower-cased utf + + +2007-03-22T13:58 C.J. Adams-Collier + Exposing an XmlDocument that represents the prebuild.xml file + passed to the program + + * src/Core/Kernel.cs + - created an object member called XmlDocument m_CurrentDoc + - created a property to access its value + - using m_CurrentDoc to load up the prebuild.xml file rather than + a local variable called "doc" + +2007-03-22 C.J. Adams-Collier + * prebuild.xml + - added autotools.xml created at https://svn.colliertech.org/mono/dnpbAutotools/dnpbAutotools/autotools.xml + * src/data/autotools.xml + - the same + * src/Core/Targets/MonoDevelopTarget.cs + - fixed bug introduced in r206 + +2007-03-07 C.J. Adams-Collier + * src/data/prebuild-1.7.xsd + - added version attribute to Solution and Project elements + +2006-11-04T00:38 C.J. Adams-Collier + * placing AssemblyInfo.cs into Properties/ + * Fixed double-mention of the package name + +2006-11-03T15:23 C.J. Adams-Collier + * corrected a problem in the Include.am generation code + * created the new .exe + * copied it to the root of the build + +2006-11-03T14:57 C.J. Adams-Collier + * Updated the .exe file + +2006-11-03 C.J. Adams-Collier + * Added a TODO file + * Added a ChangeLog file + * applied some fixes for autotools gac and pkg-config installation + problems diff --git a/Prebuild/INSTALL b/Prebuild/INSTALL new file mode 100644 index 0000000000..23e5f25d0e --- /dev/null +++ b/Prebuild/INSTALL @@ -0,0 +1,236 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. (Caching is +disabled by default to prevent problems with accidental use of stale +cache files.) + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You only need +`configure.ac' if you want to change it or regenerate `configure' using +a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not support the `VPATH' +variable, you have to compile the package for one architecture at a +time in the source code directory. After you have installed the +package for one architecture, use `make distclean' before reconfiguring +for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). Here is a another example: + + /bin/bash ./configure CONFIG_SHELL=/bin/bash + +Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +configuration-related scripts to be executed by `/bin/bash'. + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/Prebuild/NEWS b/Prebuild/NEWS new file mode 100644 index 0000000000..bea28dac26 --- /dev/null +++ b/Prebuild/NEWS @@ -0,0 +1,200 @@ +Prebuild is an XML-driven pre-build tool allowing developers to easily generate project or make files for major IDE's and .NET development tools including: Visual Studio 2005, Visual Studio 2003, Visual Studio 2002, SharpDevelop, MonoDevelop, and NAnt. + +Documentation and downloads are available at http://dnpb.sourceforge.net. + +Prebuild is licensed under the BSD license. + +[ XXXXXXX XX, XXX - 1.3.2 ] + + Added Keyfile signing to NAnt target and VS2005 target + + Updated XSD file to 1.7 + + Boo and VisualBasic Language support in VS2005 target + + Added basic Autotools target. It creates a non-recursive Autotools system. + ! Multiple files can be excluded from the Match node + ! VS2005 now handles .resx files correctly. + ! NAnt and Autotools now handle defines + ! NAnt and Autotools now handle resources + + Conditional XML variables can be passed through the command line. + + Added /install and /remove command line flags to install and remove assemblies from the GAC + + Many fixes to VS2005 target + +[ July 21, 2006 - 1.3.1 ] + ! VS2005 fixes from Rob Loach + ! NAnt fixes from Rob Loach and David Hudson + ! XML doc fixes from Rob Loach + + Added SharpDevelop2 target (really just uses VS2005 target) + ! Fixed bug with BuildEvents in Monodevelop target + + Passing /yes will default to answering yes to any warnings + +[ February 28, 2006 - 1.3 ] + + Added MonoDevelop target. + + Added NAnt target. + + Lots of fixes to all targets. + * Cleaned up the code using FXCop. + * Updated schema to 1.6 to fix a typo and add a new parameter. + * jendave is now the maintainer of the project. RobLoach has been added as a developer. + * Removed references to 'dnpb'. + + Added rudimentary support for pre- and post- build scripts + * Updated examples. + +[ August 5, 2004 - 1.2 ] + + Added Visual Studio Express (vs2005express) target contributed by Borrillis and modified for use with different languages + + Added the allowedgroups command line option followed by a pipe-delimited list of project group filter flags (eg. Group1|Group2) allow optional filtering of all projects that dont have at least one of these flags + + Added the filterGroups XML attribute to the project node and updated the scheme to v1.5 for this addition, it is used to specified the delimited list of filter groups to which a project belongs + * Modified the removedir command line option to allow for a pipe-delimited list of directory names + ! Modified the resource loading code to search for resourced without the prepended namespace (as Visual Studio .NET does it) to allow for it to be compiled with SharpDevelop as well + + Added the GenerateXmlDocFile boolean option to the Options XML element + * Changed the behavior of the XmlDocFile option so that if not specified it uses the assemblyName (without file extension) + .xml for the file name instead of just not generating the file since the new GenerateXmlDocFile takes care of this + +[ January 3, 2004 - 1.1 ] + ! Replaced regex use for more efficient manual parsing to allow use on non-windows platforms with Mono which has Regex problems + + Added the checkOsVars attribute to the root element for enabling interpolation for Enviroment variables in the form ${var}, otherwise no checking is performed for efficiency-sake + * Make the version attribute on the root element optional as it isn't used and not needed since the schema url contains the version + +[ December 30, 2004 - 1.1 ] + ! Applied Leed's fix for SharpDevelop references + + Rewrote much of the processing for better validation and without the use of a temp file + + Added support for configurations at the project level which are named All. They now apply changes to all Solution level defined configs + * Changed all spaces into tabs + + Added support for the None build action + * Replaced all sequence's in the XML schema for all's because the order doesn't matter since the xml file is loaded into an XmlDocument + +[ December 25, 2004 - 1.0 ] + + Added the /removedir option for cleaning directories like obj before file releases + + Changed WriteTempXml() and the new DeleteTempXml() methods to be compiled only in DEBUG builds + * Made path optional for Match elements (defaults to current directory) and updates schema for it + ! Fixed XML example in the readme.txt + + Added example xml files to docs directory + * Updated license.txt to add Dan Moorehead and update copyright years + + Updated prebuild.xml to take advantage of the default path attribute for match elements + + Updated Clean to delete the obj directories + +[ December 25, 2004 - 0.13 ] + + Added dnpb.exe so that it can be used to generate the project files + + Added dnpb.ico + * Added parameterless Write statement to Log for writing a single line + * Changed scehema to version 1.3 for support of icon attribute + * Added support for All configuration name under a Project node signifying common settings for all configurations + ! Fixed the SupressWarnings by adding the corresponding field to OptionsNode + * Wrote documentation in docs/readme.txt + * Added Dan Moorehead to copyrights and extended date from 2004 to 2004-2005 + * Updated prebuild.xml + * Optimized Log class + * Updated OutputUsage() + * /clean targets all by default + * No log file is used by default, /log without value specified uses default file name + + Added support for the /pause which pauses the utility after execution to observe output + + +[ September 27, 2004 - 0.12.2a ] + ! Fixed a nasty bug when trying to delete our temp file for pre-processing. + +[ September 15, 2004 - 0.12.2 ] + + Expanded platform identification, thanks to the NAnt guys for shedding some + light on how to properly check for UNIX platforms! Thanks guys! + * POSIX OS identifier changed to UNIX. Valid OS names are now "Win32", "UNIX", + and "Unknown". + ! Fixed SharpDevelop target to properly use the 'rootNamespace' attribute of + the Project tag. + + New command-line switch, /ppo, forces DNPB to pre-process the file and write + the pre-processed file. This allows you to test/debug your pre-processor + macros. No other processing will be done. You can specify a target file as + a paramter for the /ppo switch, or DNPB will write the file 'preprocessed.xml' + if you do not specify a file. + + The Match tag now has a 'buildAction' attribute which functions exactly like + the attribute of the same name for the File tag. + +[ August 5, 2004 - 0.12.1 ] + + Added environment variable expansion for all values. Environment variables + should be listed in the form ${VAR}. + +[ July 30, 2004 - 0.12.0 ] + + Added preprocessing via XML processing information tags. Available tags + are: ?>, ?>, and . The + current expression parser is very basic, but will be replaced with a more + capable parser over time. Current operators available are: =, !=, <, >, + <=, >=. Current test variables available: OS, RuntimeVersion, RuntimeMajor, + RuntimeMinor, RuntimeRevision. + +[ July 27, 2004 - 0.11.4 ] + + Added 'useRegex' attribute to the Match tag. Matches can now use regular + expressions to match filenames. + + Added the 'assemblyName' attribute to the Project tag. Projects can now + set their output assembly name. + ! Fixed several bugs in the way that Project tags inheirt their parent + Solutions configuration options. This operation should now work fully as + intended. + ! Due to some wierdness, Project Guid's are now stored as part of the Project + node and created at parse time. + +[ May 11, 2004 - 0.11.3 ] + ! Fixed a bug where I was writing the wrong property name for a projects root + namespace. + ! Removed a DEBUG statement I had left in the code in 0.11.2. + ! Fixed a bug in the VS2002 writer which caused the version variables to not + be overriden correctly. + + Added the rootNamespace property to the element, allowing you to + specify the root namespace. + * /target and /clean are now mutually exclusive command line switches, and + they both now take the all option. In the case of /target all, all build + file for all targets will be created. In the case of /clean all, the user + will be prompted to make sure they want to do it, and if so, will clean + all build files for all targets. + +[ April 22, 2004 - 0.11.2 ] + ! Fixed a bug with the /file command-line operator. Was using the unresolved + file path rather then the resolved one, was making the attempt to open the + dnpb file fail. + ! Fixed a bug in the schema that required at least 1 solution and 1 reference + path. We can do just fine with 0 of either of these. Some files may be all + statements and not have any tags. + ! Fixed a bug that caused the project references not to be written with the + SharpDevelop target. + * Changed the schema to version 1.2, allowing for Configuration nodes to exist + under project nodes. The inheritance of values is hierarchical. Meaning, if + you define a configuration named Debug at the Soltion level, and one by the + same name at the Project level, the one at the Project level will first + inherit the options of the Solution level configuration, then set it's own + options. If you define a configuration at the Project level and it does not + exist at the Solution level, it will be created at the Solution level. + * Project references should now work correctly across the board. Note that due + to a restriction in Visual Studio, you can only reference projects in the same + solution. + +[ April 21, 2004 - 0.11.1 ] + ! Fixed a problem with resolving paths in various targets. Was not properly + setting the CWD. + * Schema updated to 1.1, moving the ReferencePath element from the Options + element to the Project element. This makes more logical sense, given that + reference paths are resolved relative to the project path. Any prebuild.xml + file referecning verison 1.0 will fail! Please update to the 1.1 schema. + +[ April 19, 2004 - 0.11.0 ] + * Added several attributes across the code to make FxCop happy + ! Fixed bugs in reference paths being written in the VS targets. + ! Fixed a bug in ProjectNode which was doing two CWDStack.Push() calls instead of + a Push/Pop pair. Was wreaking havoc with tags. + ! Fixed some bugs in the path tracking, both the Project and Solution nodes now + have a FullPath property, which is the full path to the file resolved at load + time. This should fix all path relativity problems. + + Added new /clean switch, allowing the target to clean up any files it generated. + in accordance, the ITarget interface has been updated to support a new Clean() + method. + + Completed addition of the tag, to allow the referencing of external + prebuild.xml files. + + Added the runtime attribute to the Project element. This allows the developer + to specify which runtime a project should target (Mono or Microsoft). This is + of course ignored in certain targets like the Visual Studio targets. + + Added the SharpDevelop target. + +[ April 13, 2004 - 0.10.1a ] + + Added the buildAction attribute to the File node. This is needed for dnpb + to even be able to bootstrap itself (dnpb-1.0.xsd must be an embedded resource) + +[ April 13, 2004 - 0.10.1 ] + * First Release + +[ Key ] +* = Change or information ++ = Addition +! = Bug Fix + diff --git a/Prebuild/README b/Prebuild/README new file mode 100644 index 0000000000..2b05fb598d --- /dev/null +++ b/Prebuild/README @@ -0,0 +1,274 @@ +Prebuild Instructions + +Prebuild is an XML-driven pre-build tool allowing developers to easily generate project or make files for major IDE's and .NET development tools including: Visual Studio 2005, Visual Studio 2003, Visual Studio 2002, SharpDevelop, SharpDevelop2, MonoDevelop, and NAnt. + +_______________________________________________________________________________ +Overview + +Prebuild can be either be run from the command line to generate the +project and make files or you can execute the included batch (*.bat) +and Unix Shell script (*.sh) files. + +_______________________________________________________________________________ +The currently supported developement tools and their associated batch +and shell script files. + +Visual Studio .NET 2005 (VS2005.bat) +Visual Studio .NET 2003 (VS2003.bat) +Visual Studio .NET 2002 (VS2002.bat) +SharpDevelop (SharpDevelop.bat) - http://www.icsharpcode.net/OpenSource/SD/ +SharpDevelop2 (SharpDevelop.bat) - http://www.icsharpcode.net/OpenSource/SD/ +MonoDevelop (MonoDevelop.sh) - http://www.monodevelop.com/ +NAnt (nant.sh and nant.bat) - http://nant.sourceforge.net/ +Autotools (autotools.bat and autotools.sh) - http://en.wikipedia.org/wiki/GNU_build_system + +Notes: + +A Unix Shell script is provided for MonoDevelop, as it does not run on +Windows at this time. + +Visual Studio .NET 2005 and the Visual Express IDE's can import +solutions from older versions of Visual Studio .NET. + +Makefiles are not currently supported. + +_______________________________________________________________________________ +Command Line Syntax: + +Example: +> Prebuild /target vs2003 + +This will generate the project files for Visual Studio.NET 2003 and +place the redirect the log to a file named PrebuildLog.txt in the +parent directory + + +The syntax structure is as below, where commandParameter is optional +depending on the command and you can provide several option-value +pairs. + +Note: The '> ' signifies the command prompt, do not enter this literally + +> Prebuild /"); + } + #endregion + + #region User File + + ps = new StreamWriter(projectFile + ".user"); + using (ps) + { + // Get the first configuration from the project. + ConfigurationNode firstConfiguration = null; + + if (project.Configurations.Count > 0) + { + firstConfiguration = project.Configurations[0]; + } + + ps.WriteLine(""); + //ps.WriteLine( "" ); + //ps.WriteLine(" <{0}>", toolInfo.XMLTag); + //ps.WriteLine(" "); + ps.WriteLine(" "); + //ps.WriteLine(" ", MakeRefPath(project)); + + if (firstConfiguration != null) + { + ps.WriteLine(" {0}", firstConfiguration.Name); + ps.WriteLine(" {0}", firstConfiguration.Platform); + } + + ps.WriteLine(" {0}", MakeRefPath(project)); + ps.WriteLine(" {0}", ProductVersion); + ps.WriteLine(" ProjectFiles"); + ps.WriteLine(" 0"); + ps.WriteLine(" "); + foreach (ConfigurationNode conf in project.Configurations) + { + ps.Write(" "); + } + ps.WriteLine(""); + } + #endregion + + kernel.CurrentWorkingDirectory.Pop(); + } + + private void WriteSolution(SolutionNode solution, bool writeSolutionToDisk) + { + kernel.Log.Write("Creating {0} solution and project files", VersionName); + + foreach (SolutionNode child in solution.Solutions) + { + kernel.Log.Write("...Creating folder: {0}", child.Name); + WriteSolution(child, false); + } + + foreach (ProjectNode project in solution.Projects) + { + kernel.Log.Write("...Creating project: {0}", project.Name); + WriteProject(solution, project); + } + + foreach (DatabaseProjectNode project in solution.DatabaseProjects) + { + kernel.Log.Write("...Creating database project: {0}", project.Name); + WriteDatabaseProject(solution, project); + } + + if (writeSolutionToDisk) // only write main solution + { + kernel.Log.Write(""); + string solutionFile = Helper.MakeFilePath(solution.FullPath, solution.Name, "sln"); + + using (StreamWriter ss = new StreamWriter(solutionFile)) + { + kernel.CurrentWorkingDirectory.Push(); + Helper.SetCurrentDir(Path.GetDirectoryName(solutionFile)); + + ss.WriteLine("Microsoft Visual Studio Solution File, Format Version {0}", SolutionVersion); + ss.WriteLine(SolutionTag); + + WriteProjectDeclarations(ss, solution, solution); + + ss.WriteLine("Global"); + + ss.WriteLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution"); + foreach (ConfigurationNode conf in solution.Configurations) + { + ss.WriteLine("\t\t{0} = {0}", conf.NameAndPlatform); + } + ss.WriteLine("\tEndGlobalSection"); + + ss.WriteLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution"); + WriteConfigurationLines(solution.Configurations, solution, ss); + ss.WriteLine("\tEndGlobalSection"); + + if (solution.Solutions.Count > 0) + { + ss.WriteLine("\tGlobalSection(NestedProjects) = preSolution"); + foreach (SolutionNode embeddedSolution in solution.Solutions) + { + WriteNestedProjectMap(ss, embeddedSolution); + } + ss.WriteLine("\tEndGlobalSection"); + } + + ss.WriteLine("EndGlobal"); + } + + kernel.CurrentWorkingDirectory.Pop(); + } + } + + private void WriteProjectDeclarations(TextWriter writer, SolutionNode actualSolution, SolutionNode embeddedSolution) + { + foreach (SolutionNode childSolution in embeddedSolution.Solutions) + { + WriteEmbeddedSolution(writer, childSolution); + WriteProjectDeclarations(writer, actualSolution, childSolution); + } + + foreach (ProjectNode project in embeddedSolution.Projects) + { + WriteProject(actualSolution, writer, project); + } + + foreach (DatabaseProjectNode dbProject in embeddedSolution.DatabaseProjects) + { + WriteProject(actualSolution, writer, dbProject); + } + + if (actualSolution.Guid == embeddedSolution.Guid) + { + WriteSolutionFiles(actualSolution, writer); + } + } + + private static void WriteNestedProjectMap(TextWriter writer, SolutionNode embeddedSolution) + { + foreach (ProjectNode project in embeddedSolution.Projects) + { + WriteNestedProject(writer, embeddedSolution, project.Guid); + } + + foreach (DatabaseProjectNode dbProject in embeddedSolution.DatabaseProjects) + { + WriteNestedProject(writer, embeddedSolution, dbProject.Guid); + } + + foreach (SolutionNode child in embeddedSolution.Solutions) + { + WriteNestedProject(writer, embeddedSolution, child.Guid); + WriteNestedProjectMap(writer, child); + } + } + + private static void WriteNestedProject(TextWriter writer, SolutionNode solution, Guid projectGuid) + { + WriteNestedFolder(writer, solution.Guid, projectGuid); + } + + private static void WriteNestedFolder(TextWriter writer, Guid parentGuid, Guid childGuid) + { + writer.WriteLine("\t\t{0} = {1}", + childGuid.ToString("B").ToUpper(), + parentGuid.ToString("B").ToUpper()); + } + + private static void WriteConfigurationLines(IEnumerable configurations, SolutionNode solution, TextWriter ss) + { + foreach (ProjectNode project in solution.Projects) + { + foreach (ConfigurationNode conf in configurations) + { + ss.WriteLine("\t\t{0}.{1}.ActiveCfg = {1}", + project.Guid.ToString("B").ToUpper(), + conf.NameAndPlatform); + + ss.WriteLine("\t\t{0}.{1}.Build.0 = {1}", + project.Guid.ToString("B").ToUpper(), + conf.NameAndPlatform); + } + } + + foreach (SolutionNode child in solution.Solutions) + { + WriteConfigurationLines(configurations, child, ss); + } + } + + private void WriteSolutionFiles(SolutionNode solution, TextWriter ss) + { + if(solution.Files != null && solution.Files.Count > 0) + WriteProject(ss, "Folder", solution.Guid, "Solution Files", "Solution Files", solution.Files); + } + + private void WriteEmbeddedSolution(TextWriter writer, SolutionNode embeddedSolution) + { + WriteProject(writer, "Folder", embeddedSolution.Guid, embeddedSolution.Name, embeddedSolution.Name, embeddedSolution.Files); + } + + private void WriteProject(SolutionNode solution, TextWriter ss, ProjectNode project) + { + WriteProject(ss, solution, project.Language, project.Guid, project.Name, project.FullPath); + } + + private void WriteProject(SolutionNode solution, TextWriter ss, DatabaseProjectNode dbProject) + { + if (solution.Files != null && solution.Files.Count > 0) + WriteProject(ss, solution, "Database", dbProject.Guid, dbProject.Name, dbProject.FullPath); + } + + const string ProjectDeclarationBeginFormat = "Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\""; + const string ProjectDeclarationEndFormat = "EndProject"; + + private void WriteProject(TextWriter ss, SolutionNode solution, string language, Guid guid, string name, string projectFullPath) + { + if (!tools.ContainsKey(language)) + throw new UnknownLanguageException("Unknown .NET language: " + language); + + ToolInfo toolInfo = tools[language]; + + string path = Helper.MakePathRelativeTo(solution.FullPath, projectFullPath); + + path = Helper.MakeFilePath(path, name, toolInfo.FileExtension); + + WriteProject(ss, language, guid, name, path); + } + + private void WriteProject(TextWriter writer, string language, Guid projectGuid, string name, string location) + { + WriteProject(writer, language, projectGuid, name, location, null); + } + + private void WriteProject(TextWriter writer, string language, Guid projectGuid, string name, string location, FilesNode files) + { + if (!tools.ContainsKey(language)) + throw new UnknownLanguageException("Unknown .NET language: " + language); + + ToolInfo toolInfo = tools[language]; + + writer.WriteLine(ProjectDeclarationBeginFormat, + toolInfo.Guid, + name, + location, + projectGuid.ToString("B").ToUpper()); + + if (files != null) + { + writer.WriteLine("\tProjectSection(SolutionItems) = preProject"); + + foreach (string file in files) + writer.WriteLine("\t\t{0} = {0}", file); + + writer.WriteLine("\tEndProjectSection"); + } + + writer.WriteLine(ProjectDeclarationEndFormat); + } + + private void WriteDatabaseProject(SolutionNode solution, DatabaseProjectNode project) + { + string projectFile = Helper.MakeFilePath(project.FullPath, project.Name, "dbp"); + IndentedTextWriter ps = new IndentedTextWriter(new StreamWriter(projectFile), " "); + + kernel.CurrentWorkingDirectory.Push(); + + Helper.SetCurrentDir(Path.GetDirectoryName(projectFile)); + + using (ps) + { + ps.WriteLine("# Microsoft Developer Studio Project File - Database Project"); + ps.WriteLine("Begin DataProject = \"{0}\"", project.Name); + ps.Indent++; + ps.WriteLine("MSDTVersion = \"80\""); + // TODO: Use the project.Files property + if (ContainsSqlFiles(Path.GetDirectoryName(projectFile))) + WriteDatabaseFoldersAndFiles(ps, Path.GetDirectoryName(projectFile)); + + ps.WriteLine("Begin DBRefFolder = \"Database References\""); + ps.Indent++; + foreach (DatabaseReferenceNode reference in project.References) + { + ps.WriteLine("Begin DBRefNode = \"{0}\"", reference.Name); + ps.Indent++; + ps.WriteLine("ConnectStr = \"{0}\"", reference.ConnectionString); + ps.WriteLine("Provider = \"{0}\"", reference.ProviderId.ToString("B").ToUpper()); + //ps.WriteLine("Colorizer = 5"); + ps.Indent--; + ps.WriteLine("End"); + } + ps.Indent--; + ps.WriteLine("End"); + ps.Indent--; + ps.WriteLine("End"); + + ps.Flush(); + } + + kernel.CurrentWorkingDirectory.Pop(); + } + + private static bool ContainsSqlFiles(string folder) + { + if(Directory.GetFiles(folder, "*.sql").Length > 0) + return true; // if the folder contains 1 .sql file, that's good enough + + foreach (string child in Directory.GetDirectories(folder)) + { + if (ContainsSqlFiles(child)) + return true; // if 1 child folder contains a .sql file, still good enough + } + + return false; + } + + private static void WriteDatabaseFoldersAndFiles(IndentedTextWriter writer, string folder) + { + foreach (string child in Directory.GetDirectories(folder)) + { + if (ContainsSqlFiles(child)) + { + writer.WriteLine("Begin Folder = \"{0}\"", Path.GetFileName(child)); + writer.Indent++; + WriteDatabaseFoldersAndFiles(writer, child); + writer.Indent--; + writer.WriteLine("End"); + } + } + foreach (string file in Directory.GetFiles(folder, "*.sql")) + { + writer.WriteLine("Script = \"{0}\"", Path.GetFileName(file)); + } + } + + private void CleanProject(ProjectNode project) + { + kernel.Log.Write("...Cleaning project: {0}", project.Name); + + ToolInfo toolInfo = tools[project.Language]; + string projectFile = Helper.MakeFilePath(project.FullPath, project.Name, toolInfo.FileExtension); + string userFile = projectFile + ".user"; + + Helper.DeleteIfExists(projectFile); + Helper.DeleteIfExists(userFile); + } + + private void CleanSolution(SolutionNode solution) + { + kernel.Log.Write("Cleaning {0} solution and project files", VersionName, solution.Name); + + string slnFile = Helper.MakeFilePath(solution.FullPath, solution.Name, "sln"); + string suoFile = Helper.MakeFilePath(solution.FullPath, solution.Name, "suo"); + + Helper.DeleteIfExists(slnFile); + Helper.DeleteIfExists(suoFile); + + foreach (ProjectNode project in solution.Projects) + { + CleanProject(project); + } + + kernel.Log.Write(""); + } + + #endregion + + #region ITarget Members + + /// + /// Writes the specified kern. + /// + /// The kern. + public virtual void Write(Kernel kern) + { + if (kern == null) + { + throw new ArgumentNullException("kern"); + } + kernel = kern; + foreach (SolutionNode sol in kernel.Solutions) + { + WriteSolution(sol, true); + } + kernel = null; + } + + /// + /// Cleans the specified kern. + /// + /// The kern. + public virtual void Clean(Kernel kern) + { + if (kern == null) + { + throw new ArgumentNullException("kern"); + } + kernel = kern; + foreach (SolutionNode sol in kernel.Solutions) + { + CleanSolution(sol); + } + kernel = null; + } + + #endregion + } +} diff --git a/Prebuild/src/Core/Targets/VSVersion.cs b/Prebuild/src/Core/Targets/VSVersion.cs new file mode 100644 index 0000000000..699b5ca019 --- /dev/null +++ b/Prebuild/src/Core/Targets/VSVersion.cs @@ -0,0 +1,54 @@ +#region BSD License +/* +Copyright (c) 2008-2009 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com), John Anderson (sontek@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +namespace Prebuild.Core.Targets +{ + /// + /// + /// + public enum VSVersion + { + /// + /// Visual Studio 2002 + /// + VS70, + /// + /// Visual Studio 2003 + /// + VS71, + /// + /// Visual Studio 2005 + /// + VS80, + /// + /// Visual Studio 2008 + /// + VS90, + /// + /// Visual Studio 2010 + /// + VS10 + } +} diff --git a/Prebuild/src/Core/Targets/XcodeTarget.cs b/Prebuild/src/Core/Targets/XcodeTarget.cs new file mode 100644 index 0000000000..5393cec05f --- /dev/null +++ b/Prebuild/src/Core/Targets/XcodeTarget.cs @@ -0,0 +1,594 @@ +#region BSD License +/* +Copyright (c) 2004 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System; +using System.IO; +using System.Reflection; +using System.Text.RegularExpressions; + +using Prebuild.Core.Attributes; +using Prebuild.Core.Interfaces; +using Prebuild.Core.Nodes; +using Prebuild.Core.Utilities; + +namespace Prebuild.Core.Targets +{ + /// + /// + /// + [Target("xcode")] + public class XcodeTarget : ITarget + { + #region Fields + + private Kernel m_Kernel; + + #endregion + + #region Private Methods + + private static string PrependPath(string path) + { + string tmpPath = Helper.NormalizePath(path, '/'); + Regex regex = new Regex(@"(\w):/(\w+)"); + Match match = regex.Match(tmpPath); + //if(match.Success || tmpPath[0] == '.' || tmpPath[0] == '/') + //{ + tmpPath = Helper.NormalizePath(tmpPath); + //} + // else + // { + // tmpPath = Helper.NormalizePath("./" + tmpPath); + // } + + return tmpPath; + } + + private static string BuildReference(SolutionNode solution, ReferenceNode refr) + { + string ret = ""; + if (solution.ProjectsTable.ContainsKey(refr.Name)) + { + ProjectNode project = (ProjectNode)solution.ProjectsTable[refr.Name]; + string fileRef = FindFileReference(refr.Name, project); + string finalPath = Helper.NormalizePath(Helper.MakeFilePath(project.FullPath + "/${build.dir}/", refr.Name, "dll"), '/'); + ret += finalPath; + return ret; + } + else + { + ProjectNode project = (ProjectNode)refr.Parent; + string fileRef = FindFileReference(refr.Name, project); + + if (refr.Path != null || fileRef != null) + { + string finalPath = (refr.Path != null) ? Helper.NormalizePath(refr.Path + "/" + refr.Name + ".dll", '/') : fileRef; + ret += finalPath; + return ret; + } + + try + { + //Assembly assem = Assembly.Load(refr.Name); + //if (assem != null) + //{ + //ret += (refr.Name + ".dll"); + //} + //else + //{ + ret += (refr.Name + ".dll"); + //} + } + catch (System.NullReferenceException e) + { + e.ToString(); + ret += refr.Name + ".dll"; + } + } + return ret; + } + + private static string BuildReferencePath(SolutionNode solution, ReferenceNode refr) + { + string ret = ""; + if (solution.ProjectsTable.ContainsKey(refr.Name)) + { + ProjectNode project = (ProjectNode)solution.ProjectsTable[refr.Name]; + string fileRef = FindFileReference(refr.Name, project); + string finalPath = Helper.NormalizePath(Helper.MakeReferencePath(project.FullPath + "/${build.dir}/"), '/'); + ret += finalPath; + return ret; + } + else + { + ProjectNode project = (ProjectNode)refr.Parent; + string fileRef = FindFileReference(refr.Name, project); + + if (refr.Path != null || fileRef != null) + { + string finalPath = (refr.Path != null) ? Helper.NormalizePath(refr.Path, '/') : fileRef; + ret += finalPath; + return ret; + } + + try + { + Assembly assem = Assembly.Load(refr.Name); + if (assem != null) + { + ret += ""; + } + else + { + ret += ""; + } + } + catch (System.NullReferenceException e) + { + e.ToString(); + ret += ""; + } + } + return ret; + } + + private static string FindFileReference(string refName, ProjectNode project) + { + foreach (ReferencePathNode refPath in project.ReferencePaths) + { + string fullPath = Helper.MakeFilePath(refPath.Path, refName, "dll"); + + if (File.Exists(fullPath)) + { + return fullPath; + } + } + + return null; + } + + /// + /// Gets the XML doc file. + /// + /// The project. + /// The conf. + /// + public static string GetXmlDocFile(ProjectNode project, ConfigurationNode conf) + { + if (conf == null) + { + throw new ArgumentNullException("conf"); + } + if (project == null) + { + throw new ArgumentNullException("project"); + } + string docFile = (string)conf.Options["XmlDocFile"]; + // if(docFile != null && docFile.Length == 0)//default to assembly name if not specified + // { + // return Path.GetFileNameWithoutExtension(project.AssemblyName) + ".xml"; + // } + return docFile; + } + + private void WriteProject(SolutionNode solution, ProjectNode project) + { + string projFile = Helper.MakeFilePath(project.FullPath, project.Name + (project.Type == ProjectType.Library ? ".dll" : ".exe"), "build"); + StreamWriter ss = new StreamWriter(projFile); + + m_Kernel.CurrentWorkingDirectory.Push(); + Helper.SetCurrentDir(Path.GetDirectoryName(projFile)); + bool hasDoc = false; + + using (ss) + { + ss.WriteLine(""); + ss.WriteLine("", project.Name); + ss.WriteLine(" ", "build"); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + foreach (ReferenceNode refr in project.References) + { + if (refr.LocalCopy) + { + ss.WriteLine(" ", '/')); + } + } + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.Write(" "); + ss.WriteLine(" ", project.RootNamespace); + foreach (string file in project.Files) + { + switch (project.Files.GetBuildAction(file)) + { + case BuildAction.EmbeddedResource: + ss.WriteLine(" {0}", ""); + break; + default: + if (project.Files.GetSubType(file) != SubType.Code && project.Files.GetSubType(file) != SubType.Settings) + { + ss.WriteLine(" ", file.Substring(0, file.LastIndexOf('.')) + ".resx"); + } + break; + } + } + //if (project.Files.GetSubType(file).ToString() != "Code") + //{ + // ps.WriteLine(" ", file.Substring(0, file.LastIndexOf('.')) + ".resx"); + + ss.WriteLine(" "); + ss.WriteLine(" "); + foreach (string file in project.Files) + { + switch (project.Files.GetBuildAction(file)) + { + case BuildAction.Compile: + ss.WriteLine(" "); + break; + default: + break; + } + } + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + foreach (ReferenceNode refr in project.References) + { + ss.WriteLine(" ", '/')); + } + ss.WriteLine(" "); + + ss.WriteLine(" "); + ss.WriteLine(" "); + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + + ss.WriteLine(" "); + if (hasDoc) + { + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.Write(" "); + } + else + { + ss.WriteLine(".exe\" />"); + } + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + // foreach(ReferenceNode refr in project.References) + // { + // string path = Helper.NormalizePath(Helper.MakePathRelativeTo(project.FullPath, BuildReferencePath(solution, refr)), '/'); + // if (path != "") + // { + // ss.WriteLine(" ", path); + // } + // } + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + } + ss.WriteLine(" "); + ss.WriteLine(""); + } + m_Kernel.CurrentWorkingDirectory.Pop(); + } + + private void WriteCombine(SolutionNode solution) + { + m_Kernel.Log.Write("Creating Xcode build files"); + foreach (ProjectNode project in solution.Projects) + { + if (m_Kernel.AllowProject(project.FilterGroups)) + { + m_Kernel.Log.Write("...Creating project: {0}", project.Name); + WriteProject(solution, project); + } + } + + m_Kernel.Log.Write(""); + DirectoryInfo directoryInfo = new DirectoryInfo(Path.Combine(solution.FullPath, solution.Name + ".xcodeproj")); + if (!directoryInfo.Exists) + { + directoryInfo.Create(); + } + string combFile = Helper.MakeFilePath(Path.Combine(solution.FullPath, solution.Name + ".xcodeproj"), "project", "pbxproj"); + StreamWriter ss = new StreamWriter(combFile); + + m_Kernel.CurrentWorkingDirectory.Push(); + Helper.SetCurrentDir(Path.GetDirectoryName(combFile)); + + using (ss) + { + ss.WriteLine(""); + ss.WriteLine("", solution.Name); + ss.WriteLine(" "); + ss.WriteLine(); + + //ss.WriteLine(" "); + //ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + + foreach (ConfigurationNode conf in solution.Configurations) + { + // Set the project.config to a non-debug configuration + if (conf.Options["DebugInformation"].ToString().ToLower() != "true") + { + ss.WriteLine(" ", conf.Name); + } + ss.WriteLine(); + ss.WriteLine(" ", conf.Name); + ss.WriteLine(" ", conf.Name); + ss.WriteLine(" ", conf.Options["DebugInformation"].ToString().ToLower()); + ss.WriteLine(" "); + ss.WriteLine(); + } + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + //ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(" "); + //foreach(ProjectNode project in solution.Projects) + //{ + // string path = Helper.MakePathRelativeTo(solution.FullPath, project.FullPath); + // ss.Write(" "); + //} + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + + foreach (ProjectNode project in solution.ProjectsTableOrder) + { + string path = Helper.MakePathRelativeTo(solution.FullPath, project.FullPath); + ss.Write(" "); + } + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(); + ss.WriteLine(" "); + ss.WriteLine(); + //ss.WriteLine(" "); + ss.WriteLine(" "); + ss.WriteLine(); + + ss.WriteLine(" "); + ss.WriteLine(" "); + foreach (ProjectNode project in solution.Projects) + { + string path = Helper.MakePathRelativeTo(solution.FullPath, project.FullPath); + ss.Write(" "); + } + ss.WriteLine(" "); + ss.WriteLine(); + ss.WriteLine(""); + } + + m_Kernel.CurrentWorkingDirectory.Pop(); + } + + private void CleanProject(ProjectNode project) + { + m_Kernel.Log.Write("...Cleaning project: {0}", project.Name); + string projectFile = Helper.MakeFilePath(project.FullPath, project.Name + (project.Type == ProjectType.Library ? ".dll" : ".exe"), "build"); + Helper.DeleteIfExists(projectFile); + } + + private void CleanSolution(SolutionNode solution) + { + m_Kernel.Log.Write("Cleaning Xcode build files for", solution.Name); + + string slnFile = Helper.MakeFilePath(solution.FullPath, solution.Name, "build"); + Helper.DeleteIfExists(slnFile); + + foreach (ProjectNode project in solution.Projects) + { + CleanProject(project); + } + + m_Kernel.Log.Write(""); + } + + #endregion + + #region ITarget Members + + /// + /// Writes the specified kern. + /// + /// The kern. + public void Write(Kernel kern) + { + if (kern == null) + { + throw new ArgumentNullException("kern"); + } + m_Kernel = kern; + foreach (SolutionNode solution in kern.Solutions) + { + WriteCombine(solution); + } + m_Kernel = null; + } + + /// + /// Cleans the specified kern. + /// + /// The kern. + public virtual void Clean(Kernel kern) + { + if (kern == null) + { + throw new ArgumentNullException("kern"); + } + m_Kernel = kern; + foreach (SolutionNode sol in kern.Solutions) + { + CleanSolution(sol); + } + m_Kernel = null; + } + + /// + /// Gets the name. + /// + /// The name. + public string Name + { + get + { + return "xcode"; + } + } + + #endregion + } +} diff --git a/Prebuild/src/Core/UnknownLanguageException.cs b/Prebuild/src/Core/UnknownLanguageException.cs new file mode 100644 index 0000000000..607b66c022 --- /dev/null +++ b/Prebuild/src/Core/UnknownLanguageException.cs @@ -0,0 +1,63 @@ +/* + * $RCSfile$ + * Copyright (C) 2004, 2005 David Hudson (jendave@yahoo.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +using System; +using System.Runtime.Serialization; + +namespace Prebuild.Core +{ + /// + /// + [Serializable()] + public class UnknownLanguageException : Exception + { + /// + /// Basic exception. + /// + public UnknownLanguageException() + { + } + + /// + /// Exception with specified string + /// + /// Exception message + public UnknownLanguageException(string message): base(message) + { + } + + /// + /// + /// + /// + /// + public UnknownLanguageException(string message, Exception exception) : base(message, exception) + { + } + + /// + /// + /// + /// + /// + protected UnknownLanguageException(SerializationInfo info, StreamingContext context) : base( info, context ) + { + } + } +} diff --git a/Prebuild/src/Core/Utilities/CommandLineCollection.cs b/Prebuild/src/Core/Utilities/CommandLineCollection.cs new file mode 100644 index 0000000000..786fa1e49d --- /dev/null +++ b/Prebuild/src/Core/Utilities/CommandLineCollection.cs @@ -0,0 +1,152 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System.Collections; +using System.Collections.Generic; + +namespace Prebuild.Core.Utilities +{ + /// + /// The CommandLine class parses and interprets the command-line arguments passed to + /// prebuild. + /// + public class CommandLineCollection : IEnumerable> + { + #region Fields + + // The raw OS arguments + private readonly string[] m_RawArgs; + + // Command-line argument storage + private readonly Dictionary m_Arguments = new Dictionary(); + + #endregion + + #region Constructors + + /// + /// Create a new CommandLine instance and set some internal variables. + /// + public CommandLineCollection(string[] args) + { + m_RawArgs = args; + + Parse(); + } + + #endregion + + #region Private Methods + + private void Parse() + { + if(m_RawArgs.Length < 1) + return; + + int idx = 0; + string lastArg = null; + + while(idx 2 && arg[0] == '/') + { + arg = arg.Substring(1); + lastArg = arg; + m_Arguments[arg] = ""; + } + else + { + if(lastArg != null) + { + m_Arguments[lastArg] = arg; + lastArg = null; + } + } + + idx++; + } + } + + #endregion + + #region Public Methods + + /// + /// Wases the passed. + /// + /// The arg. + /// + public bool WasPassed(string arg) + { + return (m_Arguments.ContainsKey(arg)); + } + + #endregion + + #region Properties + + /// + /// Gets the parameter associated with the command line option + /// + /// Returns null if option was not specified, + /// null string if no parameter was specified, and the value if a parameter was specified + public string this[string index] + { + get + { + if(m_Arguments.ContainsKey(index)) + { + return (m_Arguments[index]); + } + return null; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator that can iterate through a collection. + /// + /// + /// An + /// that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + return m_Arguments.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + } +} diff --git a/Prebuild/src/Core/Utilities/CurrentDirectory.cs b/Prebuild/src/Core/Utilities/CurrentDirectory.cs new file mode 100644 index 0000000000..9624c35ed5 --- /dev/null +++ b/Prebuild/src/Core/Utilities/CurrentDirectory.cs @@ -0,0 +1,68 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System; +using System.Collections.Generic; + +namespace Prebuild.Core.Utilities +{ + /// + /// + /// + public class CurrentDirectory + { + #region Fields + + private readonly Stack m_Stack = new Stack(); + + #endregion + + #region Public Methods + + /// + /// Pushes this instance. + /// + public void Push() + { + m_Stack.Push(Environment.CurrentDirectory); + } + + /// + /// Pops this instance. + /// + public void Pop() + { + if(m_Stack.Count < 1) + { + return; + } + + string cwd = m_Stack.Pop(); + Helper.SetCurrentDir(cwd); + } + + #endregion + } +} diff --git a/Prebuild/src/Core/Utilities/Helper.cs b/Prebuild/src/Core/Utilities/Helper.cs new file mode 100644 index 0000000000..8c3e9680c5 --- /dev/null +++ b/Prebuild/src/Core/Utilities/Helper.cs @@ -0,0 +1,575 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Collections.Specialized; +using System.Xml; +using Prebuild.Core.Nodes; + +namespace Prebuild.Core.Utilities +{ + /// + /// + /// + public class Helper + { + #region Fields + + static bool checkForOSVariables; + + /// + /// + /// + public static bool CheckForOSVariables + { + get + { + return checkForOSVariables; + } + set + { + checkForOSVariables = value; + } + } + + #endregion + + #region Public Methods + + #region String Parsing + + public delegate string StringLookup(string key); + + /// + /// Gets a collection of StringLocationPair objects that represent the matches + /// + /// The target. + /// The before group. + /// The after group. + /// if set to true [include delimiters in substrings]. + /// + public static StringCollection FindGroups(string target, string beforeGroup, string afterGroup, bool includeDelimitersInSubstrings) + { + if( beforeGroup == null ) + { + throw new ArgumentNullException("beforeGroup"); + } + if( afterGroup == null ) + { + throw new ArgumentNullException("afterGroup"); + } + StringCollection results = new StringCollection(); + if(target == null || target.Length == 0) + { + return results; + } + + int beforeMod = 0; + int afterMod = 0; + if(includeDelimitersInSubstrings) + { + //be sure to not exlude the delims + beforeMod = beforeGroup.Length; + afterMod = afterGroup.Length; + } + int startIndex = 0; + while((startIndex = target.IndexOf(beforeGroup,startIndex)) != -1) { + int endIndex = target.IndexOf(afterGroup,startIndex);//the index of the char after it + if(endIndex == -1) + { + break; + } + int length = endIndex - startIndex - beforeGroup.Length;//move to the first char in the string + string substring = substring = target.Substring(startIndex + beforeGroup.Length - beforeMod, + length - afterMod); + + results.Add(substring); + //results.Add(new StringLocationPair(substring,startIndex)); + startIndex = endIndex + 1; + //the Interpolate*() methods will not work if expressions are expandded inside expression due to an optimization + //so start after endIndex + + } + return results; + } + + /// + /// Replaces the groups. + /// + /// The target. + /// The before group. + /// The after group. + /// The lookup. + /// + public static string ReplaceGroups(string target, string beforeGroup, string afterGroup, StringLookup lookup) { + if( target == null ) + { + throw new ArgumentNullException("target"); + } + //int targetLength = target.Length; + StringCollection strings = FindGroups(target,beforeGroup,afterGroup,false); + if( lookup == null ) + { + throw new ArgumentNullException("lookup"); + } + foreach(string substring in strings) + { + target = target.Replace(beforeGroup + substring + afterGroup, lookup(substring) ); + } + return target; + } + + /// + /// Replaces ${var} statements in a string with the corresonding values as detirmined by the lookup delegate + /// + /// The target. + /// The lookup. + /// + public static string InterpolateForVariables(string target, StringLookup lookup) + { + return ReplaceGroups(target, "${" , "}" , lookup); + } + + /// + /// Replaces ${var} statements in a string with the corresonding environment variable with name var + /// + /// + /// + public static string InterpolateForEnvironmentVariables(string target) + { + return InterpolateForVariables(target, new StringLookup(Environment.GetEnvironmentVariable)); + } + + #endregion + + /// + /// Translates the value. + /// + /// Type of the translate. + /// The translation item. + /// + public static object TranslateValue(Type translateType, string translationItem) + { + if(translationItem == null) + { + return null; + } + + try + { + string lowerVal = translationItem.ToLower(); + if(translateType == typeof(bool)) + { + return (lowerVal == "true" || lowerVal == "1" || lowerVal == "y" || lowerVal == "yes" || lowerVal == "on"); + } + else if(translateType == typeof(int)) + { + return (Int32.Parse(translationItem)); + } + else + { + return translationItem; + } + } + catch(FormatException) + { + return null; + } + } + + /// + /// Deletes if exists. + /// + /// The file. + /// + public static bool DeleteIfExists(string file) + { + string resFile = null; + try + { + resFile = ResolvePath(file); + } + catch(ArgumentException) + { + return false; + } + + if(!File.Exists(resFile)) + { + return false; + } + + File.Delete(resFile); + return true; + } + + static readonly char seperator = Path.DirectorySeparatorChar; + + // This little gem was taken from the NeL source, thanks guys! + /// + /// Makes a relative path + /// + /// Path to start from + /// Path to end at + /// Path that will get from startPath to endPath + public static string MakePathRelativeTo(string startPath, string endPath) + { + string tmp = NormalizePath(startPath, seperator); + string src = NormalizePath(endPath, seperator); + string prefix = ""; + + while(true) + { + if((String.Compare(tmp, 0, src, 0, tmp.Length) == 0)) + { + string ret; + int size = tmp.Length; + if(size == src.Length) + { + return "./"; + } + if((src.Length > tmp.Length) && src[tmp.Length - 1] != seperator) + { + } + else + { + ret = prefix + endPath.Substring(size, endPath.Length - size); + ret = ret.Trim(); + if(ret[0] == seperator) + { + ret = "." + ret; + } + + return NormalizePath(ret); + } + + } + + if(tmp.Length < 2) + { + break; + } + + int lastPos = tmp.LastIndexOf(seperator, tmp.Length - 2); + int prevPos = tmp.IndexOf(seperator); + + if((lastPos == prevPos) || (lastPos == -1)) + { + break; + } + + tmp = tmp.Substring(0, lastPos + 1); + prefix += ".." + seperator.ToString(); + } + + return endPath; + } + + /// + /// Resolves the path. + /// + /// The path. + /// + public static string ResolvePath(string path) + { + string tmpPath = NormalizePath(path); + if(tmpPath.Length < 1) + { + tmpPath = "."; + } + + tmpPath = Path.GetFullPath(tmpPath); + if(!File.Exists(tmpPath) && !Directory.Exists(tmpPath)) + { + throw new ArgumentException("Path could not be resolved: " + tmpPath); + } + + return tmpPath; + } + + /// + /// Normalizes the path. + /// + /// The path. + /// The separator character. + /// + public static string NormalizePath(string path, char separatorCharacter) + { + if(path == null || path == "" || path.Length < 1) + { + return ""; + } + + string tmpPath = path.Replace('\\', '/'); + tmpPath = tmpPath.Replace('/', separatorCharacter); + return tmpPath; + } + + /// + /// Normalizes the path. + /// + /// The path. + /// + public static string NormalizePath(string path) + { + return NormalizePath(path, Path.DirectorySeparatorChar); + } + + /// + /// Ends the path. + /// + /// The path. + /// The separator character. + /// + public static string EndPath(string path, char separatorCharacter) + { + if(path == null || path == "" || path.Length < 1) + { + return ""; + } + + if(!path.EndsWith(separatorCharacter.ToString())) + { + return (path + separatorCharacter); + } + + return path; + } + + /// + /// Ends the path. + /// + /// The path. + /// + public static string EndPath(string path) + { + return EndPath(path, Path.DirectorySeparatorChar); + } + + /// + /// Makes the file path. + /// + /// The path. + /// The name. + /// The ext. + /// + public static string MakeFilePath(string path, string name, string ext) + { + string ret = EndPath(NormalizePath(path)); + + if( name == null ) + { + throw new ArgumentNullException("name"); + } + + ret += name; + if(!name.EndsWith("." + ext)) + { + ret += "." + ext; + } + + //foreach(char c in Path.GetInvalidPathChars()) + //{ + // ret = ret.Replace(c, '_'); + //} + + return ret; + } + + /// + /// Makes the file path. + /// + /// The path. + /// The name. + /// + public static string MakeFilePath(string path, string name) + { + string ret = EndPath(NormalizePath(path)); + + if( name == null ) + { + throw new ArgumentNullException("name"); + } + + ret += name; + + //foreach (char c in Path.GetInvalidPathChars()) + //{ + // ret = ret.Replace(c, '_'); + //} + + return ret; + } + + /// + /// + /// + /// + /// + public static string MakeReferencePath(string path) + { + string ret = EndPath(NormalizePath(path)); + + //foreach (char c in Path.GetInvalidPathChars()) + //{ + // ret = ret.Replace(c, '_'); + //} + + return ret; + } + + /// + /// Sets the current dir. + /// + /// The path. + public static void SetCurrentDir(string path) + { + if( path == null ) + { + throw new ArgumentNullException("path"); + } + if(path.Length < 1) + { + return; + } + + Environment.CurrentDirectory = path; + } + + /// + /// Checks the type. + /// + /// The type to check. + /// The attr. + /// The inter. + /// + public static object CheckType(Type typeToCheck, Type attr, Type inter) + { + if(typeToCheck == null || attr == null) + { + return null; + } + + object[] attrs = typeToCheck.GetCustomAttributes(attr, false); + if(attrs == null || attrs.Length < 1) + { + return null; + } + if( inter == null ) + { + throw new ArgumentNullException("inter"); + } + + if(typeToCheck.GetInterface(inter.FullName) == null) + { + return null; + } + + return attrs[0]; + } + + /// + /// Attributes the value. + /// + /// The node. + /// The attr. + /// The def. + /// + public static string AttributeValue(XmlNode node, string attr, string def) + { + if( node == null ) + { + throw new ArgumentNullException("node"); + } + if(node.Attributes[attr] == null) + { + return def; + } + string val = node.Attributes[attr].Value; + if(!CheckForOSVariables) + { + return val; + } + + return InterpolateForEnvironmentVariables(val); + } + + /// + /// Parses the boolean. + /// + /// The node. + /// The attr. + /// if set to true [default value]. + /// + public static bool ParseBoolean(XmlNode node, string attr, bool defaultValue) + { + if( node == null ) + { + throw new ArgumentNullException("node"); + } + if(node.Attributes[attr] == null) + { + return defaultValue; + } + return bool.Parse(node.Attributes[attr].Value); + } + + /// + /// Enums the attribute value. + /// + /// The node. + /// The attr. + /// Type of the enum. + /// The def. + /// + public static object EnumAttributeValue(XmlNode node, string attr, Type enumType, object def) + { + if( def == null ) + { + throw new ArgumentNullException("def"); + } + string val = AttributeValue(node, attr, def.ToString()); + return Enum.Parse(enumType, val, true); + } + + /// + /// + /// + /// + /// + /// + public static string AssemblyFullName(string assemblyName, ProjectType projectType) + { + return assemblyName + (projectType == ProjectType.Library ? ".dll" : ".exe"); + } + + #endregion + } +} diff --git a/Prebuild/src/Core/Utilities/Log.cs b/Prebuild/src/Core/Utilities/Log.cs new file mode 100644 index 0000000000..4df3defdd0 --- /dev/null +++ b/Prebuild/src/Core/Utilities/Log.cs @@ -0,0 +1,276 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System; +using System.IO; + +namespace Prebuild.Core.Utilities +{ + /// + /// + /// + public enum LogType + { + /// + /// + /// + None, + /// + /// + /// + Info, + /// + /// + /// + Warning, + /// + /// + /// + Error + } + + /// + /// + /// + [Flags] + public enum LogTargets + { + /// + /// + /// + None = 0, + /// + /// + /// + Null = 1, + /// + /// + /// + File = 2, + /// + /// + /// + Console = 4 + } + + /// + /// Summary description for Log. + /// + public class Log : IDisposable + { + #region Fields + + private TextWriter m_Writer; + private LogTargets m_Target = LogTargets.Null; + bool disposed; + + #endregion + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The target. + /// Name of the file. + public Log(LogTargets target, string fileName) + { + m_Target = target; + + if ((m_Target & LogTargets.File) != 0) + { + m_Writer = new StreamWriter(fileName, false); + } + else if ((m_Target & LogTargets.Console) != 0) + { + // Prevents null reference exceptions when outputing to the log file. + // This seems to only happen when running on a network drive. + m_Writer = Console.Out; + } + } + + #endregion + + #region Public Methods + + /// + /// Writes this instance. + /// + public void Write() + { + Write(string.Empty); + } + + /// + /// Writes the specified MSG. + /// + /// The MSG. + public void Write(string msg) + { + if((m_Target & LogTargets.Null) != 0) + { + return; + } + + if((m_Target & LogTargets.Console) != 0) + { + Console.WriteLine(msg); + } + if((m_Target & LogTargets.File) != 0 && m_Writer != null) + { + m_Writer.WriteLine(msg); + } + } + + /// + /// Writes the specified format. + /// + /// The format. + /// The args. + public void Write(string format, params object[] args) + { + Write(string.Format(format,args)); + } + + /// + /// Writes the specified type. + /// + /// The type. + /// The format. + /// The args. + public void Write(LogType type, string format, params object[] args) + { + if((m_Target & LogTargets.Null) != 0) + { + return; + } + + string str = ""; + switch(type) + { + case LogType.Info: + str = "[I] "; + break; + case LogType.Warning: + str = "[!] "; + break; + case LogType.Error: + str = "[X] "; + break; + } + + Write(str + format,args); + } + + /// + /// Writes the exception. + /// + /// The type. + /// The ex. + public void WriteException(LogType type, Exception ex) + { + if(ex != null) + { + Write(type, ex.Message); + //#if DEBUG + m_Writer.WriteLine("Exception @{0} stack trace [[", ex.TargetSite.Name); + m_Writer.WriteLine(ex.StackTrace); + m_Writer.WriteLine("]]"); + //#endif + } + } + + /// + /// Flushes this instance. + /// + public void Flush() + { + if(m_Writer != null) + { + m_Writer.Flush(); + } + } + + #endregion + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or + /// resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Dispose objects + /// + /// + /// If true, it will dispose close the handle + /// + /// + /// Will dispose managed and unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (!this.disposed) + { + if (disposing) + { + if (m_Writer != null) + { + m_Writer.Close(); + m_Writer = null; + } + } + } + this.disposed = true; + } + + /// + /// + /// + ~Log() + { + this.Dispose(false); + } + + /// + /// Closes and destroys this object + /// + /// + /// Same as Dispose(true) + /// + public void Close() + { + Dispose(); + } + + #endregion + } +} diff --git a/Prebuild/src/Core/WarningException.cs b/Prebuild/src/Core/WarningException.cs new file mode 100644 index 0000000000..b7c3668cce --- /dev/null +++ b/Prebuild/src/Core/WarningException.cs @@ -0,0 +1,84 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +using System; +using System.Runtime.Serialization; + +namespace Prebuild.Core +{ + /// + /// + /// + [Serializable] + public class WarningException : Exception + { + #region Constructors + + /// + /// + /// + public WarningException() + { + } + + /// + /// + /// + /// + /// + public WarningException(string format, params object[] args) + : base(String.Format(format, args)) + { + } + + /// + /// Exception with specified string + /// + /// Exception message + public WarningException(string message): base(message) + { + } + + /// + /// + /// + /// + /// + public WarningException(string message, Exception exception) : base(message, exception) + { + } + + /// + /// + /// + /// + /// + protected WarningException(SerializationInfo info, StreamingContext context) : base( info, context ) + { + } + + #endregion + } +} diff --git a/Prebuild/src/Prebuild.cs b/Prebuild/src/Prebuild.cs new file mode 100644 index 0000000000..35a5dfaf44 --- /dev/null +++ b/Prebuild/src/Prebuild.cs @@ -0,0 +1,165 @@ +#region BSD License +/* +Copyright (c) 2004-2005 Matthew Holmes (matthew@wildfiregames.com), Dan Moorehead (dan05a@gmail.com) + +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. +* The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +*/ +#endregion + +#region CVS Information +/* + * $Source$ + * $Author: kunnis $ + * $Date: 2009-04-14 21:33:14 -0400 (Tue, 14 Apr 2009) $ + * $Revision: 308 $ + */ +#endregion + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.EnterpriseServices.Internal; + +using Prebuild.Core; +using Prebuild.Core.Utilities; + +namespace Prebuild +{ + /// + /// + /// + class Prebuild + { + #region Main + + [STAThread] + static void Main(string[] args) + { + Kernel kernel = null; + try + { + kernel = Kernel.Instance; + kernel.Initialize(LogTargets.File | LogTargets.Console, args); + bool exit = false; + + if(kernel.CommandLine.WasPassed("usage")) + { + exit = true; + OutputUsage(); + } + if(kernel.CommandLine.WasPassed("showtargets")) + { + exit = true; + OutputTargets(kernel); + } + if(kernel.CommandLine.WasPassed("install")) + { + exit = true; + InstallAssembly(kernel); + } + if(kernel.CommandLine.WasPassed("remove")) + { + exit = true; + RemoveAssembly(kernel); + } + + if(!exit) + { + kernel.Process(); + } + } +#if !DEBUG + catch (Exception ex) + { + Console.WriteLine("Unhandled error: {0}", ex.Message); + Console.WriteLine("{0}", ex.StackTrace); + } +#endif + finally + { + if(kernel != null && kernel.PauseAfterFinish) + { + Console.WriteLine("\nPress enter to continue..."); + Console.ReadLine(); + } + } + } + + #endregion + + #region Private Methods + + private static void InstallAssembly(Kernel kernel) + { + Publish publish = new Publish(); + string file = kernel.CommandLine["install"]; + //Console.WriteLine(".."+file+".."); + publish.GacInstall(file); + } + + private static void RemoveAssembly(Kernel kernel) + { + Publish publish = new Publish(); + string file = kernel.CommandLine["remove"]; + publish.GacRemove(file); + } + + private static void OutputUsage() + { + Console.WriteLine("Usage: prebuild /target [options]"); + Console.WriteLine("Available command-line switches:"); + Console.WriteLine(); + Console.WriteLine("/target Target for Prebuild"); + Console.WriteLine("/clean Clean the build files for the given target"); + Console.WriteLine("/file XML file to process"); + Console.WriteLine("/log Log file to write to"); + Console.WriteLine("/ppo Pre-process the file, but perform no other processing"); + Console.WriteLine("/pause Pauses the application after execution to view the output"); + Console.WriteLine("/yes Default to yes to any questions asked"); + Console.WriteLine("/install Install assembly into the GAC"); + Console.WriteLine("/remove Remove assembly from the GAC"); + Console.WriteLine(); + Console.WriteLine("See 'prebuild /showtargets for a list of available targets"); + Console.WriteLine("See readme.txt or check out http://dnpb.sourceforge.net for more information"); + Console.WriteLine(); + } + + private static void OutputTargets(Kernel kern) + { + Console.WriteLine("Targets available in Prebuild:"); + Console.WriteLine(""); + if(kern.Targets.Keys.Count > 0) + { + string[] targs = new string[kern.Targets.Keys.Count]; + kern.Targets.Keys.CopyTo(targs, 0); + Array.Sort(targs); + foreach(string target in targs) + { + Console.WriteLine(target); + } + } + Console.WriteLine(""); + } + + #endregion + } +} diff --git a/Prebuild/src/Prebuild.snk b/Prebuild/src/Prebuild.snk new file mode 100644 index 0000000000..f9dce054ff Binary files /dev/null and b/Prebuild/src/Prebuild.snk differ diff --git a/Prebuild/src/Properties/AssemblyInfo.cs b/Prebuild/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..013743d665 --- /dev/null +++ b/Prebuild/src/Properties/AssemblyInfo.cs @@ -0,0 +1,113 @@ +#region BSD License +/* + +Copyright (c) 2004 - 2008 +Matthew Holmes (matthew@wildfiregames.com), +Dan Moorehead (dan05a@gmail.com), +Dave Hudson (jendave@yahoo.com), +Rob Loach (http://www.robloach.net), +C.J. Adams-Collier (cjac@colliertech.org), + +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. + +* The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + +*/ +#endregion + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Resources; + + +// FxCop recommended attributes +[assembly: ComVisible(false)] +[assembly: FileIOPermission(SecurityAction.RequestMinimum, Unrestricted=true)] +[assembly: CLSCompliant(true)] + +// +// 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(".NET Prebuild")] +[assembly: AssemblyDescription("A .NET project file build tool")] +[assembly: AssemblyConfiguration(".NET CLR")] +[assembly: AssemblyCompany("The Prebuild Project")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright 2004-2013 " + + "Matthew Holmes, " + + "Dan Moorehead, " + + "C.J. Adams-Collier, " + + "Rob Loach, " + + "David Hudson," + + "John Hurliman")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguageAttribute("en-US")] +[assembly: AssemblyVersion("2.0.6.*")] + +// +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +// +// In order to sign your assembly you must specify a key to use. Refer to the +// Microsoft .NET Framework documentation for more information on assembly signing. +// +// Use the attributes below to control which key is used for signing. +// +// Notes: +// (*) If no key is specified, the assembly is not signed. +// (*) KeyName refers to a key that has been installed in the Crypto Service +// Provider (CSP) on your machine. KeyFile refers to a file which contains +// a key. +// (*) If the KeyFile and the KeyName values are both specified, the +// following processing occurs: +// (1) If the KeyName can be found in the CSP, that key is used. +// (2) If the KeyName does not exist and the KeyFile does exist, the key +// in the KeyFile is installed into the CSP and used. +// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility. +// When specifying the KeyFile, the location of the KeyFile should be +// relative to the project output directory which is +// %Project Directory%\obj\. For example, if your KeyFile is +// located in the project directory, you would specify the AssemblyKeyFile +// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")] +// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework +// documentation for more information on this. +// +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyName("")] diff --git a/Prebuild/src/data/autotools.xml b/Prebuild/src/data/autotools.xml new file mode 100644 index 0000000000..ee4b0643dc --- /dev/null +++ b/Prebuild/src/data/autotools.xml @@ -0,0 +1,790 @@ + + + + + #!/bin/sh +# Run this to generate all the initial makefiles, etc. +# Ripped off from Mono, which ripped off from GNOME macros version + +DIE=0 + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +if [ -n "$MONO_PATH" ]; then + # from -> /mono/lib:/another/mono/lib + # to -> /mono /another/mono + for i in `echo ${MONO_PATH} | tr ":" " "`; do + i=`dirname ${i}` + if [ -n "{i}" -a -d "${i}/share/aclocal" ]; then + ACLOCAL_FLAGS="-I ${i}/share/aclocal $ACLOCAL_FLAGS" + fi + if [ -n "{i}" -a -d "${i}/bin" ]; then + PATH="${i}/bin:$PATH" + fi + done + export PATH +fi + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile Mono." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if [ -z "$LIBTOOL" ]; then + LIBTOOL=`which glibtool 2>/dev/null` + if [ ! -x "$LIBTOOL" ]; then + LIBTOOL=`which libtool` + fi +fi + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNU_GETTEXT" $srcdir/configure.ac >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.ac >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed to compile Mono." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$NOCONFIGURE"; then + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + + +if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + ${LIBTOOL}ize --force --copy + fi +fi + +echo "Running aclocal $ACLOCAL_FLAGS ..." +aclocal $ACLOCAL_FLAGS || { + echo + echo "**Error**: aclocal failed. This may mean that you have not" + echo "installed all of the packages you need, or you may need to" + echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\"" + echo "for the prefix where you installed the packages whose" + echo "macros were not found" + exit 1 +} + +if grep "^AM_CONFIG_HEADER" configure.ac >/dev/null; then + echo "Running autoheader..." + autoheader || { echo "**Error**: autoheader failed."; exit 1; } +fi + +echo "Running automake --gnu $am_opt ..." +automake --add-missing --gnu $am_opt || + { echo "**Error**: automake failed."; exit 1; } +echo "Running autoconf ..." +autoconf || { echo "**Error**: autoconf failed."; exit 1; } + +conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME || exit 1 +else + echo Skipping configure process. +fi + + + + + + + + + + + + + + + + AC_INIT([],[]) + +AC_PREREQ(2.60) +AC_CANONICAL_SYSTEM +AC_CONFIG_AUX_DIR(.) +AM_INIT_AUTOMAKE([1.9 tar-ustar foreign]) +AM_MAINTAINER_MODE +dnl AC_PROG_INTLTOOL([0.25]) +AC_PROG_INSTALL + +ASSEMBLY_NAME= +PROJECT_NAME= +PROJECT_VERSION=$VERSION +PROJECT_DESCRIPTION="" +PROJECT_TYPE="" + +AC_SUBST(ASSEMBLY_NAME) +AC_SUBST(PROJECT_NAME) +AC_SUBST(PROJECT_VERSION) +AC_SUBST(DESCRIPTION) + +AC_MSG_CHECKING([assembly type]) +case $PROJECT_TYPE in + *Exe) + ASSEMBLY_EXTENSION=exe + ;; + *Library) + ASSEMBLY_EXTENSION=dll + ;; + *) + AC_MSG_ERROR([*** Please add support for project type $PROJECT_TYPE to configure.ac checks!]) + ;; +esac +AC_MSG_RESULT([$PROJECT_TYPE]) + +AC_SUBST(ASSEMBLY_EXTENSION) + +AC_MSG_CHECKING([whether we're compiling from an RCS]) +if test -f "$srcdir/.cvs_version" ; then + from_rcs=cvs +else + if test -f "$srcdir/.svn/entries" ; then + from_rcs=svn + else + from_rcs=no + fi +fi + +AC_MSG_RESULT($from_rcs) + +MONO_REQUIRED_VERSION=1.1 + +PKG_CHECK_MODULES(MONO_DEPENDENCY, mono >= $MONO_REQUIRED_VERSION, has_mono=true, has_mono=false) + +if test "x$has_mono" = "xtrue"; then + AC_PATH_PROG(RUNTIME, mono, no) + AC_PATH_PROG(CSC, gmcs, no) + AC_PATH_PROG(RESGEN, resgen2, no) + if test `uname -s` = "Darwin"; then + LIB_PREFIX= + LIB_SUFFIX=.dylib + else + LIB_PREFIX=.so + LIB_SUFFIX= + fi +else + AC_PATH_PROG(CSC, csc.exe, no) + if test x$CSC = "xno"; then + AC_MSG_ERROR([You need to install either mono or .Net]) + else + RUNTIME= + LIB_PREFIX= + LIB_SUFFIX= + fi +fi + +AC_PATH_PROG(GACUTIL, gacutil) +if test "x$GACUTIL" = "xno" ; then + AC_MSG_ERROR([No gacutil tool found]) +fi + +GACUTIL_FLAGS='/package /gacdir $(DESTDIR)$(prefix)/lib' +AC_SUBST(GACUTIL_FLAGS) + +AC_SUBST(PATH) +AC_SUBST(LD_LIBRARY_PATH) + +AC_SUBST(LIB_PREFIX) +AC_SUBST(LIB_SUFFIX) +AC_SUBST(RUNTIME) +AC_SUBST(CSC) +AC_SUBST(RESGEN) +AC_SUBST(GACUTIL) + +AC_SUBST(BASE_DEPENDENCIES_CFLAGS) +AC_SUBST(BASE_DEPENDENCIES_LIBS) + +dnl Find monodoc +MONODOC_REQUIRED_VERSION=1.0 +AC_SUBST(MONODOC_REQUIRED_VERSION) + +PKG_CHECK_MODULES(MONODOC_DEPENDENCY, monodoc >= $MONODOC_REQUIRED_VERSION, enable_monodoc=yes, enable_monodoc=no) + +if test "x$enable_monodoc" = "xyes"; then + AC_PATH_PROG(MONODOC, monodoc, no) + if test x$MONODOC = xno; then + enable_monodoc=no + fi +else + MONODOC= +fi + +AC_SUBST(MONODOC) +AM_CONDITIONAL(ENABLE_MONODOC, test "x$enable_monodoc" = "xyes") + +winbuild=no +case "$host" in + *-*-mingw*|*-*-cygwin*) + winbuild=yes + ;; +esac +AM_CONDITIONAL(WINBUILD, test x$winbuild = xyes) + +AC_CONFIG_FILES() +AC_CONFIG_FILES(.pc) + +AC_CONFIG_FILES(Makefile) +AC_OUTPUT + +echo "===" +echo "" +echo "Project configuration summary" +echo "" +echo " * Installation prefix: $prefix" +echo " * compiler: $CSC" +echo " * Documentation: $enable_monodoc ($MONODOC)" +echo " * Project Name: $PROJECT_NAME" +echo " * Version: $PROJECT_VERSION" +echo "" +echo "===" + + + + + + + + + + + + + + + + + + + + + + + + + + +ASSEMBLY=$(ASSEMBLY_NAME).$(ASSEMBLY_EXTENSION) + + +dir = $(prefix)/lib/ +_DATA = $(ASSEMBLY) $(ASSEMBLY).config + +bin_SCRIPTS= + +pkgconfigdir = $(prefix)/lib/pkgconfig +pkgconfig_DATA = .pc + +dir = $(prefix)/lib/mono/ +_DATA = $(ASSEMBLY).config + +noinst_DATA = $(ASSEMBLY) + + +PACKAGES = +BINARY_LIBS = +SYSTEM_LIBS = +RESOURCES_SRC = +RESOURCES = $(RESOURCES_SRC:.resx=.resources) +SOURCES = + +EXTRA_DIST=$(SOURCES) $(BINARY_LIBS) $(RESOURCES_SRC) install-sh missing + +CLEANFILES=$(ASSEMBLY) + + + + + + + +/$(ASSEMBLY): $(srcdir)/$(ASSEMBLY).response $(RESOURCES) $(SOURCES) $(BINARY_LIBS) + mkdir -p doc && mkdir -p && $(CSC) /out:$@ \ + /target: \ + $(addprefix /resource:$(srcdir)/, $(RESOURCES)) \ + $(addprefix /pkg:, $(PACKAGES)) \ + $(addprefix /r:, $(SYSTEM_LIBS)) \ + $(addprefix /r:$(srcdir)/, $(BINARY_LIBS)) \ + @$(srcdir)/$(ASSEMBLY).response \ + /doc:doc/ \ + /keyfile:$(srcdir)/ \ + /unsafe \ + && rm -f $(ASSEMBLY) \ + && ln $@ $(ASSEMBLY) + +CLEANFILES+=/$(ASSEMBLY) + + +EXTRA_DIST+= + +: /$(ASSEMBLY) + rm -f $(ASSEMBLY) \ + && ln /$(ASSEMBLY) $(ASSEMBLY) + + + + + +_install-data-local: /$(ASSEMBLY) + echo "$(GACUTIL) /i $(ASSEMBLY) /f $(GACUTIL_FLAGS)"; \ + $(GACUTIL) /i $(ASSEMBLY) /f $(GACUTIL_FLAGS) || exit 1; + +_uninstall-local: + if [`gacutil -l | grep "Number" | awk -F= '{print $$2}'` -gt "0" ] ; \ + then \ + echo "$(GACUTIL) /u $(ASSEMBLY_NAME) $(GACUTIL_FLAGS)"; \ + $(GACUTIL) /u $(ASSEMBLY_NAME) $(GACUTIL_FLAGS) || exit 1; \ + fi + + + +noinst__dir = $(prefix)/lib/mono/ +noinst___DATA = /$(ASSEMBLY) + + + + + + +$(ASSEMBLY): + + +$(srcdir)/$(ASSEMBLY).response: $(srcdir)/Makefile + echo "$(addprefix $(srcdir)/, $(SOURCES))" > $@ + + +all: $(ASSEMBLY) + +# rule to compile .resx files to .resources +%.resources: %.resx + $(RESGEN) /useSourcePath /compile $(@:.resources=.resx) + + + + + +install-data-local: _install-data-local + +uninstall-local: _uninstall-local + + + +#dir+=$(noinst__dir) +#_DATA+=$(noinst___DATA) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +prefix=@prefix@ +exec_prefix=${prefix} +libdir=${exec_prefix}/lib/mono/@PROJECT_NAME@ + +Name: +Description: +Version: @PROJECT_VERSION@ +Requires: +Libs: -r:${libdir}/@PROJECT_NAME@.dll + + + + + + + + + + + #!/bin/sh +# Run this to generate all the initial makefiles, etc. +# Ripped off from Mono, which ripped off from GNOME macros version + +DIE=0 + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir=. + +if [ -n "$MONO_PATH" ]; then + # from -> /mono/lib:/another/mono/lib + # to -> /mono /another/mono + for i in `echo ${MONO_PATH} | tr ":" " "`; do + i=`dirname ${i}` + if [ -n "{i}" -a -d "${i}/share/aclocal" ]; then + ACLOCAL_FLAGS="-I ${i}/share/aclocal $ACLOCAL_FLAGS" + fi + if [ -n "{i}" -a -d "${i}/bin" ]; then + PATH="${i}/bin:$PATH" + fi + done + export PATH +fi + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to compile Mono." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +if [ -z "$LIBTOOL" ]; then + LIBTOOL=`which glibtool 2>/dev/null` + if [ ! -x "$LIBTOOL" ]; then + LIBTOOL=`which libtool` + fi +fi + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.ac >/dev/null) && { + ($LIBTOOL --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNU_GETTEXT" $srcdir/configure.ac >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.ac >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed to compile Mono." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed to compile Mono." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + + +if grep "^AM_PROG_LIBTOOL" configure.ac >/dev/null; then + if test -z "$NO_LIBTOOLIZE" ; then + echo "Running libtoolize..." + ${LIBTOOL}ize --force --copy + fi +fi + +echo "Running aclocal $ACLOCAL_FLAGS ..." +aclocal $ACLOCAL_FLAGS || { + echo + echo "**Error**: aclocal failed. This may mean that you have not" + echo "installed all of the packages you need, or you may need to" + echo "set ACLOCAL_FLAGS to include \"-I \$prefix/share/aclocal\"" + echo "for the prefix where you installed the packages whose" + echo "macros were not found" + exit 1 +} + +if grep "^AM_CONFIG_HEADER" configure.ac >/dev/null; then + echo "Running autoheader..." + autoheader || { echo "**Error**: autoheader failed."; exit 1; } +fi + +echo "Running automake --gnu $am_opt ..." +automake --add-missing --gnu $am_opt || + { echo "**Error**: automake failed."; exit 1; } +echo "Running autoconf ..." +autoconf || { echo "**Error**: autoconf failed."; exit 1; } + + +echo Running /autogen.sh ... +(cd $srcdir/ ; NOCONFIGURE=1 /bin/sh ./autogen.sh "$@") +echo Done running /autogen.sh ... + + +conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME || exit 1 +else + echo Skipping configure process. +fi + + + + + + + + + + +AC_INIT([]-solution,[]) +AC_CONFIG_AUX_DIR(.) +AM_INIT_AUTOMAKE([1.9 tar-ustar foreign]) +EXTRA_DIST="install-sh missing" +SOLUTION_NAME= +SOLUTION_VERSION=$VERSION +SOLUTION_DESCRIPTION="" +AC_SUBST(DESCRIPTION) + +AM_MAINTAINER_MODE + +dnl AC_PROG_INTLTOOL([0.25]) + +AC_PROG_INSTALL + +AC_MSG_CHECKING([whether we're building from an RCS]) +if test -f "$srcdir/.cvs_version" ; then + from_rcs=cvs +else + if test -f "$srcdir/.svn/entries" ; then + from_rcs=svn + else + from_rcs=no + fi +fi + +AC_MSG_RESULT($from_rcs) + +CONFIG="Release" +AC_SUBST(CONFIG) + +AC_CONFIG_SUBDIRS( +) + + +AC_OUTPUT([ +Makefile +]) + +echo "===" +echo "" +echo "Solution configuration summary" +echo "" +echo " * Solution Name: $SOLUTION_NAME" +echo " * Version: $SOLUTION_VERSION" +echo " * Packages:" +echo " - " +echo "" +echo "===" + + + + + + + + + SUBDIRS = + + + + + + + + + + + + +#! /bin/sh + +PACKAGE= +prefix=@prefix@ +exec_prefix=@exec_prefix@ + +# %%$@%$# why oh why isn't it $sharedir/ +# Day changed to 30 Mar 2007 +# ... +# 07:50 < cj> why are we installing .exe assemblies to $prefix/lib/$package/ and +# not $prefix/share/$package ? +# 07:50 < jonp> momentum. +# 07:50 < jonp> and it's hard to say that a .exe isn't platform specific +# 07:50 < jonp> as it can still contain DllImport's which make platform +# assumptions + +packagedir=$prefix/lib/ +export MONO_PATH=$MONO_PATH + +exec @RUNTIME@ $packagedir/$PACKAGE.exe "$@" + + + + + diff --git a/Prebuild/src/data/dnpb-1.0.xsd b/Prebuild/src/data/dnpb-1.0.xsd new file mode 100644 index 0000000000..b9e0e4eaf5 --- /dev/null +++ b/Prebuild/src/data/dnpb-1.0.xsd @@ -0,0 +1,183 @@ + + + + + Copyright (c) 2004 Matthew Holmes (kerion@houston.rr.com) + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Prebuild/src/data/dnpb-1.1.xsd b/Prebuild/src/data/dnpb-1.1.xsd new file mode 100644 index 0000000000..2c065a3297 --- /dev/null +++ b/Prebuild/src/data/dnpb-1.1.xsd @@ -0,0 +1,184 @@ + + + + + Copyright (c) 2004 Matthew Holmes (kerion@houston.rr.com) + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Prebuild/src/data/dnpb-1.2.xsd b/Prebuild/src/data/dnpb-1.2.xsd new file mode 100644 index 0000000000..d694ab43d7 --- /dev/null +++ b/Prebuild/src/data/dnpb-1.2.xsd @@ -0,0 +1,198 @@ + + + + + Copyright (c) 2004 Matthew Holmes (calefaction _at_ houston _._ rr _._ com) + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Prebuild/src/data/dnpb-1.3.xsd b/Prebuild/src/data/dnpb-1.3.xsd new file mode 100644 index 0000000000..8f31a544ab --- /dev/null +++ b/Prebuild/src/data/dnpb-1.3.xsd @@ -0,0 +1,206 @@ + + + + + Copyright (c) 2004-2005 Matthew Holmes (calefaction at houston . rr . com), Dan Moorehead (dan05a at gmail . com) + + .NET Pre-Build is an XML-driven pre-build tool allowing developers to + easily generate project or make files for major IDE's and .NET + development tools including: Visual Studio 2003, Visual Studio 2002, + SharpDevelop, MonoDevelop, and NAnt. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Prebuild/src/data/dnpb-1.4.xsd b/Prebuild/src/data/dnpb-1.4.xsd new file mode 100644 index 0000000000..1da50fcc7a --- /dev/null +++ b/Prebuild/src/data/dnpb-1.4.xsd @@ -0,0 +1,212 @@ + + + + + Copyright (c) 2004-2005 Matthew Holmes (calefaction at houston . rr . com), Dan Moorehead (dan05a at gmail . com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002 and + 2003, SharpDevelop, MonoDevelop, and NAnt. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/dnpb-1.5.xsd b/Prebuild/src/data/dnpb-1.5.xsd new file mode 100644 index 0000000000..e2b21f0c69 --- /dev/null +++ b/Prebuild/src/data/dnpb-1.5.xsd @@ -0,0 +1,215 @@ + + + + + Copyright (c) 2004-2005 Matthew Holmes (calefaction at houston . rr . com), Dan Moorehead (dan05a at gmail . com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002 and + 2003, SharpDevelop, MonoDevelop, and NAnt. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/prebuild-1.10.xsd b/Prebuild/src/data/prebuild-1.10.xsd new file mode 100644 index 0000000000..eee07a8f01 --- /dev/null +++ b/Prebuild/src/data/prebuild-1.10.xsd @@ -0,0 +1,338 @@ + + + + + Copyright (c) 2004-2007 + Matthew Holmes (calefaction at houston . rr . com), + Dan Moorehead (dan05a at gmail . com), + David Hudson (jendave at yahoo dot com), + C.J. Adams-Collier (cjac at colliertech dot com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002, + 2003, and 2005, SharpDevelop, MonoDevelop, NAnt, Xcode and the GNU Autotools. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/prebuild-1.6.xsd b/Prebuild/src/data/prebuild-1.6.xsd new file mode 100644 index 0000000000..57ebd2eb9d --- /dev/null +++ b/Prebuild/src/data/prebuild-1.6.xsd @@ -0,0 +1,231 @@ + + + + + Copyright (c) 2004-2005 Matthew Holmes (calefaction at houston . rr . com), Dan Moorehead (dan05a at gmail . com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002 and + 2003, SharpDevelop, MonoDevelop, and NAnt. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/prebuild-1.7.xsd b/Prebuild/src/data/prebuild-1.7.xsd new file mode 100644 index 0000000000..a7f5c88d09 --- /dev/null +++ b/Prebuild/src/data/prebuild-1.7.xsd @@ -0,0 +1,350 @@ + + + + + Copyright (c) 2004-2007 + Matthew Holmes (calefaction at houston . rr . com), + Dan Moorehead (dan05a at gmail . com), + David Hudson (jendave at yahoo dot com), + C.J. Adams-Collier (cjac at colliertech dot com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002, + 2003, and 2005, SharpDevelop, MonoDevelop, NAnt, Xcode and the GNU Autotools. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/prebuild-1.8.xsd b/Prebuild/src/data/prebuild-1.8.xsd new file mode 100644 index 0000000000..8f5c1a4566 --- /dev/null +++ b/Prebuild/src/data/prebuild-1.8.xsd @@ -0,0 +1,331 @@ + + + + + Copyright (c) 2004-2007 + Matthew Holmes (calefaction at houston . rr . com), + Dan Moorehead (dan05a at gmail . com), + David Hudson (jendave at yahoo dot com), + C.J. Adams-Collier (cjac at colliertech dot com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002, + 2003, and 2005, SharpDevelop, MonoDevelop, NAnt, Xcode and the GNU Autotools. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/src/data/prebuild-1.9.xsd b/Prebuild/src/data/prebuild-1.9.xsd new file mode 100644 index 0000000000..d647e087ae --- /dev/null +++ b/Prebuild/src/data/prebuild-1.9.xsd @@ -0,0 +1,336 @@ + + + + + Copyright (c) 2004-2007 + Matthew Holmes (calefaction at houston . rr . com), + Dan Moorehead (dan05a at gmail . com), + David Hudson (jendave at yahoo dot com), + C.J. Adams-Collier (cjac at colliertech dot com) + + .NET Prebuild is a cross-platform XML-driven pre-build tool which + allows developers to easily generate project or make files for major + IDE's and .NET development tools including: Visual Studio .NET 2002, + 2003, and 2005, SharpDevelop, MonoDevelop, NAnt, Xcode and the GNU Autotools. + + BSD License: + + 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. + * The name of the author may not be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR '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 AUTHOR 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. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Prebuild/tests/Makefile b/Prebuild/tests/Makefile new file mode 100644 index 0000000000..4a8ddf459d --- /dev/null +++ b/Prebuild/tests/Makefile @@ -0,0 +1,24 @@ +# +# Executable +# + +# Executables +PREBUILD = mono ../src/bin/Release/prebuild.exe /target makefile + +# Files +PREBUILDS = $(wildcard *.prebuild) +RESULTS = $(PREBUILDS:prebuild=results) +TESTS = $(PREBUILDS:prebuild=test) + +all: $(TESTS) + +clean: + rm -f *~ *.log + rm -f $(RESULTS) + +%.test: %.prebuild + $(PREBUILD) /log $*.log /file $*.prebuild /ppi $*.results >& /dev/null + if ! cmp $*.expected $*.results; then \ + echo $*.prebuild failed; \ + false; \ + fi diff --git a/Prebuild/tests/README.txt b/Prebuild/tests/README.txt new file mode 100644 index 0000000000..2c2de3bc50 --- /dev/null +++ b/Prebuild/tests/README.txt @@ -0,0 +1,5 @@ +There are some tests that are run via a Makfile in this +directory. They haven't been included in any form of unit tests, but +they are there to help test the functionality in some what. Simply +build prebuild (so there is a src/bin/Release/prebuild.exe) and type +`make` in this directory. Everything should pass without errors. diff --git a/Prebuild/tests/include-001.expected b/Prebuild/tests/include-001.expected new file mode 100644 index 0000000000..26374b5bc7 --- /dev/null +++ b/Prebuild/tests/include-001.expected @@ -0,0 +1,46 @@ + + + + + DEBUG;TRACE + false + bin/Debug + true + + + + + TRACE + bin/Release + true + false + + + + + + DEBUG;TRACE + false + bin/Debug + true + Prebuild.snk + 1595 + + + + + TRACE + bin/Release + true + false + Prebuild.snk + 1595 + + + + + + + + + diff --git a/Prebuild/tests/include-001.include b/Prebuild/tests/include-001.include new file mode 100644 index 0000000000..7f75962a7a --- /dev/null +++ b/Prebuild/tests/include-001.include @@ -0,0 +1,33 @@ + + + + DEBUG;TRACE + false + bin/Debug + true + Prebuild.snk + 1595 + + + + + TRACE + bin/Release + true + false + Prebuild.snk + 1595 + + + + + + + diff --git a/Prebuild/tests/include-001.prebuild b/Prebuild/tests/include-001.prebuild new file mode 100644 index 0000000000..8047008951 --- /dev/null +++ b/Prebuild/tests/include-001.prebuild @@ -0,0 +1,22 @@ + + + + + + DEBUG;TRACE + false + bin/Debug + true + + + + + TRACE + bin/Release + true + false + + + + + diff --git a/Prebuild/tests/include-002-2.include b/Prebuild/tests/include-002-2.include new file mode 100644 index 0000000000..661b7f3b06 --- /dev/null +++ b/Prebuild/tests/include-002-2.include @@ -0,0 +1,8 @@ + + + DEBUG;TRACE + false + bin/Debug + true + + diff --git a/Prebuild/tests/include-002.expected b/Prebuild/tests/include-002.expected new file mode 100644 index 0000000000..9849658b1e --- /dev/null +++ b/Prebuild/tests/include-002.expected @@ -0,0 +1,26 @@ + + + + + DEBUG;TRACE + false + bin/Debug + true + + + + + + DEBUG;TRACE + false + bin/Debug + true + + + + + + + + + diff --git a/Prebuild/tests/include-002.include b/Prebuild/tests/include-002.include new file mode 100644 index 0000000000..64f4058dc0 --- /dev/null +++ b/Prebuild/tests/include-002.include @@ -0,0 +1,14 @@ + + + + + + + diff --git a/Prebuild/tests/include-002.prebuild b/Prebuild/tests/include-002.prebuild new file mode 100644 index 0000000000..9e85f20f54 --- /dev/null +++ b/Prebuild/tests/include-002.prebuild @@ -0,0 +1,7 @@ + + + + + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000000..2aea328de2 --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +Welcome to OpenSim! + +# Overview + +OpenSim is a BSD Licensed Open Source project to develop a functioning +virtual worlds server platform capable of supporting multiple clients +and servers in a heterogeneous grid structure. OpenSim is written in +C#, and can run under Mono or the Microsoft .NET runtimes. + +This is considered an alpha release. Some stuff works, a lot doesn't. +If it breaks, you get to keep *both* pieces. + +# Compiling OpenSim + +Please see BUILDING.md if you downloaded a source distribution and +need to build OpenSim before running it. + +# Running OpenSim on Windows + +You will need .NET 4.0 installed to run OpenSimulator. + +We recommend that you run OpenSim from a command prompt on Windows in order +to capture any errors. + +To run OpenSim from a command prompt + + * cd to the bin/ directory where you unpacked OpenSim + * run OpenSim.exe + +Now see the "Configuring OpenSim" section + +# Running OpenSim on Linux + +You will need Mono >= 2.10.8.1 to run OpenSimulator. On some Linux distributions you +may need to install additional packages. See http://opensimulator.org/wiki/Dependencies +for more information. + +To run OpenSim, from the unpacked distribution type: + + * cd bin + * mono OpenSim.exe + +Now see the "Configuring OpenSim" section + +# Configuring OpenSim + +When OpenSim starts for the first time, you will be prompted with a +series of questions that look something like: + + [09-17 03:54:40] DEFAULT REGION CONFIG: Simulator Name [OpenSim Test]: + +For all the options except simulator name, you can safely hit enter to accept +the default if you want to connect using a client on the same machine or over +your local network. + +You will then be asked "Do you wish to join an existing estate?". If you're +starting OpenSim for the first time then answer no (which is the default) and +provide an estate name. + +Shortly afterwards, you will then be asked to enter an estate owner first name, +last name, password and e-mail (which can be left blank). Do not forget these +details, since initially only this account will be able to manage your region +in-world. You can also use these details to perform your first login. + +Once you are presented with a prompt that looks like: + + Region (My region name) # + +You have successfully started OpenSim. + +If you want to create another user account to login rather than the estate +account, then type "create user" on the OpenSim console and follow the prompts. + +Helpful resources: + * http://opensimulator.org/wiki/Configuration + * http://opensimulator.org/wiki/Configuring_Regions + +# Connecting to your OpenSim + +By default your sim will be available for login on port 9000. You can login by +adding -loginuri http://127.0.0.1:9000 to the command that starts Second Life +(e.g. in the Target: box of the client icon properties on Windows). You can +also login using the network IP address of the machine running OpenSim (e.g. +http://192.168.1.2:9000) + +To login, use the avatar details that you gave for your estate ownership or the +one you set up using the "create user" command. + +# Bug reports + +In the very likely event of bugs biting you (err, your OpenSim) we +encourage you to see whether the problem has already been reported on +the [OpenSim mantis system](http://opensimulator.org/mantis/main_page.php). + +If your bug has already been reported, you might want to add to the +bug description and supply additional information. + +If your bug has not been reported yet, file a bug report ("opening a +mantis"). Useful information to include: + * description of what went wrong + * stack trace + * OpenSim.log (attach as file) + * OpenSim.ini (attach as file) + * if running under mono: run OpenSim.exe with the "--debug" flag: + + mono --debug OpenSim.exe + +# More Information on OpenSim + +More extensive information on building, running, and configuring +OpenSim, as well as how to report bugs, and participate in the OpenSim +project can always be found at http://opensimulator.org. + +Thanks for trying OpenSim, we hope it is a pleasant experience. + + diff --git a/TESTING.txt b/TESTING.txt new file mode 100644 index 0000000000..da9dd60a9e --- /dev/null +++ b/TESTING.txt @@ -0,0 +1,74 @@ += The Quick Guide to OpenSim Unit Testing = + +== Running Tests == + +On Linux you will need to have NUnit installed (http://www.nunit.org). +This is commonly available in distribution package repositories. + +When this is installed, run the command + + > nant test + +Please see the TESTING ON WINDOWS section below for Windows instructions. + +== Adding Tests == + +Tests should not be added to production assemblies. They should +instead be added to assemblies of the name +My.Production.Assembly.Tests.dll. This lets them easily be removed +from production environments that don't want the bloat. + +Tests should be as close to the code as possible. It is recommended +that if you are writing tests they end up in a "Tests" sub-directory +of the directory where the code you are testing resides. + +If you have added a new test assembly that hasn't existed before you +must list it in both ".nant/local.include" +for it to be accessible to Linux users and to the continuous +integration system. + +== TESTING ON WINDOWS == + +To use nunit testing on opensim code, you have a variety of methods. The +easiast methods involve using IDE capabilities to test code. Using +VS2005/2008 I recommend using the testing capabilities of Resharper(commercial) +or TestDriven.Net(free). Both will recognize nunit tests within your +application and allow you to test them individually, or all at once, etc. You +will also be able to step into debug mode into a test through these add-ins +enabling a developer to jump right in and see how a specific +test-case/scenerio works. + +Additionally, it is my understanding that sharpdevelop and monodevelop have +their own nunit testing plugins within their IDE. Though I am not certain of +their exact feature set or stability. + +== Using NUnit Directly == +The NUnit project is a very mature testing application. It can be obtained +from www.nunit.org are via various package distrobutions for Linux. Please be +sure to get a .Net 2.0 version of Nunit, as OpenSim makes use of .Net 2.0 +functionality. + +Nunit comes with 2 tools that will enable you to run tests from assembly +inputs. Nunit-gui and nunit-console. NUnit-gui is a console that will let +you view the execution of various tests within your assemblies and give visual +indication of teir success or failure. This is a useful tool for those who +lack IDE addins ( or lack IDEs at all ). + +Nunit console allows you to execute the nunit tests of assemblies via console. +Its output will show test failures and successes and a summary of what +happened. This is very useful for a quick overview and/or automated testing. + +=== Windows === +Windows version of nunit-console is by default .Net 2.0 if you downloaded the +.Net 2.0 version of Nunit. Be sure to setup your PATH environment variable. + +=== Linux & OSX === +On these operating systems you will have to use the command "nunit-console2" + +=== Example === + +nunit-console2 OpenSim.Framework.Tests.dll (on linux) +nunit-console OpenSim.Framework.Tests.dll (on windows) + +See the file OpenSim/Data/Tests/Resources/TestDataConnections.ini +for information to setup testing for data diff --git a/ThirdParty/SmartThreadPool/CallerThreadContext.cs b/ThirdParty/SmartThreadPool/CallerThreadContext.cs new file mode 100644 index 0000000000..e63add594e --- /dev/null +++ b/ThirdParty/SmartThreadPool/CallerThreadContext.cs @@ -0,0 +1,138 @@ + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + +using System; +using System.Diagnostics; +using System.Threading; +using System.Reflection; +using System.Web; +using System.Runtime.Remoting.Messaging; + + +namespace Amib.Threading.Internal +{ +#region CallerThreadContext class + + /// + /// This class stores the caller call context in order to restore + /// it when the work item is executed in the thread pool environment. + /// + internal class CallerThreadContext + { +#region Prepare reflection information + + // Cached type information. + private static readonly MethodInfo getLogicalCallContextMethodInfo = + typeof(Thread).GetMethod("GetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); + + private static readonly MethodInfo setLogicalCallContextMethodInfo = + typeof(Thread).GetMethod("SetLogicalCallContext", BindingFlags.Instance | BindingFlags.NonPublic); + + private static string HttpContextSlotName = GetHttpContextSlotName(); + + private static string GetHttpContextSlotName() + { + FieldInfo fi = typeof(HttpContext).GetField("CallContextSlotName", BindingFlags.Static | BindingFlags.NonPublic); + + if (fi != null) + { + return (string) fi.GetValue(null); + } + + return "HttpContext"; + } + + #endregion + +#region Private fields + + private HttpContext _httpContext; + private LogicalCallContext _callContext; + + #endregion + + /// + /// Constructor + /// + private CallerThreadContext() + { + } + + public bool CapturedCallContext + { + get + { + return (null != _callContext); + } + } + + public bool CapturedHttpContext + { + get + { + return (null != _httpContext); + } + } + + /// + /// Captures the current thread context + /// + /// + public static CallerThreadContext Capture( + bool captureCallContext, + bool captureHttpContext) + { + Debug.Assert(captureCallContext || captureHttpContext); + + CallerThreadContext callerThreadContext = new CallerThreadContext(); + + // TODO: In NET 2.0, redo using the new feature of ExecutionContext class - Capture() + // Capture Call Context + if(captureCallContext && (getLogicalCallContextMethodInfo != null)) + { + callerThreadContext._callContext = (LogicalCallContext)getLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, null); + if (callerThreadContext._callContext != null) + { + callerThreadContext._callContext = (LogicalCallContext)callerThreadContext._callContext.Clone(); + } + } + + // Capture httpContext + if (captureHttpContext && (null != HttpContext.Current)) + { + callerThreadContext._httpContext = HttpContext.Current; + } + + return callerThreadContext; + } + + /// + /// Applies the thread context stored earlier + /// + /// + public static void Apply(CallerThreadContext callerThreadContext) + { + if (null == callerThreadContext) + { + throw new ArgumentNullException("callerThreadContext"); + } + + // Todo: In NET 2.0, redo using the new feature of ExecutionContext class - Run() + // Restore call context + if ((callerThreadContext._callContext != null) && (setLogicalCallContextMethodInfo != null)) + { + setLogicalCallContextMethodInfo.Invoke(Thread.CurrentThread, new object[] { callerThreadContext._callContext }); + } + + // Restore HttpContext + if (callerThreadContext._httpContext != null) + { + HttpContext.Current = callerThreadContext._httpContext; + //CallContext.SetData(HttpContextSlotName, callerThreadContext._httpContext); + } + } + } + + #endregion +} +#endif diff --git a/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs new file mode 100644 index 0000000000..5752957878 --- /dev/null +++ b/ThirdParty/SmartThreadPool/CanceledWorkItemsGroup.cs @@ -0,0 +1,14 @@ +namespace Amib.Threading.Internal +{ + internal class CanceledWorkItemsGroup + { + public readonly static CanceledWorkItemsGroup NotCanceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + public CanceledWorkItemsGroup() + { + IsCanceled = false; + } + + public bool IsCanceled { get; set; } + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandle.cs b/ThirdParty/SmartThreadPool/EventWaitHandle.cs new file mode 100644 index 0000000000..25be07a530 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandle.cs @@ -0,0 +1,104 @@ +#if (_WINDOWS_CE) + +using System; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandle class + /// In WindowsCE this class doesn't exist and I needed the WaitAll and WaitAny implementation. + /// So I wrote this class to implement these two methods with some of their overloads. + /// It uses the WaitForMultipleObjects API to do the WaitAll and WaitAny. + /// Note that this class doesn't even inherit from WaitHandle! + /// + public class STPEventWaitHandle + { + #region Public Constants + + public const int WaitTimeout = Timeout.Infinite; + + #endregion + + #region Private External Constants + + private const Int32 WAIT_FAILED = -1; + private const Int32 WAIT_TIMEOUT = 0x102; + private const UInt32 INFINITE = 0xFFFFFFFF; + + #endregion + + #region WaitAll and WaitAny + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + + private static IntPtr[] PrepareNativeHandles(WaitHandle[] waitHandles) + { + IntPtr[] nativeHandles = new IntPtr[waitHandles.Length]; + for (int i = 0; i < waitHandles.Length; i++) + { + nativeHandles[i] = waitHandles[i].Handle; + } + return nativeHandles; + } + + public static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, true, timeout); + + if (result == WAIT_TIMEOUT || result == WAIT_FAILED) + { + return false; + } + + return true; + } + + + public static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + uint timeout = millisecondsTimeout < 0 ? INFINITE : (uint)millisecondsTimeout; + + IntPtr[] nativeHandles = PrepareNativeHandles(waitHandles); + + int result = WaitForMultipleObjects((uint)waitHandles.Length, nativeHandles, false, timeout); + + if (result >= 0 && result < waitHandles.Length) + { + return result; + } + + return -1; + } + + public static int WaitAny(WaitHandle[] waitHandles) + { + return WaitAny(waitHandles, Timeout.Infinite, false); + } + + public static int WaitAny(WaitHandle[] waitHandles, TimeSpan timeout, bool exitContext) + { + int millisecondsTimeout = (int)timeout.TotalMilliseconds; + + return WaitAny(waitHandles, millisecondsTimeout, false); + } + + #endregion + + #region External methods + + [DllImport("coredll.dll", SetLastError = true)] + public static extern int WaitForMultipleObjects(uint nCount, IntPtr[] lpHandles, bool fWaitAll, uint dwMilliseconds); + + #endregion + } +} +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs new file mode 100644 index 0000000000..3c9c849205 --- /dev/null +++ b/ThirdParty/SmartThreadPool/EventWaitHandleFactory.cs @@ -0,0 +1,82 @@ +using System.Threading; + +#if (_WINDOWS_CE) +using System; +using System.Runtime.InteropServices; +#endif + +namespace Amib.Threading.Internal +{ + /// + /// EventWaitHandleFactory class. + /// This is a static class that creates AutoResetEvent and ManualResetEvent objects. + /// In WindowCE the WaitForMultipleObjects API fails to use the Handle property + /// of XxxResetEvent. It can use only handles that were created by the CreateEvent API. + /// Consequently this class creates the needed XxxResetEvent and replaces the handle if + /// it's a WindowsCE OS. + /// + public static class EventWaitHandleFactory + { + /// + /// Create a new AutoResetEvent object + /// + /// Return a new AutoResetEvent object + public static AutoResetEvent CreateAutoResetEvent() + { + AutoResetEvent waitHandle = new AutoResetEvent(false); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, false, false); +#endif + + return waitHandle; + } + + /// + /// Create a new ManualResetEvent object + /// + /// Return a new ManualResetEvent object + public static ManualResetEvent CreateManualResetEvent(bool initialState) + { + ManualResetEvent waitHandle = new ManualResetEvent(initialState); + +#if (_WINDOWS_CE) + ReplaceEventHandle(waitHandle, true, initialState); +#endif + + return waitHandle; + } + +#if (_WINDOWS_CE) + + /// + /// Replace the event handle + /// + /// The WaitHandle object which its handle needs to be replaced. + /// Indicates if the event is a ManualResetEvent (true) or an AutoResetEvent (false) + /// The initial state of the event + private static void ReplaceEventHandle(WaitHandle waitHandle, bool manualReset, bool initialState) + { + // Store the old handle + IntPtr oldHandle = waitHandle.Handle; + + // Create a new event + IntPtr newHandle = CreateEvent(IntPtr.Zero, manualReset, initialState, null); + + // Replace the old event with the new event + waitHandle.Handle = newHandle; + + // Close the old event + CloseHandle (oldHandle); + } + + [DllImport("coredll.dll", SetLastError = true)] + public static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName); + + //Handle + [DllImport("coredll.dll", SetLastError = true)] + public static extern bool CloseHandle(IntPtr hObject); +#endif + + } +} diff --git a/ThirdParty/SmartThreadPool/Exceptions.cs b/ThirdParty/SmartThreadPool/Exceptions.cs new file mode 100644 index 0000000000..6c6a88bab1 --- /dev/null +++ b/ThirdParty/SmartThreadPool/Exceptions.cs @@ -0,0 +1,111 @@ +using System; +#if !(_WINDOWS_CE) +using System.Runtime.Serialization; +#endif + +namespace Amib.Threading +{ + #region Exceptions + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + public sealed partial class WorkItemCancelException : Exception + { + public WorkItemCancelException() + { + } + + public WorkItemCancelException(string message) + : base(message) + { + } + + public WorkItemCancelException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemTimeoutException : Exception + { + public WorkItemTimeoutException() + { + } + + public WorkItemTimeoutException(string message) + : base(message) + { + } + + public WorkItemTimeoutException(string message, Exception e) + : base(message, e) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + public sealed partial class WorkItemResultException : Exception + { + public WorkItemResultException() + { + } + + public WorkItemResultException(string message) + : base(message) + { + } + + public WorkItemResultException(string message, Exception e) + : base(message, e) + { + } + } + + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// Represents an exception in case IWorkItemResult.GetResult has been canceled + /// + [Serializable] + public sealed partial class WorkItemCancelException + { + public WorkItemCancelException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemTimeoutException + { + public WorkItemTimeoutException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + + /// + /// Represents an exception in case IWorkItemResult.GetResult has been timed out + /// + [Serializable] + public sealed partial class WorkItemResultException + { + public WorkItemResultException(SerializationInfo si, StreamingContext sc) + : base(si, sc) + { + } + } + +#endif + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/Interfaces.cs b/ThirdParty/SmartThreadPool/Interfaces.cs new file mode 100644 index 0000000000..513422ff9f --- /dev/null +++ b/ThirdParty/SmartThreadPool/Interfaces.cs @@ -0,0 +1,628 @@ +using System; +using System.Threading; + +namespace Amib.Threading +{ + #region Delegates + + /// + /// A delegate that represents the method to run as the work item + /// + /// A state object for the method to run + public delegate object WorkItemCallback(object state); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call after the WorkItemCallback completed + /// + /// The work item result object + public delegate void PostExecuteWorkItemCallback(IWorkItemResult wir); + + /// + /// A delegate to call when a WorkItemsGroup becomes idle + /// + /// A reference to the WorkItemsGroup that became idle + public delegate void WorkItemsGroupIdleHandler(IWorkItemsGroup workItemsGroup); + + /// + /// A delegate to call after a thread is created, but before + /// it's first use. + /// + public delegate void ThreadInitializationHandler(); + + /// + /// A delegate to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + public delegate void ThreadTerminationHandler(); + + #endregion + + #region WorkItem Priority + + /// + /// Defines the availeable priorities of a work item. + /// The higher the priority a work item has, the sooner + /// it will be executed. + /// + public enum WorkItemPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } + + #endregion + + #region IWorkItemsGroup interface + + /// + /// IWorkItemsGroup interface + /// Created by SmartThreadPool.CreateWorkItemsGroup() + /// + public interface IWorkItemsGroup + { + /// + /// Get/Set the name of the WorkItemsGroup + /// + string Name { get; set; } + + /// + /// Get/Set the maximum number of workitem that execute cocurrency on the thread pool + /// + int Concurrency { get; set; } + + /// + /// Get the number of work items waiting in the queue. + /// + int WaitingCallbacks { get; } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + object[] GetStates(); + + /// + /// Get the WorkItemsGroup start information + /// + WIGStartInfo WIGStartInfo { get; } + + /// + /// Starts to execute work items + /// + void Start(); + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + void Cancel(); + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + void Cancel(bool abortExecution); + + /// + /// Wait for all work item to complete. + /// + void WaitForIdle(); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(TimeSpan timeout); + + /// + /// Wait for all work item to complete, until timeout expired + /// + /// How long to wait for the work items to complete in milliseconds + /// Returns true if work items completed within the timeout, otherwise false. + bool WaitForIdle(int millisecondsTimeout); + + /// + /// IsIdle is true when there are no work items running or queued. + /// + bool IsIdle { get; } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + event WorkItemsGroupIdleHandler OnIdle; + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute); + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, PostExecuteWorkItemCallback postExecuteWorkItemCallback, CallToPostExecute callToPostExecute, WorkItemPriority workItemPriority); + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback); + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state); + + #endregion + + #region QueueWorkItem(Action<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult object, but its GetResult() will always return null + IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority); + + #endregion + + #region QueueWorkItem(Func<...>) + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T arg); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3); + + /// + /// Queue a work item. + /// + /// Returns a IWorkItemResult<TResult> object. + /// its GetResult() returns a TResult object + IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion + } + + #endregion + + #region CallToPostExecute enumerator + + [Flags] + public enum CallToPostExecute + { + /// + /// Never call to the PostExecute call back + /// + Never = 0x00, + + /// + /// Call to the PostExecute only when the work item is cancelled + /// + WhenWorkItemCanceled = 0x01, + + /// + /// Call to the PostExecute only when the work item is not cancelled + /// + WhenWorkItemNotCanceled = 0x02, + + /// + /// Always call to the PostExecute + /// + Always = WhenWorkItemCanceled | WhenWorkItemNotCanceled, + } + + #endregion + + #region IWorkItemResult interface + + /// + /// The common interface of IWorkItemResult and IWorkItemResult<T> + /// + public interface IWaitableResult + { + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResult(); + + /// + /// This method intent is for internal use. + /// + /// + IWorkItemResult GetWorkItemResultT(); + } + + /// + /// IWorkItemResult interface. + /// Created when a WorkItemCallback work item is queued. + /// + public interface IWorkItemResult : IWorkItemResult + { + } + + /// + /// IWorkItemResult<TResult> interface. + /// Created when a Func<TResult> work item is queued. + /// + public interface IWorkItemResult : IWaitableResult + { + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// The result of the work item + TResult GetResult(); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits. + /// + /// Filled with the exception if one was thrown + /// The result of the work item + TResult GetResult(out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout. + /// + /// + /// Filled with the exception if one was thrown + /// + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// Timeout in milliseconds, or -1 for infinite + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the blocking if needed + /// Filled with the exception if one was thrown + /// The result of the work item + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits until timeout or until the cancelWaitHandle is signaled. + /// + /// The result of the work item + /// + /// Filled with the exception if one was thrown + /// + /// + /// On timeout throws WorkItemTimeoutException + /// On cancel throws WorkItemCancelException + TResult GetResult( + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e); + + /// + /// Gets an indication whether the asynchronous operation has completed. + /// + bool IsCompleted { get; } + + /// + /// Gets an indication whether the asynchronous operation has been canceled. + /// + bool IsCanceled { get; } + + /// + /// Gets the user-defined object that contains context data + /// for the work item method. + /// + object State { get; } + + /// + /// Same as Cancel(false). + /// + bool Cancel(); + + /// + /// Cancel the work item execution. + /// If the work item is in the queue then it won't execute + /// If the work item is completed, it will remain completed + /// If the work item is in progress then the user can check the SmartThreadPool.IsWorkItemCanceled + /// property to check if the work item has been cancelled. If the abortExecution is set to true then + /// the Smart Thread Pool will send an AbortException to the running thread to stop the execution + /// of the work item. When an in progress work item is canceled its GetResult will throw WorkItemCancelException. + /// If the work item is already cancelled it will remain cancelled + /// + /// When true send an AbortException to the executing thread. + /// Returns true if the work item was not completed, otherwise false. + bool Cancel(bool abortExecution); + + /// + /// Get the work item's priority + /// + WorkItemPriority WorkItemPriority { get; } + + /// + /// Return the result, same as GetResult() + /// + TResult Result { get; } + + /// + /// Returns the exception if occured otherwise returns null. + /// + object Exception { get; } + } + + #endregion + + #region .NET 3.5 + + // All these delegate are built-in .NET 3.5 + // Comment/Remove them when compiling to .NET 3.5 to avoid ambiguity. + + public delegate void Action(); + public delegate void Action(T1 arg1, T2 arg2); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3); + public delegate void Action(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + public delegate TResult Func(); + public delegate TResult Func(T arg1); + public delegate TResult Func(T1 arg1, T2 arg2); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3); + public delegate TResult Func(T1 arg1, T2 arg2, T3 arg3, T4 arg4); + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/InternalInterfaces.cs b/ThirdParty/SmartThreadPool/InternalInterfaces.cs new file mode 100644 index 0000000000..0072e10019 --- /dev/null +++ b/ThirdParty/SmartThreadPool/InternalInterfaces.cs @@ -0,0 +1,27 @@ + +namespace Amib.Threading.Internal +{ + /// + /// An internal delegate to call when the WorkItem starts or completes + /// + internal delegate void WorkItemStateCallback(WorkItem workItem); + + internal interface IInternalWorkItemResult + { + event WorkItemStateCallback OnWorkItemStarted; + event WorkItemStateCallback OnWorkItemCompleted; + } + + internal interface IInternalWaitableResult + { + /// + /// This method is intent for internal use. + /// + IWorkItemResult GetWorkItemResult(); + } + + public interface IHasWorkItemPriority + { + WorkItemPriority WorkItemPriority { get; } + } +} diff --git a/ThirdParty/SmartThreadPool/PriorityQueue.cs b/ThirdParty/SmartThreadPool/PriorityQueue.cs new file mode 100644 index 0000000000..409c879a1f --- /dev/null +++ b/ThirdParty/SmartThreadPool/PriorityQueue.cs @@ -0,0 +1,239 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Amib.Threading.Internal +{ + #region PriorityQueue class + + /// + /// PriorityQueue class + /// This class is not thread safe because we use external lock + /// + public sealed class PriorityQueue : IEnumerable + { + #region Private members + + /// + /// The number of queues, there is one for each type of priority + /// + private const int _queuesCount = WorkItemPriority.Highest-WorkItemPriority.Lowest+1; + + /// + /// Work items queues. There is one for each type of priority + /// + private readonly LinkedList[] _queues = new LinkedList[_queuesCount]; + + /// + /// The total number of work items within the queues + /// + private int _workItemsCount; + + /// + /// Use with IEnumerable interface + /// + private int _version; + + #endregion + + #region Contructor + + public PriorityQueue() + { + for(int i = 0; i < _queues.Length; ++i) + { + _queues[i] = new LinkedList(); + } + } + + #endregion + + #region Methods + + /// + /// Enqueue a work item. + /// + /// A work item + public void Enqueue(IHasWorkItemPriority workItem) + { + Debug.Assert(null != workItem); + + int queueIndex = _queuesCount-(int)workItem.WorkItemPriority-1; + Debug.Assert(queueIndex >= 0); + Debug.Assert(queueIndex < _queuesCount); + + _queues[queueIndex].AddLast(workItem); + ++_workItemsCount; + ++_version; + } + + /// + /// Dequeque a work item. + /// + /// Returns the next work item + public IHasWorkItemPriority Dequeue() + { + IHasWorkItemPriority workItem = null; + + if(_workItemsCount > 0) + { + int queueIndex = GetNextNonEmptyQueue(-1); + Debug.Assert(queueIndex >= 0); + workItem = _queues[queueIndex].First.Value; + _queues[queueIndex].RemoveFirst(); + Debug.Assert(null != workItem); + --_workItemsCount; + ++_version; + } + + return workItem; + } + + /// + /// Find the next non empty queue starting at queue queueIndex+1 + /// + /// The index-1 to start from + /// + /// The index of the next non empty queue or -1 if all the queues are empty + /// + private int GetNextNonEmptyQueue(int queueIndex) + { + for(int i = queueIndex+1; i < _queuesCount; ++i) + { + if(_queues[i].Count > 0) + { + return i; + } + } + return -1; + } + + /// + /// The number of work items + /// + public int Count + { + get + { + return _workItemsCount; + } + } + + /// + /// Clear all the work items + /// + public void Clear() + { + if (_workItemsCount > 0) + { + foreach(LinkedList queue in _queues) + { + queue.Clear(); + } + _workItemsCount = 0; + ++_version; + } + } + + #endregion + + #region IEnumerable Members + + /// + /// Returns an enumerator to iterate over the work items + /// + /// Returns an enumerator + public IEnumerator GetEnumerator() + { + return new PriorityQueueEnumerator(this); + } + + #endregion + + #region PriorityQueueEnumerator + + /// + /// The class the implements the enumerator + /// + private class PriorityQueueEnumerator : IEnumerator + { + private readonly PriorityQueue _priorityQueue; + private int _version; + private int _queueIndex; + private IEnumerator _enumerator; + + public PriorityQueueEnumerator(PriorityQueue priorityQueue) + { + _priorityQueue = priorityQueue; + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } + + #region IEnumerator Members + + public void Reset() + { + _version = _priorityQueue._version; + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(-1); + if (_queueIndex >= 0) + { + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + } + else + { + _enumerator = null; + } + } + + public object Current + { + get + { + Debug.Assert(null != _enumerator); + return _enumerator.Current; + } + } + + public bool MoveNext() + { + if (null == _enumerator) + { + return false; + } + + if(_version != _priorityQueue._version) + { + throw new InvalidOperationException("The collection has been modified"); + + } + if (!_enumerator.MoveNext()) + { + _queueIndex = _priorityQueue.GetNextNonEmptyQueue(_queueIndex); + if(-1 == _queueIndex) + { + return false; + } + _enumerator = _priorityQueue._queues[_queueIndex].GetEnumerator(); + _enumerator.MoveNext(); + return true; + } + return true; + } + + #endregion + } + + #endregion + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4728c1fc0b --- /dev/null +++ b/ThirdParty/SmartThreadPool/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Amib.Threading")] +[assembly: AssemblyDescription("Smart Thread Pool")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Amib.Threading")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("c764a3de-c4f8-434d-85b5-a09830d1e44f")] +[assembly: AssemblyVersion("2.2.3.0")] + +#if (_PUBLISH) +[assembly: InternalsVisibleTo("STPTests,PublicKey=00240000048000009400000006020000002400005253413100040000010001004fe3d39add741ba7c8d52cd1eb0d94c7d79060ad956cbaff0e51c1dce94db10356b261778bc1ac3114b3218434da6fcd8416dd5507653809598f7d2afc422099ce4f6b7b0477f18e6c57c727ef2a7ab6ee56e6b4589fe44cb0e25f2875a3c65ab0383ee33c4dd93023f7ce1218bebc8b7a9a1dac878938f5c4f45ea74b6bd8ad")] +#else +[assembly: InternalsVisibleTo("STPTests")] +#endif + + diff --git a/ThirdParty/SmartThreadPool/SLExt.cs b/ThirdParty/SmartThreadPool/SLExt.cs new file mode 100644 index 0000000000..23a60bcb90 --- /dev/null +++ b/ThirdParty/SmartThreadPool/SLExt.cs @@ -0,0 +1,16 @@ +#if _SILVERLIGHT + +using System.Threading; + +namespace Amib.Threading +{ + public enum ThreadPriority + { + Lowest, + BelowNormal, + Normal, + AboveNormal, + Highest, + } +} +#endif diff --git a/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs new file mode 100644 index 0000000000..9b17f694a5 --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPEventWaitHandle.cs @@ -0,0 +1,62 @@ +#if !(_WINDOWS_CE) + +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ +#if _WINDOWS || WINDOWS_PHONE + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout); + } + } +#else + internal static class STPEventWaitHandle + { + public const int WaitTimeout = Timeout.Infinite; + + internal static bool WaitAll(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + + internal static int WaitAny(WaitHandle[] waitHandles) + { + return WaitHandle.WaitAny(waitHandles); + } + + internal static int WaitAny(WaitHandle[] waitHandles, int millisecondsTimeout, bool exitContext) + { + return WaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + } + + internal static bool WaitOne(WaitHandle waitHandle, int millisecondsTimeout, bool exitContext) + { + return waitHandle.WaitOne(millisecondsTimeout, exitContext); + } + } +#endif + +} + +#endif \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs new file mode 100644 index 0000000000..0663d1dfb8 --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPPerformanceCounter.cs @@ -0,0 +1,448 @@ +using System; +using System.Diagnostics; +using System.Threading; + +namespace Amib.Threading +{ + public interface ISTPPerformanceCountersReader + { + long InUseThreads { get; } + long ActiveThreads { get; } + long WorkItemsQueued { get; } + long WorkItemsProcessed { get; } + } +} + +namespace Amib.Threading.Internal +{ + internal interface ISTPInstancePerformanceCounters : IDisposable + { + void Close(); + void SampleThreads(long activeThreads, long inUseThreads); + void SampleWorkItems(long workItemsQueued, long workItemsProcessed); + void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime); + void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime); + } +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + internal enum STPPerformanceCounterType + { + // Fields + ActiveThreads = 0, + InUseThreads = 1, + OverheadThreads = 2, + OverheadThreadsPercent = 3, + OverheadThreadsPercentBase = 4, + + WorkItems = 5, + WorkItemsInQueue = 6, + WorkItemsProcessed = 7, + + WorkItemsQueuedPerSecond = 8, + WorkItemsProcessedPerSecond = 9, + + AvgWorkItemWaitTime = 10, + AvgWorkItemWaitTimeBase = 11, + + AvgWorkItemProcessTime = 12, + AvgWorkItemProcessTimeBase = 13, + + WorkItemsGroups = 14, + + LastCounter = 14, + } + + + /// + /// Summary description for STPPerformanceCounter. + /// + internal class STPPerformanceCounter + { + // Fields + private readonly PerformanceCounterType _pcType; + protected string _counterHelp; + protected string _counterName; + + // Methods + public STPPerformanceCounter( + string counterName, + string counterHelp, + PerformanceCounterType pcType) + { + _counterName = counterName; + _counterHelp = counterHelp; + _pcType = pcType; + } + + public void AddCounterToCollection(CounterCreationDataCollection counterData) + { + CounterCreationData counterCreationData = new CounterCreationData( + _counterName, + _counterHelp, + _pcType); + + counterData.Add(counterCreationData); + } + + // Properties + public string Name + { + get + { + return _counterName; + } + } + } + + internal class STPPerformanceCounters + { + // Fields + internal STPPerformanceCounter[] _stpPerformanceCounters; + private static readonly STPPerformanceCounters _instance; + internal const string _stpCategoryHelp = "SmartThreadPool performance counters"; + internal const string _stpCategoryName = "SmartThreadPool"; + + // Methods + static STPPerformanceCounters() + { + _instance = new STPPerformanceCounters(); + } + + private STPPerformanceCounters() + { + STPPerformanceCounter[] stpPerformanceCounters = new STPPerformanceCounter[] + { + new STPPerformanceCounter("Active threads", "The current number of available in the thread pool.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("In use threads", "The current number of threads that execute a work item.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Overhead threads", "The current number of threads that are active, but are not in use.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("% overhead threads", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawFraction), + new STPPerformanceCounter("% overhead threads base", "The current number of threads that are active, but are not in use in percents.", PerformanceCounterType.RawBase), + + new STPPerformanceCounter("Work Items", "The number of work items in the Smart Thread Pool. Both queued and processed.", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items in queue", "The current number of work items in the queue", PerformanceCounterType.NumberOfItems32), + new STPPerformanceCounter("Work Items processed", "The number of work items already processed", PerformanceCounterType.NumberOfItems32), + + new STPPerformanceCounter("Work Items queued/sec", "The number of work items queued per second", PerformanceCounterType.RateOfCountsPerSecond32), + new STPPerformanceCounter("Work Items processed/sec", "The number of work items processed per second", PerformanceCounterType.RateOfCountsPerSecond32), + + new STPPerformanceCounter("Avg. Work Item wait time/sec", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item wait time base", "The average time a work item supends in the queue waiting for its turn to execute.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Avg. Work Item process time/sec", "The average time it takes to process a work item.", PerformanceCounterType.AverageCount64), + new STPPerformanceCounter("Avg. Work Item process time base", "The average time it takes to process a work item.", PerformanceCounterType.AverageBase), + + new STPPerformanceCounter("Work Items Groups", "The current number of work item groups associated with the Smart Thread Pool.", PerformanceCounterType.NumberOfItems32), + }; + + _stpPerformanceCounters = stpPerformanceCounters; + SetupCategory(); + } + + private void SetupCategory() + { + if (!PerformanceCounterCategory.Exists(_stpCategoryName)) + { + CounterCreationDataCollection counters = new CounterCreationDataCollection(); + + for (int i = 0; i < _stpPerformanceCounters.Length; i++) + { + _stpPerformanceCounters[i].AddCounterToCollection(counters); + } + + PerformanceCounterCategory.Create( + _stpCategoryName, + _stpCategoryHelp, + PerformanceCounterCategoryType.MultiInstance, + counters); + + } + } + + // Properties + public static STPPerformanceCounters Instance + { + get + { + return _instance; + } + } + } + + internal class STPInstancePerformanceCounter : IDisposable + { + // Fields + private bool _isDisposed; + private PerformanceCounter _pcs; + + // Methods + protected STPInstancePerformanceCounter() + { + _isDisposed = false; + } + + public STPInstancePerformanceCounter( + string instance, + STPPerformanceCounterType spcType) : this() + { + STPPerformanceCounters counters = STPPerformanceCounters.Instance; + _pcs = new PerformanceCounter( + STPPerformanceCounters._stpCategoryName, + counters._stpPerformanceCounters[(int) spcType].Name, + instance, + false); + _pcs.RawValue = _pcs.RawValue; + } + + + public void Close() + { + if (_pcs != null) + { + _pcs.RemoveInstance(); + _pcs.Close(); + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + Close(); + } + } + _isDisposed = true; + } + + public virtual void Increment() + { + _pcs.Increment(); + } + + public virtual void IncrementBy(long val) + { + _pcs.IncrementBy(val); + } + + public virtual void Set(long val) + { + _pcs.RawValue = val; + } + } + + internal class STPInstanceNullPerformanceCounter : STPInstancePerformanceCounter + { + // Methods + public override void Increment() {} + public override void IncrementBy(long value) {} + public override void Set(long val) {} + } + + + + internal class STPInstancePerformanceCounters : ISTPInstancePerformanceCounters + { + private bool _isDisposed; + // Fields + private STPInstancePerformanceCounter[] _pcs; + private static readonly STPInstancePerformanceCounter _stpInstanceNullPerformanceCounter; + + // Methods + static STPInstancePerformanceCounters() + { + _stpInstanceNullPerformanceCounter = new STPInstanceNullPerformanceCounter(); + } + + public STPInstancePerformanceCounters(string instance) + { + _isDisposed = false; + _pcs = new STPInstancePerformanceCounter[(int)STPPerformanceCounterType.LastCounter]; + + // Call the STPPerformanceCounters.Instance so the static constructor will + // intialize the STPPerformanceCounters singleton. + STPPerformanceCounters.Instance.GetHashCode(); + + for (int i = 0; i < _pcs.Length; i++) + { + if (instance != null) + { + _pcs[i] = new STPInstancePerformanceCounter( + instance, + (STPPerformanceCounterType) i); + } + else + { + _pcs[i] = _stpInstanceNullPerformanceCounter; + } + } + } + + + public void Close() + { + if (null != _pcs) + { + for (int i = 0; i < _pcs.Length; i++) + { + if (null != _pcs[i]) + { + _pcs[i].Dispose(); + } + } + _pcs = null; + } + } + + public void Dispose() + { + Dispose(true); + } + + public virtual void Dispose(bool disposing) + { + if (!_isDisposed) + { + if (disposing) + { + Close(); + } + } + _isDisposed = true; + } + + private STPInstancePerformanceCounter GetCounter(STPPerformanceCounterType spcType) + { + return _pcs[(int) spcType]; + } + + public void SampleThreads(long activeThreads, long inUseThreads) + { + GetCounter(STPPerformanceCounterType.ActiveThreads).Set(activeThreads); + GetCounter(STPPerformanceCounterType.InUseThreads).Set(inUseThreads); + GetCounter(STPPerformanceCounterType.OverheadThreads).Set(activeThreads-inUseThreads); + + GetCounter(STPPerformanceCounterType.OverheadThreadsPercentBase).Set(activeThreads-inUseThreads); + GetCounter(STPPerformanceCounterType.OverheadThreadsPercent).Set(inUseThreads); + } + + public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) + { + GetCounter(STPPerformanceCounterType.WorkItems).Set(workItemsQueued+workItemsProcessed); + GetCounter(STPPerformanceCounterType.WorkItemsInQueue).Set(workItemsQueued); + GetCounter(STPPerformanceCounterType.WorkItemsProcessed).Set(workItemsProcessed); + + GetCounter(STPPerformanceCounterType.WorkItemsQueuedPerSecond).Set(workItemsQueued); + GetCounter(STPPerformanceCounterType.WorkItemsProcessedPerSecond).Set(workItemsProcessed); + } + + public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) + { + GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTime).IncrementBy((long)workItemWaitTime.TotalMilliseconds); + GetCounter(STPPerformanceCounterType.AvgWorkItemWaitTimeBase).Increment(); + } + + public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) + { + GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTime).IncrementBy((long)workItemProcessTime.TotalMilliseconds); + GetCounter(STPPerformanceCounterType.AvgWorkItemProcessTimeBase).Increment(); + } + } +#endif + + internal class NullSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader + { + private static readonly NullSTPInstancePerformanceCounters _instance = new NullSTPInstancePerformanceCounters(); + + public static NullSTPInstancePerformanceCounters Instance + { + get { return _instance; } + } + + public void Close() {} + public void Dispose() {} + + public void SampleThreads(long activeThreads, long inUseThreads) {} + public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) {} + public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) {} + public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) {} + public long InUseThreads + { + get { return 0; } + } + + public long ActiveThreads + { + get { return 0; } + } + + public long WorkItemsQueued + { + get { return 0; } + } + + public long WorkItemsProcessed + { + get { return 0; } + } + } + + internal class LocalSTPInstancePerformanceCounters : ISTPInstancePerformanceCounters, ISTPPerformanceCountersReader + { + public void Close() { } + public void Dispose() { } + + private long _activeThreads; + private long _inUseThreads; + private long _workItemsQueued; + private long _workItemsProcessed; + + public long InUseThreads + { + get { return _inUseThreads; } + } + + public long ActiveThreads + { + get { return _activeThreads; } + } + + public long WorkItemsQueued + { + get { return _workItemsQueued; } + } + + public long WorkItemsProcessed + { + get { return _workItemsProcessed; } + } + + public void SampleThreads(long activeThreads, long inUseThreads) + { + _activeThreads = activeThreads; + _inUseThreads = inUseThreads; + } + + public void SampleWorkItems(long workItemsQueued, long workItemsProcessed) + { + _workItemsQueued = workItemsQueued; + _workItemsProcessed = workItemsProcessed; + } + + public void SampleWorkItemsWaitTime(TimeSpan workItemWaitTime) + { + // Not supported + } + + public void SampleWorkItemsProcessTime(TimeSpan workItemProcessTime) + { + // Not supported + } + } +} diff --git a/ThirdParty/SmartThreadPool/STPStartInfo.cs b/ThirdParty/SmartThreadPool/STPStartInfo.cs new file mode 100644 index 0000000000..96fa094b3d --- /dev/null +++ b/ThirdParty/SmartThreadPool/STPStartInfo.cs @@ -0,0 +1,212 @@ +using System; +using System.Threading; + +namespace Amib.Threading +{ + /// + /// Summary description for STPStartInfo. + /// + public class STPStartInfo : WIGStartInfo + { + private int _idleTimeout = SmartThreadPool.DefaultIdleTimeout; + private int _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; + private int _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; +#if !(WINDOWS_PHONE) + private ThreadPriority _threadPriority = SmartThreadPool.DefaultThreadPriority; +#endif + private string _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; + private bool _areThreadsBackground = SmartThreadPool.DefaultAreThreadsBackground; + private bool _enableLocalPerformanceCounters; + private string _threadPoolName = SmartThreadPool.DefaultThreadPoolName; + private int? _maxStackSize = SmartThreadPool.DefaultMaxStackSize; + + public STPStartInfo() + { + _performanceCounterInstanceName = SmartThreadPool.DefaultPerformanceCounterInstanceName; +#if !(WINDOWS_PHONE) + _threadPriority = SmartThreadPool.DefaultThreadPriority; +#endif + _maxWorkerThreads = SmartThreadPool.DefaultMaxWorkerThreads; + _idleTimeout = SmartThreadPool.DefaultIdleTimeout; + _minWorkerThreads = SmartThreadPool.DefaultMinWorkerThreads; + } + + public STPStartInfo(STPStartInfo stpStartInfo) + : base(stpStartInfo) + { + _idleTimeout = stpStartInfo.IdleTimeout; + _minWorkerThreads = stpStartInfo.MinWorkerThreads; + _maxWorkerThreads = stpStartInfo.MaxWorkerThreads; +#if !(WINDOWS_PHONE) + _threadPriority = stpStartInfo.ThreadPriority; +#endif + _performanceCounterInstanceName = stpStartInfo.PerformanceCounterInstanceName; + _enableLocalPerformanceCounters = stpStartInfo._enableLocalPerformanceCounters; + _threadPoolName = stpStartInfo._threadPoolName; + _areThreadsBackground = stpStartInfo.AreThreadsBackground; +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + _apartmentState = stpStartInfo._apartmentState; +#endif + } + + /// + /// Get/Set the idle timeout in milliseconds. + /// If a thread is idle (starved) longer than IdleTimeout then it may quit. + /// + public virtual int IdleTimeout + { + get { return _idleTimeout; } + set + { + ThrowIfReadOnly(); + _idleTimeout = value; + } + } + + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public virtual int MinWorkerThreads + { + get { return _minWorkerThreads; } + set + { + ThrowIfReadOnly(); + _minWorkerThreads = value; + } + } + + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public virtual int MaxWorkerThreads + { + get { return _maxWorkerThreads; } + set + { + ThrowIfReadOnly(); + _maxWorkerThreads = value; + } + } + +#if !(WINDOWS_PHONE) + /// + /// Get/Set the scheduling priority of the threads in the pool. + /// The Os handles the scheduling. + /// + public virtual ThreadPriority ThreadPriority + { + get { return _threadPriority; } + set + { + ThrowIfReadOnly(); + _threadPriority = value; + } + } +#endif + /// + /// Get/Set the thread pool name. Threads will get names depending on this. + /// + public virtual string ThreadPoolName { + get { return _threadPoolName; } + set + { + ThrowIfReadOnly (); + _threadPoolName = value; + } + } + + /// + /// Get/Set the performance counter instance name of this SmartThreadPool + /// The default is null which indicate not to use performance counters at all. + /// + public virtual string PerformanceCounterInstanceName + { + get { return _performanceCounterInstanceName; } + set + { + ThrowIfReadOnly(); + _performanceCounterInstanceName = value; + } + } + + /// + /// Enable/Disable the local performance counter. + /// This enables the user to get some performance information about the SmartThreadPool + /// without using Windows performance counters. (Useful on WindowsCE, Silverlight, etc.) + /// The default is false. + /// + public virtual bool EnableLocalPerformanceCounters + { + get { return _enableLocalPerformanceCounters; } + set + { + ThrowIfReadOnly(); + _enableLocalPerformanceCounters = value; + } + } + + /// + /// Get/Set backgroundness of thread in thread pool. + /// + public virtual bool AreThreadsBackground + { + get { return _areThreadsBackground; } + set + { + ThrowIfReadOnly (); + _areThreadsBackground = value; + } + } + + /// + /// Get a readonly version of this STPStartInfo. + /// + /// Returns a readonly reference to this STPStartInfo + public new STPStartInfo AsReadOnly() + { + return new STPStartInfo(this) { _readOnly = true }; + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + private ApartmentState _apartmentState = SmartThreadPool.DefaultApartmentState; + + /// + /// Get/Set the apartment state of threads in the thread pool + /// + public ApartmentState ApartmentState + { + get { return _apartmentState; } + set + { + ThrowIfReadOnly(); + _apartmentState = value; + } + } + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + + /// + /// Get/Set the max stack size of threads in the thread pool + /// + public int? MaxStackSize + { + get { return _maxStackSize; } + set + { + ThrowIfReadOnly(); + if (value.HasValue && value.Value < 0) + { + throw new ArgumentOutOfRangeException("value", "Value must be greater than 0."); + } + _maxStackSize = value; + } + } +#endif + +#endif + } +} diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs new file mode 100644 index 0000000000..d9502bbc4c --- /dev/null +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.ThreadEntry.cs @@ -0,0 +1,60 @@ + +using System; +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + public partial class SmartThreadPool + { + #region ThreadEntry class + + internal class ThreadEntry + { + /// + /// The thread creation time + /// The value is stored as UTC value. + /// + private readonly DateTime _creationTime; + + /// + /// The last time this thread has been running + /// It is updated by IAmAlive() method + /// The value is stored as UTC value. + /// + private DateTime _lastAliveTime; + + /// + /// A reference from each thread in the thread pool to its SmartThreadPool + /// object container. + /// With this variable a thread can know whatever it belongs to a + /// SmartThreadPool. + /// + private readonly SmartThreadPool _associatedSmartThreadPool; + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + public WorkItem CurrentWorkItem { get; set; } + + public ThreadEntry(SmartThreadPool stp) + { + _associatedSmartThreadPool = stp; + _creationTime = DateTime.UtcNow; + _lastAliveTime = DateTime.MinValue; + } + + public SmartThreadPool AssociatedSmartThreadPool + { + get { return _associatedSmartThreadPool; } + } + + public void IAmAlive() + { + _lastAliveTime = DateTime.UtcNow; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/SmartThreadPool.cs b/ThirdParty/SmartThreadPool/SmartThreadPool.cs new file mode 100644 index 0000000000..615518ecf1 --- /dev/null +++ b/ThirdParty/SmartThreadPool/SmartThreadPool.cs @@ -0,0 +1,1732 @@ +#region Release History + +// Smart Thread Pool +// 7 Aug 2004 - Initial release +// +// 14 Sep 2004 - Bug fixes +// +// 15 Oct 2004 - Added new features +// - Work items return result. +// - Support waiting synchronization for multiple work items. +// - Work items can be cancelled. +// - Passage of the caller thread’s context to the thread in the pool. +// - Minimal usage of WIN32 handles. +// - Minor bug fixes. +// +// 26 Dec 2004 - Changes: +// - Removed static constructors. +// - Added finalizers. +// - Changed Exceptions so they are serializable. +// - Fixed the bug in one of the SmartThreadPool constructors. +// - Changed the SmartThreadPool.WaitAll() so it will support any number of waiters. +// The SmartThreadPool.WaitAny() is still limited by the .NET Framework. +// - Added PostExecute with options on which cases to call it. +// - Added option to dispose of the state objects. +// - Added a WaitForIdle() method that waits until the work items queue is empty. +// - Added an STPStartInfo class for the initialization of the thread pool. +// - Changed exception handling so if a work item throws an exception it +// is rethrown at GetResult(), rather then firing an UnhandledException event. +// Note that PostExecute exception are always ignored. +// +// 25 Mar 2005 - Changes: +// - Fixed lost of work items bug +// +// 3 Jul 2005: Changes. +// - Fixed bug where Enqueue() throws an exception because PopWaiter() returned null, hardly reconstructed. +// +// 16 Aug 2005: Changes. +// - Fixed bug where the InUseThreads becomes negative when canceling work items. +// +// 31 Jan 2006 - Changes: +// - Added work items priority +// - Removed support of chained delegates in callbacks and post executes (nobody really use this) +// - Added work items groups +// - Added work items groups idle event +// - Changed SmartThreadPool.WaitAll() behavior so when it gets empty array +// it returns true rather then throwing an exception. +// - Added option to start the STP and the WIG as suspended +// - Exception behavior changed, the real exception is returned by an +// inner exception +// - Added option to keep the Http context of the caller thread. (Thanks to Steven T.) +// - Added performance counters +// - Added priority to the threads in the pool +// +// 13 Feb 2006 - Changes: +// - Added a call to the dispose of the Performance Counter so +// their won't be a Performance Counter leak. +// - Added exception catch in case the Performance Counters cannot +// be created. +// +// 17 May 2008 - Changes: +// - Changed the dispose behavior and removed the Finalizers. +// - Enabled the change of the MaxThreads and MinThreads at run time. +// - Enabled the change of the Concurrency of a IWorkItemsGroup at run +// time If the IWorkItemsGroup is a SmartThreadPool then the Concurrency +// refers to the MaxThreads. +// - Improved the cancel behavior. +// - Added events for thread creation and termination. +// - Fixed the HttpContext context capture. +// - Changed internal collections so they use generic collections +// - Added IsIdle flag to the SmartThreadPool and IWorkItemsGroup +// - Added support for WinCE +// - Added support for Action and Func +// +// 07 April 2009 - Changes: +// - Added support for Silverlight and Mono +// - Added Join, Choice, and Pipe to SmartThreadPool. +// - Added local performance counters (for Mono, Silverlight, and WindowsCE) +// - Changed duration measures from DateTime.Now to Stopwatch. +// - Queues changed from System.Collections.Queue to System.Collections.Generic.LinkedList. +// +// 21 December 2009 - Changes: +// - Added work item timeout (passive) +// +// 20 August 2012 - Changes: +// - Added set name to threads +// - Fixed the WorkItemsQueue.Dequeue. +// Replaced while (!Monitor.TryEnter(this)); with lock(this) { ... } +// - Fixed SmartThreadPool.Pipe +// - Added IsBackground option to threads +// - Added ApartmentState to threads +// - Fixed thread creation when queuing many work items at the same time. +// +// 24 August 2012 - Changes: +// - Enabled cancel abort after cancel. See: http://smartthreadpool.codeplex.com/discussions/345937 by alecswan +// - Added option to set MaxStackSize of threads + +#endregion + +using System; +using System.Security; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +using Amib.Threading.Internal; + +namespace Amib.Threading +{ + #region SmartThreadPool class + /// + /// Smart thread pool class. + /// + public partial class SmartThreadPool : WorkItemsGroupBase, IDisposable + { + #region Public Default Constants + + /// + /// Default minimum number of threads the thread pool contains. (0) + /// + public const int DefaultMinWorkerThreads = 0; + + /// + /// Default maximum number of threads the thread pool contains. (25) + /// + public const int DefaultMaxWorkerThreads = 25; + + /// + /// Default idle timeout in milliseconds. (One minute) + /// + public const int DefaultIdleTimeout = 60*1000; // One minute + + /// + /// Indicate to copy the security context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerCallContext = false; + + /// + /// Indicate to copy the HTTP context of the caller and then use it in the call. (false) + /// + public const bool DefaultUseCallerHttpContext = false; + + /// + /// Indicate to dispose of the state objects if they support the IDispose interface. (false) + /// + public const bool DefaultDisposeOfStateObjects = false; + + /// + /// The default option to run the post execute (CallToPostExecute.Always) + /// + public const CallToPostExecute DefaultCallToPostExecute = CallToPostExecute.Always; + + /// + /// The default post execute method to run. (None) + /// When null it means not to call it. + /// + public static readonly PostExecuteWorkItemCallback DefaultPostExecuteWorkItemCallback; + + /// + /// The default work item priority (WorkItemPriority.Normal) + /// + public const WorkItemPriority DefaultWorkItemPriority = WorkItemPriority.Normal; + + /// + /// The default is to work on work items as soon as they arrive + /// and not to wait for the start. (false) + /// + public const bool DefaultStartSuspended = false; + + /// + /// The default name to use for the performance counters instance. (null) + /// + public static readonly string DefaultPerformanceCounterInstanceName; + +#if !(WINDOWS_PHONE) + + /// + /// The default thread priority (ThreadPriority.Normal) + /// + public const ThreadPriority DefaultThreadPriority = ThreadPriority.Normal; +#endif + /// + /// The default thread pool name. (SmartThreadPool) + /// + public const string DefaultThreadPoolName = "SmartThreadPool"; + + /// + /// The default Max Stack Size. (SmartThreadPool) + /// + public static readonly int? DefaultMaxStackSize = null; + + /// + /// The default fill state with params. (false) + /// It is relevant only to QueueWorkItem of Action<...>/Func<...> + /// + public const bool DefaultFillStateWithArgs = false; + + /// + /// The default thread backgroundness. (true) + /// + public const bool DefaultAreThreadsBackground = true; + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// The default apartment state of a thread in the thread pool. + /// The default is ApartmentState.Unknown which means the STP will not + /// set the apartment of the thread. It will use the .NET default. + /// + public const ApartmentState DefaultApartmentState = ApartmentState.Unknown; +#endif + + #endregion + + #region Member Variables + + /// + /// Dictionary of all the threads in the thread pool. + /// + private readonly SynchronizedDictionary _workerThreads = new SynchronizedDictionary(); + + /// + /// Queue of work items. + /// + private readonly WorkItemsQueue _workItemsQueue = new WorkItemsQueue(); + + /// + /// Count the work items handled. + /// Used by the performance counter. + /// + private int _workItemsProcessed; + + /// + /// Number of threads that currently work (not idle). + /// + private int _inUseWorkerThreads; + + /// + /// Stores a copy of the original STPStartInfo. + /// It is used to change the MinThread and MaxThreads + /// + private STPStartInfo _stpStartInfo; + + /// + /// Total number of work items that are stored in the work items queue + /// plus the work items that the threads in the pool are working on. + /// + private int _currentWorkItemsCount; + + /// + /// Signaled when the thread pool is idle, i.e. no thread is busy + /// and the work items queue is empty + /// + //private ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// An event to signal all the threads to quit immediately. + /// + //private ManualResetEvent _shuttingDownEvent = new ManualResetEvent(false); + private ManualResetEvent _shuttingDownEvent = EventWaitHandleFactory.CreateManualResetEvent(false); + + /// + /// A flag to indicate if the Smart Thread Pool is now suspended. + /// + private bool _isSuspended; + + /// + /// A flag to indicate the threads to quit. + /// + private bool _shutdown; + + /// + /// Counts the threads created in the pool. + /// It is used to name the threads. + /// + private int _threadCounter; + + /// + /// Indicate that the SmartThreadPool has been disposed + /// + private bool _isDisposed; + + /// + /// Holds all the WorkItemsGroup instaces that have at least one + /// work item int the SmartThreadPool + /// This variable is used in case of Shutdown + /// + private readonly SynchronizedDictionary _workItemsGroups = new SynchronizedDictionary(); + + /// + /// A common object for all the work items int the STP + /// so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + /// + /// Windows STP performance counters + /// + private ISTPInstancePerformanceCounters _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + /// + /// Local STP performance counters + /// + private ISTPInstancePerformanceCounters _localPCs = NullSTPInstancePerformanceCounters.Instance; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _threadEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _threadEntrySlot = Thread.AllocateDataSlot(); +#else + [ThreadStatic] + private static ThreadEntry _threadEntry; + +#endif + + /// + /// An event to call after a thread is created, but before + /// it's first use. + /// + private event ThreadInitializationHandler _onThreadInitialization; + + /// + /// An event to call when a thread is about to exit, after + /// it is no longer belong to the pool. + /// + private event ThreadTerminationHandler _onThreadTermination; + + #endregion + + #region Per thread properties + + /// + /// A reference to the current work item a thread from the thread pool + /// is executing. + /// + internal static ThreadEntry CurrentThreadEntry + { +#if (WINDOWS_PHONE) + get + { + lock(_threadEntries) + { + ThreadEntry threadEntry; + if (_threadEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out threadEntry)) + { + return threadEntry; + } + } + return null; + } + set + { + lock(_threadEntries) + { + _threadEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + //Thread.CurrentThread.ManagedThreadId + return Thread.GetData(_threadEntrySlot) as ThreadEntry; + } + set + { + Thread.SetData(_threadEntrySlot, value); + } +#else + get + { + return _threadEntry; + } + set + { + _threadEntry = value; + } +#endif + } + #endregion + + #region Construction and Finalization + + /// + /// Constructor + /// + public SmartThreadPool() + { + _stpStartInfo = new STPStartInfo(); + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + public SmartThreadPool(int idleTimeout) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// Idle timeout in milliseconds + /// Upper limit of threads in the pool + /// Lower limit of threads in the pool + public SmartThreadPool( + int idleTimeout, + int maxWorkerThreads, + int minWorkerThreads) + { + _stpStartInfo = new STPStartInfo + { + IdleTimeout = idleTimeout, + MaxWorkerThreads = maxWorkerThreads, + MinWorkerThreads = minWorkerThreads, + }; + Initialize(); + } + + /// + /// Constructor + /// + /// A SmartThreadPool configuration that overrides the default behavior + public SmartThreadPool(STPStartInfo stpStartInfo) + { + _stpStartInfo = new STPStartInfo(stpStartInfo); + Initialize(); + } + + private void Initialize() + { + Name = _stpStartInfo.ThreadPoolName; + ValidateSTPStartInfo(); + + // _stpStartInfoRW stores a read/write copy of the STPStartInfo. + // Actually only MaxWorkerThreads and MinWorkerThreads are overwritten + + _isSuspended = _stpStartInfo.StartSuspended; + +#if (_WINDOWS_CE) || (_SILVERLIGHT) || (_MONO) || (WINDOWS_PHONE) + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + throw new NotSupportedException("Performance counters are not implemented for Compact Framework/Silverlight/Mono, instead use StpStartInfo.EnableLocalPerformanceCounters"); + } +#else + if (null != _stpStartInfo.PerformanceCounterInstanceName) + { + try + { + _windowsPCs = new STPInstancePerformanceCounters(_stpStartInfo.PerformanceCounterInstanceName); + } + catch (Exception e) + { + Debug.WriteLine("Unable to create Performance Counters: " + e); + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + } + } +#endif + + if (_stpStartInfo.EnableLocalPerformanceCounters) + { + _localPCs = new LocalSTPInstancePerformanceCounters(); + } + + // If the STP is not started suspended then start the threads. + if (!_isSuspended) + { + StartOptimalNumberOfThreads(); + } + } + + private void StartOptimalNumberOfThreads() + { + int threadsCount = Math.Max(_workItemsQueue.Count, _stpStartInfo.MinWorkerThreads); + threadsCount = Math.Min(threadsCount, _stpStartInfo.MaxWorkerThreads); + threadsCount -= _workerThreads.Count; + if (threadsCount > 0) + { + StartThreads(threadsCount); + } + } + + private void ValidateSTPStartInfo() + { + if (_stpStartInfo.MinWorkerThreads < 0) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads", "MinWorkerThreads cannot be negative"); + } + + if (_stpStartInfo.MaxWorkerThreads <= 0) + { + throw new ArgumentOutOfRangeException( + "MaxWorkerThreads", "MaxWorkerThreads must be greater than zero"); + } + + if (_stpStartInfo.MinWorkerThreads > _stpStartInfo.MaxWorkerThreads) + { + throw new ArgumentOutOfRangeException( + "MinWorkerThreads, maxWorkerThreads", + "MaxWorkerThreads must be greater or equal to MinWorkerThreads"); + } + } + + private static void ValidateCallback(Delegate callback) + { + if(callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } + + #endregion + + #region Thread Processing + + /// + /// Waits on the queue for a work item, shutdown, or timeout. + /// + /// + /// Returns the WaitingCallback or null in case of timeout or shutdown. + /// + private WorkItem Dequeue() + { + WorkItem workItem = + _workItemsQueue.DequeueWorkItem(_stpStartInfo.IdleTimeout, _shuttingDownEvent); + + return workItem; + } + + /// + /// Put a new work item in the queue + /// + /// A work item to queue + internal override void Enqueue(WorkItem workItem) + { + // Make sure the workItem is not null + Debug.Assert(null != workItem); + + IncrementWorkItemsCount(); + + workItem.CanceledSmartThreadPool = _canceledSmartThreadPool; + _workItemsQueue.EnqueueWorkItem(workItem); + workItem.WorkItemIsQueued(); + + // If all the threads are busy then try to create a new one + if (_currentWorkItemsCount > _workerThreads.Count) + { + StartThreads(1); + } + } + + private void IncrementWorkItemsCount() + { + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + + int count = Interlocked.Increment(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 1) + { + IsIdle = false; + _isIdleWaitHandle.Reset(); + } + } + + private void DecrementWorkItemsCount() + { + int count = Interlocked.Decrement(ref _currentWorkItemsCount); + //Trace.WriteLine("WorkItemsCount = " + _currentWorkItemsCount.ToString()); + if (count == 0) + { + IsIdle = true; + _isIdleWaitHandle.Set(); + } + + Interlocked.Increment(ref _workItemsProcessed); + + if (!_shutdown) + { + // The counter counts even if the work item was cancelled + _windowsPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + _localPCs.SampleWorkItems(_workItemsQueue.Count, _workItemsProcessed); + } + + } + + internal void RegisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + _workItemsGroups[workItemsGroup] = workItemsGroup; + } + + internal void UnregisterWorkItemsGroup(IWorkItemsGroup workItemsGroup) + { + if (_workItemsGroups.Contains(workItemsGroup)) + { + _workItemsGroups.Remove(workItemsGroup); + } + } + + /// + /// Inform that the current thread is about to quit or quiting. + /// The same thread may call this method more than once. + /// + private void InformCompleted() + { + // There is no need to lock the two methods together + // since only the current thread removes itself + // and the _workerThreads is a synchronized dictionary + if (_workerThreads.Contains(Thread.CurrentThread)) + { + _workerThreads.Remove(Thread.CurrentThread); + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + + /// + /// Starts new threads + /// + /// The number of threads to start + private void StartThreads(int threadsCount) + { + if (_isSuspended) + { + return; + } + + lock(_workerThreads.SyncRoot) + { + // Don't start threads on shut down + if (_shutdown) + { + return; + } + + for(int i = 0; i < threadsCount; ++i) + { + // Don't create more threads then the upper limit + if (_workerThreads.Count >= _stpStartInfo.MaxWorkerThreads) + { + return; + } + + // Create a new thread + +#if (_SILVERLIGHT) || (WINDOWS_PHONE) + Thread workerThread = new Thread(ProcessQueuedItems); +#else + Thread workerThread = + _stpStartInfo.MaxStackSize.HasValue + ? new Thread(ProcessQueuedItems, _stpStartInfo.MaxStackSize.Value) + : new Thread(ProcessQueuedItems); +#endif + // Configure the new thread and start it + workerThread.IsBackground = _stpStartInfo.AreThreadsBackground; + +#if !(_SILVERLIGHT) && !(_WINDOWS_CE) && !(WINDOWS_PHONE) + if (_stpStartInfo.ApartmentState != ApartmentState.Unknown) + { + workerThread.SetApartmentState(_stpStartInfo.ApartmentState); + } +#endif + +#if !(_SILVERLIGHT) && !(WINDOWS_PHONE) + workerThread.Priority = _stpStartInfo.ThreadPriority; +#endif + workerThread.Start(); + workerThread.Name = string.Format("STP:{0}:{1}", Name, _threadCounter); + ++_threadCounter; + + // Add it to the dictionary and update its creation time. + _workerThreads[workerThread] = new ThreadEntry(this); + + _windowsPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, _inUseWorkerThreads); + } + } + } + + /// + /// A worker thread method that processes work items from the work items queue. + /// + private void ProcessQueuedItems() + { + // Keep the entry of the dictionary as thread's variable to avoid the synchronization locks + // of the dictionary. + CurrentThreadEntry = _workerThreads[Thread.CurrentThread]; + + FireOnThreadInitialization(); + + try + { + bool bInUseWorkerThreadsWasIncremented = false; + + // Process until shutdown. + while(!_shutdown) + { + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // The following block handles the when the MaxWorkerThreads has been + // incremented by the user at run-time. + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + lock (_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MaxWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + + // Wait for a work item, shutdown, or timeout + WorkItem workItem = Dequeue(); + + // Update the last time this thread was seen alive. + // It's good for debugging. + CurrentThreadEntry.IAmAlive(); + + // On timeout or shut down. + if (null == workItem) + { + // Double lock for quit. + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + lock(_workerThreads.SyncRoot) + { + if (_workerThreads.Count > _stpStartInfo.MinWorkerThreads) + { + // Inform that the thread is quiting and then quit. + // This method must be called within this lock or else + // more threads will quit and the thread pool will go + // below the lower limit. + InformCompleted(); + break; + } + } + } + } + + // If we didn't quit then skip to the next iteration. + if (null == workItem) + { + continue; + } + + try + { + // Initialize the value to false + bInUseWorkerThreadsWasIncremented = false; + + // Set the Current Work Item of the thread. + // Store the Current Work Item before the workItem.StartingWorkItem() is called, + // so WorkItem.Cancel can work when the work item is between InQueue and InProgress + // states. + // If the work item has been cancelled BEFORE the workItem.StartingWorkItem() + // (work item is in InQueue state) then workItem.StartingWorkItem() will return false. + // If the work item has been cancelled AFTER the workItem.StartingWorkItem() then + // (work item is in InProgress state) then the thread will be aborted + CurrentThreadEntry.CurrentWorkItem = workItem; + + // Change the state of the work item to 'in progress' if possible. + // We do it here so if the work item has been canceled we won't + // increment the _inUseWorkerThreads. + // The cancel mechanism doesn't delete items from the queue, + // it marks the work item as canceled, and when the work item + // is dequeued, we just skip it. + // If the post execute of work item is set to always or to + // call when the work item is canceled then the StartingWorkItem() + // will return true, so the post execute can run. + if (!workItem.StartingWorkItem()) + { + continue; + } + + // Execute the callback. Make sure to accurately + // record how many callbacks are currently executing. + int inUseWorkerThreads = Interlocked.Increment(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + + // Mark that the _inUseWorkerThreads incremented, so in the finally{} + // statement we will decrement it correctly. + bInUseWorkerThreadsWasIncremented = true; + + workItem.FireWorkItemStarted(); + + ExecuteWorkItem(workItem); + } + catch(Exception ex) + { + ex.GetHashCode(); + // Do nothing + } + finally + { + workItem.DisposeOfState(); + + // Set the CurrentWorkItem to null, since we + // no longer run user's code. + CurrentThreadEntry.CurrentWorkItem = null; + + // Decrement the _inUseWorkerThreads only if we had + // incremented it. Note the cancelled work items don't + // increment _inUseWorkerThreads. + if (bInUseWorkerThreadsWasIncremented) + { + int inUseWorkerThreads = Interlocked.Decrement(ref _inUseWorkerThreads); + _windowsPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + _localPCs.SampleThreads(_workerThreads.Count, inUseWorkerThreads); + } + + // Notify that the work item has been completed. + // WorkItemsGroup may enqueue their next work item. + workItem.FireWorkItemCompleted(); + + // Decrement the number of work items here so the idle + // ManualResetEvent won't fluctuate. + DecrementWorkItemsCount(); + } + } + } + catch(ThreadAbortException tae) + { + tae.GetHashCode(); + // Handle the abort exception gracfully. +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } + catch(Exception e) + { + Debug.Assert(null != e); + } + finally + { + InformCompleted(); + FireOnThreadTermination(); + } + } + + private void ExecuteWorkItem(WorkItem workItem) + { + _windowsPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + _localPCs.SampleWorkItemsWaitTime(workItem.WaitingTime); + try + { + workItem.Execute(); + } + finally + { + _windowsPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + _localPCs.SampleWorkItemsProcessTime(workItem.ProcessTime); + } + } + + + #endregion + + #region Public Methods + + private void ValidateWaitForIdle() + { + if (null != CurrentThreadEntry && CurrentThreadEntry.AssociatedSmartThreadPool == this) + { + throw new NotSupportedException( + "WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + internal static void ValidateWorkItemsGroupWaitForIdle(IWorkItemsGroup workItemsGroup) + { + if (null == CurrentThreadEntry) + { + return; + } + + WorkItem workItem = CurrentThreadEntry.CurrentWorkItem; + ValidateWorkItemsGroupWaitForIdleImpl(workItemsGroup, workItem); + if ((null != workItemsGroup) && + (null != workItem) && + CurrentThreadEntry.CurrentWorkItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ValidateWorkItemsGroupWaitForIdleImpl(IWorkItemsGroup workItemsGroup, WorkItem workItem) + { + if ((null != workItemsGroup) && + (null != workItem) && + workItem.WasQueuedBy(workItemsGroup)) + { + throw new NotSupportedException("WaitForIdle cannot be called from a thread on its SmartThreadPool, it causes a deadlock"); + } + } + + /// + /// Force the SmartThreadPool to shutdown + /// + public void Shutdown() + { + Shutdown(true, 0); + } + + /// + /// Force the SmartThreadPool to shutdown with timeout + /// + public void Shutdown(bool forceAbort, TimeSpan timeout) + { + Shutdown(forceAbort, (int)timeout.TotalMilliseconds); + } + + /// + /// Empties the queue of work items and abort the threads in the pool. + /// + public void Shutdown(bool forceAbort, int millisecondsTimeout) + { + ValidateNotDisposed(); + + ISTPInstancePerformanceCounters pcs = _windowsPCs; + + if (NullSTPInstancePerformanceCounters.Instance != _windowsPCs) + { + // Set the _pcs to "null" to stop updating the performance + // counters + _windowsPCs = NullSTPInstancePerformanceCounters.Instance; + + pcs.Dispose(); + } + + Thread [] threads; + lock(_workerThreads.SyncRoot) + { + // Shutdown the work items queue + _workItemsQueue.Dispose(); + + // Signal the threads to exit + _shutdown = true; + _shuttingDownEvent.Set(); + + // Make a copy of the threads' references in the pool + threads = new Thread [_workerThreads.Count]; + _workerThreads.Keys.CopyTo(threads, 0); + } + + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + //DateTime start = DateTime.UtcNow; + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + bool timeout = false; + + // Each iteration we update the time left for the timeout. + foreach(Thread thread in threads) + { + // Join don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + timeout = true; + break; + } + + // Wait for the thread to terminate + bool success = thread.Join(millisecondsLeft); + if(!success) + { + timeout = true; + break; + } + + if(!waitInfinitely) + { + // Update the time left to wait + //TimeSpan ts = DateTime.UtcNow - start; + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + + if (timeout && forceAbort) + { + // Abort the threads in the pool + foreach(Thread thread in threads) + { + + if ((thread != null) +#if !(_WINDOWS_CE) + && thread.IsAlive +#endif + ) + { + try + { + thread.Abort(); // Shutdown + } + catch(SecurityException e) + { + e.GetHashCode(); + } + catch(ThreadStateException ex) + { + ex.GetHashCode(); + // In case the thread has been terminated + // after the check if it is alive. + } + } + } + } + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults) + { + return WaitAll(waitableResults, Timeout.Infinite, true); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAll(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in workItemResults has completed; otherwise false. + /// + public static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAll(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if any of the work items has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults) + { + return WaitAny(waitableResults, Timeout.Infinite, true); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult[] waitableResults, + TimeSpan timeout, + bool exitContext) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or a TimeSpan that represents -1 milliseconds to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + TimeSpan timeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WaitAny(waitableResults, (int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, null); + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + public static int WaitAny( + IWaitableResult [] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + return WorkItem.WaitAny(waitableResults, millisecondsTimeout, exitContext, cancelWaitHandle); + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, _stpStartInfo); + return workItemsGroup; + } + + /// + /// Creates a new WorkItemsGroup. + /// + /// The number of work items that can be run concurrently + /// A WorkItemsGroup configuration that overrides the default behavior + /// A reference to the WorkItemsGroup + public IWorkItemsGroup CreateWorkItemsGroup(int concurrency, WIGStartInfo wigStartInfo) + { + IWorkItemsGroup workItemsGroup = new WorkItemsGroup(this, concurrency, wigStartInfo); + return workItemsGroup; + } + + #region Fire Thread's Events + + private void FireOnThreadInitialization() + { + if (null != _onThreadInitialization) + { + foreach (ThreadInitializationHandler tih in _onThreadInitialization.GetInvocationList()) + { + try + { + tih(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + private void FireOnThreadTermination() + { + if (null != _onThreadTermination) + { + foreach (ThreadTerminationHandler tth in _onThreadTermination.GetInvocationList()) + { + try + { + tth(); + } + catch (Exception e) + { + e.GetHashCode(); + Debug.Assert(false); + throw; + } + } + } + } + + #endregion + + /// + /// This event is fired when a thread is created. + /// Use it to initialize a thread before the work items use it. + /// + public event ThreadInitializationHandler OnThreadInitialization + { + add { _onThreadInitialization += value; } + remove { _onThreadInitialization -= value; } + } + + /// + /// This event is fired when a thread is terminating. + /// Use it for cleanup. + /// + public event ThreadTerminationHandler OnThreadTermination + { + add { _onThreadTermination += value; } + remove { _onThreadTermination -= value; } + } + + + internal void CancelAbortWorkItemsGroup(WorkItemsGroup wig) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + workItem.WasQueuedBy(wig) && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + + + + #endregion + + #region Properties + + /// + /// Get/Set the lower limit of threads in the pool. + /// + public int MinThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MinWorkerThreads; + } + set + { + Debug.Assert(value >= 0); + Debug.Assert(value <= _stpStartInfo.MaxWorkerThreads); + if (_stpStartInfo.MaxWorkerThreads < value) + { + _stpStartInfo.MaxWorkerThreads = value; + } + _stpStartInfo.MinWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + + /// + /// Get/Set the upper limit of threads in the pool. + /// + public int MaxThreads + { + get + { + ValidateNotDisposed(); + return _stpStartInfo.MaxWorkerThreads; + } + + set + { + Debug.Assert(value > 0); + Debug.Assert(value >= _stpStartInfo.MinWorkerThreads); + if (_stpStartInfo.MinWorkerThreads > value) + { + _stpStartInfo.MinWorkerThreads = value; + } + _stpStartInfo.MaxWorkerThreads = value; + StartOptimalNumberOfThreads(); + } + } + /// + /// Get the number of threads in the thread pool. + /// Should be between the lower and the upper limits. + /// + public int ActiveThreads + { + get + { + ValidateNotDisposed(); + return _workerThreads.Count; + } + } + + /// + /// Get the number of busy (not idle) threads in the thread pool. + /// + public int InUseThreads + { + get + { + ValidateNotDisposed(); + return _inUseWorkerThreads; + } + } + + /// + /// Returns true if the current running work item has been cancelled. + /// Must be used within the work item's callback method. + /// The work item should sample this value in order to know if it + /// needs to quit before its completion. + /// + public static bool IsWorkItemCanceled + { + get + { + return CurrentThreadEntry.CurrentWorkItem.IsCanceled; + } + } + + /// + /// Checks if the work item has been cancelled, and if yes then abort the thread. + /// Can be used with Cancel and timeout + /// + public static void AbortOnWorkItemCancel() + { + if (IsWorkItemCanceled) + { + Thread.CurrentThread.Abort(); + } + } + + /// + /// Thread Pool start information (readonly) + /// + public STPStartInfo STPStartInfo + { + get + { + return _stpStartInfo.AsReadOnly(); + } + } + + public bool IsShuttingdown + { + get { return _shutdown; } + } + + /// + /// Return the local calculated performance counters + /// Available only if STPStartInfo.EnableLocalPerformanceCounters is true. + /// + public ISTPPerformanceCountersReader PerformanceCountersReader + { + get { return (ISTPPerformanceCountersReader)_localPCs; } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + if (!_shutdown) + { + Shutdown(); + } + + if (null != _shuttingDownEvent) + { + _shuttingDownEvent.Close(); + _shuttingDownEvent = null; + } + _workerThreads.Clear(); + + if (null != _isIdleWaitHandle) + { + _isIdleWaitHandle.Close(); + _isIdleWaitHandle = null; + } + + _isDisposed = true; + } + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + #endregion + + #region WorkItemsGroupBase Overrides + + /// + /// Get/Set the maximum number of work items that execute cocurrency on the thread pool + /// + public override int Concurrency + { + get { return MaxThreads; } + set { MaxThreads = value; } + } + + /// + /// Get the number of work items in the queue. + /// + public override int WaitingCallbacks + { + get + { + ValidateNotDisposed(); + return _workItemsQueue.Count; + } + } + + /// + /// Get an array with all the state objects of the currently running items. + /// The array represents a snap shot and impact performance. + /// + public override object[] GetStates() + { + object[] states = _workItemsQueue.GetStates(); + return states; + } + + /// + /// WorkItemsGroup start information (readonly) + /// + public override WIGStartInfo WIGStartInfo + { + get { return _stpStartInfo.AsReadOnly(); } + } + + /// + /// Start the thread pool if it was started suspended. + /// If it is already running, this method is ignored. + /// + public override void Start() + { + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.OnSTPIsStarting(); + } + + StartOptimalNumberOfThreads(); + } + + /// + /// Cancel all work items using thread abortion + /// + /// True to stop work items by raising ThreadAbortException + public override void Cancel(bool abortExecution) + { + _canceledSmartThreadPool.IsCanceled = true; + _canceledSmartThreadPool = new CanceledWorkItemsGroup(); + + ICollection workItemsGroups = _workItemsGroups.Values; + foreach (WorkItemsGroup workItemsGroup in workItemsGroups) + { + workItemsGroup.Cancel(abortExecution); + } + + if (abortExecution) + { + foreach (ThreadEntry threadEntry in _workerThreads.Values) + { + WorkItem workItem = threadEntry.CurrentWorkItem; + if (null != workItem && + threadEntry.AssociatedSmartThreadPool == this && + !workItem.IsCanceled) + { + threadEntry.CurrentWorkItem.GetWorkItemResult().Cancel(true); + } + } + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + ValidateWaitForIdle(); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + /// + /// This event is fired when all work items are completed. + /// (When IsIdle changes to true) + /// This event only work on WorkItemsGroup. On SmartThreadPool + /// it throws the NotImplementedException. + /// + public override event WorkItemsGroupIdleHandler OnIdle + { + add + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle += value; + } + remove + { + throw new NotImplementedException("This event is not implemented in the SmartThreadPool class. Please create a WorkItemsGroup in order to use this feature."); + //_onIdle -= value; + } + } + + internal override void PreQueueWorkItem() + { + ValidateNotDisposed(); + } + + #endregion + + #region Join, Choice, Pipe, etc. + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + foreach (Action action in actions) + { + workItemsGroup.QueueWorkItem(action); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes all actions in parallel. + /// Returns when they all finish. + /// + /// Actions to execute + public void Join(params Action[] actions) + { + Join((IEnumerable)actions); + } + + private class ChoiceIndex + { + public int _index = -1; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(IEnumerable actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(int.MaxValue, wigStartInfo); + + ManualResetEvent anActionCompleted = new ManualResetEvent(false); + + ChoiceIndex choiceIndex = new ChoiceIndex(); + + int i = 0; + foreach (Action action in actions) + { + Action act = action; + int value = i; + workItemsGroup.QueueWorkItem(() => { act(); Interlocked.CompareExchange(ref choiceIndex._index, value, -1); anActionCompleted.Set(); }); + ++i; + } + workItemsGroup.Start(); + anActionCompleted.WaitOne(); + + return choiceIndex._index; + } + + /// + /// Executes all actions in parallel + /// Returns when the first one completes + /// + /// Actions to execute + public int Choice(params Action[] actions) + { + return Choice((IEnumerable)actions); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// A state context that passes + /// Actions to execute in the order they should run + public void Pipe(T pipeState, IEnumerable> actions) + { + WIGStartInfo wigStartInfo = new WIGStartInfo { StartSuspended = true }; + IWorkItemsGroup workItemsGroup = CreateWorkItemsGroup(1, wigStartInfo); + foreach (Action action in actions) + { + Action act = action; + workItemsGroup.QueueWorkItem(() => act(pipeState)); + } + workItemsGroup.Start(); + workItemsGroup.WaitForIdle(); + } + + /// + /// Executes actions in sequence asynchronously. + /// Returns immediately. + /// + /// + /// Actions to execute in the order they should run + public void Pipe(T pipeState, params Action[] actions) + { + Pipe(pipeState, (IEnumerable>)actions); + } + #endregion + } + #endregion +} diff --git a/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs new file mode 100644 index 0000000000..0cce19ffac --- /dev/null +++ b/ThirdParty/SmartThreadPool/SynchronizedDictionary.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; + +namespace Amib.Threading.Internal +{ + internal class SynchronizedDictionary + { + private readonly Dictionary _dictionary; + private readonly object _lock; + + public SynchronizedDictionary() + { + _lock = new object(); + _dictionary = new Dictionary(); + } + + public int Count + { + get { return _dictionary.Count; } + } + + public bool Contains(TKey key) + { + lock (_lock) + { + return _dictionary.ContainsKey(key); + } + } + + public void Remove(TKey key) + { + lock (_lock) + { + _dictionary.Remove(key); + } + } + + public object SyncRoot + { + get { return _lock; } + } + + public TValue this[TKey key] + { + get + { + lock (_lock) + { + return _dictionary[key]; + } + } + set + { + lock (_lock) + { + _dictionary[key] = value; + } + } + } + + public Dictionary.KeyCollection Keys + { + get + { + lock (_lock) + { + return _dictionary.Keys; + } + } + } + + public Dictionary.ValueCollection Values + { + get + { + lock (_lock) + { + return _dictionary.Values; + } + } + } + public void Clear() + { + lock (_lock) + { + _dictionary.Clear(); + } + } + } +} diff --git a/ThirdParty/SmartThreadPool/WIGStartInfo.cs b/ThirdParty/SmartThreadPool/WIGStartInfo.cs new file mode 100644 index 0000000000..8af195b2af --- /dev/null +++ b/ThirdParty/SmartThreadPool/WIGStartInfo.cs @@ -0,0 +1,171 @@ +using System; + +namespace Amib.Threading +{ + /// + /// Summary description for WIGStartInfo. + /// + public class WIGStartInfo + { + private bool _useCallerCallContext; + private bool _useCallerHttpContext; + private bool _disposeOfStateObjects; + private CallToPostExecute _callToPostExecute; + private PostExecuteWorkItemCallback _postExecuteWorkItemCallback; + private bool _startSuspended; + private WorkItemPriority _workItemPriority; + private bool _fillStateWithArgs; + + protected bool _readOnly; + + public WIGStartInfo() + { + _fillStateWithArgs = SmartThreadPool.DefaultFillStateWithArgs; + _workItemPriority = SmartThreadPool.DefaultWorkItemPriority; + _startSuspended = SmartThreadPool.DefaultStartSuspended; + _postExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + _callToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + _disposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + _useCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + _useCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; + } + + public WIGStartInfo(WIGStartInfo wigStartInfo) + { + _useCallerCallContext = wigStartInfo.UseCallerCallContext; + _useCallerHttpContext = wigStartInfo.UseCallerHttpContext; + _disposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + _callToPostExecute = wigStartInfo.CallToPostExecute; + _postExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + _workItemPriority = wigStartInfo.WorkItemPriority; + _startSuspended = wigStartInfo.StartSuspended; + _fillStateWithArgs = wigStartInfo.FillStateWithArgs; + } + + protected void ThrowIfReadOnly() + { + if (_readOnly) + { + throw new NotSupportedException("This is a readonly instance and set is not supported"); + } + } + + /// + /// Get/Set if to use the caller's security context + /// + public virtual bool UseCallerCallContext + { + get { return _useCallerCallContext; } + set + { + ThrowIfReadOnly(); + _useCallerCallContext = value; + } + } + + + /// + /// Get/Set if to use the caller's HTTP context + /// + public virtual bool UseCallerHttpContext + { + get { return _useCallerHttpContext; } + set + { + ThrowIfReadOnly(); + _useCallerHttpContext = value; + } + } + + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public virtual bool DisposeOfStateObjects + { + get { return _disposeOfStateObjects; } + set + { + ThrowIfReadOnly(); + _disposeOfStateObjects = value; + } + } + + + /// + /// Get/Set the run the post execute options + /// + public virtual CallToPostExecute CallToPostExecute + { + get { return _callToPostExecute; } + set + { + ThrowIfReadOnly(); + _callToPostExecute = value; + } + } + + + /// + /// Get/Set the default post execute callback + /// + public virtual PostExecuteWorkItemCallback PostExecuteWorkItemCallback + { + get { return _postExecuteWorkItemCallback; } + set + { + ThrowIfReadOnly(); + _postExecuteWorkItemCallback = value; + } + } + + + /// + /// Get/Set if the work items execution should be suspended until the Start() + /// method is called. + /// + public virtual bool StartSuspended + { + get { return _startSuspended; } + set + { + ThrowIfReadOnly(); + _startSuspended = value; + } + } + + + /// + /// Get/Set the default priority that a work item gets when it is enqueued + /// + public virtual WorkItemPriority WorkItemPriority + { + get { return _workItemPriority; } + set { _workItemPriority = value; } + } + + /// + /// Get/Set the if QueueWorkItem of Action<...>/Func<...> fill the + /// arguments as an object array into the state of the work item. + /// The arguments can be access later by IWorkItemResult.State. + /// + public virtual bool FillStateWithArgs + { + get { return _fillStateWithArgs; } + set + { + ThrowIfReadOnly(); + _fillStateWithArgs = value; + } + } + + /// + /// Get a readonly version of this WIGStartInfo + /// + /// Returns a readonly reference to this WIGStartInfoRO + public WIGStartInfo AsReadOnly() + { + return new WIGStartInfo(this) { _readOnly = true }; + } + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs new file mode 100644 index 0000000000..435a14bf2e --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItem.WorkItemResult.cs @@ -0,0 +1,190 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public partial class WorkItem + { + #region WorkItemResult class + + private class WorkItemResult : IWorkItemResult, IInternalWorkItemResult, IInternalWaitableResult + { + /// + /// A back reference to the work item + /// + private readonly WorkItem _workItem; + + public WorkItemResult(WorkItem workItem) + { + _workItem = workItem; + } + + internal WorkItem GetWorkItem() + { + return _workItem; + } + + #region IWorkItemResult Members + + public bool IsCompleted + { + get + { + return _workItem.IsCompleted; + } + } + + public bool IsCanceled + { + get + { + return _workItem.IsCanceled; + } + } + + public object GetResult() + { + return _workItem.GetResult(Timeout.Infinite, true, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null); + } + + public object GetResult(TimeSpan timeout, bool exitContext) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle); + } + + public object GetResult(out Exception e) + { + return _workItem.GetResult(Timeout.Infinite, true, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, null, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, null, out e); + } + + public object GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public object GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return _workItem.GetResult((int)timeout.TotalMilliseconds, exitContext, cancelWaitHandle, out e); + } + + public bool Cancel() + { + return Cancel(false); + } + + public bool Cancel(bool abortExecution) + { + return _workItem.Cancel(abortExecution); + } + + public object State + { + get + { + return _workItem._state; + } + } + + public WorkItemPriority WorkItemPriority + { + get + { + return _workItem._workItemInfo.WorkItemPriority; + } + } + + /// + /// Return the result, same as GetResult() + /// + public object Result + { + get { return GetResult(); } + } + + /// + /// Returns the exception if occured otherwise returns null. + /// This value is valid only after the work item completed, + /// before that it is always null. + /// + public object Exception + { + get { return _workItem._exception; } + } + + #endregion + + #region IInternalWorkItemResult Members + + public event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItem.OnWorkItemStarted += value; + } + remove + { + _workItem.OnWorkItemStarted -= value; + } + } + + + public event WorkItemStateCallback OnWorkItemCompleted + { + add + { + _workItem.OnWorkItemCompleted += value; + } + remove + { + _workItem.OnWorkItemCompleted -= value; + } + } + + #endregion + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return this; + } + + public IWorkItemResult GetWorkItemResultT() + { + return new WorkItemResultTWrapper(this); + } + + #endregion + } + + #endregion + + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItem.cs b/ThirdParty/SmartThreadPool/WorkItem.cs new file mode 100644 index 0000000000..185f10ce58 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItem.cs @@ -0,0 +1,1002 @@ +using System; +using System.Threading; +using System.Diagnostics; + +namespace Amib.Threading.Internal +{ + /// + /// Holds a callback delegate and the state for that delegate. + /// + public partial class WorkItem : IHasWorkItemPriority + { + #region WorkItemState enum + + /// + /// Indicates the state of the work item in the thread pool + /// + private enum WorkItemState + { + InQueue = 0, // Nexts: InProgress, Canceled + InProgress = 1, // Nexts: Completed, Canceled + Completed = 2, // Stays Completed + Canceled = 3, // Stays Canceled + } + + private static bool IsValidStatesTransition(WorkItemState currentState, WorkItemState nextState) + { + bool valid = false; + + switch (currentState) + { + case WorkItemState.InQueue: + valid = (WorkItemState.InProgress == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.InProgress: + valid = (WorkItemState.Completed == nextState) || (WorkItemState.Canceled == nextState); + break; + case WorkItemState.Completed: + case WorkItemState.Canceled: + // Cannot be changed + break; + default: + // Unknown state + Debug.Assert(false); + break; + } + + return valid; + } + + #endregion + + #region Fields + + /// + /// Callback delegate for the callback. + /// + private readonly WorkItemCallback _callback; + + /// + /// State with which to call the callback delegate. + /// + private object _state; + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + /// + /// Stores the caller's context + /// + private readonly CallerThreadContext _callerContext; +#endif + /// + /// Holds the result of the mehtod + /// + private object _result; + + /// + /// Hold the exception if the method threw it + /// + private Exception _exception; + + /// + /// Hold the state of the work item + /// + private WorkItemState _workItemState; + + /// + /// A ManualResetEvent to indicate that the result is ready + /// + private ManualResetEvent _workItemCompleted; + + /// + /// A reference count to the _workItemCompleted. + /// When it reaches to zero _workItemCompleted is Closed + /// + private int _workItemCompletedRefCount; + + /// + /// Represents the result state of the work item + /// + private readonly WorkItemResult _workItemResult; + + /// + /// Work item info + /// + private readonly WorkItemInfo _workItemInfo; + + /// + /// Called when the WorkItem starts + /// + private event WorkItemStateCallback _workItemStartedEvent; + + /// + /// Called when the WorkItem completes + /// + private event WorkItemStateCallback _workItemCompletedEvent; + + /// + /// A reference to an object that indicates whatever the + /// WorkItemsGroup has been canceled + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// + /// A reference to an object that indicates whatever the + /// SmartThreadPool has been canceled + /// + private CanceledWorkItemsGroup _canceledSmartThreadPool = CanceledWorkItemsGroup.NotCanceledWorkItemsGroup; + + /// + /// The work item group this work item belong to. + /// + private readonly IWorkItemsGroup _workItemsGroup; + + /// + /// The thread that executes this workitem. + /// This field is available for the period when the work item is executed, before and after it is null. + /// + private Thread _executingThread; + + /// + /// The absulote time when the work item will be timeout + /// + private long _expirationTime; + + #region Performance Counter fields + + + + + /// + /// Stores how long the work item waited on the stp queue + /// + private Stopwatch _waitingOnQueueStopwatch; + + /// + /// Stores how much time it took the work item to execute after it went out of the queue + /// + private Stopwatch _processingStopwatch; + + #endregion + + #endregion + + #region Properties + + public TimeSpan WaitingTime + { + get + { + return _waitingOnQueueStopwatch.Elapsed; + } + } + + public TimeSpan ProcessTime + { + get + { + return _processingStopwatch.Elapsed; + } + } + + internal WorkItemInfo WorkItemInfo + { + get + { + return _workItemInfo; + } + } + + #endregion + + #region Construction + + /// + /// Initialize the callback holding object. + /// + /// The workItemGroup of the workitem + /// The WorkItemInfo of te workitem + /// Callback delegate for the callback. + /// State with which to call the callback delegate. + /// + /// We assume that the WorkItem object is created within the thread + /// that meant to run the callback + public WorkItem( + IWorkItemsGroup workItemsGroup, + WorkItemInfo workItemInfo, + WorkItemCallback callback, + object state) + { + _workItemsGroup = workItemsGroup; + _workItemInfo = workItemInfo; + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + if (_workItemInfo.UseCallerCallContext || _workItemInfo.UseCallerHttpContext) + { + _callerContext = CallerThreadContext.Capture(_workItemInfo.UseCallerCallContext, _workItemInfo.UseCallerHttpContext); + } +#endif + + _callback = callback; + _state = state; + _workItemResult = new WorkItemResult(this); + Initialize(); + } + + internal void Initialize() + { + // The _workItemState is changed directly instead of using the SetWorkItemState + // method since we don't want to go throught IsValidStateTransition. + _workItemState = WorkItemState.InQueue; + + _workItemCompleted = null; + _workItemCompletedRefCount = 0; + _waitingOnQueueStopwatch = new Stopwatch(); + _processingStopwatch = new Stopwatch(); + _expirationTime = + _workItemInfo.Timeout > 0 ? + DateTime.UtcNow.Ticks + _workItemInfo.Timeout * TimeSpan.TicksPerMillisecond : + long.MaxValue; + } + + internal bool WasQueuedBy(IWorkItemsGroup workItemsGroup) + { + return (workItemsGroup == _workItemsGroup); + } + + + #endregion + + #region Methods + + internal CanceledWorkItemsGroup CanceledWorkItemsGroup + { + get { return _canceledWorkItemsGroup; } + set { _canceledWorkItemsGroup = value; } + } + + internal CanceledWorkItemsGroup CanceledSmartThreadPool + { + get { return _canceledSmartThreadPool; } + set { _canceledSmartThreadPool = value; } + } + + /// + /// Change the state of the work item to in progress if it wasn't canceled. + /// + /// + /// Return true on success or false in case the work item was canceled. + /// If the work item needs to run a post execute then the method will return true. + /// + public bool StartingWorkItem() + { + _waitingOnQueueStopwatch.Stop(); + _processingStopwatch.Start(); + + lock (this) + { + if (IsCanceled) + { + bool result = false; + if ((_workItemInfo.PostExecuteWorkItemCallback != null) && + ((_workItemInfo.CallToPostExecute & CallToPostExecute.WhenWorkItemCanceled) == CallToPostExecute.WhenWorkItemCanceled)) + { + result = true; + } + + return result; + } + + Debug.Assert(WorkItemState.InQueue == GetWorkItemState()); + + // No need for a lock yet, only after the state has changed to InProgress + _executingThread = Thread.CurrentThread; + + SetWorkItemState(WorkItemState.InProgress); + } + + return true; + } + + /// + /// Execute the work item and the post execute + /// + public void Execute() + { + CallToPostExecute currentCallToPostExecute = 0; + + // Execute the work item if we are in the correct state + switch (GetWorkItemState()) + { + case WorkItemState.InProgress: + currentCallToPostExecute |= CallToPostExecute.WhenWorkItemNotCanceled; + ExecuteWorkItem(); + break; + case WorkItemState.Canceled: + currentCallToPostExecute |= CallToPostExecute.WhenWorkItemCanceled; + break; + default: + Debug.Assert(false); + throw new NotSupportedException(); + } + + // Run the post execute as needed + if ((currentCallToPostExecute & _workItemInfo.CallToPostExecute) != 0) + { + PostExecute(); + } + + _processingStopwatch.Stop(); + } + + internal void FireWorkItemCompleted() + { + try + { + if (null != _workItemCompletedEvent) + { + _workItemCompletedEvent(this); + } + } + catch // Suppress exceptions + { } + } + + internal void FireWorkItemStarted() + { + try + { + if (null != _workItemStartedEvent) + { + _workItemStartedEvent(this); + } + } + catch // Suppress exceptions + { } + } + + /// + /// Execute the work item + /// + private void ExecuteWorkItem() + { + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + CallerThreadContext ctc = null; + if (null != _callerContext) + { + ctc = CallerThreadContext.Capture(_callerContext.CapturedCallContext, _callerContext.CapturedHttpContext); + CallerThreadContext.Apply(_callerContext); + } +#endif + + Exception exception = null; + object result = null; + + try + { + try + { + result = _callback(_state); + } + catch (Exception e) + { + // Save the exception so we can rethrow it later + exception = e; + } + + // Remove the value of the execution thread, so it will be impossible to cancel the work item, + // since it is already completed. + // Cancelling a work item that already completed may cause the abortion of the next work item!!! + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + + if (null == executionThread) + { + // Oops! we are going to be aborted..., Wait here so we can catch the ThreadAbortException + Thread.Sleep(60 * 1000); + + // If after 1 minute this thread was not aborted then let it continue working. + } + } + // We must treat the ThreadAbortException or else it will be stored in the exception variable + catch (ThreadAbortException tae) + { + tae.GetHashCode(); + // Check if the work item was cancelled + // If we got a ThreadAbortException and the STP is not shutting down, it means the + // work items was cancelled. + if (!SmartThreadPool.CurrentThreadEntry.AssociatedSmartThreadPool.IsShuttingdown) + { +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + Thread.ResetAbort(); +#endif + } + } + +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + if (null != _callerContext) + { + CallerThreadContext.Apply(ctc); + } +#endif + + if (!SmartThreadPool.IsWorkItemCanceled) + { + SetResult(result, exception); + } + } + + /// + /// Runs the post execute callback + /// + private void PostExecute() + { + if (null != _workItemInfo.PostExecuteWorkItemCallback) + { + try + { + _workItemInfo.PostExecuteWorkItemCallback(_workItemResult); + } + catch (Exception e) + { + Debug.Assert(null != e); + } + } + } + + /// + /// Set the result of the work item to return + /// + /// The result of the work item + /// The exception that was throw while the workitem executed, null + /// if there was no exception. + internal void SetResult(object result, Exception exception) + { + _result = result; + _exception = exception; + SignalComplete(false); + } + + /// + /// Returns the work item result + /// + /// The work item result + internal IWorkItemResult GetWorkItemResult() + { + return _workItemResult; + } + + /// + /// Wait for all work items to complete + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// true when every work item in waitableResults has completed; otherwise false. + /// + internal static bool WaitAll( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + if (0 == waitableResults.Length) + { + return true; + } + + bool success; + WaitHandle[] waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); + + if ((null == cancelWaitHandle) && (waitHandles.Length <= 64)) + { + success = STPEventWaitHandle.WaitAll(waitHandles, millisecondsTimeout, exitContext); + } + else + { + success = true; + int millisecondsLeft = millisecondsTimeout; + Stopwatch stopwatch = Stopwatch.StartNew(); + + WaitHandle[] whs; + if (null != cancelWaitHandle) + { + whs = new WaitHandle[] { null, cancelWaitHandle }; + } + else + { + whs = new WaitHandle[] { null }; + } + + bool waitInfinitely = (Timeout.Infinite == millisecondsTimeout); + // Iterate over the wait handles and wait for each one to complete. + // We cannot use WaitHandle.WaitAll directly, because the cancelWaitHandle + // won't affect it. + // Each iteration we update the time left for the timeout. + for (int i = 0; i < waitableResults.Length; ++i) + { + // WaitAny don't work with negative numbers + if (!waitInfinitely && (millisecondsLeft < 0)) + { + success = false; + break; + } + + whs[0] = waitHandles[i]; + int result = STPEventWaitHandle.WaitAny(whs, millisecondsLeft, exitContext); + if ((result > 0) || (STPEventWaitHandle.WaitTimeout == result)) + { + success = false; + break; + } + + if (!waitInfinitely) + { + // Update the time left to wait + millisecondsLeft = millisecondsTimeout - (int)stopwatch.ElapsedMilliseconds; + } + } + } + // Release the wait handles + ReleaseWaitHandles(waitableResults); + + return success; + } + + /// + /// Waits for any of the work items in the specified array to complete, cancel, or timeout + /// + /// Array of work item result objects + /// The number of milliseconds to wait, or Timeout.Infinite (-1) to wait indefinitely. + /// + /// true to exit the synchronization domain for the context before the wait (if in a synchronized context), and reacquire it; otherwise, false. + /// + /// A cancel wait handle to interrupt the wait if needed + /// + /// The array index of the work item result that satisfied the wait, or WaitTimeout if no work item result satisfied the wait and a time interval equivalent to millisecondsTimeout has passed or the work item has been canceled. + /// + internal static int WaitAny( + IWaitableResult[] waitableResults, + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + WaitHandle[] waitHandles; + + if (null != cancelWaitHandle) + { + waitHandles = new WaitHandle[waitableResults.Length + 1]; + GetWaitHandles(waitableResults, waitHandles); + waitHandles[waitableResults.Length] = cancelWaitHandle; + } + else + { + waitHandles = new WaitHandle[waitableResults.Length]; + GetWaitHandles(waitableResults, waitHandles); + } + + int result = STPEventWaitHandle.WaitAny(waitHandles, millisecondsTimeout, exitContext); + + // Treat cancel as timeout + if (null != cancelWaitHandle) + { + if (result == waitableResults.Length) + { + result = STPEventWaitHandle.WaitTimeout; + } + } + + ReleaseWaitHandles(waitableResults); + + return result; + } + + /// + /// Fill an array of wait handles with the work items wait handles. + /// + /// An array of work item results + /// An array of wait handles to fill + private static void GetWaitHandles( + IWaitableResult[] waitableResults, + WaitHandle[] waitHandles) + { + for (int i = 0; i < waitableResults.Length; ++i) + { + WorkItemResult wir = waitableResults[i].GetWorkItemResult() as WorkItemResult; + Debug.Assert(null != wir, "All waitableResults must be WorkItemResult objects"); + + waitHandles[i] = wir.GetWorkItem().GetWaitHandle(); + } + } + + /// + /// Release the work items' wait handles + /// + /// An array of work item results + private static void ReleaseWaitHandles(IWaitableResult[] waitableResults) + { + for (int i = 0; i < waitableResults.Length; ++i) + { + WorkItemResult wir = (WorkItemResult)waitableResults[i].GetWorkItemResult(); + + wir.GetWorkItem().ReleaseWaitHandle(); + } + } + + #endregion + + #region Private Members + + private WorkItemState GetWorkItemState() + { + lock (this) + { + if (WorkItemState.Completed == _workItemState) + { + return _workItemState; + } + + long nowTicks = DateTime.UtcNow.Ticks; + + if (WorkItemState.Canceled != _workItemState && nowTicks > _expirationTime) + { + _workItemState = WorkItemState.Canceled; + } + + if (WorkItemState.InProgress == _workItemState) + { + return _workItemState; + } + + if (CanceledSmartThreadPool.IsCanceled || CanceledWorkItemsGroup.IsCanceled) + { + return WorkItemState.Canceled; + } + + return _workItemState; + } + } + + + /// + /// Sets the work item's state + /// + /// The state to set the work item to + private void SetWorkItemState(WorkItemState workItemState) + { + lock (this) + { + if (IsValidStatesTransition(_workItemState, workItemState)) + { + _workItemState = workItemState; + } + } + } + + /// + /// Signals that work item has been completed or canceled + /// + /// Indicates that the work item has been canceled + private void SignalComplete(bool canceled) + { + SetWorkItemState(canceled ? WorkItemState.Canceled : WorkItemState.Completed); + lock (this) + { + // If someone is waiting then signal. + if (null != _workItemCompleted) + { + _workItemCompleted.Set(); + } + } + } + + internal void WorkItemIsQueued() + { + _waitingOnQueueStopwatch.Start(); + } + + #endregion + + #region Members exposed by WorkItemResult + + /// + /// Cancel the work item if it didn't start running yet. + /// + /// Returns true on success or false if the work item is in progress or already completed + private bool Cancel(bool abortExecution) + { +#if (_WINDOWS_CE) + if(abortExecution) + { + throw new ArgumentOutOfRangeException("abortExecution", "WindowsCE doesn't support this feature"); + } +#endif + bool success = false; + bool signalComplete = false; + + lock (this) + { + switch (GetWorkItemState()) + { + case WorkItemState.Canceled: + //Debug.WriteLine("Work item already canceled"); + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + // No need to signalComplete, because we already cancelled this work item + // so it already signaled its completion. + //signalComplete = true; + } + } + success = true; + break; + case WorkItemState.Completed: + //Debug.WriteLine("Work item cannot be canceled"); + break; + case WorkItemState.InProgress: + if (abortExecution) + { + Thread executionThread = Interlocked.CompareExchange(ref _executingThread, null, _executingThread); + if (null != executionThread) + { + executionThread.Abort(); // "Cancel" + success = true; + signalComplete = true; + } + } + else + { + // ************************** + // Stock SmartThreadPool 2.2.3 sets these to true and relies on the thread to check the + // WorkItem cancellation status. However, OpenSimulator uses a different mechanism to notify + // scripts of co-operative termination and the abort code also relies on this method + // returning false in order to implement a small wait. + // + // Therefore, as was the case previously with STP, we will not signal successful cancellation + // here. It's possible that OpenSimulator code could be changed in the future to remove + // the need for this change. + // ************************** + success = false; + signalComplete = false; + } + break; + case WorkItemState.InQueue: + // Signal to the wait for completion that the work + // item has been completed (canceled). There is no + // reason to wait for it to get out of the queue + signalComplete = true; + //Debug.WriteLine("Work item canceled"); + success = true; + break; + } + + if (signalComplete) + { + SignalComplete(true); + } + } + return success; + } + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. + /// In case of error the method throws and exception + /// + /// The result of the work item + private object GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle) + { + Exception e; + object result = GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + if (null != e) + { + throw new WorkItemResultException("The work item caused an excpetion, see the inner exception for details", e); + } + return result; + } + + /// + /// Get the result of the work item. + /// If the work item didn't run yet then the caller waits for the result, timeout, or cancel. + /// In case of error the e argument is filled with the exception + /// + /// The result of the work item + private object GetResult( + int millisecondsTimeout, + bool exitContext, + WaitHandle cancelWaitHandle, + out Exception e) + { + e = null; + + // Check for cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } + + // Check for completion + if (IsCompleted) + { + e = _exception; + return _result; + } + + // If no cancelWaitHandle is provided + if (null == cancelWaitHandle) + { + WaitHandle wh = GetWaitHandle(); + + bool timeout = !STPEventWaitHandle.WaitOne(wh, millisecondsTimeout, exitContext); + + ReleaseWaitHandle(); + + if (timeout) + { + throw new WorkItemTimeoutException("Work item timeout"); + } + } + else + { + WaitHandle wh = GetWaitHandle(); + int result = STPEventWaitHandle.WaitAny(new WaitHandle[] { wh, cancelWaitHandle }); + ReleaseWaitHandle(); + + switch (result) + { + case 0: + // The work item signaled + // Note that the signal could be also as a result of canceling the + // work item (not the get result) + break; + case 1: + case STPEventWaitHandle.WaitTimeout: + throw new WorkItemTimeoutException("Work item timeout"); + default: + Debug.Assert(false); + break; + + } + } + + // Check for cancel + if (WorkItemState.Canceled == GetWorkItemState()) + { + throw new WorkItemCancelException("Work item canceled"); + } + + Debug.Assert(IsCompleted); + + e = _exception; + + // Return the result + return _result; + } + + /// + /// A wait handle to wait for completion, cancel, or timeout + /// + private WaitHandle GetWaitHandle() + { + lock (this) + { + if (null == _workItemCompleted) + { + _workItemCompleted = EventWaitHandleFactory.CreateManualResetEvent(IsCompleted); + } + ++_workItemCompletedRefCount; + } + return _workItemCompleted; + } + + private void ReleaseWaitHandle() + { + lock (this) + { + if (null != _workItemCompleted) + { + --_workItemCompletedRefCount; + if (0 == _workItemCompletedRefCount) + { + _workItemCompleted.Close(); + _workItemCompleted = null; + } + } + } + } + + /// + /// Returns true when the work item has completed or canceled + /// + private bool IsCompleted + { + get + { + lock (this) + { + WorkItemState workItemState = GetWorkItemState(); + return ((workItemState == WorkItemState.Completed) || + (workItemState == WorkItemState.Canceled)); + } + } + } + + /// + /// Returns true when the work item has canceled + /// + public bool IsCanceled + { + get + { + lock (this) + { + return (GetWorkItemState() == WorkItemState.Canceled); + } + } + } + + #endregion + + #region IHasWorkItemPriority Members + + /// + /// Returns the priority of the work item + /// + public WorkItemPriority WorkItemPriority + { + get + { + return _workItemInfo.WorkItemPriority; + } + } + + #endregion + + internal event WorkItemStateCallback OnWorkItemStarted + { + add + { + _workItemStartedEvent += value; + } + remove + { + _workItemStartedEvent -= value; + } + } + + internal event WorkItemStateCallback OnWorkItemCompleted + { + add + { + _workItemCompletedEvent += value; + } + remove + { + _workItemCompletedEvent -= value; + } + } + + public void DisposeOfState() + { + if (_workItemInfo.DisposeOfStateObjects) + { + IDisposable disp = _state as IDisposable; + if (null != disp) + { + disp.Dispose(); + _state = null; + } + } + } + } +} diff --git a/ThirdParty/SmartThreadPool/WorkItemFactory.cs b/ThirdParty/SmartThreadPool/WorkItemFactory.cs new file mode 100644 index 0000000000..16ccd81a54 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemFactory.cs @@ -0,0 +1,343 @@ +using System; + +namespace Amib.Threading.Internal +{ + #region WorkItemFactory class + + public class WorkItemFactory + { + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// The priority of the work item + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + WorkItemPriority workItemPriority) + { + return CreateWorkItem(workItemsGroup, wigStartInfo, callback, null, workItemPriority); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// Work item info + /// A callback to execute + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback) + { + return CreateWorkItem( + workItemsGroup, + wigStartInfo, + workItemInfo, + callback, + null); + } + + /// + /// Create a new work item + /// + /// The WorkItemsGroup of this workitem + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = wigStartInfo.PostExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemInfo workItemInfo, + WorkItemCallback callback, + object state) + { + ValidateCallback(callback); + ValidateCallback(workItemInfo.PostExecuteWorkItemCallback); + + WorkItem workItem = new WorkItem( + workItemsGroup, + new WorkItemInfo(workItemInfo), + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = wigStartInfo.CallToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = workItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + workItemInfo.WorkItemPriority = wigStartInfo.WorkItemPriority; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + /// + /// Create a new work item + /// + /// The work items group + /// Work item group start information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item + public static WorkItem CreateWorkItem( + IWorkItemsGroup workItemsGroup, + WIGStartInfo wigStartInfo, + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + + ValidateCallback(callback); + ValidateCallback(postExecuteWorkItemCallback); + + WorkItemInfo workItemInfo = new WorkItemInfo(); + workItemInfo.UseCallerCallContext = wigStartInfo.UseCallerCallContext; + workItemInfo.UseCallerHttpContext = wigStartInfo.UseCallerHttpContext; + workItemInfo.PostExecuteWorkItemCallback = postExecuteWorkItemCallback; + workItemInfo.CallToPostExecute = callToPostExecute; + workItemInfo.WorkItemPriority = workItemPriority; + workItemInfo.DisposeOfStateObjects = wigStartInfo.DisposeOfStateObjects; + + WorkItem workItem = new WorkItem( + workItemsGroup, + workItemInfo, + callback, + state); + + return workItem; + } + + private static void ValidateCallback(Delegate callback) + { + if (callback != null && callback.GetInvocationList().Length > 1) + { + throw new NotSupportedException("SmartThreadPool doesn't support delegates chains"); + } + } + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/WorkItemInfo.cs b/ThirdParty/SmartThreadPool/WorkItemInfo.cs new file mode 100644 index 0000000000..0d7fc85c65 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemInfo.cs @@ -0,0 +1,69 @@ +namespace Amib.Threading +{ + #region WorkItemInfo class + + /// + /// Summary description for WorkItemInfo. + /// + public class WorkItemInfo + { + public WorkItemInfo() + { + UseCallerCallContext = SmartThreadPool.DefaultUseCallerCallContext; + UseCallerHttpContext = SmartThreadPool.DefaultUseCallerHttpContext; + DisposeOfStateObjects = SmartThreadPool.DefaultDisposeOfStateObjects; + CallToPostExecute = SmartThreadPool.DefaultCallToPostExecute; + PostExecuteWorkItemCallback = SmartThreadPool.DefaultPostExecuteWorkItemCallback; + WorkItemPriority = SmartThreadPool.DefaultWorkItemPriority; + } + + public WorkItemInfo(WorkItemInfo workItemInfo) + { + UseCallerCallContext = workItemInfo.UseCallerCallContext; + UseCallerHttpContext = workItemInfo.UseCallerHttpContext; + DisposeOfStateObjects = workItemInfo.DisposeOfStateObjects; + CallToPostExecute = workItemInfo.CallToPostExecute; + PostExecuteWorkItemCallback = workItemInfo.PostExecuteWorkItemCallback; + WorkItemPriority = workItemInfo.WorkItemPriority; + Timeout = workItemInfo.Timeout; + } + + /// + /// Get/Set if to use the caller's security context + /// + public bool UseCallerCallContext { get; set; } + + /// + /// Get/Set if to use the caller's HTTP context + /// + public bool UseCallerHttpContext { get; set; } + + /// + /// Get/Set if to dispose of the state object of a work item + /// + public bool DisposeOfStateObjects { get; set; } + + /// + /// Get/Set the run the post execute options + /// + public CallToPostExecute CallToPostExecute { get; set; } + + /// + /// Get/Set the post execute callback + /// + public PostExecuteWorkItemCallback PostExecuteWorkItemCallback { get; set; } + + /// + /// Get/Set the work item's priority + /// + public WorkItemPriority WorkItemPriority { get; set; } + + /// + /// Get/Set the work item's timout in milliseconds. + /// This is a passive timout. When the timout expires the work item won't be actively aborted! + /// + public long Timeout { get; set; } + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs new file mode 100644 index 0000000000..d1eff95184 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemResultTWrapper.cs @@ -0,0 +1,128 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemResultTWrapper class + + internal class WorkItemResultTWrapper : IWorkItemResult, IInternalWaitableResult + { + private readonly IWorkItemResult _workItemResult; + + public WorkItemResultTWrapper(IWorkItemResult workItemResult) + { + _workItemResult = workItemResult; + } + + #region IWorkItemResult Members + + public TResult GetResult() + { + return (TResult)_workItemResult.GetResult(); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle); + } + + public TResult GetResult(out Exception e) + { + return (TResult)_workItemResult.GetResult(out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, out e); + } + + public TResult GetResult(int millisecondsTimeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(millisecondsTimeout, exitContext, cancelWaitHandle, out e); + } + + public TResult GetResult(TimeSpan timeout, bool exitContext, WaitHandle cancelWaitHandle, out Exception e) + { + return (TResult)_workItemResult.GetResult(timeout, exitContext, cancelWaitHandle, out e); + } + + public bool IsCompleted + { + get { return _workItemResult.IsCompleted; } + } + + public bool IsCanceled + { + get { return _workItemResult.IsCanceled; } + } + + public object State + { + get { return _workItemResult.State; } + } + + public bool Cancel() + { + return _workItemResult.Cancel(); + } + + public bool Cancel(bool abortExecution) + { + return _workItemResult.Cancel(abortExecution); + } + + public WorkItemPriority WorkItemPriority + { + get { return _workItemResult.WorkItemPriority; } + } + + public TResult Result + { + get { return (TResult)_workItemResult.Result; } + } + + public object Exception + { + get { return (TResult)_workItemResult.Exception; } + } + + #region IInternalWorkItemResult Members + + public IWorkItemResult GetWorkItemResult() + { + return _workItemResult.GetWorkItemResult(); + } + + public IWorkItemResult GetWorkItemResultT() + { + return (IWorkItemResult)this; + } + + #endregion + + #endregion + } + + #endregion + +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroup.cs b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs new file mode 100644 index 0000000000..d9d34ac8b2 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsGroup.cs @@ -0,0 +1,361 @@ +using System; +using System.Threading; +using System.Runtime.CompilerServices; +using System.Diagnostics; + +namespace Amib.Threading.Internal +{ + + #region WorkItemsGroup class + + /// + /// Summary description for WorkItemsGroup. + /// + public class WorkItemsGroup : WorkItemsGroupBase + { + #region Private members + + private readonly object _lock = new object(); + + /// + /// A reference to the SmartThreadPool instance that created this + /// WorkItemsGroup. + /// + private readonly SmartThreadPool _stp; + + /// + /// The OnIdle event + /// + private event WorkItemsGroupIdleHandler _onIdle; + + /// + /// A flag to indicate if the Work Items Group is now suspended. + /// + private bool _isSuspended; + + /// + /// Defines how many work items of this WorkItemsGroup can run at once. + /// + private int _concurrency; + + /// + /// Priority queue to hold work items before they are passed + /// to the SmartThreadPool. + /// + private readonly PriorityQueue _workItemsQueue; + + /// + /// Indicate how many work items are waiting in the SmartThreadPool + /// queue. + /// This value is used to apply the concurrency. + /// + private int _workItemsInStpQueue; + + /// + /// Indicate how many work items are currently running in the SmartThreadPool. + /// This value is used with the Cancel, to calculate if we can send new + /// work items to the STP. + /// + private int _workItemsExecutingInStp = 0; + + /// + /// WorkItemsGroup start information + /// + private readonly WIGStartInfo _workItemsGroupStartInfo; + + /// + /// Signaled when all of the WorkItemsGroup's work item completed. + /// + //private readonly ManualResetEvent _isIdleWaitHandle = new ManualResetEvent(true); + private readonly ManualResetEvent _isIdleWaitHandle = EventWaitHandleFactory.CreateManualResetEvent(true); + + /// + /// A common object for all the work items that this work items group + /// generate so we can mark them to cancel in O(1) + /// + private CanceledWorkItemsGroup _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + + #endregion + + #region Construction + + public WorkItemsGroup( + SmartThreadPool stp, + int concurrency, + WIGStartInfo wigStartInfo) + { + if (concurrency <= 0) + { + throw new ArgumentOutOfRangeException( + "concurrency", +#if !(_WINDOWS_CE) && !(_SILVERLIGHT) && !(WINDOWS_PHONE) + concurrency, +#endif + "concurrency must be greater than zero"); + } + _stp = stp; + _concurrency = concurrency; + _workItemsGroupStartInfo = new WIGStartInfo(wigStartInfo).AsReadOnly(); + _workItemsQueue = new PriorityQueue(); + Name = "WorkItemsGroup"; + + // The _workItemsInStpQueue gets the number of currently executing work items, + // because once a work item is executing, it cannot be cancelled. + _workItemsInStpQueue = _workItemsExecutingInStp; + + _isSuspended = _workItemsGroupStartInfo.StartSuspended; + } + + #endregion + + #region WorkItemsGroupBase Overrides + + public override int Concurrency + { + get { return _concurrency; } + set + { + Debug.Assert(value > 0); + + int diff = value - _concurrency; + _concurrency = value; + if (diff > 0) + { + EnqueueToSTPNextNWorkItem(diff); + } + } + } + + public override int WaitingCallbacks + { + get { return _workItemsQueue.Count; } + } + + public override object[] GetStates() + { + lock (_lock) + { + object[] states = new object[_workItemsQueue.Count]; + int i = 0; + foreach (WorkItem workItem in _workItemsQueue) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; + } + } + + /// + /// WorkItemsGroup start information + /// + public override WIGStartInfo WIGStartInfo + { + get { return _workItemsGroupStartInfo; } + } + + /// + /// Start the Work Items Group if it was started suspended + /// + public override void Start() + { + // If the Work Items Group already started then quit + if (!_isSuspended) + { + return; + } + _isSuspended = false; + + EnqueueToSTPNextNWorkItem(Math.Min(_workItemsQueue.Count, _concurrency)); + } + + public override void Cancel(bool abortExecution) + { + lock (_lock) + { + _canceledWorkItemsGroup.IsCanceled = true; + _workItemsQueue.Clear(); + _workItemsInStpQueue = 0; + _canceledWorkItemsGroup = new CanceledWorkItemsGroup(); + } + + if (abortExecution) + { + _stp.CancelAbortWorkItemsGroup(this); + } + } + + /// + /// Wait for the thread pool to be idle + /// + public override bool WaitForIdle(int millisecondsTimeout) + { + SmartThreadPool.ValidateWorkItemsGroupWaitForIdle(this); + return STPEventWaitHandle.WaitOne(_isIdleWaitHandle, millisecondsTimeout, false); + } + + public override event WorkItemsGroupIdleHandler OnIdle + { + add { _onIdle += value; } + remove { _onIdle -= value; } + } + + #endregion + + #region Private methods + + private void RegisterToWorkItemCompletion(IWorkItemResult wir) + { + IInternalWorkItemResult iwir = (IInternalWorkItemResult)wir; + iwir.OnWorkItemStarted += OnWorkItemStartedCallback; + iwir.OnWorkItemCompleted += OnWorkItemCompletedCallback; + } + + public void OnSTPIsStarting() + { + if (_isSuspended) + { + return; + } + + EnqueueToSTPNextNWorkItem(_concurrency); + } + + public void EnqueueToSTPNextNWorkItem(int count) + { + for (int i = 0; i < count; ++i) + { + EnqueueToSTPNextWorkItem(null, false); + } + } + + private object FireOnIdle(object state) + { + FireOnIdleImpl(_onIdle); + return null; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void FireOnIdleImpl(WorkItemsGroupIdleHandler onIdle) + { + if(null == onIdle) + { + return; + } + + Delegate[] delegates = onIdle.GetInvocationList(); + foreach(WorkItemsGroupIdleHandler eh in delegates) + { + try + { + eh(this); + } + catch { } // Suppress exceptions + } + } + + private void OnWorkItemStartedCallback(WorkItem workItem) + { + lock(_lock) + { + ++_workItemsExecutingInStp; + } + } + + private void OnWorkItemCompletedCallback(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(null, true); + } + + internal override void Enqueue(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(workItem); + } + + private void EnqueueToSTPNextWorkItem(WorkItem workItem) + { + EnqueueToSTPNextWorkItem(workItem, false); + } + + private void EnqueueToSTPNextWorkItem(WorkItem workItem, bool decrementWorkItemsInStpQueue) + { + lock(_lock) + { + // Got here from OnWorkItemCompletedCallback() + if (decrementWorkItemsInStpQueue) + { + --_workItemsInStpQueue; + + if(_workItemsInStpQueue < 0) + { + _workItemsInStpQueue = 0; + } + + --_workItemsExecutingInStp; + + if(_workItemsExecutingInStp < 0) + { + _workItemsExecutingInStp = 0; + } + } + + // If the work item is not null then enqueue it + if (null != workItem) + { + workItem.CanceledWorkItemsGroup = _canceledWorkItemsGroup; + + RegisterToWorkItemCompletion(workItem.GetWorkItemResult()); + _workItemsQueue.Enqueue(workItem); + //_stp.IncrementWorkItemsCount(); + + if ((1 == _workItemsQueue.Count) && + (0 == _workItemsInStpQueue)) + { + _stp.RegisterWorkItemsGroup(this); + IsIdle = false; + _isIdleWaitHandle.Reset(); + } + } + + // If the work items queue of the group is empty than quit + if (0 == _workItemsQueue.Count) + { + if (0 == _workItemsInStpQueue) + { + _stp.UnregisterWorkItemsGroup(this); + IsIdle = true; + _isIdleWaitHandle.Set(); + if (decrementWorkItemsInStpQueue && _onIdle != null && _onIdle.GetInvocationList().Length > 0) + { + _stp.QueueWorkItem(new WorkItemCallback(FireOnIdle)); + } + } + return; + } + + if (!_isSuspended) + { + if (_workItemsInStpQueue < _concurrency) + { + WorkItem nextWorkItem = _workItemsQueue.Dequeue() as WorkItem; + try + { + _stp.Enqueue(nextWorkItem); + } + catch (ObjectDisposedException e) + { + e.GetHashCode(); + // The STP has been shutdown + } + + ++_workItemsInStpQueue; + } + } + } + } + + #endregion + } + + #endregion +} diff --git a/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs new file mode 100644 index 0000000000..27fae5e813 --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsGroupBase.cs @@ -0,0 +1,471 @@ +using System; +using System.Threading; + +namespace Amib.Threading.Internal +{ + public abstract class WorkItemsGroupBase : IWorkItemsGroup + { + #region Private Fields + + /// + /// Contains the name of this instance of SmartThreadPool. + /// Can be changed by the user. + /// + private string _name = "WorkItemsGroupBase"; + + public WorkItemsGroupBase() + { + IsIdle = true; + } + + #endregion + + #region IWorkItemsGroup Members + + #region Public Methods + + /// + /// Get/Set the name of the SmartThreadPool/WorkItemsGroup instance + /// + public string Name + { + get { return _name; } + set { _name = value; } + } + + #endregion + + #region Abstract Methods + + public abstract int Concurrency { get; set; } + public abstract int WaitingCallbacks { get; } + public abstract object[] GetStates(); + public abstract WIGStartInfo WIGStartInfo { get; } + public abstract void Start(); + public abstract void Cancel(bool abortExecution); + public abstract bool WaitForIdle(int millisecondsTimeout); + public abstract event WorkItemsGroupIdleHandler OnIdle; + + internal abstract void Enqueue(WorkItem workItem); + internal virtual void PreQueueWorkItem() { } + + #endregion + + #region Common Base Methods + + /// + /// Cancel all the work items. + /// Same as Cancel(false) + /// + public virtual void Cancel() + { + Cancel(false); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public void WaitForIdle() + { + WaitForIdle(Timeout.Infinite); + } + + /// + /// Wait for the SmartThreadPool/WorkItemsGroup to be idle + /// + public bool WaitForIdle(TimeSpan timeout) + { + return WaitForIdle((int)timeout.TotalMilliseconds); + } + + /// + /// IsIdle is true when there are no work items running or queued. + /// + public bool IsIdle { get; protected set; } + + #endregion + + #region QueueWorkItem + + /// + /// Queue a work item + /// + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// The priority of the work item + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item info + /// A callback to execute + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state) + { + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemCallback callback, object state, WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// Work item information + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem(WorkItemInfo workItemInfo, WorkItemCallback callback, object state) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, workItemInfo, callback, state); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + /// + /// Queue a work item + /// + /// A callback to execute + /// + /// The context object of the work item. Used for passing arguments to the work item. + /// + /// + /// A delegate to call after the callback completion + /// + /// Indicates on which cases to call to the post execute callback + /// The work item priority + /// Returns a work item result + public IWorkItemResult QueueWorkItem( + WorkItemCallback callback, + object state, + PostExecuteWorkItemCallback postExecuteWorkItemCallback, + CallToPostExecute callToPostExecute, + WorkItemPriority workItemPriority) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem(this, WIGStartInfo, callback, state, postExecuteWorkItemCallback, callToPostExecute, workItemPriority); + Enqueue(workItem); + return workItem.GetWorkItemResult(); + } + + #endregion + + #region QueueWorkItem(Action<...>) + + public IWorkItemResult QueueWorkItem(Action action) + { + return QueueWorkItem (action, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + delegate + { + action.Invoke (); + return null; + }, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T arg) + { + return QueueWorkItem (action, arg, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T arg, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2) + { + return QueueWorkItem (action, arg1, arg2, SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem(Action action, T1 arg1, T2 arg2, T3 arg3) + { + return QueueWorkItem (action, arg1, arg2, arg3, SmartThreadPool.DefaultWorkItemPriority); + ; + } + + public IWorkItemResult QueueWorkItem (Action action, T1 arg1, T2 arg2, T3 arg3, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + public IWorkItemResult QueueWorkItem( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + return QueueWorkItem (action, arg1, arg2, arg3, arg4, + SmartThreadPool.DefaultWorkItemPriority); + } + + public IWorkItemResult QueueWorkItem ( + Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, WorkItemPriority priority) + { + PreQueueWorkItem (); + WorkItem workItem = WorkItemFactory.CreateWorkItem ( + this, + WIGStartInfo, + state => + { + action.Invoke (arg1, arg2, arg3, arg4); + return null; + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null, priority); + Enqueue (workItem); + return workItem.GetWorkItemResult (); + } + + #endregion + + #region QueueWorkItem(Func<...>) + + public IWorkItemResult QueueWorkItem(Func func) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(); + }); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T arg) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem(Func func, T1 arg1, T2 arg2) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + public IWorkItemResult QueueWorkItem( + Func func, T1 arg1, T2 arg2, T3 arg3, T4 arg4) + { + PreQueueWorkItem(); + WorkItem workItem = WorkItemFactory.CreateWorkItem( + this, + WIGStartInfo, + state => + { + return func.Invoke(arg1, arg2, arg3, arg4); + }, + WIGStartInfo.FillStateWithArgs ? new object[] { arg1, arg2, arg3, arg4 } : null); + Enqueue(workItem); + return new WorkItemResultTWrapper(workItem.GetWorkItemResult()); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/ThirdParty/SmartThreadPool/WorkItemsQueue.cs b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs new file mode 100644 index 0000000000..e0bc9168ec --- /dev/null +++ b/ThirdParty/SmartThreadPool/WorkItemsQueue.cs @@ -0,0 +1,645 @@ +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Amib.Threading.Internal +{ + #region WorkItemsQueue class + + /// + /// WorkItemsQueue class. + /// + public class WorkItemsQueue : IDisposable + { + #region Member variables + + /// + /// Waiters queue (implemented as stack). + /// + private readonly WaiterEntry _headWaiterEntry = new WaiterEntry(); + + /// + /// Waiters count + /// + private int _waitersCount = 0; + + /// + /// Work items queue + /// + private readonly PriorityQueue _workItems = new PriorityQueue(); + + /// + /// Indicate that work items are allowed to be queued + /// + private bool _isWorkItemsQueueActive = true; + + +#if (WINDOWS_PHONE) + private static readonly Dictionary _waiterEntries = new Dictionary(); +#elif (_WINDOWS_CE) + private static LocalDataStoreSlot _waiterEntrySlot = Thread.AllocateDataSlot(); +#else + + [ThreadStatic] + private static WaiterEntry _waiterEntry; +#endif + + + /// + /// Each thread in the thread pool keeps its own waiter entry. + /// + private static WaiterEntry CurrentWaiterEntry + { +#if (WINDOWS_PHONE) + get + { + lock (_waiterEntries) + { + WaiterEntry waiterEntry; + if (_waiterEntries.TryGetValue(Thread.CurrentThread.ManagedThreadId, out waiterEntry)) + { + return waiterEntry; + } + } + return null; + } + set + { + lock (_waiterEntries) + { + _waiterEntries[Thread.CurrentThread.ManagedThreadId] = value; + } + } +#elif (_WINDOWS_CE) + get + { + return Thread.GetData(_waiterEntrySlot) as WaiterEntry; + } + set + { + Thread.SetData(_waiterEntrySlot, value); + } +#else + get + { + return _waiterEntry; + } + set + { + _waiterEntry = value; + } +#endif + } + + /// + /// A flag that indicates if the WorkItemsQueue has been disposed. + /// + private bool _isDisposed = false; + + #endregion + + #region Public properties + + /// + /// Returns the current number of work items in the queue + /// + public int Count + { + get + { + return _workItems.Count; + } + } + + /// + /// Returns the current number of waiters + /// + public int WaitersCount + { + get + { + return _waitersCount; + } + } + + + #endregion + + #region Public methods + + /// + /// Enqueue a work item to the queue. + /// + public bool EnqueueWorkItem(WorkItem workItem) + { + // A work item cannot be null, since null is used in the + // WaitForWorkItem() method to indicate timeout or cancel + if (null == workItem) + { + throw new ArgumentNullException("workItem" , "workItem cannot be null"); + } + + bool enqueue = true; + + // First check if there is a waiter waiting for work item. During + // the check, timed out waiters are ignored. If there is no + // waiter then the work item is queued. + lock(this) + { + ValidateNotDisposed(); + + if (!_isWorkItemsQueueActive) + { + return false; + } + + while(_waitersCount > 0) + { + // Dequeue a waiter. + WaiterEntry waiterEntry = PopWaiter(); + + // Signal the waiter. On success break the loop + if (waiterEntry.Signal(workItem)) + { + enqueue = false; + break; + } + } + + if (enqueue) + { + // Enqueue the work item + _workItems.Enqueue(workItem); + } + } + return true; + } + + + /// + /// Waits for a work item or exits on timeout or cancel + /// + /// Timeout in milliseconds + /// Cancel wait handle + /// Returns true if the resource was granted + public WorkItem DequeueWorkItem( + int millisecondsTimeout, + WaitHandle cancelEvent) + { + // This method cause the caller to wait for a work item. + // If there is at least one waiting work item then the + // method returns immidiately with it. + // + // If there are no waiting work items then the caller + // is queued between other waiters for a work item to arrive. + // + // If a work item didn't come within millisecondsTimeout or + // the user canceled the wait by signaling the cancelEvent + // then the method returns null to indicate that the caller + // didn't get a work item. + + WaiterEntry waiterEntry; + WorkItem workItem = null; + lock (this) + { + ValidateNotDisposed(); + + // If there are waiting work items then take one and return. + if (_workItems.Count > 0) + { + workItem = _workItems.Dequeue() as WorkItem; + return workItem; + } + + // No waiting work items ... + + // Get the waiter entry for the waiters queue + waiterEntry = GetThreadWaiterEntry(); + + // Put the waiter with the other waiters + PushWaiter(waiterEntry); + } + + // Prepare array of wait handle for the WaitHandle.WaitAny() + WaitHandle [] waitHandles = new WaitHandle[] { + waiterEntry.WaitHandle, + cancelEvent }; + + // Wait for an available resource, cancel event, or timeout. + + // During the wait we are supposes to exit the synchronization + // domain. (Placing true as the third argument of the WaitAny()) + // It just doesn't work, I don't know why, so I have two lock(this) + // statments instead of one. + + int index = STPEventWaitHandle.WaitAny( + waitHandles, + millisecondsTimeout, + true); + + lock(this) + { + // success is true if it got a work item. + bool success = (0 == index); + + // The timeout variable is used only for readability. + // (We treat cancel as timeout) + bool timeout = !success; + + // On timeout update the waiterEntry that it is timed out + if (timeout) + { + // The Timeout() fails if the waiter has already been signaled + timeout = waiterEntry.Timeout(); + + // On timeout remove the waiter from the queue. + // Note that the complexity is O(1). + if(timeout) + { + RemoveWaiter(waiterEntry, false); + } + + // Again readability + success = !timeout; + } + + // On success return the work item + if (success) + { + workItem = waiterEntry.WorkItem; + + if (null == workItem) + { + workItem = _workItems.Dequeue() as WorkItem; + } + } + } + // On failure return null. + return workItem; + } + + /// + /// Cleanup the work items queue, hence no more work + /// items are allowed to be queue + /// + private void Cleanup() + { + lock(this) + { + // Deactivate only once + if (!_isWorkItemsQueueActive) + { + return; + } + + // Don't queue more work items + _isWorkItemsQueueActive = false; + + foreach(WorkItem workItem in _workItems) + { + workItem.DisposeOfState(); + } + + // Clear the work items that are already queued + _workItems.Clear(); + + // Note: + // I don't iterate over the queue and dispose of work items's states, + // since if a work item has a state object that is still in use in the + // application then I must not dispose it. + + // Tell the waiters that they were timed out. + // It won't signal them to exit, but to ignore their + // next work item. + while(_waitersCount > 0) + { + WaiterEntry waiterEntry = PopWaiter(); + waiterEntry.Timeout(); + } + } + } + + public object[] GetStates() + { + lock (this) + { + object[] states = new object[_workItems.Count]; + int i = 0; + foreach (WorkItem workItem in _workItems) + { + states[i] = workItem.GetWorkItemResult().State; + ++i; + } + return states; + } + } + + #endregion + + #region Private methods + + /// + /// Returns the WaiterEntry of the current thread + /// + /// + /// In order to avoid creation and destuction of WaiterEntry + /// objects each thread has its own WaiterEntry object. + private static WaiterEntry GetThreadWaiterEntry() + { + if (null == CurrentWaiterEntry) + { + CurrentWaiterEntry = new WaiterEntry(); + } + CurrentWaiterEntry.Reset(); + return CurrentWaiterEntry; + } + + #region Waiters stack methods + + /// + /// Push a new waiter into the waiter's stack + /// + /// A waiter to put in the stack + public void PushWaiter(WaiterEntry newWaiterEntry) + { + // Remove the waiter if it is already in the stack and + // update waiter's count as needed + RemoveWaiter(newWaiterEntry, false); + + // If the stack is empty then newWaiterEntry is the new head of the stack + if (null == _headWaiterEntry._nextWaiterEntry) + { + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + + } + // If the stack is not empty then put newWaiterEntry as the new head + // of the stack. + else + { + // Save the old first waiter entry + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Update the links + _headWaiterEntry._nextWaiterEntry = newWaiterEntry; + newWaiterEntry._nextWaiterEntry = oldFirstWaiterEntry; + newWaiterEntry._prevWaiterEntry = _headWaiterEntry; + oldFirstWaiterEntry._prevWaiterEntry = newWaiterEntry; + } + + // Increment the number of waiters + ++_waitersCount; + } + + /// + /// Pop a waiter from the waiter's stack + /// + /// Returns the first waiter in the stack + private WaiterEntry PopWaiter() + { + // Store the current stack head + WaiterEntry oldFirstWaiterEntry = _headWaiterEntry._nextWaiterEntry; + + // Store the new stack head + WaiterEntry newHeadWaiterEntry = oldFirstWaiterEntry._nextWaiterEntry; + + // Update the old stack head list links and decrement the number + // waiters. + RemoveWaiter(oldFirstWaiterEntry, true); + + // Update the new stack head + _headWaiterEntry._nextWaiterEntry = newHeadWaiterEntry; + if (null != newHeadWaiterEntry) + { + newHeadWaiterEntry._prevWaiterEntry = _headWaiterEntry; + } + + // Return the old stack head + return oldFirstWaiterEntry; + } + + /// + /// Remove a waiter from the stack + /// + /// A waiter entry to remove + /// If true the waiter count is always decremented + private void RemoveWaiter(WaiterEntry waiterEntry, bool popDecrement) + { + // Store the prev entry in the list + WaiterEntry prevWaiterEntry = waiterEntry._prevWaiterEntry; + + // Store the next entry in the list + WaiterEntry nextWaiterEntry = waiterEntry._nextWaiterEntry; + + // A flag to indicate if we need to decrement the waiters count. + // If we got here from PopWaiter then we must decrement. + // If we got here from PushWaiter then we decrement only if + // the waiter was already in the stack. + bool decrementCounter = popDecrement; + + // Null the waiter's entry links + waiterEntry._prevWaiterEntry = null; + waiterEntry._nextWaiterEntry = null; + + // If the waiter entry had a prev link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != prevWaiterEntry) + { + prevWaiterEntry._nextWaiterEntry = nextWaiterEntry; + decrementCounter = true; + } + + // If the waiter entry had a next link then update it. + // It also means that the waiter is already in the list and we + // need to decrement the waiters count. + if (null != nextWaiterEntry) + { + nextWaiterEntry._prevWaiterEntry = prevWaiterEntry; + decrementCounter = true; + } + + // Decrement the waiters count if needed + if (decrementCounter) + { + --_waitersCount; + } + } + + #endregion + + #endregion + + #region WaiterEntry class + + // A waiter entry in the _waiters queue. + public sealed class WaiterEntry : IDisposable + { + #region Member variables + + /// + /// Event to signal the waiter that it got the work item. + /// + //private AutoResetEvent _waitHandle = new AutoResetEvent(false); + private AutoResetEvent _waitHandle = EventWaitHandleFactory.CreateAutoResetEvent(); + + /// + /// Flag to know if this waiter already quited from the queue + /// because of a timeout. + /// + private bool _isTimedout = false; + + /// + /// Flag to know if the waiter was signaled and got a work item. + /// + private bool _isSignaled = false; + + /// + /// A work item that passed directly to the waiter withou going + /// through the queue + /// + private WorkItem _workItem = null; + + private bool _isDisposed = false; + + // Linked list members + internal WaiterEntry _nextWaiterEntry = null; + internal WaiterEntry _prevWaiterEntry = null; + + #endregion + + #region Construction + + public WaiterEntry() + { + Reset(); + } + + #endregion + + #region Public methods + + public WaitHandle WaitHandle + { + get { return _waitHandle; } + } + + public WorkItem WorkItem + { + get + { + return _workItem; + } + } + + /// + /// Signal the waiter that it got a work item. + /// + /// Return true on success + /// The method fails if Timeout() preceded its call + public bool Signal(WorkItem workItem) + { + lock(this) + { + if (!_isTimedout) + { + _workItem = workItem; + _isSignaled = true; + _waitHandle.Set(); + return true; + } + } + return false; + } + + /// + /// Mark the wait entry that it has been timed out + /// + /// Return true on success + /// The method fails if Signal() preceded its call + public bool Timeout() + { + lock(this) + { + // Time out can happen only if the waiter wasn't marked as + // signaled + if (!_isSignaled) + { + // We don't remove the waiter from the queue, the DequeueWorkItem + // method skips _waiters that were timed out. + _isTimedout = true; + return true; + } + } + return false; + } + + /// + /// Reset the wait entry so it can be used again + /// + public void Reset() + { + _workItem = null; + _isTimedout = false; + _isSignaled = false; + _waitHandle.Reset(); + } + + /// + /// Free resources + /// + public void Close() + { + if (null != _waitHandle) + { + _waitHandle.Close(); + _waitHandle = null; + } + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + lock (this) + { + if (!_isDisposed) + { + Close(); + } + _isDisposed = true; + } + } + + #endregion + } + + #endregion + + #region IDisposable Members + + public void Dispose() + { + if (!_isDisposed) + { + Cleanup(); + } + _isDisposed = true; + } + + private void ValidateNotDisposed() + { + if(_isDisposed) + { + throw new ObjectDisposedException(GetType().ToString(), "The SmartThreadPool has been shutdown"); + } + } + + #endregion + } + + #endregion +} + diff --git a/ThirdPartyLicenses/Aurora-Sim.txt b/ThirdPartyLicenses/Aurora-Sim.txt new file mode 100644 index 0000000000..162d552b90 --- /dev/null +++ b/ThirdPartyLicenses/Aurora-Sim.txt @@ -0,0 +1,26 @@ +/* + * Copyright (c) Contributors, http://aurora-sim.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 Aurora-Sim 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. + */ diff --git a/ThirdPartyLicenses/Axiom.txt b/ThirdPartyLicenses/Axiom.txt new file mode 100644 index 0000000000..fe97db2356 --- /dev/null +++ b/ThirdPartyLicenses/Axiom.txt @@ -0,0 +1,141 @@ +GNU LESSER GENERAL PUBLIC LICENSE +Version 2.1, February 1999 + + +Copyright (C) 1991, 1999 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + +Preamble +The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish); that you receive source code or can get it if you want it; that you can change the software and use pieces of it in new free programs; and that you are informed that you can do these things. + +To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. + +Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. + +When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. + +We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. + +For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. + +Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION +0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". + +A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. + +The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) + +"Source code" for a work means the preferred form of the work for making modifications to it. For a library, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the library. + +Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. + +1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and distribute a copy of this License along with the Library. + +You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: + + +a) The modified work must itself be a software library. +b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. +c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. +d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. +(For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Library, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. + +3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. + +Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of the Library into a program that is not a library. + +4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. + +5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. + +However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. + +When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. + +6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: + + +a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) +b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. +c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. +d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. +e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. +For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. + +It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. + +7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: + + +a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. +b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. +8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. + +9. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. + +10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. + +11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply, and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. + +This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. + +12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. + +13. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. + +14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. + +NO WARRANTY + +15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. \ No newline at end of file diff --git a/ThirdPartyLicenses/BclExtras.txt b/ThirdPartyLicenses/BclExtras.txt new file mode 100644 index 0000000000..a8a02925b1 --- /dev/null +++ b/ThirdPartyLicenses/BclExtras.txt @@ -0,0 +1,60 @@ +MICROSOFT PUBLIC LICENSE (Ms-PL) + +This license governs use of the accompanying software. If you use the software, +you accept this license. If you do not accept the license, do not use the +software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" +have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the +software. + +A "contributor" is any person that distributes its contribution under this +license. + +"Licensed patents" are a contributor's patent claims that read directly on its +contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free copyright license to reproduce its +contribution, prepare derivative works of its contribution, and distribute its +contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license +conditions and limitations in section 3, each contributor grants you a +non-exclusive, worldwide, royalty-free license under its licensed patents to +make, have made, use, sell, offer for sale, import, and/or otherwise dispose of +its contribution in the software or derivative works of the contribution in the +software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any +contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you +claim are infringed by the software, your patent license from such contributor +to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all +copyright, patent, trademark, and attribution notices that are present in the +software. + +(D) If you distribute any portion of the software in source code form, you may +do so only under this license by including a complete copy of this license with +your distribution. If you distribute any portion of the software in compiled or +object code form, you may only do so under a license that complies with this +license. + +(E) The software is licensed "as-is." You bear the risk of using it. The +contributors give no express warranties, guarantees or conditions. You may have +additional consumer rights under your local laws which this license cannot +change. To the extent permitted under your local laws, the contributors exclude +the implied warranties of merchantability, fitness for a particular purpose and +non-infringement. diff --git a/ThirdPartyLicenses/Bullet for Xna (ModifiedBulletX).txt b/ThirdPartyLicenses/Bullet for Xna (ModifiedBulletX).txt new file mode 100644 index 0000000000..a8ed924e89 --- /dev/null +++ b/ThirdPartyLicenses/Bullet for Xna (ModifiedBulletX).txt @@ -0,0 +1,19 @@ + Bullet for XNA Copyright (c) 2003-2007 Vsevolod Klementjev http://www.codeplex.com/xnadevru + Bullet original C++ version Copyright (c) 2003-2007 Erwin Coumans http://bulletphysics.com + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + \ No newline at end of file diff --git a/ThirdPartyLicenses/Bullet-XNA.txt b/ThirdPartyLicenses/Bullet-XNA.txt new file mode 100644 index 0000000000..5d8899ba57 --- /dev/null +++ b/ThirdPartyLicenses/Bullet-XNA.txt @@ -0,0 +1,23 @@ +/* + * C# / XNA port of Bullet (c) 2011 Mark Neale + * http://code.google.com/p/bullet-xna/ + * + * Bullet Continuous Collision Detection and Physics Library + * Copyright (c) 2003-2008 Erwin Coumans http://www.bulletphysics.com/ + * + * This software is provided 'as-is', without any express or implied warranty. + * In no event will the authors be held liable for any damages arising from + * the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ \ No newline at end of file diff --git a/ThirdPartyLicenses/BulletLicense.txt b/ThirdPartyLicenses/BulletLicense.txt new file mode 100644 index 0000000000..c3ec68c21f --- /dev/null +++ b/ThirdPartyLicenses/BulletLicense.txt @@ -0,0 +1,17 @@ +/* +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +Free for commercial use, but please mail bullet@erwincoumans.com to report projects, and join the forum at +www.continuousphysics.com/Bullet/phpBB2 diff --git a/ThirdPartyLicenses/C# Webserver.txt b/ThirdPartyLicenses/C# Webserver.txt new file mode 100644 index 0000000000..b99689154b --- /dev/null +++ b/ThirdPartyLicenses/C# Webserver.txt @@ -0,0 +1,71 @@ +Files: HttpServer_OpenSim.dll + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of this License; and + +2. You must cause any modified files to carry prominent notices stating that You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. \ No newline at end of file diff --git a/ThirdPartyLicenses/C5.txt b/ThirdPartyLicenses/C5.txt new file mode 100644 index 0000000000..4c3a0496ca --- /dev/null +++ b/ThirdPartyLicenses/C5.txt @@ -0,0 +1,19 @@ +Copyright (c) 2003-2008 Niels Kokholm and Peter Sestoft. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE \ No newline at end of file diff --git a/ThirdPartyLicenses/CSCompilerTools.txt b/ThirdPartyLicenses/CSCompilerTools.txt new file mode 100644 index 0000000000..c2567bde0e --- /dev/null +++ b/ThirdPartyLicenses/CSCompilerTools.txt @@ -0,0 +1,12 @@ +The LSL compiler is generated with "Compiler tools in C#" version 4.7[1] by Dr. Malcolm Crowe[2]. The code is used with permission by the author: + +There is no problem with using the code in any way you like, as long as +somewhere you say that that is what you have done (in the source for +example). +And of course we disclaim all responsibility for any resulting +damage... +Best wishes +Malcolm Crowe + +[1] http://cis.paisley.ac.uk/crow-ci0/CSTools47.zip +[2] http://cis.paisley.ac.uk/crow-ci0/ diff --git a/ThirdPartyLicenses/CSJ2K.txt b/ThirdPartyLicenses/CSJ2K.txt new file mode 100644 index 0000000000..303254816b --- /dev/null +++ b/ThirdPartyLicenses/CSJ2K.txt @@ -0,0 +1,28 @@ +Copyright (c) 1999/2000 JJ2000 Partners. + +This software module was originally developed by Raphal Grosbois and +Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel +Askelf (Ericsson Radio Systems AB); and Bertrand Berthelot, David +Bouchard, Flix Henry, Gerard Mozelle and Patrice Onno (Canon Research +Centre France S.A) in the course of development of the JPEG2000 +standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This +software module is an implementation of a part of the JPEG 2000 +Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio +Systems AB and Canon Research Centre France S.A (collectively JJ2000 +Partners) agree not to assert against ISO/IEC and users of the JPEG +2000 Standard (Users) any of their rights under the copyright, not +including other intellectual property rights, for this software module +with respect to the usage by ISO/IEC and Users of this software module +or modifications thereof for use in hardware or software products +claiming conformance to the JPEG 2000 Standard. Those intending to use +this software module in hardware or software products are advised that +their use may infringe existing patents. The original developers of +this software module, JJ2000 Partners and ISO/IEC assume no liability +for use of this software module or modifications thereof. No license +or right to this software module is granted for non JPEG 2000 Standard +conforming products. JJ2000 Partners have full right to use this +software module for his/her own purpose, assign or donate this +software module to any third party and to inhibit third parties from +using this software module for non JPEG 2000 Standard conforming +products. This copyright notice must be included in all copies or +derivative works of this software module. diff --git a/ThirdPartyLicenses/CircularBuffer.txt b/ThirdPartyLicenses/CircularBuffer.txt new file mode 100644 index 0000000000..09c37d9ff4 --- /dev/null +++ b/ThirdPartyLicenses/CircularBuffer.txt @@ -0,0 +1,16 @@ +Copyright (c) 2012, Alex Regueiro +All rights reserved. +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR 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. \ No newline at end of file diff --git a/ThirdPartyLicenses/ConvexDecompositionDotNet.txt b/ThirdPartyLicenses/ConvexDecompositionDotNet.txt new file mode 100644 index 0000000000..714ae898d8 --- /dev/null +++ b/ThirdPartyLicenses/ConvexDecompositionDotNet.txt @@ -0,0 +1,28 @@ +ConvexDecompositionDotNet +------------------------- + +The MIT License + +Copyright (c) 2010 Intel Corporation. +All rights reserved. + +Based on the convexdecomposition library from + by John W. Ratcliff and Stan Melax. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ThirdPartyLicenses/DefaultTerrain.txt b/ThirdPartyLicenses/DefaultTerrain.txt new file mode 100644 index 0000000000..112beffa0b --- /dev/null +++ b/ThirdPartyLicenses/DefaultTerrain.txt @@ -0,0 +1,23 @@ + COPYRIGHT AND PERMISSION NOTICE + +Second Life(TM) Viewer Artwork. Copyright (C) 2008 Linden Research, Inc. + +Linden Research, Inc. ("Linden Lab") licenses the Second Life viewer +artwork and other works in the files distributed with this Notice under +the Creative Commons Attribution-Share Alike 3.0 License, available at +http://creativecommons.org/licenses/by-sa/3.0/legalcode. [^] For the license +summary, see http://creativecommons.org/licenses/by-sa/3.0/. + +Notwithstanding the foregoing, all of Linden Lab's trademarks, including +but not limited to the Second Life brand name and Second Life Eye-in-Hand +logo, are subject to our trademark policy at +http://secondlife.com/corporate/trademark/. + +If you distribute any copies or adaptations of the Second Life viewer +artwork or any other works in these files, you must include this Notice +and clearly identify any changes made to the original works. Include +this Notice and information where copyright notices are usually included, +for example, after your own copyright notice acknowledging your use of +the Second Life viewer artwork, in a text file distributed with your +program, in your application's About window, or on a credits page for +your work. diff --git a/ThirdPartyLicenses/DotNetOpenid.txt b/ThirdPartyLicenses/DotNetOpenid.txt new file mode 100644 index 0000000000..6833494f80 --- /dev/null +++ b/ThirdPartyLicenses/DotNetOpenid.txt @@ -0,0 +1,10 @@ +Copyright (c) 2008, Andrew Arnott, Scott Hanselman, Jason Alexander, et. al +All rights reserved. + +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 DotNetOpenId 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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. \ No newline at end of file diff --git a/ThirdPartyLicenses/DotNetZip-bzip2.txt b/ThirdPartyLicenses/DotNetZip-bzip2.txt new file mode 100644 index 0000000000..f8b4346fe9 --- /dev/null +++ b/ThirdPartyLicenses/DotNetZip-bzip2.txt @@ -0,0 +1,29 @@ + +The managed BZIP2 code included in Ionic.BZip2.dll and Ionic.Zip.dll is +modified code, based on the bzip2 code in the Apache commons compress +library. + +The original BZip2 was created by Julian Seward, and is licensed under +the BSD license. + +The following license applies to the Apache code: +----------------------------------------------------------------------- + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ diff --git a/ThirdPartyLicenses/DotNetZip-zlib.txt b/ThirdPartyLicenses/DotNetZip-zlib.txt new file mode 100644 index 0000000000..801e941903 --- /dev/null +++ b/ThirdPartyLicenses/DotNetZip-zlib.txt @@ -0,0 +1,70 @@ + +The following licenses govern use of the accompanying software, the +DotNetZip library ("the software"). If you use the software, you accept +these licenses. If you do not accept the license, do not use the software. + +The managed ZLIB code included in Ionic.Zlib.dll and Ionic.Zip.dll is +modified code, based on jzlib. + + + +The following notice applies to jzlib: +----------------------------------------------------------------------- + +Copyright (c) 2000,2001,2002,2003 ymnk, JCraft,Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +2. 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. + +3. The names of the authors may not be used to endorse or promote products +derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 JCRAFT, +INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE 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. + +----------------------------------------------------------------------- + +jzlib is based on zlib-1.1.3. + +The following notice applies to zlib: + +----------------------------------------------------------------------- + +Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler + + The ZLIB software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly jloup@gzip.org + Mark Adler madler@alumni.caltech.edu + + +----------------------------------------------------------------------- diff --git a/ThirdPartyLicenses/DotNetZip.txt b/ThirdPartyLicenses/DotNetZip.txt new file mode 100644 index 0000000000..c3103fd07d --- /dev/null +++ b/ThirdPartyLicenses/DotNetZip.txt @@ -0,0 +1,33 @@ +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software, the DotNetZip library ("the software"). If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + + diff --git a/ThirdPartyLicenses/GTCache.txt b/ThirdPartyLicenses/GTCache.txt new file mode 100644 index 0000000000..0e3496fac6 --- /dev/null +++ b/ThirdPartyLicenses/GTCache.txt @@ -0,0 +1,477 @@ +GlynnTucker.Cache + +http://gtcache.sourceforge.net/ + +The GlynnTucker.Cache assembly provides a data structure for caching slow data retrievals, for example data retrieved from a database server over the network. Think of it as a Hashtable that can automatically expire its data after a set amount of time or a specified period of inactivity, on a per-object basis. It is written in C# and dual licensed under the GPL/MPL, it should work with any .NET language. + + + MOZILLA PUBLIC LICENSE + Version 1.1 + + --------------- + +1. Definitions. + + 1.0.1. "Commercial Use" means distribution or otherwise making the + Covered Code available to a third party. + + 1.1. "Contributor" means each entity that creates or contributes to + the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Code, prior Modifications used by a Contributor, and the Modifications + made by that particular Contributor. + + 1.3. "Covered Code" means the Original Code or Modifications or the + combination of the Original Code and Modifications, in each case + including portions thereof. + + 1.4. "Electronic Distribution Mechanism" means a mechanism generally + accepted in the software development community for the electronic + transfer of data. + + 1.5. "Executable" means Covered Code in any form other than Source + Code. + + 1.6. "Initial Developer" means the individual or entity identified + as the Initial Developer in the Source Code notice required by Exhibit + A. + + 1.7. "Larger Work" means a work which combines Covered Code or + portions thereof with code not governed by the terms of this License. + + 1.8. "License" means this document. + + 1.8.1. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed herein. + + 1.9. "Modifications" means any addition to or deletion from the + substance or structure of either the Original Code or any previous + Modifications. When Covered Code is released as a series of files, a + Modification is: + A. Any addition to or deletion from the contents of a file + containing Original Code or previous Modifications. + + B. Any new file that contains any part of the Original Code or + previous Modifications. + + 1.10. "Original Code" means Source Code of computer software code + which is described in the Source Code notice required by Exhibit A as + Original Code, and which, at the time of its release under this + License is not already Covered Code governed by this License. + + 1.10.1. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, process, + and apparatus claims, in any patent Licensable by grantor. + + 1.11. "Source Code" means the preferred form of the Covered Code for + making modifications to it, including all modules it contains, plus + any associated interface definition files, scripts used to control + compilation and installation of an Executable, or source code + differential comparisons against either the Original Code or another + well known, available Covered Code of the Contributor's choice. The + Source Code can be in a compressed or archival form, provided the + appropriate decompression or de-archiving software is widely available + for no charge. + + 1.12. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms of, this + License or a future version of this License issued under Section 6.1. + For legal entities, "You" includes any entity which controls, is + controlled by, or is under common control with You. For purposes of + this definition, "control" means (a) the power, direct or indirect, + to cause the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty percent + (50%) of the outstanding shares or beneficial ownership of such + entity. + +2. Source Code License. + + 2.1. The Initial Developer Grant. + The Initial Developer hereby grants You a world-wide, royalty-free, + non-exclusive license, subject to third party intellectual property + claims: + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer to use, reproduce, + modify, display, perform, sublicense and distribute the Original + Code (or portions thereof) with or without Modifications, and/or + as part of a Larger Work; and + + (b) under Patents Claims infringed by the making, using or + selling of Original Code, to make, have made, use, practice, + sell, and offer for sale, and/or otherwise dispose of the + Original Code (or portions thereof). + + (c) the licenses granted in this Section 2.1(a) and (b) are + effective on the date Initial Developer first distributes + Original Code under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: 1) for code that You delete from the Original Code; 2) + separate from the Original Code; or 3) for infringements caused + by: i) the modification of the Original Code or ii) the + combination of the Original Code with other software or devices. + + 2.2. Contributor Grant. + Subject to third party intellectual property claims, each Contributor + hereby grants You a world-wide, royalty-free, non-exclusive license + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor, to use, reproduce, modify, + display, perform, sublicense and distribute the Modifications + created by such Contributor (or portions thereof) either on an + unmodified basis, with other Modifications, as Covered Code + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either alone + and/or in combination with its Contributor Version (or portions + of such combination), to make, use, sell, offer for sale, have + made, and/or otherwise dispose of: 1) Modifications made by that + Contributor (or portions thereof); and 2) the combination of + Modifications made by that Contributor with its Contributor + Version (or portions of such combination). + + (c) the licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first makes Commercial Use of + the Covered Code. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: 1) for any code that Contributor has deleted from the + Contributor Version; 2) separate from the Contributor Version; + 3) for infringements caused by: i) third party modifications of + Contributor Version or ii) the combination of Modifications made + by that Contributor with other software (except as part of the + Contributor Version) or other devices; or 4) under Patent Claims + infringed by Covered Code in the absence of Modifications made by + that Contributor. + +3. Distribution Obligations. + + 3.1. Application of License. + The Modifications which You create or to which You contribute are + governed by the terms of this License, including without limitation + Section 2.2. The Source Code version of Covered Code may be + distributed only under the terms of this License or a future version + of this License released under Section 6.1, and You must include a + copy of this License with every copy of the Source Code You + distribute. You may not offer or impose any terms on any Source Code + version that alters or restricts the applicable version of this + License or the recipients' rights hereunder. However, You may include + an additional document offering the additional rights described in + Section 3.5. + + 3.2. Availability of Source Code. + Any Modification which You create or to which You contribute must be + made available in Source Code form under the terms of this License + either on the same media as an Executable version or via an accepted + Electronic Distribution Mechanism to anyone to whom you made an + Executable version available; and if made available via Electronic + Distribution Mechanism, must remain available for at least twelve (12) + months after the date it initially became available, or at least six + (6) months after a subsequent version of that particular Modification + has been made available to such recipients. You are responsible for + ensuring that the Source Code version remains available even if the + Electronic Distribution Mechanism is maintained by a third party. + + 3.3. Description of Modifications. + You must cause all Covered Code to which You contribute to contain a + file documenting the changes You made to create that Covered Code and + the date of any change. You must include a prominent statement that + the Modification is derived, directly or indirectly, from Original + Code provided by the Initial Developer and including the name of the + Initial Developer in (a) the Source Code, and (b) in any notice in an + Executable version or related documentation in which You describe the + origin or ownership of the Covered Code. + + 3.4. Intellectual Property Matters + (a) Third Party Claims. + If Contributor has knowledge that a license under a third party's + intellectual property rights is required to exercise the rights + granted by such Contributor under Sections 2.1 or 2.2, + Contributor must include a text file with the Source Code + distribution titled "LEGAL" which describes the claim and the + party making the claim in sufficient detail that a recipient will + know whom to contact. If Contributor obtains such knowledge after + the Modification is made available as described in Section 3.2, + Contributor shall promptly modify the LEGAL file in all copies + Contributor makes available thereafter and shall take other steps + (such as notifying appropriate mailing lists or newsgroups) + reasonably calculated to inform those who received the Covered + Code that new knowledge has been obtained. + + (b) Contributor APIs. + If Contributor's Modifications include an application programming + interface and Contributor has knowledge of patent licenses which + are reasonably necessary to implement that API, Contributor must + also include this information in the LEGAL file. + + (c) Representations. + Contributor represents that, except as disclosed pursuant to + Section 3.4(a) above, Contributor believes that Contributor's + Modifications are Contributor's original creation(s) and/or + Contributor has sufficient rights to grant the rights conveyed by + this License. + + 3.5. Required Notices. + You must duplicate the notice in Exhibit A in each file of the Source + Code. If it is not possible to put such notice in a particular Source + Code file due to its structure, then You must include such notice in a + location (such as a relevant directory) where a user would be likely + to look for such a notice. If You created one or more Modification(s) + You may add your name as a Contributor to the notice described in + Exhibit A. You must also duplicate this License in any documentation + for the Source Code where You describe recipients' rights or ownership + rights relating to Covered Code. You may choose to offer, and to + charge a fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Code. However, You + may do so only on Your own behalf, and not on behalf of the Initial + Developer or any Contributor. You must make it absolutely clear than + any such warranty, support, indemnity or liability obligation is + offered by You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred by the + Initial Developer or such Contributor as a result of warranty, + support, indemnity or liability terms You offer. + + 3.6. Distribution of Executable Versions. + You may distribute Covered Code in Executable form only if the + requirements of Section 3.1-3.5 have been met for that Covered Code, + and if You include a notice stating that the Source Code version of + the Covered Code is available under the terms of this License, + including a description of how and where You have fulfilled the + obligations of Section 3.2. The notice must be conspicuously included + in any notice in an Executable version, related documentation or + collateral in which You describe recipients' rights relating to the + Covered Code. You may distribute the Executable version of Covered + Code or ownership rights under a license of Your choice, which may + contain terms different from this License, provided that You are in + compliance with the terms of this License and that the license for the + Executable version does not attempt to limit or alter the recipient's + rights in the Source Code version from the rights set forth in this + License. If You distribute the Executable version under a different + license You must make it absolutely clear that any terms which differ + from this License are offered by You alone, not by the Initial + Developer or any Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred by + the Initial Developer or such Contributor as a result of any such + terms You offer. + + 3.7. Larger Works. + You may create a Larger Work by combining Covered Code with other code + not governed by the terms of this License and distribute the Larger + Work as a single product. In such a case, You must make sure the + requirements of this License are fulfilled for the Covered Code. + +4. Inability to Comply Due to Statute or Regulation. + + If it is impossible for You to comply with any of the terms of this + License with respect to some or all of the Covered Code due to + statute, judicial order, or regulation then You must: (a) comply with + the terms of this License to the maximum extent possible; and (b) + describe the limitations and the code they affect. Such description + must be included in the LEGAL file described in Section 3.4 and must + be included with all distributions of the Source Code. Except to the + extent prohibited by statute or regulation, such description must be + sufficiently detailed for a recipient of ordinary skill to be able to + understand it. + +5. Application of this License. + + This License applies to code to which the Initial Developer has + attached the notice in Exhibit A and to related Covered Code. + +6. Versions of the License. + + 6.1. New Versions. + Netscape Communications Corporation ("Netscape") may publish revised + and/or new versions of the License from time to time. Each version + will be given a distinguishing version number. + + 6.2. Effect of New Versions. + Once Covered Code has been published under a particular version of the + License, You may always continue to use it under the terms of that + version. You may also choose to use such Covered Code under the terms + of any subsequent version of the License published by Netscape. No one + other than Netscape has the right to modify the terms applicable to + Covered Code created under this License. + + 6.3. Derivative Works. + If You create or use a modified version of this License (which you may + only do in order to apply it to code which is not already Covered Code + governed by this License), You must (a) rename Your license so that + the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", + "MPL", "NPL" or any confusingly similar phrase do not appear in your + license (except to note that your license differs from this License) + and (b) otherwise make it clear that Your version of the license + contains terms which differ from the Mozilla Public License and + Netscape Public License. (Filling in the name of the Initial + Developer, Original Code or Contributor in the notice described in + Exhibit A shall not of themselves be deemed to be modifications of + this License.) + +7. DISCLAIMER OF WARRANTY. + + COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF + DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. + THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE + IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, + YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE + COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER + OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. + +8. TERMINATION. + + 8.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to cure + such breach within 30 days of becoming aware of the breach. All + sublicenses to the Covered Code which are properly granted shall + survive any termination of this License. Provisions which, by their + nature, must remain in effect beyond the termination of this License + shall survive. + + 8.2. If You initiate litigation by asserting a patent infringement + claim (excluding declatory judgment actions) against Initial Developer + or a Contributor (the Initial Developer or Contributor against whom + You file such action is referred to as "Participant") alleging that: + + (a) such Participant's Contributor Version directly or indirectly + infringes any patent, then any and all rights granted by such + Participant to You under Sections 2.1 and/or 2.2 of this License + shall, upon 60 days notice from Participant terminate prospectively, + unless if within 60 days after receipt of notice You either: (i) + agree in writing to pay Participant a mutually agreeable reasonable + royalty for Your past and future use of Modifications made by such + Participant, or (ii) withdraw Your litigation claim with respect to + the Contributor Version against such Participant. If within 60 days + of notice, a reasonable royalty and payment arrangement are not + mutually agreed upon in writing by the parties or the litigation claim + is not withdrawn, the rights granted by Participant to You under + Sections 2.1 and/or 2.2 automatically terminate at the expiration of + the 60 day notice period specified above. + + (b) any software, hardware, or device, other than such Participant's + Contributor Version, directly or indirectly infringes any patent, then + any rights granted to You by such Participant under Sections 2.1(b) + and 2.2(b) are revoked effective as of the date You first made, used, + sold, distributed, or had made, Modifications made by that + Participant. + + 8.3. If You assert a patent infringement claim against Participant + alleging that such Participant's Contributor Version directly or + indirectly infringes any patent where such claim is resolved (such as + by license or settlement) prior to the initiation of patent + infringement litigation, then the reasonable value of the licenses + granted by such Participant under Sections 2.1 or 2.2 shall be taken + into account in determining the amount or value of any payment or + license. + + 8.4. In the event of termination under Sections 8.1 or 8.2 above, + all end user license agreements (excluding distributors and resellers) + which have been validly granted by You or any distributor hereunder + prior to termination shall survive termination. + +9. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL + DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, + OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR + ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY + CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, + WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY + RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW + PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE + EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO + THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. + +10. U.S. GOVERNMENT END USERS. + + The Covered Code is a "commercial item," as that term is defined in + 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer + software" and "commercial computer software documentation," as such + terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 + C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), + all U.S. Government End Users acquire Covered Code with only those + rights set forth herein. + +11. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed by + California law provisions (except to the extent applicable law, if + any, provides otherwise), excluding its conflict-of-law provisions. + With respect to disputes in which at least one party is a citizen of, + or an entity chartered or registered to do business in the United + States of America, any litigation relating to this License shall be + subject to the jurisdiction of the Federal Courts of the Northern + District of California, with venue lying in Santa Clara County, + California, with the losing party responsible for costs, including + without limitation, court costs and reasonable attorneys' fees and + expenses. The application of the United Nations Convention on + Contracts for the International Sale of Goods is expressly excluded. + Any law or regulation which provides that the language of a contract + shall be construed against the drafter shall not apply to this + License. + +12. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or indirectly, + out of its utilization of rights under this License and You agree to + work with Initial Developer and Contributors to distribute such + responsibility on an equitable basis. Nothing herein is intended or + shall be deemed to constitute any admission of liability. + +13. MULTIPLE-LICENSED CODE. + + Initial Developer may designate portions of the Covered Code as + "Multiple-Licensed". "Multiple-Licensed" means that the Initial + Developer permits you to utilize portions of the Covered Code under + Your choice of the NPL or the alternative licenses, if any, specified + by the Initial Developer in the file described in Exhibit A. + +EXHIBIT A -Mozilla Public License. + + ``The contents of this file are subject to the Mozilla Public License + Version 1.1 (the "License"); you may not use this file except in + compliance with the License. You may obtain a copy of the License at + http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + License for the specific language governing rights and limitations + under the License. + + The Original Code is ______________________________________. + + The Initial Developer of the Original Code is ________________________. + Portions created by ______________________ are Copyright (C) ______ + _______________________. All Rights Reserved. + + Contributor(s): ______________________________________. + + Alternatively, the contents of this file may be used under the terms + of the _____ license (the "[___] License"), in which case the + provisions of [______] License are applicable instead of those + above. If you wish to allow use of your version of this file only + under the terms of the [____] License and not to allow others to use + your version of this file under the MPL, indicate your decision by + deleting the provisions above and replace them with the notice and + other provisions required by the [___] License. If you do not delete + the provisions above, a recipient may use your version of this file + under either the MPL or the [___] License." + + [NOTE: The text of this Exhibit A may differ slightly from the text of + the notices in the Source Code files of the Original Code. You should + use the text of this Exhibit A rather than the text found in the + Original Code Source Code for Your Modifications.] + diff --git a/ThirdPartyLicenses/GoogleProtoBuffer.txt b/ThirdPartyLicenses/GoogleProtoBuffer.txt new file mode 100644 index 0000000000..e0df00c71a --- /dev/null +++ b/ThirdPartyLicenses/GoogleProtoBuffer.txt @@ -0,0 +1,31 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// http://github.com/jskeet/dotnet-protobufs/ +// Original C++/Java/Python code: +// http://code.google.com/p/protobuf/ +// +// 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 Google Inc. 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 COPYRIGHT HOLDERS AND CONTRIBUTORS +// "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 COPYRIGHT +// OWNER OR 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. diff --git a/ThirdPartyLicenses/ICSharpCode.SharpZipLib.license.txt b/ThirdPartyLicenses/ICSharpCode.SharpZipLib.license.txt new file mode 100644 index 0000000000..c384be424e --- /dev/null +++ b/ThirdPartyLicenses/ICSharpCode.SharpZipLib.license.txt @@ -0,0 +1,17 @@ +The library is released under the GPL with the following exception: + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent modules, +and to copy and distribute the resulting executable under terms of your +choice, provided that you also meet, for each linked independent module, +the terms and conditions of the license of that module. An independent +module is a module which is not derived from or based on this library. + +If you modify this library, you may extend this exception to your +version of the library, but you are not obligated to do so. If you do not +wish to do so, delete this exception statement from your version. diff --git a/ThirdPartyLicenses/MXP.txt b/ThirdPartyLicenses/MXP.txt new file mode 100644 index 0000000000..43f51cecb0 --- /dev/null +++ b/ThirdPartyLicenses/MXP.txt @@ -0,0 +1,15 @@ +Metaverse Exchange Protocol specification and reference implementation. + +Copyright 2008-2009 Bubble Cloud Comminity (http://www.bubblecloud.org) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/ThirdPartyLicenses/Mono.Xna (MonoXnaCompactMaths).txt b/ThirdPartyLicenses/Mono.Xna (MonoXnaCompactMaths).txt new file mode 100644 index 0000000000..ef324b6bbe --- /dev/null +++ b/ThirdPartyLicenses/Mono.Xna (MonoXnaCompactMaths).txt @@ -0,0 +1,22 @@ +MIT License +Copyright 2006 The Mono.Xna Team + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/ThirdPartyLicenses/MonoAddins.txt b/ThirdPartyLicenses/MonoAddins.txt new file mode 100644 index 0000000000..10c23b37dd --- /dev/null +++ b/ThirdPartyLicenses/MonoAddins.txt @@ -0,0 +1,41 @@ +Authors: Lluis Sanchez Gual + +The MIT License + +Copyright (C) 2007 Novell, Inc (http://www.novell.com) + + + +Permission is hereby granted, free of charge, to any person obtaining +a copy + of this software and associated documentation files (the "Software"), +to deal + in the Software without restriction, including without limitation +the rights +to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell +copies of the Software, and to permit persons to whom the +Software is +furnished to do so, subject to the following conditions: + + + +The above copyright notice and this permission notice shall be included in + +all copies or substantial portions of the Software. + + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. +IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR +THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/ThirdPartyLicenses/MySQL.txt b/ThirdPartyLicenses/MySQL.txt new file mode 100644 index 0000000000..bbda93619a --- /dev/null +++ b/ThirdPartyLicenses/MySQL.txt @@ -0,0 +1,122 @@ +GPLv2 + MySQL FLOSS License Exception (files COPYING and EXCEPTIONS) + +--- file: EXCEPTIONS --- +MySQL FLOSS License Exception + +The MySQL AB Exception for Free/Libre and Open Source +Software-only Applications Using MySQL Client Libraries (the +"FLOSS Exception"). + +Version 0.6, 7 March 2007 + +Exception Intent + +We want specified Free/Libre and Open Source Software (``FLOSS'') +applications to be able to use specified GPL-licensed MySQL client +libraries (the ``Program'') despite the fact that not all FLOSS +licenses are compatible with version 2 of the GNU General Public +License (the ``GPL''). + +Legal Terms and Conditions + +As a special exception to the terms and conditions of version 2.0 +of the GPL: + + 1. You are free to distribute a Derivative Work that is formed + entirely from the Program and one or more works (each, a + "FLOSS Work") licensed under one or more of the licenses + listed below in section 1, as long as: + a. You obey the GPL in all respects for the Program and the + Derivative Work, except for identifiable sections of the + Derivative Work which are not derived from the Program, + and which can reasonably be considered independent and + separate works in themselves, + b. all identifiable sections of the Derivative Work which + are not derived from the Program, and which can + reasonably be considered independent and separate works + in themselves, + i. are distributed subject to one of the FLOSS licenses + listed below, and + ii. the object code or executable form of those sections + are accompanied by the complete corresponding + machine-readable source code for those sections on + the same medium and under the same FLOSS license as + the corresponding object code or executable forms of + those sections, and + c. any works which are aggregated with the Program or with a + Derivative Work on a volume of a storage or distribution + medium in accordance with the GPL, can reasonably be + considered independent and separate works in themselves + which are not derivatives of either the Program, a + Derivative Work or a FLOSS Work. + If the above conditions are not met, then the Program may only + be copied, modified, distributed or used under the terms and + conditions of the GPL or another valid licensing option from + MySQL AB. + + 2. FLOSS License List + +License name Version(s)/Copyright Date +Academic Free License 2.0 +Apache Software License 1.0/1.1/2.0 +Apple Public Source License 2.0 +Artistic license From Perl 5.8.0 +BSD license "July 22 1999" +Common Development and Distribution License (CDDL) 1.0 +Common Public License 1.0 +Eclipse Public License 1.0 +GNU Library or "Lesser" General Public License (LGPL) 2.0/2.1 +Jabber Open Source License 1.0 +MIT license (As listed in file MIT-License.txt) --- +Mozilla Public License (MPL) 1.0/1.1 +Open Software License 2.0 +OpenSSL license (with original SSLeay license) "2003" ("1998") +PHP License 3.0 +Python license (CNRI Python License) --- +Python Software Foundation License 2.1.1 +Sleepycat License "1999" +University of Illinois/NCSA Open Source License --- +W3C License "2001" +X11 License "2001" +Zlib/libpng License --- +Zope Public License 2.0 + + Due to the many variants of some of the above licenses, we + require that any version follow the 2003 version of the Free + Software Foundation's Free Software Definition + (http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of + the Open Source Definition by the Open Source Initiative + (http://www.opensource.org/docs/definition.php). + + 3. Definitions + + a. Terms used, but not defined, herein shall have the + meaning provided in the GPL. + b. Derivative Work means a derivative work under copyright + law. + + 4. Applicability: This FLOSS Exception applies to all Programs + that contain a notice placed by MySQL AB saying that the + Program may be distributed under the terms of this FLOSS + Exception. If you create or distribute a work which is a + Derivative Work of both the Program and any other work + licensed under the GPL, then this FLOSS Exception is not + available for that work; thus, you must remove the FLOSS + Exception notice from that work and comply with the GPL in all + respects, including by retaining all GPL notices. You may + choose to redistribute a copy of the Program exclusively under + the terms of the GPL by removing the FLOSS Exception notice + from that copy of the Program, provided that the copy has + never been modified by you or any third party. + +Appendix A. Qualified Libraries and Packages + +The following is a non-exhaustive list of libraries and packages +which are covered by the FLOSS License Exception. Please note that +this appendix is provided merely as an additional service to +specific FLOSS projects wishing to simplify licensing information +for their users. Compliance with one of the licenses noted under +the "FLOSS license list" section remains a prerequisite. + +Package Name Qualifying License and Version +Apache Portable Runtime (APR) Apache Software License 2.0 diff --git a/ThirdPartyLicenses/Nini.txt b/ThirdPartyLicenses/Nini.txt new file mode 100644 index 0000000000..fe38b627cf --- /dev/null +++ b/ThirdPartyLicenses/Nini.txt @@ -0,0 +1,23 @@ + +Nini Configuration Project. +Copyright (c) 2006 Brent R. Matzelle + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/ThirdPartyLicenses/Npgsql.txt b/ThirdPartyLicenses/Npgsql.txt new file mode 100644 index 0000000000..e2e4f1283b --- /dev/null +++ b/ThirdPartyLicenses/Npgsql.txt @@ -0,0 +1,7 @@ +Copyright (c) 2002-2007, The Npgsql Development Team + +Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. + +IN NO EVENT SHALL THE NPGSQL DEVELOPMENT TEAM BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE NPGSQL DEVELOPMENT TEAM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +THE NPGSQL DEVELOPMENT TEAM SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE NPGSQL DEVELOPMENT TEAM HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. diff --git a/ThirdPartyLicenses/ODE.txt b/ThirdPartyLicenses/ODE.txt new file mode 100644 index 0000000000..53a93dbe89 --- /dev/null +++ b/ThirdPartyLicenses/ODE.txt @@ -0,0 +1,13 @@ +Open Dynamics Engine +Copyright (c) 2001-2004, Russell L. Smith. +All rights reserved. + +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 names of ODE's copyright owner 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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. \ No newline at end of file diff --git a/ThirdPartyLicenses/OpenJpeg.txt b/ThirdPartyLicenses/OpenJpeg.txt new file mode 100644 index 0000000000..d1e5b6a533 --- /dev/null +++ b/ThirdPartyLicenses/OpenJpeg.txt @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2002-2007, Communications and Remote Sensing Laboratory, Universite catholique de Louvain (UCL), Belgium + * Copyright (c) 2002-2007, Professor Benoit Macq + * Copyright (c) 2001-2003, David Janssens + * Copyright (c) 2002-2003, Yannick Verschueren + * Copyright (c) 2003-2007, Francois-Olivier Devaux and Antonin Descampe + * Copyright (c) 2005, Herve Drolon, FreeImage Team + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS `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 COPYRIGHT OWNER OR 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. + */ \ No newline at end of file diff --git a/ThirdPartyLicenses/Protobuf-net.txt b/ThirdPartyLicenses/Protobuf-net.txt new file mode 100644 index 0000000000..8eb6c71333 --- /dev/null +++ b/ThirdPartyLicenses/Protobuf-net.txt @@ -0,0 +1,21 @@ +The core Protocol Buffers technology is provided courtesy of Google. +At the time of writing, this is released under the BSD license. +Full details can be found here: + +http://code.google.com/p/protobuf/ + + +This .NET implementation is Copyright 2008 Marc Gravell + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/ThirdPartyLicenses/Prototype.txt b/ThirdPartyLicenses/Prototype.txt new file mode 100644 index 0000000000..61e491823b --- /dev/null +++ b/ThirdPartyLicenses/Prototype.txt @@ -0,0 +1,16 @@ +Copyright (c) 2005-2008 Sam Stephenson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ThirdPartyLicenses/SmartThreadPool.txt b/ThirdPartyLicenses/SmartThreadPool.txt new file mode 100644 index 0000000000..7bfc997e59 --- /dev/null +++ b/ThirdPartyLicenses/SmartThreadPool.txt @@ -0,0 +1,22 @@ +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software. If you use the software, you +accept this license. If you do not accept the license, do not use the software. + +1. Definitions +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the +same meaning here as under U.S. copyright law. +A "contribution" is the original software, or any additions or changes to the software. +A "contributor" is any person that distributes its contribution under this license. +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. diff --git a/ThirdPartyLicenses/XML-RPC.NET.txt b/ThirdPartyLicenses/XML-RPC.NET.txt new file mode 100644 index 0000000000..2aff37f7e1 --- /dev/null +++ b/ThirdPartyLicenses/XML-RPC.NET.txt @@ -0,0 +1,27 @@ +XML-RPC.NET - XML-RPC for .NET +v2.1.0 +Copyright (C) 2001-2006 Charles Cook (chascook@gmail.com) + +xmlrpcgen +Copyright (C) 2003 Joe Bork + +For more information about XML-RPC.NET visit http://www.xml-rpc.net. + +XML-RPC.NET is licensed with MIT X11 license. +(see http://www.xml-rpc.net/faq/xmlrpcnetfaq.html#6.12) + +For more information about XML-RPC refer to http://www.xmlrpc.com/ + + +PREQUISITES +----------- +Assembly CookComputing.XmlRpc.dll requires 1.0 .NET runtime and +runs on all later versions. + +Assembly CookComputing.XmlRpcV2.dll requires 2.0 .NET runtime. + + +DOCUMENTATION +------------- +For help on using XML-RPC.NET, see +http://www.xml-rpc.net/faq/xmlrpcnetfaq.html. \ No newline at end of file diff --git a/ThirdPartyLicenses/libsl.txt b/ThirdPartyLicenses/libsl.txt new file mode 100644 index 0000000000..73951dc734 --- /dev/null +++ b/ThirdPartyLicenses/libsl.txt @@ -0,0 +1,23 @@ +Copyright (c) 2006, Second Life Reverse Engineering Team +All rights reserved. + +- 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. +- Neither the name of the Second Life Reverse Engineering Team 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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. \ No newline at end of file diff --git a/addon-modules/README b/addon-modules/README new file mode 100644 index 0000000000..b5bf2e007e --- /dev/null +++ b/addon-modules/README @@ -0,0 +1,9 @@ +In this directory you can place addon modules for OpenSim + +Each module should be in it's own tree and the root of the tree +should contain a file named "prebuild.xml", which will be included in the +main prebuild file. + +The prebuild.xml should only contain and associated child tags. +The , , and tags should not be +included since the add-on modules prebuild.xml will be inserted directly into the main prebuild.xml diff --git a/bin/Axiom.MathLib.dll b/bin/Axiom.MathLib.dll new file mode 100755 index 0000000000..b00cf1d1da Binary files /dev/null and b/bin/Axiom.MathLib.dll differ diff --git a/bin/BulletXNA.dll b/bin/BulletXNA.dll new file mode 100755 index 0000000000..b3ddc32297 Binary files /dev/null and b/bin/BulletXNA.dll differ diff --git a/bin/BulletXNA.pdb b/bin/BulletXNA.pdb new file mode 100644 index 0000000000..ed3baaddd7 Binary files /dev/null and b/bin/BulletXNA.pdb differ diff --git a/bin/C5.dll b/bin/C5.dll new file mode 100755 index 0000000000..42093e5f84 Binary files /dev/null and b/bin/C5.dll differ diff --git a/bin/CSJ2K.dll b/bin/CSJ2K.dll new file mode 100755 index 0000000000..238291f7e7 Binary files /dev/null and b/bin/CSJ2K.dll differ diff --git a/bin/CookComputing.XmlRpcV2.dll b/bin/CookComputing.XmlRpcV2.dll new file mode 100755 index 0000000000..4dd869c803 Binary files /dev/null and b/bin/CookComputing.XmlRpcV2.dll differ diff --git a/bin/DotNetOpenId.dll b/bin/DotNetOpenId.dll new file mode 100755 index 0000000000..aa62790a15 Binary files /dev/null and b/bin/DotNetOpenId.dll differ diff --git a/bin/DotNetOpenMail.dll b/bin/DotNetOpenMail.dll new file mode 100755 index 0000000000..70af3bf3db Binary files /dev/null and b/bin/DotNetOpenMail.dll differ diff --git a/bin/GlynnTucker.Cache.dll b/bin/GlynnTucker.Cache.dll new file mode 100755 index 0000000000..c6ab8d59ca Binary files /dev/null and b/bin/GlynnTucker.Cache.dll differ diff --git a/bin/HttpServer_OpenSim.dll b/bin/HttpServer_OpenSim.dll new file mode 100755 index 0000000000..38a4cb795c Binary files /dev/null and b/bin/HttpServer_OpenSim.dll differ diff --git a/bin/HttpServer_OpenSim.pdb b/bin/HttpServer_OpenSim.pdb new file mode 100644 index 0000000000..cfff9a791b Binary files /dev/null and b/bin/HttpServer_OpenSim.pdb differ diff --git a/bin/HttpServer_OpenSim.xml b/bin/HttpServer_OpenSim.xml new file mode 100644 index 0000000000..61c3ad8b4e --- /dev/null +++ b/bin/HttpServer_OpenSim.xml @@ -0,0 +1,5574 @@ + + + + HttpServer_OpenSim + + + + + Delegate used to find a realm/domain. + + + + + Realms are used during HTTP Authentication + + + + + + + A complete HTTP server, you need to add a module to it to be able to handle incoming requests. + + + + // this small example will add two web site modules, thus handling + // two different sites. In reality you should add Controller modules or something + // two the website modules to be able to handle different requests. + HttpServer server = new HttpServer(); + server.Add(new WebSiteModule("www.gauffin.com", "Gauffin Telecom AB")); + server.Add(new WebSiteModule("www.vapadi.se", "Remote PBX")); + + // start regular http + server.Start(IPAddress.Any, 80); + + // start https + server.Start(IPAddress.Any, 443, myCertificate); + + + + + + + + + Initializes a new instance of the class. + + Used to get all components used in the server.. + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + + + + + + Initializes a new instance of the class. + + A session store is used to save and retrieve sessions + + + + + Initializes a new instance of the class. + + The log writer. + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + The log writer. + + + + + + + Initializes a new instance of the class. + + Form decoders are used to convert different types of posted data to the object types. + A session store is used to save and retrieve sessions + The log writer. + + + + + + + + Adds the specified rule. + + The rule. + + + + Add a to the server. + + mode to add + + + + Decodes the request body. + + The request. + Failed to decode form data. + + + + Generate a HTTP error page (that will be added to the response body). + response status code is also set. + + Response that the page will be generated in. + . + response body contents. + + + + Generate a HTTP error page (that will be added to the response body). + response status code is also set. + + Response that the page will be generated in. + exception. + + + + Realms are used by the s. + + HTTP request + domain/realm. + + + + Process an incoming request. + + connection to client + request information + response that should be filled + session information + + + + Can be overloaded to implement stuff when a client have been connected. + + + Default implementation does nothing. + + client that disconnected + disconnect reason + + + + Handle authentication + + + + + true if request can be handled; false if not. + Invalid authorization header + + + + Will request authentication. + + + Sends respond to client, nothing else can be done with the response after this. + + + + + + + + Received from a when a request have been parsed successfully. + + that received the request. + The request. + + + + To be able to track request count. + + + + + + + Start the web server using regular HTTP. + + IP Address to listen on, use IpAddress.Any to accept connections on all IP addresses/network cards. + Port to listen on. 80 can be a good idea =) + address is null. + Port must be a positive number. + + + + Accept secure connections. + + IP Address to listen on, use to accept connections on all IP Addresses / network cards. + Port to listen on. 80 can be a good idea =) + Certificate to use + address is null. + Port must be a positive number. + + + + shut down the server and listeners + + + + + write an entry to the log file + + importance of the message + log message + + + + write an entry to the log file + + object that wrote the message + importance of the message + log message + + + + Server that is handling the current request. + + + Will be set as soon as a request arrives to the object. + + + + + Modules used for authentication. The module that is is added first is used as + the default authentication module. + + Use the corresponding property + in the if you are using multiple websites. + + + + Form decoder providers are used to decode request body (which normally contains form data). + + + + + Server name sent in HTTP responses. + + + Do NOT include version in name, since it makes it + easier for hackers. + + + + + Name of cookie where session id is stored. + + + + + Specified where logging should go. + + + + + + + + Number of connections that can wait to be accepted by the server. + + Default is 10. + + + + Gets or sets maximum number of allowed simultaneous requests. + + + + This property is useful in busy systems. The HTTP server + will start queuing new requests if this limit is hit, instead + of trying to process all incoming requests directly. + + + The default number if allowed simultaneous requests are 10. + + + + + + Gets or sets maximum number of requests queuing to be handled. + + + + The WebServer will start turning requests away if response code + to indicate that the server + is too busy to be able to handle the request. + + + + + + Realms are used during HTTP authentication. + Default realm is same as server name. + + + + + Let's to receive unhandled exceptions from the threads. + + + Exceptions will be thrown during debug mode if this event is not used, + exceptions will be printed to console and suppressed during release mode. + + + + + Serves files that are stored in embedded resources. + + + + + A HttpModule can be used to serve Uri's. The module itself + decides if it should serve a Uri or not. In this way, you can + get a very flexible http application since you can let multiple modules + serve almost similar urls. + + + Throw if you are using a and want to prompt for user name/password. + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + true if this module handled the request. + + + + Set the log writer to use. + + logwriter to use. + + + + Log something. + + importance of log message + message + + + + If true specifies that the module doesn't consume the processing of a request so that subsequent modules + can continue processing afterwards. Default is false. + + + + + Initializes a new instance of the class. + Runs to make sure the basic mime types are available, they can be cleared later + through the use of if desired. + + + + + Initializes a new instance of the class. + Runs to make sure the basic mime types are available, they can be cleared later + through the use of if desired. + + The log writer to use when logging events + + + + Mimtypes that this class can handle per default + + + + + Loads resources from a namespace in the given assembly to an uri + + The uri to map the resources to + The assembly in which the resources reside + The namespace from which to load the resources + + resourceLoader.LoadResources("/user/", typeof(User).Assembly, "MyLib.Models.User.Views"); + + will make ie the resource MyLib.Models.User.Views.stylesheet.css accessible via /user/stylesheet.css + + The amount of loaded files, giving you the possibility of making sure the resources needed gets loaded + + + + Returns true if the module can handle the request + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + true if this module handled the request. + + + + List with all mime-type that are allowed. + + All other mime types will result in a Forbidden http status code. + + + + Contains some kind of input from the browser/client. + can be QueryString, form data or any other request body content. + + + + + Base class for request data containers + + + + + Adds a parameter mapped to the presented name + + The name to map the parameter to + The parameter value + + + + Returns true if the container contains the requested parameter + + Parameter id + True if parameter exists + + + + Returns a request parameter + + The name associated with the parameter + + + + Representation of a non-initialized class instance + + + Variable telling the class that it is non-initialized + + + + Initializes a new instance of the class. + + form name. + + + + Initializes a new instance of the class. + + form name. + if set to true all changes will be ignored. + this constructor should only be used by Empty + + + Creates a deep copy of the HttpInput class + The object to copy + The function makes a deep copy of quite a lot which can be slow + + + + Add a new element. Form array elements are parsed + and added in a correct hierarchy. + + Name is converted to lower case. + + name is null. + Cannot add stuff to . + + + + Returns true if the class contains a with the corresponding name. + + The field/query string name + True if the value exists + + + + Parses an item and returns it. + This function is primarily used to parse array items as in user[name]. + + + + + + + Outputs the instance representing all its values joined together + + + + Returns all items as an unescaped query string. + + + + + Extracts one parameter from an array + + Containing the string array + All but the first value + + string test1 = ExtractOne("system[user][extension][id]"); + string test2 = ExtractOne(test1); + string test3 = ExtractOne(test2); + // test1 = user[extension][id] + // test2 = extension[id] + // test3 = id + + + + Resets all data contained by class + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Form name as lower case + + + + + Get a form item. + + + Returns if item was not found. + + + + The server understood the request, but is refusing to fulfill it. + Authorization will not help and the request SHOULD NOT be repeated. + If the request method was not HEAD and the server wishes to make public why the request has not been fulfilled, + it SHOULD describe the reason for the refusal in the entity. If the server does not wish to make this information + available to the client, the status code 404 (Not Found) can be used instead. + + Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php + + + + + All HTTP based exceptions will derive this class. + + + + + Create a new HttpException + + http status code (sent in the response) + error description + + + + Create a new HttpException + + http status code (sent in the response) + error description + inner exception + + + + status code to use in the response. + + + + + Initializes a new instance of the class. + + error message + + + + This class is created as a wrapper, since there are two different cookie types in .Net (Cookie and HttpCookie). + The framework might switch class in the future and we dont want to have to replace all instances + + + + + Let's copy all the cookies. + + value from cookie header. + + + + Adds a cookie in the collection. + + cookie to add + cookie is null + + + + Gets a collection enumerator on the cookie list. + + collection enumerator + + + + Remove all cookies. + + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Remove a cookie from the collection. + + Name of cookie. + + + + Gets the count of cookies in the collection. + + + + + Gets the cookie of a given identifier (null if not existing). + + + + + Contains a connection to a browser/client. + + + + + Disconnect from client + + error to report in the event. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + HTML body contents, can be null or empty. + A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty + If is invalid. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + + + + Send a response. + + + + + + send a whole buffer + + buffer to send + + + + + Send data using the stream + + Contains data to send + Start position in buffer + number of bytes to send + + + + + + Closes the streams and disposes of the unmanaged resources + + + + + Using SSL or other encryption method. + + + + + Using SSL or other encryption method. + + + + + The context have been disconnected. + + + Event can be used to clean up a context, or to reuse it. + + + + + A request have been received in the context. + + + + + A have been disconnected. + + + + + Initializes a new instance of the class. + + Reason to disconnection. + + + + Gets reason to why client disconnected. + + + + + + + + + + Initializes a new instance of the class. + + The request. + + + + Gets received request. + + + + + Returns item either from a form or a query string (checks them in that order) + + + + Representation of a non-initialized HttpParam + + + Initialises the class to hold a value either from a post request or a querystring request + + + + The add method is not availible for HttpParam + since HttpParam checks both Request.Form and Request.QueryString + + name identifying the value + value to add + + + + + Checks whether the form or querystring has the specified value + + Name, case sensitive + true if found; otherwise false. + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Fetch an item from the form or querystring (in that order). + + + Item if found; otherwise HttpInputItem.EmptyLanguageNode + + + + Container class for posted files + + + + + Creates a container for a posted file + + The identifier of the post field + The file path + The content type of the file + The name of the file uploaded + If any parameter is null or empty + + + + Creates a container for a posted file + + If any parameter is null or empty + + + Destructor disposing the file + + + + Deletes the temporary file + + True if manual dispose + + + + Disposing interface, cleans up managed resources (the temporary file) and suppresses finalization + + + + + The name/id of the file + + + + + The full file path + + + + + The name of the uploaded file + + + + + The type of file + + + + + This decoder converts XML documents to form items. + Each element becomes a subitem in the form, and each attribute becomes an item. + + + // xml: somethingdata + // result: + // form["hello"].Value = "something" + // form["hello"]["id"].Value = 1 + // form["hello"]["world]["id"].Value = 1 + // form["hello"]["world"].Value = "data" + + + The original xml document is stored in form["__xml__"].Value. + + + + + Interface for form content decoders. + + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream enconding + A http form, or null if content could not be parsed. + If contents in the stream is not valid input data. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream encoding + Note: contentType and encoding are not used? + A http form, or null if content could not be parsed. + + + + + Recursive function that will go through an xml element and store it's content + to the form item. + + (parent) Item in form that content should be added to. + Node that should be parsed. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + The object form class takes an object and creates form items for it. + + + + + Initializes a new instance of the class. + + + form name *and* id. + action to do when form is posted. + + + + + Initializes a new instance of the class. + + form name *and* id. + action to do when form is posted. + object to get values from + + + + Initializes a new instance of the class. + + form action. + object to get values from. + + + + write out the FORM-tag. + + generated html code + + + + Writeout the form tag + + form should be posted through ajax. + generated html code + + + + Generates a text box. + + + + generated html code + + + + password box + + + + generated html code + + + + Hiddens the specified property name. + + Name of the property. + The options. + generated html code + + + + Labels the specified property name. + + property in object. + caption + generated html code + + + + Generate a checkbox + + property in object + checkbox value + additional html attributes. + generated html code + + + + Write a html select tag + + object property. + id column + The title column. + The options. + + + + + Selects the specified property name. + + Name of the property. + The items. + The id column. + The title column. + The options. + + + + + Write a submit tag. + + button caption + html submit tag + + + + html end form tag + + html + + + + + + + http://www.faqs.org/rfcs/rfc1867.html + + + + + multipart/form-data + + + + + form-data + + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream enconding + A http form, or null if content could not be parsed. + If contents in the stream is not valid input data. + If any parameter is null + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + The request could not be understood by the server due to malformed syntax. + The client SHOULD NOT repeat the request without modifications. + + Text taken from: http://www.submissionchamber.com/help-guides/error-codes.php + + + + + Create a new bad request exception. + + reason to why the request was bad. + + + + Create a new bad request exception. + + reason to why the request was bad. + inner exception + + + + Cookies that should be set. + + + + + Adds a cookie in the collection. + + cookie to add + cookie is null + + + + Copy a request cookie + + + When the cookie should expire + + + + Gets a collection enumerator on the cookie list. + + collection enumerator + + + + Remove all cookies + + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Gets the count of cookies in the collection. + + + + + Gets the cookie of a given identifier (null if not existing). + + + + + cookie being sent back to the browser. + + + + + + cookie sent by the client/browser + + + + + + Constructor. + + cookie identifier + cookie content + id or content is null + id is empty + + + + Gets the cookie HTML representation. + + cookie string + + + + Gets the cookie identifier. + + + + + Cookie value. Set to null to remove cookie. + + + + + Constructor. + + cookie identifier + cookie content + cookie expiration date. Use DateTime.MinValue for session cookie. + id or content is null + id is empty + + + + Create a new cookie + + name identifying the cookie + cookie value + when the cookie expires. Setting DateTime.MinValue will delete the cookie when the session is closed. + Path to where the cookie is valid + Domain that the cookie is valid for. + + + + Create a new cookie + + Name and value will be used + when the cookie expires. + + + + Gets the cookie HTML representation. + + cookie string + + + + When the cookie expires. + DateTime.MinValue means that the cookie expires when the session do so. + + + + + Cookie is only valid under this path. + + + + + Inversion of control interface. + + + + + Add a component instance + + Interface type + Instance to add + + + + Get a component. + + Interface type + Component if registered, otherwise null. + + Component will get created if needed. + + + + + Checks if the specified component interface have been added. + + + true if found; otherwise false. + + + + Add a component. + + Type being requested. + Type being created. + + + + Contains a listener that doesn't do anything with the connections. + + + + + Listen for regular HTTP connections + + IP Address to accept connections on + TCP Port to listen on, default HTTP port is 80. + Factory used to create es. + address is null. + Port must be a positive number. + + + + Initializes a new instance of the class. + + IP Address to accept connections on + TCP Port to listen on, default HTTPS port is 443 + Factory used to create es. + Certificate to use + + + + Initializes a new instance of the class. + + IP Address to accept connections on + TCP Port to listen on, default HTTPS port is 443 + Factory used to create es. + Certificate to use + which HTTPS protocol to use, default is TLS. + + + Exception. + + + + Will try to accept connections one more time. + + If any exceptions is thrown. + + + + Can be used to create filtering of new connections. + + Accepted socket + true if connection can be accepted; otherwise false. + + + + Start listen for new connections + + Number of connections that can stand in a queue to be accepted. + Listener have already been started. + + + + Stop the listener + + + + + + Gives you a change to receive log entries for all internals of the HTTP library. + + + You may not switch log writer after starting the listener. + + + + + True if we should turn on trace logs. + + + + + Catch exceptions not handled by the listener. + + + Exceptions will be thrown during debug mode if this event is not used, + exceptions will be printed to console and suppressed during release mode. + + + + + A request have been received from a . + + + + + New implementation of the HTTP listener. + + + Use the Create methods to create a default listener. + + + + + Initializes a new instance of the class. + + IP Address to accept connections on + TCP Port to listen on, default HTTP port is 80. + Factory used to create es. + address is null. + Port must be a positive number. + + + + Initializes a new instance of the class. + + The address. + The port. + The factory. + The certificate. + + + + Initializes a new instance of the class. + + The address. + The port. + The factory. + The certificate. + The protocol. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Created HTTP listener. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Certificate to use + Created HTTP listener. + + + + Creates a new instance with default factories. + + Address that the listener should accept connections on. + Port that listener should accept connections on. + Certificate to use + which HTTPS protocol to use, default is TLS. + Created HTTP listener. + + + + Can be used to create filtering of new connections. + + Accepted socket + + true if connection can be accepted; otherwise false. + + + + + A client have been accepted, but not handled, by the listener. + + + + + redirects from one URL to another. + + + + + Rules are used to perform operations before a request is being handled. + Rules can be used to create routing etc. + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + If request or response is null. + + + + Initializes a new instance of the class. + + Absolute path (no server name) + Absolute path (no server name) + + server.Add(new RedirectRule("/", "/user/index")); + + + + + Initializes a new instance of the class. + + Absolute path (no server name) + Absolute path (no server name) + true if request should be redirected, false if the request URI should be replaced. + + server.Add(new RedirectRule("/", "/user/index")); + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + + + + Gets string to match request URI with. + + Is compared to request.Uri.AbsolutePath + + + + Gets where to redirect. + + + + + Gets whether server should redirect client. + + + false means that the rule will replace + the current request URI with the new one from this class. + true means that a redirect response is sent to the client. + + + + + Parses a HTTP request directly from a stream + + + + + Event driven parser used to parse incoming HTTP requests. + + + The parser supports partial messages and keeps the states between + each parsed buffer. It's therefore important that the parser gets + ed if a client disconnects. + + + + + Parse partial or complete message. + + buffer containing incoming bytes + where in buffer that parsing should start + number of bytes to parse + Unparsed bytes left in buffer. + BadRequestException. + + + + Clear parser state. + + + + + Current state in parser. + + + + + A request have been successfully parsed. + + + + + More body bytes have been received. + + + + + Request line have been received. + + + + + A header have been received. + + + + + Gets or sets the log writer. + + + + + Create a new request parser + + delegate receiving log entries. + + + + Add a number of bytes to the body + + buffer containing more body bytes. + starting offset in buffer + number of bytes, from offset, to read. + offset to continue from. + + + + Remove all state information for the request. + + + + + Parse request line + + + If line is incorrect + Expects the following format: "Method SP Request-URI SP HTTP-Version CRLF" + + + + We've parsed a new header. + + Name in lower case + Value, unmodified. + If content length cannot be parsed. + + + + Parse a message + + bytes to parse. + where in buffer that parsing should start + number of bytes to parse, starting on . + offset (where to start parsing next). + BadRequestException. + + + + Gets or sets the log writer. + + + + + Current state in parser. + + + + + A request have been successfully parsed. + + + + + More body bytes have been received. + + + + + Request line have been received. + + + + + A header have been received. + + + + + A thread-safe lockless queue that supports multiple readers and + multiple writers + + + + Queue head + + + Queue tail + + + Queue item count + + + + Constructor + + + + + Enqueue an item + + Item to enqeue + + + + Try to dequeue an item + + Dequeued item if the dequeue was successful + True if an item was successfully deqeued, otherwise false + + + Gets the current number of items in the queue. Since this + is a lockless collection this value should be treated as a close + estimate + + + + Provides a node container for data in a singly linked list + + + + Pointer to the next node in list + + + The data contained by the node + + + + Constructor + + + + + Constructor + + + + + Contains server side HTTP request information. + + + + + Called during parsing of a . + + Name of the header, should not be URL encoded + Value of the header, should not be URL encoded + If a header is incorrect. + + + + Add bytes to the body + + buffer to read bytes from + where to start read + number of bytes to read + Number of bytes actually read (same as length unless we got all body bytes). + If body is not writable + bytes is null. + offset is out of range. + + + + Clear everything in the request + + + + + Decode body into a form. + + A list with form decoders. + If body contents is not valid for the chosen decoder. + If body is still being transferred. + + + + Sets the cookies. + + The cookies. + + + + Create a response object. + + Context for the connected client. + A new . + + + + Gets kind of types accepted by the client. + + + + + Gets or sets body stream. + + + + + Gets whether the body is complete. + + + + + Gets or sets kind of connection used for the session. + + + + + Gets or sets number of bytes in the body. + + + + + Gets cookies that was sent with the request. + + + + + Gets form parameters. + + + + + Gets headers sent by the client. + + + + + Gets or sets version of HTTP protocol that's used. + + + Probably or . + + + + + + Gets whether the request was made by Ajax (Asynchronous JavaScript) + + + + + Gets or sets requested method. + + + Will always be in upper case. + + + + + + Gets parameter from or . + + + + + Gets variables sent in the query string + + + + + Gets or sets requested URI. + + + + + Gets URI absolute path divided into parts. + + + // URI is: http://gauffin.com/code/tiny/ + Console.WriteLine(request.UriParts[0]); // result: code + Console.WriteLine(request.UriParts[1]); // result: tiny + + + If you're using controllers than the first part is controller name, + the second part is method name and the third part is Id property. + + + + + + Gets or sets path and query. + + + + Are only used during request parsing. Cannot be set after "Host" header have been + added. + + + + + PrototypeJS implementation of the javascript functions. + + + + + Purpose of this class is to create a javascript toolkit independent javascript helper. + + + + + Generates a list with JS options. + + StringBuilder that the options should be added to. + the javascript options. name, value pairs. each string value should be escaped by YOU! + true if we should start with a comma. + + + + Removes any javascript parameters from an array of parameters + + The array of parameters to remove javascript params from + An array of html parameters + + + + javascript action that should be added to the "onsubmit" event in the form tag. + + + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Requests a url through ajax + + url to fetch + optional options in format "key, value, key, value", used in JS request object. + a link tag + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Ajax requests that updates an element with + the fetched content + + Url to fetch content from + element to update + optional options in format "key, value, key, value", used in JS updater object. + A link tag. + All javascript option names should end with colon. + + + JSHelper.AjaxUpdater("/user/show/1", "userInfo", "onsuccess:", "alert('Successful!');"); + + + + + + A link that pop ups a Dialog (overlay div) + + url to contents of dialog + link title + A "a"-tag that popups a dialog when clicked + name/value of html attributes + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Creates a new modal dialog window + + url to open in window. + window title (may not be supported by all js implementations) + + + + + + Requests a url through ajax + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + optional options in format "key, value, key, value", used in JS request object. All keys should end with colon. + a link tag + onclick attribute is used by this method. + + + // plain text + JSHelper.AjaxRequest("'/user/show/1'"); + + // ajax request using this.href + string link = "<a href=\"/user/call/1\" onclick=\"" + JSHelper.AjaxRequest("this.href") + "/<call user</a>"; + + + + + + Determins if a list of strings contains a specific value + + options to check in + value to find + true if value was found + case insensitive + + + + Ajax requests that updates an element with + the fetched content + + URL to fetch. URL is NOT enclosed in quotes by the implementation. You need to do that yourself. + element to update + options in format "key, value, key, value". All keys should end with colon. + A link tag. + + + JSHelper.AjaxUpdater("'/user/show/1'", "user", "onsuccess:", "alert('hello');", "asynchronous:", "true"); + + + + + + A link that pop ups a Dialog (overlay div) + + URL to contents of dialog + link title + name, value, name, value + + A "a"-tag that popups a dialog when clicked + + Requires Control.Modal found here: http://livepipe.net/projects/control_modal/ + And the following JavaScript (load it in application.js): + + Event.observe(window, 'load', + function() { + document.getElementsByClassName('modal').each(function(link){ new Control.Modal(link); }); + } + ); + + + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + create a modal dialog (usually using DIVs) + + url to fetch + dialog title + javascript/html attributes. javascript options ends with colon ':'. + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + javascript action that should be added to the "onsubmit" event in the form tag. + + remember to encapsulate strings in '' + + All javascript option names should end with colon. + + + JSHelper.AjaxRequest("/user/show/1", "onsuccess:", "$('userInfo').update(result);"); + + + + + + Helpers making it easier to work with forms. + + + + + + Used to let the website use different JavaScript libraries. + Default is + + + + + Create a <form> tag. + + name of form + action to invoke on submit + form should be posted as Ajax + HTML code + + + // without options + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax); + + // with options + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax, "style", "display:inline", "class", "greenForm"); + + + HTML attributes or JavaScript options. + Method will ALWAYS be POST. + options must consist of name, value, name, value + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + string containing a SELECT-tag. + + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + Id of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + string containing a SELECT-tag. + + + + // Class that is going to be used in a SELECT-tag. + public class User + { + private readonly string _realName; + private readonly int _id; + public User(int id, string realName) + { + _id = id; + _realName = realName; + } + public string RealName + { + get { return _realName; } + } + + public int Id + { + get { return _id; } + } + } + + // Using an inline delegate to generate the select list + public void UserInlineDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, delegate(object o, out object id, out object value) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + }, 2, true); + } + + // Using an method as delegate to generate the select list. + public void UseExternalDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, UserOptions, 1, true); + } + + // delegate returning id and title + public static void UserOptions(object o, out object id, out object title) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + } + + + name, id, collection or getIdTitle is null. + + + + Creates a select list with the values in a collection. + + Name of the SELECT-tag + Id of the SELECT-tag + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + name, value collection of extra HTML attributes. + string containing a SELECT-tag. + + name, id, collection or getIdTitle is null. + Invalid HTML attribute list. + + + + Generate a list of HTML options + + collection used to generate options. + delegate used to return id and title from objects. + value that should be marked as selected. + First row should contain an empty value. + + collection or getIdTitle is null. + + + sb is null. + + + + Creates a check box. + + element name + element value + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + Creates a check box. + + element name + element id + element value + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + value in your business object. (check box will be selected if it matches the element value) + + + + + Creates a check box. + + element name + element id + determines if the check box is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + will set value to "1". + + + + Creates a RadioButton. + + element name + element value + determines if the radio button is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + Creates a RadioButton. + + element name + element id + element value + determines if the radio button is selected or not. This is done differently depending on the + type of variable. A boolean simply triggers checked or not, all other types are compared with "value" to determine if + the box is checked or not. + a list with additional attributes (name, value, name, value). + a generated radio button + + + + form close tag + + + + + + We dont want to let the server to die due to exceptions thrown in worker threads. + therefore we use this delegate to give you a change to handle uncaught exceptions. + + Class that the exception was thrown in. + Exception + + Server will throw a InternalServerException in release version if you dont + handle this delegate. + + + + + Implements HTTP Digest authentication. It's more secure than Basic auth since password is + encrypted with a "key" from the server. + + + Keep in mind that the password is encrypted with MD5. Use a combination of SSL and digest auth to be secure. + + + + + Authentication modules are used to implement different + kind of HTTP authentication. + + + + + Tag used for authentication. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + Array with optional options. + A correct authentication request. + If realm is empty or null. + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + options to specific implementations + Authentication object that is stored for the request. A user class or something like that. + if is invalid + If any of the parameters is empty or null. + + + + Used to invoke the authentication delegate that is used to lookup the user name/realm. + + Realm (domain) that user want to authenticate in + User name + Password used for validation. Some implementations got password in clear text, they are then sent to client. + object that will be stored in the request to help you identify the user if authentication was successful. + true if authentication was successful + + + + Determines if authentication is required. + + HTTP request from browser + true if user should be authenticated. + throw from your delegate if no more attempts are allowed. + If no more attempts are allowed + + + + name used in HTTP request. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Used by test classes to be able to use hardcoded values + + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + First option: true if username/password is correct but not cnonce + + Authentication object that is stored for the request. A user class or something like that. + + if authenticationHeader is invalid + If any of the paramters is empty or null. + + + + Encrypts parameters into a Digest string + + Realm that the user want to log into. + User logging in + Users password. + HTTP method. + Uri/domain that generated the login prompt. + Quality of Protection. + "Number used ONCE" + Hexadecimal request counter. + "Client Number used ONCE" + Digest encrypted string + + + + + + Md5 hex encoded "userName:realm:password", without the quotes. + Md5 hex encoded "method:uri", without the quotes + Quality of Protection + "Number used ONCE" + Hexadecimal request counter. + Client number used once + + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + First options specifies if true if username/password is correct but not cnonce. + A correct auth request. + If realm is empty or null. + + + + Decodes authorization header value + + header value + Encoding that the buffer is in + All headers and their values if successful; otherwise null + + NameValueCollection header = DigestAuthentication.Decode("response=\"6629fae49393a05397450978507c4ef1\",\r\nc=00001", Encoding.ASCII); + + Can handle lots of whitespaces and new lines without failing. + + + + Gets the current nonce. + + + + + + Gets the Md5 hash bin hex2. + + To be hashed. + + + + + determines if the nonce is valid or has expired. + + nonce value (check wikipedia for info) + true if the nonce has not expired. + + + + name used in http request. + + + + + Gets or sets whether the token supplied in is a + HA1 generated string. + + + + + Generic helper functions for HTTP + + + + + Version string for HTTP v1.0 + + + + + Version string for HTTP v1.1 + + + + + An empty URI + + + + + Parses a query string. + + Query string (URI encoded) + A object if successful; otherwise + queryString is null. + If string cannot be parsed. + + + + Delegate used to let authentication modules authenticate the user name and password. + + Realm that the user want to authenticate in + User name specified by client + Can either be user password or implementation specific token. + object that will be stored in a session variable called if authentication was successful. + throw forbidden exception if too many attempts have been made. + + + Use to specify that the token is a HA1 token. (MD5 generated + string from realm, user name and password); Md5String(userName + ":" + realm + ":" + password); + + + + + + Let's you decide on a system level if authentication is required. + + HTTP request from client + true if user should be authenticated. + throw if no more attempts are allowed. + If no more attempts are allowed + + + + Arguments used when more body bytes have come. + + + + + Initializes a new instance of the class. + + buffer that contains the received bytes. + offset in buffer where to start processing. + number of bytes from that should be parsed. + + + + Initializes a new instance of the class. + + + + + Gets or sets buffer that contains the received bytes. + + + + + Gets or sets number of bytes from that should be parsed. + + + + + Gets or sets offset in buffer where to start processing. + + + + + Contains all HTTP Methods (according to the HTTP 1.1 specification) + + See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + + + + + + The DELETE method requests that the origin server delete the resource identified by the Request-URI. + + + + This method MAY be overridden by human intervention (or other means) on the origin server. + The client cannot be guaranteed that the operation has been carried out, even if the status code + returned from the origin server indicates that the action has been completed successfully. + + + However, the server SHOULD NOT indicate success unless, at the time the response is given, + it intends to delete the resource or move it to an inaccessible location. + + + A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, + 202 (Accepted) if the action has not yet been enacted, + or 204 (No Content) if the action has been enacted but the response does not include an entity. + + + If the request passes through a cache and the Request-URI identifies one or more currently cached entities, + those entries SHOULD be treated as stale. Responses to this method are not cacheable. + + + + + + The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. + + + + If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the + entity in the response and not the source text of the process, unless that text happens to be the output of the process. + + + The semantics of the GET method change to a "conditional GET" if the request message includes an + If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. + A conditional GET method requests that the entity be transferred only under the circumstances described + by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network + usage by allowing cached entities to be refreshed without requiring multiple requests or transferring + data already held by the client. + + + + + + The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. + + + The meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the + information sent in response to a GET request. This method can be used for obtaining meta information about + the entity implied by the request without transferring the entity-body itself. + + This method is often used for testing hypertext links for validity, accessibility, and recent modification. + + + + + The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. + + + This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval. + + + + + The POST method is used to request that the origin server accept the entity enclosed + in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. + + + POST is designed to allow a uniform method to cover the following functions: + + + Annotation of existing resources; + + Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; + + Providing a block of data, such as the result of submitting a form, to a data-handling process; + + Extending a database through an append operation. + + + + If a resource has been created on the origin server, the response SHOULD be 201 (Created) and + contain an entity which describes the status of the request and refers to the new resource, and a + Location header (see section 14.30). + + + The action performed by the POST method might not result in a resource that can be identified by a URI. + In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on + whether or not the response includes an entity that describes the result. + + Responses to this method are not cacheable, unless the response includes appropriate Cache-Control + or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent + to retrieve a cacheable resource. + + + + + + The PUT method requests that the enclosed entity be stored under the supplied Request-URI. + + + + + If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a + modified version of the one residing on the origin server. + + If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new + resource by the requesting user agent, the origin server can create the resource with that URI. + + If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. + + If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to + indicate successful completion of the request. + + If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be + given that reflects the nature of the problem. + + + + The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not + understand or implement and MUST return a 501 (Not Implemented) response in such cases. + + + + + + The TRACE method is used to invoke a remote, application-layer loop- back of the request message. + + + + + Contains all HTTP Methods (according to the HTTP 1.1 specification) + + See: http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + + + + + + The DELETE method requests that the origin server delete the resource identified by the Request-URI. + + + + This method MAY be overridden by human intervention (or other means) on the origin server. + The client cannot be guaranteed that the operation has been carried out, even if the status code + returned from the origin server indicates that the action has been completed successfully. + + + However, the server SHOULD NOT indicate success unless, at the time the response is given, + it intends to delete the resource or move it to an inaccessible location. + + + A successful response SHOULD be 200 (OK) if the response includes an entity describing the status, + 202 (Accepted) if the action has not yet been enacted, + or 204 (No Content) if the action has been enacted but the response does not include an entity. + + + If the request passes through a cache and the Request-URI identifies one or more currently cached entities, + those entries SHOULD be treated as stale. Responses to this method are not cacheable. + + + + + + The GET method means retrieve whatever information (in the form of an entity) is identified by the Request-URI. + + + + If the Request-URI refers to a data-producing process, it is the produced data which shall be returned as the + entity in the response and not the source text of the process, unless that text happens to be the output of the process. + + + The semantics of the GET method change to a "conditional GET" if the request message includes an + If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match, or If-Range header field. + A conditional GET method requests that the entity be transferred only under the circumstances described + by the conditional header field(s). The conditional GET method is intended to reduce unnecessary network + usage by allowing cached entities to be refreshed without requiring multiple requests or transferring + data already held by the client. + + + + + + The HEAD method is identical to GET except that the server MUST NOT return a message-body in the response. + + + The meta information contained in the HTTP headers in response to a HEAD request SHOULD be identical to the + information sent in response to a GET request. This method can be used for obtaining meta information about + the entity implied by the request without transferring the entity-body itself. + + This method is often used for testing hypertext links for validity, accessibility, and recent modification. + + + + + The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. + + + This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval. + + + + + The POST method is used to request that the origin server accept the entity enclosed + in the request as a new subordinate of the resource identified by the Request-URI in the Request-Line. + + + POST is designed to allow a uniform method to cover the following functions: + + + Annotation of existing resources; + + Posting a message to a bulletin board, newsgroup, mailing list, or similar group of articles; + + Providing a block of data, such as the result of submitting a form, to a data-handling process; + + Extending a database through an append operation. + + + + If a resource has been created on the origin server, the response SHOULD be 201 (Created) and + contain an entity which describes the status of the request and refers to the new resource, and a + Location header (see section 14.30). + + + The action performed by the POST method might not result in a resource that can be identified by a URI. + In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on + whether or not the response includes an entity that describes the result. + + Responses to this method are not cacheable, unless the response includes appropriate Cache-Control + or Expires header fields. However, the 303 (See Other) response can be used to direct the user agent + to retrieve a cacheable resource. + + + + + + The PUT method requests that the enclosed entity be stored under the supplied Request-URI. + + + + + If the Request-URI refers to an already existing resource, the enclosed entity SHOULD be considered as a + modified version of the one residing on the origin server. + + If the Request-URI does not point to an existing resource, and that URI is capable of being defined as a new + resource by the requesting user agent, the origin server can create the resource with that URI. + + If a new resource is created, the origin server MUST inform the user agent via the 201 (Created) response. + + If an existing resource is modified, either the 200 (OK) or 204 (No Content) response codes SHOULD be sent to + indicate successful completion of the request. + + If the resource could not be created or modified with the Request-URI, an appropriate error response SHOULD be + given that reflects the nature of the problem. + + + + The recipient of the entity MUST NOT ignore any Content-* (e.g. Content-Range) headers that it does not + understand or implement and MUST return a 501 (Not Implemented) response in such cases. + + + + + + The TRACE method is used to invoke a remote, application-layer loop- back of the request message. + + + + + Used to create and reuse contexts. + + + + + Used to create es. + + + + + Creates a that handles a connected client. + + Client socket (accepted by the ). + A creates . + + + + Create a secure . + + Client socket (accepted by the ). + HTTPS certificate to use. + Kind of HTTPS protocol. Usually TLS or SSL. + A created . + + + + Server is shutting down so shut down the factory + + + + + A request have been received from one of the contexts. + + + + + Initializes a new instance of the class. + + The writer. + Amount of bytes to read from the incoming socket stream. + Used to create a request parser. + + + + Create a new context. + + true if socket is running HTTPS. + Client that connected + Network/SSL stream. + A context. + + + + Create a new context. + + true if HTTPS is used. + Remote client + Network stream, uses . + A new context (always). + + + + Create a secure . + + Client socket (accepted by the ). + HTTPS certificate to use. + Kind of HTTPS protocol. Usually TLS or SSL. + + A created . + + + + + Creates a that handles a connected client. + + Client socket (accepted by the ). + + A creates . + + + + + Server is shutting down so shut down the factory + + + + + True if detailed trace logs should be written. + + + + + A request have been received from one of the contexts. + + + + + Custom network stream to mark sockets as reusable when disposing the stream. + + + + + Creates a new instance of the class for the specified . + + + The that the will use to send and receive data. + + + The parameter is null. + + + The parameter is not connected. + -or- + The property of the parameter is not . + -or- + The parameter is in a nonblocking state. + + + + + Initializes a new instance of the class for the specified with the specified ownership. + + + The that the will use to send and receive data. + + + Set to true to indicate that the will take ownership of the ; otherwise, false. + + + The parameter is null. + + + The parameter is not connected. + -or- + the value of the property of the parameter is not . + -or- + the parameter is in a nonblocking state. + + + + + Creates a new instance of the class for the specified with the specified access rights. + + + The that the will use to send and receive data. + + + A bitwise combination of the values that specify the type of access given to the over the provided . + + + The parameter is null. + + + The parameter is not connected. + -or- + the property of the parameter is not . + -or- + the parameter is in a nonblocking state. + + + + + Creates a new instance of the class for the specified with the specified access rights and the specified ownership. + + + The that the will use to send and receive data. + + + A bitwise combination of the values that specifies the type of access given to the over the provided . + + + Set to true to indicate that the will take ownership of the ; otherwise, false. + + + The parameter is null. + + + The parameter is not connected. + -or- + The property of the parameter is not . + -or- + The parameter is in a nonblocking state. + + + + + Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream. + + + + + Releases the unmanaged resources used by the and optionally releases the managed resources. + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Invoked when a client have been accepted by the + + + Can be used to revoke incoming connections + + + + + Initializes a new instance of the class. + + The socket. + + + + Client may not be handled. + + + + + Accepted socket. + + + + + Client should be revoked. + + + + + A session stored in memory. + + + + + Interface for sessions + + + + + Remove everything from the session + + + + + Remove everything from the session + + True if the session is cleared due to expiration + + + + Session id + + + + + Should + + Name of the session variable + null if it's not set + If the object cant be serialized. + + + + When the session was last accessed. + This property is touched by the http server each time the + session is requested. + + + + + Number of session variables. + + + + + Event triggered upon clearing the session + + + + + + + A unique id used by the sessions store to identify the session + + + + Id + + + + + + Remove everything from the session + + + + + Clears the specified expire. + + True if the session is cleared due to expiration + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + 2 + + + + Session id + + + + + Should + + Name of the session variable + null if it's not set + + + + when the session was last accessed. + + + Used to determine when the session should be removed. + + + + + Number of values in the session + + + + + Flag to indicate that the session have been changed + and should be saved into the session store. + + + + + Event triggered upon clearing the session + + + + + A reverse proxy are used to act as a bridge between local (protected/hidden) websites + and public clients. + + A typical usage is to allow web servers on non standard ports to still be available + to the public clients, or allow web servers on private ips to be available. + + + + + + + Base url requested from browser + Base url on private web server + + // this will return contents from http://192.168.1.128/view/jonas when client requests http://www.gauffin.com/user/view/jonas + _server.Add(new ReverseProxyModule("http://www.gauffin.com/user/", "http://192.168.1.128/"); + + + + + Method that determines if an url should be handled or not by the module + + Url requested by the client. + true if module should handle the url. + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + + + + Can handle application/x-www-form-urlencoded + + + + + + Stream containing the content + Content type (with any additional info like boundry). Content type is always supplied in lower case + Stream encoding + + A HTTP form, or null if content could not be parsed. + + If contents in the stream is not valid input data. + + + + Checks if the decoder can handle the mime type + + Content type (with any additional info like boundry). Content type is always supplied in lower case. + True if the decoder can parse the specified content type + + + + This provider is used to let us implement any type of form decoding we want without + having to rewrite anything else in the server. + + + + + + + Should contain boundary and type, as in: multipart/form-data; boundary=---------------------------230051238959 + Stream containing form data. + Encoding used when decoding the stream + if no parser was found. + If stream is null or not readable. + If stream contents cannot be decoded properly. + + + + Add a decoder. + + + + + + + Number of added decoders. + + + + + Use with care. + + + + + Decoder used for unknown content types. + + + + + The server encountered an unexpected condition which prevented it from fulfilling the request. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + error message. + + + + Initializes a new instance of the class. + + error message. + inner exception. + + + + Response that is sent back to the web browser / client. + + A response can be sent if different ways. The easiest one is + to just fill the Body stream with content, everything else + will then be taken care of by the framework. The default content-type + is text/html, you should change it if you send anything else. + + The second and slighty more complex way is to send the response + as parts. Start with sending the header using the SendHeaders method and + then you can send the body using SendBody method, but do not forget + to set ContentType and ContentLength before doing so. + + + public void MyHandler(IHttpRequest request, IHttpResponse response) + { + + } + + + + + Add another header to the document. + + Name of the header, case sensitive, use lower cases. + Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n + If headers already been sent. + If value conditions have not been met. + Adding any header will override the default ones and those specified by properties. + + + + Send headers and body to the browser. + + If content have already been sent. + + + + Make sure that you have specified ContentLength and sent the headers first. + + + If headers have not been sent. + + offest of first byte to send + number of bytes to send. + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Make sure that you have specified ContentLength and sent the headers first. + + + If headers have not been sent. + + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Send headers to the client. + + If headers already been sent. + + + + + + + Redirect client to somewhere else using the 302 status code. + + Destination of the redirect + If headers already been sent. + You can not do anything more with the request when a redirect have been done. This should be your last + action. + + + + redirect to somewhere + + where the redirect should go + + No body are allowed when doing redirects. + + + + + The body stream is used to cache the body contents + before sending everything to the client. It's the simplest + way to serve documents. + + + + + Defines the version of the HTTP Response for applications where it's required + for this to be forced. + + + + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + + + + Kind of connection + + + + + Encoding to use when sending stuff to the client. + + Default is UTF8 + + + + Number of seconds to keep connection alive + + Only used if Connection property is set to ConnectionType.KeepAlive + + + + Status code that is sent to the client. + + Default is HttpStatusCode.Ok + + + + Information about why a specific status code was used. + + + + + Size of the body. MUST be specified before sending the header, + unless property Chunked is set to true. + + + + + Kind of content in the body + + Default is text/html + + + + Headers have been sent to the client- + + You can not send any additional headers if they have already been sent. + + + + The whole response have been sent. + + + + + Cookies that should be created/changed. + + + + + Type of HTTP connection + + + + + Connection is closed after each request-response + + + + + Connection is kept alive for X seconds (unless another request have been made) + + + + + The website module let's you handle multiple websites in the same server. + It uses the "Host" header to check which site you want. + + It's recommended that you do not + add any other modules to HttpServer if you are using the website module. Instead, + add all wanted modules to each website. + + + + + + domain name that should be handled. + + + + + Method that process the url + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + + + + Name of site. + + + + + Used to inform http server that + + + + + Eventarguments used when an exception is thrown by a module + + the exception + + + + Exception thrown in a module + + + + + represents a HTTP input item. Each item can have multiple sub items, a sub item + is made in a HTML form by using square brackets + + + // becomes: + Console.WriteLine("Value: {0}", form["user"]["FirstName"].Value); + + + All names in a form SHOULD be in lowercase. + + + + Representation of a non-initialized . + + + + Initializes an input item setting its name/identifier and value + + Parameter name/id + Parameter value + + + Creates a deep copy of the item specified + The item to copy + The function makes a deep copy of quite a lot which can be slow + + + + Add another value to this item + + Value to add. + Cannot add stuff to . + + + + checks if a sub-item exists (and has a value). + + name in lower case + true if the sub-item exists and has a value; otherwise false. + + + Returns a formatted representation of the instance with the values of all contained parameters + + + + Outputs the string in a formatted manner + + A prefix to append, used internally + produce a query string + + + + Add a sub item. + + Can contain array formatting, the item is then parsed and added in multiple levels + Value to add. + Argument is null. + Cannot add stuff to . + + + + Returns an enumerator that iterates through the collection. + + + + A that can be used to iterate through the collection. + + 1 + + + + Returns an enumerator that iterates through a collection. + + + + An object that can be used to iterate through the collection. + + 2 + + + + Outputs the string in a formatted manner + + A prefix to append, used internally + + + + + Number of values + + + + + Get a sub item + + name in lower case. + if no item was found. + + + + Name of item (in lower case). + + + + + Returns the first value, or null if no value exist. + + + + + Returns the last value, or null if no value exist. + + + + + Returns the list with values. + + + + + + + name in lower case + + + + Class to handle loading of resource files + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + logger. + + + + Loads resources from a namespace in the given assembly to an URI + + The URI to map the resources to + The assembly in which the resources reside + The namespace from which to load the resources + + + resourceLoader.LoadResources("/user/", typeof(User).Assembly, "MyLib.Models.User.Views"); + + Will make the resource MyLib.Models.User.Views.list.Haml accessible via /user/list.haml or /user/list/ + + The amount of loaded files, giving you the possibility of making sure the resources needed gets loaded + If a resource has already been mapped to an uri + + + + Retrieves a stream for the specified resource path if loaded otherwise null + + Path to the resource to retrieve a stream for + A stream or null if the resource couldn't be found + + + + Fetch all files from the resource that matches the specified arguments. + + The path to the resource to extract + + a list of files if found; or an empty array if no files are found. + + Search path must end with an asterisk for finding arbitrary files + + + + Fetch all files from the resource that matches the specified arguments. + + Where the file should reside. + Files to check + + a list of files if found; or an empty array if no files are found. + + + + + Returns whether or not the loader has an instance of the file requested + + The name of the template/file + True if the loader can provide the file + + + + Used when the request line have been successfully parsed. + + + + + Initializes a new instance of the class. + + The HTTP method. + The URI path. + The HTTP version. + + + + Initializes a new instance of the class. + + + + + Gets or sets http method. + + + Should be one of the methods declared in . + + + + + Gets or sets the version of the HTTP protocol that the client want to use. + + + + + Gets or sets requested URI path. + + + + + Class that receives Requests from a . + + + + + Client have been disconnected. + + Client that was disconnected. + Reason + + + + + Invoked when a client context have received a new HTTP request + + Client that received the request. + Request that was received. + + + + Container for posted form data + + + Instance to help mark a non-initialized form + + + Initializes a form container with the specified name + + + + Makes a deep copy of the input + + The input to copy + + + + Adds a file to the collection of posted files + + The file to add + If the file is already added + If file is null + If the instance is HttpForm.EmptyForm which cannot be modified + + + + Checks if the form contains a specified file + + Field name of the file parameter + True if the file exists + If the instance is HttpForm.EmptyForm which cannot be modified + + + + Retrieves a file held by by the form + + The identifier of the file + The requested file or null if the file was not found + If name is null or empty + If the instance is HttpForm.EmptyForm which cannot be modified + + + Disposes all held HttpFile's and resets values + + + + Retrieves the number of files added to the + + 0 if no files are added + + + + Contains a connection to a browser/client. + + + Remember to after you have hooked the event. + + TODO: Maybe this class should be broken up into HttpClientChannel and HttpClientContext? + + + + Initializes a new instance of the class. + + true if the connection is secured (SSL/TLS) + client that connected. + Stream used for communication + Used to create a . + Size of buffer to use when reading data. Must be at least 4096 bytes. + If fails + Stream must be writable and readable. + + + + Process incoming body bytes. + + + Bytes + + + + + + + + + + + Start reading content. + + + Make sure to call base.Start() if you override this method. + + + + + Clean up context. + + + Make sure to call base.Cleanup() if you override the method. + + + + + Disconnect from client + + error to report in the event. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + HTML body contents, can be null or empty. + A content type to return the body as, i.e. 'text/html' or 'text/plain', defaults to 'text/html' if null or empty + If is invalid. + + + + Send a response. + + Either or + HTTP status code + reason for the status code. + + + + Send a response. + + + + + + send a whole buffer + + buffer to send + + + + + Send data using the stream + + Contains data to send + Start position in buffer + number of bytes to send + + + + + + This context have been cleaned, which means that it can be reused. + + + + + Context have been started (a new client have connected) + + + + + Overload to specify own type. + + + Must be specified before the context is being used. + + + + + Using SSL or other encryption method. + + + + + Using SSL or other encryption method. + + + + + Specify which logger to use. + + + + + Gets or sets the network stream. + + + + + Gets or sets IP address that the client connected from. + + + + + Gets or sets port that the client connected from. + + + + + The context have been disconnected. + + + Event can be used to clean up a context, or to reuse it. + + + + + A request have been received in the context. + + + + + Helpers to make XML handling easier + + + + + Serializes object to XML. + + object to serialize. + XML + + Removes name spaces and adds indentation + + + + + Create an object from a XML string + + Type of object + XML string + object + + + + + + + + + + + Represents a field in a multipart form + + + + Small design by contract implementation. + + + + + Check whether a parameter is empty. + + Parameter value + Parameter name, or error description. + value is empty. + + + + Checks whether a parameter is null. + + Parameter value + Parameter name, or error description. + value is null. + + + + Checks whether a parameter is null. + + + Parameter value + Parameter name, or error description. + value is null. + + + + Priority for log entries + + + + + + Very detailed logs to be able to follow the flow of the program. + + + + + Logs to help debug errors in the application + + + + + Information to be able to keep track of state changes etc. + + + + + Something did not go as we expected, but it's no problem. + + + + + Something that should not fail failed, but we can still keep + on going. + + + + + Something failed, and we cannot handle it properly. + + + + + Interface used to write to log files. + + + + + Write an entry to the log file. + + object that is writing to the log + importance of the log message + the message + + + + This class writes to the console. It colors the output depending on the logprio and includes a 3-level stacktrace (in debug mode) + + + + + + The actual instance of this class. + + + + + Logwriters the specified source. + + object that wrote the logentry. + Importance of the log message + The message. + + + + Get color for the specified logprio + + prio for the log entry + A for the prio + + + + Default log writer, writes everything to null (nowhere). + + + + + + The logging instance. + + + + + Writes everything to null + + object that wrote the log entry. + Importance of the log message + The message. + + + + Response that is sent back to the web browser / client. + + + + A response can be sent if different ways. The easiest one is + to just fill the Body stream with content, everything else + will then be taken care of by the framework. The default content-type + is text/html, you should change it if you send anything else. + + The second and slightly more complex way is to send the response + as parts. Start with sending the header using the SendHeaders method and + then you can send the body using SendBody method, but do not forget + to set and before doing so. + + + + + // Example using response body. + class MyModule : HttpModule + { + public override bool Process(IHttpRequest request, IHttpResponse response, IHttpSession session) + { + StreamWriter writer = new StreamWriter(response.Body); + writer.WriteLine("Hello dear World!"); + writer.Flush(); + + // return true to tell webserver that we've handled the url + return true; + } + } + + + todo: add two examples, using SendHeaders/SendBody and just the Body stream. + + + + Initializes a new instance of the class. + + Client that send the . + Contains information of what the client want to receive. + cannot be empty. + + + + Initializes a new instance of the class. + + Client that send the . + Version of HTTP protocol that the client uses. + Type of HTTP connection used. + + + + Add another header to the document. + + Name of the header, case sensitive, use lower cases. + Header values can span over multiple lines as long as each line starts with a white space. New line chars should be \r\n + If headers already been sent. + If value conditions have not been met. + Adding any header will override the default ones and those specified by properties. + + + + Send headers and body to the browser. + + If content have already been sent. + + + + Make sure that you have specified and sent the headers first. + + + If headers have not been sent. + + offset of first byte to send + number of bytes to send. + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Make sure that you have specified and sent the headers first. + + + If headers have not been sent. + + + + This method can be used if you want to send body contents without caching them first. This + is recommended for larger files to keep the memory usage low. + + + + Send headers to the client. + + If headers already been sent. + + + + + + + Redirect client to somewhere else using the 302 status code. + + Destination of the redirect + If headers already been sent. + You can not do anything more with the request when a redirect have been done. This should be your last + action. + + + + redirect to somewhere + + where the redirect should go + + No body are allowed when doing redirects. + + + + + The body stream is used to cache the body contents + before sending everything to the client. It's the simplest + way to serve documents. + + + + + The chunked encoding modifies the body of a message in order to + transfer it as a series of chunks, each with its own size indicator, + followed by an OPTIONAL trailer containing entity-header fields. This + allows dynamically produced content to be transferred along with the + information necessary for the recipient to verify that it has + received the full message. + + + + + Defines the version of the HTTP Response for applications where it's required + for this to be forced. + + + + + Kind of connection + + + + + Encoding to use when sending stuff to the client. + + Default is UTF8 + + + + Number of seconds to keep connection alive + + Only used if Connection property is set to . + + + + Status code that is sent to the client. + + Default is + + + + Information about why a specific status code was used. + + + + + Size of the body. MUST be specified before sending the header, + unless property Chunked is set to true. + + + + + Kind of content in the body + + Default type is "text/html" + + + + Headers have been sent to the client- + + You can not send any additional headers if they have already been sent. + + + + The whole response have been sent. + + + + + Cookies that should be created/changed. + + + + + The requested resource was not found in the web server. + + + + + Create a new exception + + message describing the error + inner exception + + + + Create a new exception + + message describing the error + + + + Timeout Manager. Checks for dead clients. Clients with open connections that are not doing anything. Closes sessions opened with keepalive. + + + + + Causes the watcher to immediately check the connections. + + + + + Environment.TickCount is an int but it counts all 32 bits so it goes positive + and negative every 24.9 days. This trims down TickCount so it doesn't wrap + for the callers. + This trims it to a 12 day interval so don't let your frame time get too long. + + + + + + Environment.TickCount is an int but it counts all 32 bits so it goes positive + and negative every 24.9 days. Subtracts the passed value (previously fetched by + 'EnvironmentTickCount()') and accounts for any wrapping. + + + + subtraction of passed prevValue from current Environment.TickCount + + + + Environment.TickCount is an int but it counts all 32 bits so it goes positive + and negative every 24.9 days. Subtracts the passed value (previously fetched by + 'EnvironmentTickCount()') and accounts for any wrapping. + + + + subtraction of passed prevValue from current Environment.TickCount + + + + Environment.TickCount is an int but it counts all 32 bits so it goes positive + and negative every 24.9 days. Subtracts the passed value (previously fetched by + 'EnvironmentTickCount()') and accounts for any wrapping. + + subtraction of passed prevValue from current Environment.TickCount + + + + Use a Thread or a Timer to monitor the ugly + + + + + Session store using memory for each session. + + + + + A session store is used to store and load sessions on a media. + The default implementation () saves/retrieves sessions from memory. + + + + + Creates a new http session with a generated id. + + A object + + + + Creates a new http session with a specific id + + Id used to identify the new cookie.. + A object. + + Id should be generated by the store implementation if it's null or . + + + + + Load an existing session. + + Session id (usually retrieved from a client side cookie). + A session if found; otherwise null. + + + + Save an updated session to the store. + + Session id (usually retrieved from a client side cookie). + If Id property have not been specified. + + + + We use the flyweight pattern which reuses small objects + instead of creating new each time. + + Unused session that should be reused next time Create is called. + + + + Remove expired sessions + + + + + Remove a session + + id of the session. + + + + Load a session from the store + + + null if session is not found. + + + + Number of minutes before a session expires. + + Default time is 20 minutes. + + + + Initializes the class setting the expirationtimer to clean the session every minute + + + + + Delegate for the cleanup timer + + + + + Creates a new http session + + + + + + Creates a new http session with a specific id + + Id used to identify the new cookie.. + A object. + + Id should be generated by the store implementation if it's null or . + + + + + Load an existing session. + + + + + + + Save an updated session to the store. + + + + + + We use the flyweight pattern which reuses small objects + instead of creating new each time. + + EmptyLanguageNode (unused) session that should be reused next time Create is called. + + + + Remove expired sessions + + + + + Remove a session + + id of the session. + + + + Load a session from the store + + + null if session is not found. + + + + Number of minutes before a session expires. + Default is 20 minutes. + + + + + Arguments sent when a is cleared + + + + + Instantiates the arguments for the event + + True if the session is cleared due to expiration + + + + Returns true if the session is cleared due to expiration + + + + + Delegate for when a IHttpSession is cleared + + this is being cleared. + Arguments for the clearing + + + + Used to queue incoming requests. + + + + + Initializes a new instance of the class. + + Called when a request should be processed. + + + + Used to process queued requests. + + + + + Gets or sets maximum number of allowed simultaneous requests. + + + + + Gets or sets maximum number of requests queuing to be handled. + + + + + Specifies how many requests the HTTP server is currently processing. + + + + + Used two queue incoming requests to avoid + thread starvation. + + + + + Method used to process a queued request + + Context that the request was received from. + Request to process. + + + + Event arguments used when a new header have been parsed. + + + + + Initializes a new instance of the class. + + Name of header. + Header value. + + + + Initializes a new instance of the class. + + + + + Gets or sets header name. + + + + + Gets or sets header value. + + + + + Contains server side HTTP request information. + + + + + Chars used to split an URL path into multiple parts. + + + + + Assign a form. + + + + + + Creates a new object that is a copy of the current instance. + + + + A new object that is a copy of this instance. + + 2 + + + + Decode body into a form. + + A list with form decoders. + If body contents is not valid for the chosen decoder. + If body is still being transferred. + + + + Cookies + + the cookies + + + + Create a response object. + + A new . + + + + Called during parsing of a . + + Name of the header, should not be URL encoded + Value of the header, should not be URL encoded + If a header is incorrect. + + + + Add bytes to the body + + buffer to read bytes from + where to start read + number of bytes to read + Number of bytes actually read (same as length unless we got all body bytes). + If body is not writable + bytes is null. + offset is out of range. + + + + Clear everything in the request + + + + + Gets or sets a value indicating whether this is secure. + + + + + Path and query (will be merged with the host header) and put in Uri + + + + + + Gets whether the body is complete. + + + + + Gets kind of types accepted by the client. + + + + + Gets or sets body stream. + + + + + Gets or sets kind of connection used for the session. + + + + + Gets or sets number of bytes in the body. + + + + + Gets headers sent by the client. + + + + + Gets or sets version of HTTP protocol that's used. + + + Probably or . + + + + + + Gets or sets requested method. + + + + Will always be in upper case. + + + + + + Gets variables sent in the query string + + + + + Gets or sets requested URI. + + + + + Uri absolute path splitted into parts. + + + // uri is: http://gauffin.com/code/tiny/ + Console.WriteLine(request.UriParts[0]); // result: code + Console.WriteLine(request.UriParts[1]); // result: tiny + + + If you're using controllers than the first part is controller name, + the second part is method name and the third part is Id property. + + + + + + Gets parameter from or . + + + + + Gets form parameters. + + + + + Gets whether the request was made by Ajax (Asynchronous JavaScript) + + + + + Gets cookies that was sent with the request. + + + + + Add a component instance + + Interface type + Instance to add + + + + Get a component. + + Interface type + Component if registered, otherwise null. + + Component will get created if needed. + + + + If instance cannot be created. + + + + Checks if the specified component interface have been added. + + + true if found; otherwise false. + + + + Add a component. + + Type being requested. + Type being created. + Type have already been mapped. + + + + Class to make dynamic binding of redirects. Instead of having to specify a number of similar redirect rules + a regular expression can be used to identify redirect URLs and their targets. + + + [a-z0-9]+)", "/users/${target}?find=true", RegexOptions.IgnoreCase) + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + + [a-zA-Z0-9]+)", "/user/${first}")); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + Regular expression options to use, can be null + + [a-zA-Z0-9]+)", "/user/{first}", RegexOptions.IgnoreCase)); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + + + + Initializes a new instance of the class. + + Expression to match URL + Expression to generate URL + Regular expression options to apply + true if request should be redirected, false if the request URI should be replaced. + + [a-zA-Z0-9]+)", "/user/${first}", RegexOptions.None)); + Result of ie. /employee1 will then be /user/employee1 + ]]> + + Argument is null. + + + + + Process the incoming request. + + incoming HTTP request + outgoing HTTP response + true if response should be sent to the browser directly (no other rules or modules will be processed). + + returning true means that no modules will get the request. Returning true is typically being done + for redirects. + + If request or response is null + + + + Container to bind resource names to assemblies + + + + + Instantiates an instance of + + The dot seperated uri the resource maps to + The full resource name + The assembly the resource exists in + + + + Retrieves a stream to the resource + + Null if the resource couldn't be located somehow + + + + Retrieves the assembly the resource resides in + + + + + Retrieves the full name/path of the assembly + + + + + Retrieves the extension of the resource + + + + Returns the Uri without extension + + + Retrieves the full path name to the resource file + + + + The "basic" authentication scheme is based on the model that the + client must authenticate itself with a user-ID and a password for + each realm. The realm value should be considered an opaque string + which can only be compared for equality with other realms on that + server. The server will service the request only if it can validate + the user-ID and password for the protection space of the Request-URI. + There are no optional authentication parameters. + + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + Delegate used to determine if authentication is required (may be null). + + + + Initializes a new instance of the class. + + Delegate used to provide information used during authentication. + + + + Create a response that can be sent in the WWW-Authenticate header. + + Realm that the user should authenticate in + Not used in basic auth + A correct auth request. + + + + An authentication response have been received from the web browser. + Check if it's correct + + Contents from the Authorization header + Realm that should be authenticated + GET/POST/PUT/DELETE etc. + Not used in basic auth + Authentication object that is stored for the request. A user class or something like that. + if authenticationHeader is invalid + If any of the paramters is empty or null. + + + + name used in http request. + + + + + Current state in the parsing. + + + + + Should parse the request line + + + + + Searching for a complete header name + + + + + Searching for colon after header name (ignoring white spaces) + + + + + Searching for start of header value (ignoring white spaces) + + + + + Searching for a complete header value (can span over multiple lines, as long as they are prefixed with one/more whitespaces) + + + + + Adding bytes to body + + + + + Will contain helper functions for javascript. + + + + + Requests a url through ajax + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + optional options in format "key, value, key, value", used in JS request object. All keys should end with colon. + a link tag + onclick attribute is used by this method. + + + // plain text + JSHelper.AjaxRequest("'/user/show/1'"); + + // ajax request using this.href + string link = "<a href=\"/user/call/1\" onclick=\"" + JSHelper.AjaxRequest("this.href") + "/<call user</a>"; + + + + + + Ajax requests that updates an element with + the fetched content + + url to fetch. Url is NOT enclosed in quotes by the implementation. You need to do that yourself. + element to update + options in format "key, value, key, value". All keys should end with colon. + A link tag. + + + JSHelper.AjaxUpdater("'/user/show/1'", "user", "onsuccess:", "alert('hello');", "asynchronous:", "true"); + + + + + + Opens contents in a dialog window. + + url to contents of dialog + link title + name, value, name, value, all parameter names should end with colon. + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Lists content type mime types. + + + + + text/plain + + + + + text/haml + + + + + content type for javascript documents = application/javascript + + + + RFC 4329 states that text/javascript have been superseeded by + application/javascript. You might still want to check browser versions + since older ones do not support application/javascript. + + Browser support: http://krijnhoetmer.nl/stuff/javascript/mime-types/ + + + + + text/xml + + + + + A list of content types + + + + + + + Semicolon separated content types. + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Searches for the specified type + + Can also be a part of a type (searching for "xml" would return true for "application/xml"). + true if type was found. + + + + Get this first content type. + + + + + Fetch a content type + + Part of type ("xml" would return "application/xml") + + All content types are in lower case. + + + + Creates request parsers when needed. + + + + + Creates request parsers when needed. + + + + + Create a new request parser. + + Used when logging should be enabled. + A new request parser. + + + + Create a new request parser. + + Used when logging should be enabled. + A new request parser. + + + + The request requires user authentication. The response MUST include a + WWW-Authenticate header field (section 14.47) containing a challenge + applicable to the requested resource. + + The client MAY repeat the request with a suitable Authorization header + field (section 14.8). If the request already included Authorization + credentials, then the 401 response indicates that authorization has been + refused for those credentials. If the 401 response contains the same challenge + as the prior response, and the user agent has already attempted authentication + at least once, then the user SHOULD be presented the entity that was given in the response, + since that entity might include relevant diagnostic information. + + HTTP access authentication is explained in rfc2617: + http://www.ietf.org/rfc/rfc2617.txt + + (description is taken from + http://www.submissionchamber.com/help-guides/error-codes.php#sec10.4.2) + + + + + Create a new unauhtorized exception. + + + + + + Create a new unauhtorized exception. + + reason to why the request was unauthorized. + inner exception + + + + Create a new unauhtorized exception. + + reason to why the request was unauthorized. + + + + The purpose of this module is to serve files. + + + + + Initializes a new instance of the class. + + Uri to serve, for instance "/files/" + Path on hard drive where we should start looking for files + If true a Last-Modifed header will be sent upon requests urging web browser to cache files + + + + Initializes a new instance of the class. + + Uri to serve, for instance "/files/" + Path on hard drive where we should start looking for files + + + + Mimtypes that this class can handle per default + + + + + Determines if the request should be handled by this module. + Invoked by the + + + true if this module should handle it. + + + Illegal path + + + + check if source contains any of the chars. + + + + + + + + Method that process the Uri. + + Information sent by the browser about the request + Information that is being sent back to the client. + Session used to + Failed to find file extension + File type is forbidden. + + + + return a file extension from an absolute Uri path (or plain filename) + + + + + + + List with all mime-type that are allowed. + + All other mime types will result in a Forbidden http status code. + + + + characters that may not exist in a path. + + + fileMod.ForbiddenChars = new string[]{ "\\", "..", ":" }; + + + + + Webhelper provides helpers for common tasks in HTML. + + + + + Used to let the website use different javascript libraries. + Default is + + + + + Creates a link that invokes through ajax. + + url to fetch + link title + + optional options in format "key, value, key, value". + Javascript options starts with ':'. + + a link tag + + WebHelper.AjaxRequest("/users/add/", "Add user", "method:", "post", "onclick", "validate('this');"); + + + + + Builds a link that updates an element with the fetched ajax content. + + Url to fetch content from + link title + html element to update with the results of the ajax request. + optional options in format "key, value, key, value" + A link tag. + + + + A link that pop ups a Dialog (overlay div) + + url to contents of dialog + link title + name/value of html attributes. + A "a"-tag that popups a dialog when clicked + + WebHelper.DialogLink("/user/show/1", "show user", "onmouseover", "alert('booh!');"); + + + + + Create/Open a dialog box using ajax + + + + + + + + + Close a javascript dialog window/div. + + javascript for closing a dialog. + + + + + Create a <form> tag. + + name of form + action to invoke on submit + form should be posted as ajax + html code + + WebHelper.FormStart("frmLogin", "/user/login", Request.IsAjax); + + + + + Create a link tag. + + url to go to + link title (text that is displayed) + html attributes, name, value, name, value + html code + + WebHelper.Link("/user/show/1", "Show user", "id", "showUser", "onclick", "return confirm('Are you shure?');"); + + + + + Build a link + + url to go to. + title of link (displayed text) + extra html attributes. + a complete link + + + + Build a link + + url to go to. + title of link (displayed text) + extra html attributes. + a complete link + more options + + + + Obsolete + + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + + + + Obsolete + + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + Obsolete + + + + Render errors into a UL with class "errors" + + class used by UL-tag. + items to list + an unordered html list. + + + + Render errors into a UL with class "errors" + + class used by UL-tag. + items to list + an unordered html list. + + + + Render errors into a UL with class "errors" + + + + + + + Generates a list with html attributes. + + StringBuilder that the options should be added to. + attributes set by user. + attributes set by any of the helper classes. + + + + Generates a list with html attributes. + + StringBuilder that the options should be added to. + + + + + Delegate used by to populate select options. + + current object (for instance a User). + Text that should be displayed in the value part of a <optiongt;-tag. + Text shown in the select list. + + // Class that is going to be used in a SELECT-tag. + public class User + { + private readonly string _realName; + private readonly int _id; + public User(int id, string realName) + { + _id = id; + _realName = realName; + } + public string RealName + { + get { return _realName; } + } + + public int Id + { + get { return _id; } + } + } + + // Using an inline delegate to generate the select list + public void UserInlineDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, delegate(object o, out object id, out object value) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + }, 2, true); + } + + // Using an method as delegate to generate the select list. + public void UseExternalDelegate() + { + List<User> items = new List<User>(); + items.Add(new User(1, "adam")); + items.Add(new User(2, "bertial")); + items.Add(new User(3, "david")); + string htmlSelect = Select("users", "users", items, UserOptions, 1, true); + } + + // delegate returning id and title + public static void UserOptions(object o, out object id, out object title) + { + User user = (User)o; + id = user.Id; + value = user.RealName; + } /// + + + diff --git a/bin/ICSharpCode.SharpZipLib.dll b/bin/ICSharpCode.SharpZipLib.dll new file mode 100644 index 0000000000..6c6a5d4ad8 Binary files /dev/null and b/bin/ICSharpCode.SharpZipLib.dll differ diff --git a/bin/Ionic.Zip.dll b/bin/Ionic.Zip.dll new file mode 100755 index 0000000000..e37f1bdea5 Binary files /dev/null and b/bin/Ionic.Zip.dll differ diff --git a/bin/LaunchSLClient.ini b/bin/LaunchSLClient.ini new file mode 100644 index 0000000000..9250a7b9c9 --- /dev/null +++ b/bin/LaunchSLClient.ini @@ -0,0 +1,15 @@ +[DeepGrid] +loginURI = http://user.deepgrid.com:8002/ +URL = www.deepgrid.com + +[OSGrid] +loginURI = http://www.osgrid.org:8002/ +URL = www.osgrid.org + +[OpenlifeGrid] +loginURI = http://logingrid.net:8002/ +URL = www.openlifegrid.com + +[Linden Lab] +loginURI = +URL = www.secondlife.com diff --git a/bin/Library/.keep b/bin/Library/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/LukeSkywalker.IPNetwork.dll b/bin/LukeSkywalker.IPNetwork.dll new file mode 100755 index 0000000000..25bcc2f5f3 Binary files /dev/null and b/bin/LukeSkywalker.IPNetwork.dll differ diff --git a/bin/Mono.Addins.CecilReflector.dll b/bin/Mono.Addins.CecilReflector.dll new file mode 100755 index 0000000000..c01c8bf9b1 Binary files /dev/null and b/bin/Mono.Addins.CecilReflector.dll differ diff --git a/bin/Mono.Addins.Setup.dll b/bin/Mono.Addins.Setup.dll new file mode 100755 index 0000000000..b52eb62c74 Binary files /dev/null and b/bin/Mono.Addins.Setup.dll differ diff --git a/bin/Mono.Addins.Setup.xml b/bin/Mono.Addins.Setup.xml new file mode 100644 index 0000000000..f5ce70a0b0 --- /dev/null +++ b/bin/Mono.Addins.Setup.xml @@ -0,0 +1,1140 @@ + + + + Mono.Addins.Setup + + + + + An IAddinInstaller implementation which interacts with the user through the console + + + + + Initializes a new instance of the class. + + + + + Gets or sets whether the installer can ask questions to the user + + + + + Log level (0:normal, 1+:verbose); + + + + + An add-in package + + + + + Creates a package object for an add-in available in an on-line repository + + + An add-in reference + + + The package + + + + + Creates a package object for a local package file + + + Package file path + + + The package + + + + + Name of the package + + + + + Returns true if the package will be installed in the shared directory, + false if it will be installed in the user directory. + + + + + A reference to an add-in available in an on-line repository + + + + + Begins downloading a support file + + + Result of the asynchronous operation, to be used when calling EndDownloadSupportFile to + get the download result. + + + Name of the file. + + + Callback to be called when the download operation ends. + + + Custom state object provided by the caller. + + + This method can be used to get the contents of a support file of an add-in. + A support file is a file referenced in the custom properties of an add-in. + + + + + Gets the result of the asynchronous download of a file + + + The downloaded file. + + + The async result object returned by BeginDownloadSupportFile. + + + + + Add-in information + + + + + Url to the add-in package + + + + + The URL of the repository + + + + + Name of the repository + + + + + An installation exception + + + + + Initializes the exception + + + Error message + + + + + Initializes the exception + + + Error message + + + Inner exception + + + + + A command line add-in manager. + + + This class can be used to provide an add-in management command line tool to applications. + + + + + Creates a new instance + + + Add-in registry to manage. + + + + + Runs the command line tool. + + + Array that contains the command line arguments + + + Index of the arguments array that has the first argument for the management tool + + + 0 if it succeeds. != 0 otherwise + + + + + Runs the command line tool. + + + Command line arguments + + + 0 if it succeeds. != 0 otherwise + + + + + Adds a custom command to the add-in manager + + + Category under which the command has to be shown in the help text + + + Name of the command + + + Short name of the command (it's an alias of the normal name) + + + Formal description of the arguments that the command accepts. For example: "[addin-id|addin-file] [--xml] [--all] [--full] [--namespace <namespace>]" + + + Short description of the command + + + Long description of the command + + + Delegate to be invoked to run the command + + + + + Prints help about the add-in management tool, or about a specific command + + + Optional command name and arguments + + + + + Display name of the host application + + + + + Default add-in namespace of the application (optional). If set, only add-ins that belong to that namespace + will be shown in add-in lists. + + + + + Enables or disables verbose output + + + + + Sets or gets the verbose output level (0: normal output, 1:verbose, 2+:extra verbose) + + + + + A command handler + + + + + A collection of packages + + + + + Initializes a new instance of the class. + + + + + Copy constructor + + + Collection where to copy from + + + + + Adds a package + + + A package + + + + + Checks if a package is present in the collection + + + The package + + + True if the package is preent + + + + + Adds a list of packages to the collection + + + The list of packages to add + + + + + Gets a package + + + Package index + + + + + Basic add-in information + + + + + Compares the versions of two add-ins + + + Another add-in + + + Result of comparison + + + + + Full identifier of the add-in + + + + + Display name of the add-in + + + + + Namespace of the add-in + + + + + Version of the add-in + + + + + Version with which this add-in is compatible + + + + + Add-in author + + + + + Add-in copyright + + + + + Web page URL with more information about the add-in + + + + + Description of the add-in + + + + + Category of the add-in + + + + + Dependencies of the add-in + + + + + Optional dependencies of the add-in + + + + + Custom properties specified in the add-in header + + + + + A registry of on-line repositories + + + This class can be used to manage on-line repository subscriptions. + + + + + Subscribes to an on-line repository + + + Progress monitor where to show progress status and log + + + URL of the repository + + + A repository reference + + + The repository index is not downloaded by default. It can be downloaded + by calling UpdateRepository. + + + + + Subscribes to an on-line repository + + + Progress monitor where to show progress status and log + + + URL of the repository + + + When set to True, the repository index will be downloaded. + + + A repository reference + + + + + Removes an on-line repository subscription. + + + URL of the repository. + + + + + Enables or disables a repository + + + URL of the repository + + + 'true' if the repository has to be enabled. + + + Disabled repositories are ignored when calling UpdateAllRepositories. + + + + + Checks if a repository is already subscribed. + + + URL of the repository + + + True if the repository is already subscribed. + + + + + Gets a list of subscribed repositories + + + A list of repositories. + + + + + Updates the add-in index of all subscribed repositories. + + + Progress monitor where to show progress status and log + + + + + Updates the add-in index of the provided repository + + + Progress monitor where to show progress status and log + + + URL of the repository + + + + + Gets a list of available add-in updates. + + + A list of add-in references. + + + The list is generated by looking at the add-ins currently installed and checking if there is any + add-in with a newer version number in any of the subscribed repositories. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available add-in updates. + + + Search flags + + + A list of add-in references. + + + The list is generated by looking at the add-ins currently installed and checking if there is any + add-in with a newer version number in any of the subscribed repositories. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available add-in updates in a specific repository. + + + The repository URL + + + A list of add-in references. + + + The list is generated by looking at the add-ins currently installed and checking if there is any + add-in with a newer version number in the provided repository. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available updates for an add-in. + + + Identifier of the add-in. + + + List of updates for the specified add-in. + + + The list is generated by checking if there is any + add-in with a newer version number in any of the subscribed repositories. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available updates for an add-in. + + + Identifier of the add-in. + + + Search flags. + + + List of updates for the specified add-in. + + + The list is generated by checking if there is any + add-in with a newer version number in any of the subscribed repositories. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available updates for an add-in in a specific repository + + + Identifier of the add-in. + + + Identifier of the add-in. + + + List of updates for the specified add-in. + + + The list is generated by checking if there is any + add-in with a newer version number in the provided repository. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of available updates for an add-in in a specific repository + + + Identifier of the add-in. + + + Identifier of the add-in. + + + Search flags. + + + List of updates for the specified add-in. + + + The list is generated by checking if there is any + add-in with a newer version number in the provided repository. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of all available add-ins + + + A list of add-ins + + + This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of all available add-ins + + + The available addins. + + + Search flags. + + + This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of all available add-ins in a repository + + + A repository URL + + + A list of add-ins + + + This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Gets a list of all available add-ins in a repository + + + A repository URL + + + Search flags. + + + A list of add-ins + + + This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Checks if an add-in is available to be installed + + + Identifier of the add-in + + + Version of the add-in (optional, it can be null) + + + A list of add-ins + + + List of references to add-ins available in on-line repositories. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Checks if an add-in is available to be installed from a repository + + + A repository URL + + + Identifier of the add-in + + + Version of the add-in (optional, it can be null) + + + A list of add-ins + + + List of references to add-ins available in the repository. This method uses cached + information from on-line repositories. Make sure you call UpdateRepository or UpdateAllRepositories + before using this method to ensure that the latest information is available. + + + + + Repository search flags. + + + + + No special search options + + + + + Only the latest version of every add-in is included in the search + + + + + An on-line add-in repository + + + + + Path to the cached add-in repository file + + + + + Url of the repository + + + + + Do not use. Use Title instead. + + + + + Title of the repository + + + + + Last change timestamp + + + + + Gets a value indicating whether this is enabled. + + + true if enabled; otherwise, false. + + + + + Helper for making web requests with support for authenticated proxies. + + + + + Sets a custom request handler that can handle requests for authenticated proxy servers. + + The custom request handler. + + + + Gets the web response, using the request handler to handle proxy authentication + if necessary. + + The response. + Callback for creating the request. + Callback for preparing the request, e.g. writing the request stream. + Cancellation token. + + Keeps sending requests until a response code that doesn't require authentication happens or if the request + requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted). + + + + + Gets the web response, using the request handler to handle proxy authentication + if necessary. + + The response. + Callback for creating the request. + Callback for preparing the request, e.g. writing the request stream. + Cancellation token. + + Keeps sending requests until a response code that doesn't require authentication happens or if the request + requires authentication and the user has stopped trying to enter them (i.e. they hit cancel when they are prompted). + + + + + Determines whether an error code is likely to have been caused by internet reachability problems. + + + + + Provides tools for managing add-ins + + + This class can be used to manage the add-ins of an application. It allows installing and uninstalling + add-ins, taking into account add-in dependencies. It provides methods for installing add-ins from on-line + repositories and tools for generating those repositories. + + + + + Initializes a new instance + + + If the add-in manager is initialized (AddinManager.Initialize has been called), then this instance + will manage the add-in registry of the initialized engine. + + + + + Initializes a new instance + + + Add-in registry to manage + + + + + Resolves add-in dependencies. + + + Progress monitor where to show progress status + + + List of add-ins to check + + + Packages that need to be installed. + + + Packages that need to be uninstalled. + + + Add-in dependencies that could not be resolved. + + + True if all dependencies could be resolved. + + + This method can be used to get a list of all packages that have to be installed in order to install + an add-in or set of add-ins. The list of packages to install will include the package that provides the + add-in, and all packages that provide the add-in dependencies. In some cases, packages may need to + be installed (for example, when an installed add-in needs to be upgraded). + + + + + Resolves add-in dependencies. + + + Progress monitor where to show progress status + + + Packages that need to be installed. + + + Packages that need to be uninstalled. + + + Add-in dependencies that could not be resolved. + + + True if all dependencies could be resolved. + + + This method can be used to get a list of all packages that have to be installed in order to satisfy + the dependencies of a package or set of packages. The 'packages' argument must have the list of packages + to be resolved. When resolving dependencies, if there is any additional package that needs to be installed, + it will be added to the same 'packages' collection. In some cases, packages may need to + be installed (for example, when an installed add-in needs to be upgraded). Those packages will be added + to the 'toUninstall' collection. Packages that could not be resolved are added to the 'unresolved' + collection. + + + + + Installs add-in packages + + + Progress monitor where to show progress status + + + Paths to the packages to install + + + True if the installation succeeded + + + + + Installs add-in packages from on-line repositories + + + Progress monitor where to show progress status + + + References to the add-ins to be installed + + + True if the installation succeeded + + + + + Installs add-in packages + + + Progress monitor where to show progress status + + + Packages to install + + + True if the installation succeeded + + + + + Uninstalls an add-in. + + + Progress monitor where to show progress status + + + Full identifier of the add-in to uninstall. + + + + + Uninstalls a set of add-ins + + + Progress monitor where to show progress status + + + Full identifiers of the add-ins to uninstall. + + + + + Gets information about an add-in + + + The add-in + + + Add-in header data + + + + + Gets a list of add-ins which depend on an add-in + + + Full identifier of an add-in. + + + When set to True, dependencies will be gathered recursivelly + + + List of dependent add-ins. + + + This methods returns a list of add-ins which have the add-in identified by 'id' as a direct + (or indirect if recursive=True) dependency. + + + + + Packages an add-in + + + Progress monitor where to show progress status + + + Directory where to generate the package + + + Paths to the add-ins to be packaged. Paths can be either the main assembly of an add-in, or an add-in + manifest (.addin or .addin.xml). + + + This method can be used to create a package for an add-in, which can then be pushed to an on-line + repository. The package will include the main assembly or manifest of the add-in and any external + file declared in the add-in metadata. + + + + + Generates an on-line repository + + + Progress monitor where to show progress status + + + Path to the directory that contains the add-ins and that is going to be published + + + This method generates the index files required to publish a directory as an online repository + of add-ins. + + + + + Gets a reference to an extensible application + + + Name of the application + + + The Application object. Null if not found. + + + + + Gets a reference to an extensible application + + + Name of the application + + + Custom paths where to look for the application. + + + The Application object. Null if not found. + + + + + Gets a lis of all known extensible applications + + + A list of applications. + + + + + Gets a lis of all known extensible applications + + + Custom paths where to look for applications. + + + A list of applications. + + + + + The add-in registry being managed + + + + + Default add-in namespace of the application (optional). If set, only add-ins that belong to that namespace + will be shown in add-in lists. + + + + + Directory where to install add-ins. If not specified, the 'addins' subdirectory of the + registry location is used. + + + + + Returns a RepositoryRegistry which can be used to manage on-line repository references + + + + + A registered extensible application + + + + + Add-in registry of the application + + + + + Description of the application + + + + + Name of the application + + + + + Path to the add-in registry + + + + + Path to the directory that contains the main executable assembly of the application + + + + + Command to be used to execute the application in add-in development mode. + + + + + Path to the default add-ins directory for the aplpication + + + + + Path to the add-in cache for the application + + + + diff --git a/bin/Mono.Addins.dll b/bin/Mono.Addins.dll new file mode 100755 index 0000000000..1a8c3bea48 Binary files /dev/null and b/bin/Mono.Addins.dll differ diff --git a/bin/Mono.Addins.dll.config b/bin/Mono.Addins.dll.config new file mode 100644 index 0000000000..39e8a66b28 --- /dev/null +++ b/bin/Mono.Addins.dll.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/bin/Mono.Addins.xml b/bin/Mono.Addins.xml new file mode 100644 index 0000000000..b9bdd4cf1c --- /dev/null +++ b/bin/Mono.Addins.xml @@ -0,0 +1,6120 @@ + + + + Mono.Addins + + + + + An add-in localizer. + + + Add-in localizers which want to provide support for localization of plural forms + can additionally implement . + + + + + Gets a localized message. + + + The localized message. + + + The message identifier. + + + + + A collection of extension nodes + + + + + Base class for add-in description collections. + + + + + Base class for add-in description collections. + + + + + Initializes a new instance of the class. + + + + + Add an object. + + + The object. + + + + + Adds a collection of objects. + + + The objects to add. + + + + + Insert an object. + + + Insertion index. + + + The object. + + + + + Removes an object. + + + Object to remove. + + + + + Checks if an object is present in the collection. + + + Objecect to check. + + + + + A collection of NodeElement objects + + + + + Gets the at the specified index + + + Index + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Gets the with the specified identifier. + + + Identifier. + + + + + Declares an extension point. + + + + + Initializes a new instance + + + + + Initializes a new instance + + + Extension path that identifies the extension point + + + + + Initializes a new instance + + + Extension path that identifies the extension point + + + Type of the extension node to be created for extensions + + + + + Initializes a new instance + + + Extension path that identifies the extension point + + + Element name to be used when defining an extension in an XML manifest. + + + Type of the extension node to be created for extensions + + + + + Extension path that identifies the extension point + + + + + Long description of the extension point. + + + + + Type of the extension node to be created for extensions + + + + + Expected extension object type (when nodes are of type TypeExtensionNode) + + + + + Element name to be used when defining an extension in an XML manifest. The default name is "Type". + + + + + Display name of the extension point. + + + + + Type of the custom attribute to be used to specify metadata for the extension point + + + + + Addin URL attribute. + + + + + Initializes the attribute + + + Url of the add-in + + + + + Url of the add-in + + + + + Declares allowed children of an extension node type. + + + This attribute allows declaring the type of children that an extension node can have. + + + + + Initializes a new instance + + + Name of the allowed child extension node. + + + + + Initializes a new instance + + + Type of the allowed child extension node. + + + + + Initializes a new instance + + + Type of the allowed child extension node. + + + Name of the allowed child extension node. + + + + + Name of the allowed child extension node. + + + + + Type of the allowed child extension node. + + + + + Provides access to add-in and extension model management operations. + + + + + Initializes the add-in engine. + + + The add-in engine needs to be initialized before doing any add-in operation. + When initialized with this method, it will look for add-ins in the global add-in registry. + + + + + Initializes the add-in engine. + + + Location of the add-in registry. + + + The add-in engine needs to be initialized before doing any add-in operation. + Configuration information about the add-in registry will be stored in the + provided location. The add-in engine will look for add-ins in an 'addins' + subdirectory of the provided directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes the add-in engine. + + + Location of the add-in registry. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + The add-in engine needs to be initialized before doing any add-in operation. + Configuration information about the add-in registry will be stored in the + provided location. The add-in engine will look for add-ins in the provided + 'addinsDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes the add-in engine. + + + Location of the add-in registry. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + Location of the add-in database. If the path is relative, it is considered to be relative + to the configDir directory. + + + The add-in engine needs to be initialized before doing any add-in operation. + Configuration information about the add-in registry will be stored in the + provided location. The add-in engine will look for add-ins in the provided + 'addinsDir' directory. Cached information about add-ins will be stored in + the 'databaseDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Finalizes an add-in engine. + + + + + Sets the default localizer to be used for this add-in engine + + + The add-in localizer + + + + + Checks if the provided add-ins are installed, and requests the installation of those + which aren't. + + + Message to show to the user when new add-ins have to be installed. + + + List of IDs of the add-ins to be checked. + + + This method checks if the specified add-ins are installed. + If some of the add-ins are not installed, it will use + the installer assigned to the DefaultAddinInstaller property + to install them. If the installation fails, or if DefaultAddinInstaller + is not set, an exception will be thrown. + + + + + Checks if an add-in has been loaded. + + + Full identifier of the add-in. + + + True if the add-in is loaded. + + + + + Forces the loading of an add-in. + + + Status monitor to keep track of the loading process. + + + Full identifier of the add-in to load. + + + This method loads all assemblies that belong to an add-in in memory. + All add-ins on which the specified add-in depends will also be loaded. + Notice that in general add-ins don't need to be explicitely loaded using + this method, since the add-in engine will load them on demand. + + + + + Creates a new extension context. + + + The new extension context. + + + Extension contexts can be used to query the extension model using particular condition values. + + + + + Returns the extension node in a path + + + Location of the node. + + + The node, or null if not found. + + + + + Returns the extension node in a path + + + Location of the node. + + + The node, or null if not found. + + + + + Gets extension nodes registered in a path. + + + An extension path.> + + + All nodes registered in the provided path. + + + + + Gets extension nodes registered in a path. + + + An extension path. + + + Expected node type. + + + A list of nodes + + + This method returns all nodes registered under the provided path. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided type. + + + + + Gets extension nodes registered in a path. + + + An extension path. + + + A list of nodes + + + This method returns all nodes registered under the provided path. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + A list of nodes + + + This method returns all extension nodes bound to the provided type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + Expected extension node type + + + A list of nodes + + + This method returns all nodes registered for the provided type. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided node type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + A list of nodes + + + This method returns all nodes registered for the provided type. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the specified node type argument. + + + + + Gets extension objects registered for a type extension point. + + + Type defining the extension point + + + A list of objects + + + + + Gets extension objects registered for a type extension point. + + + A list of objects + + + The type argument of this generic method is the type that defines + the extension point. + + + + + Gets extension objects registered for a type extension point. + + + Type defining the extension point + + + When set to True, it will return instances created in previous calls. + + + A list of extension objects. + + + + + Gets extension objects registered for a type extension point. + + + When set to True, it will return instances created in previous calls. + + + A list of extension objects. + + + The type argument of this generic method is the type that defines + the extension point. + + + + + Gets extension objects registered in a path + + + An extension path. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true) + + + + + Gets extension objects registered in a path. + + + An extension path. + + + Type of the return array elements. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + Type of the return array elements. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true). + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true). + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Register a listener of extension node changes. + + + Path of the node. + + + A handler method. + + + Hosts can call this method to be subscribed to an extension change + event for a specific path. The event will be fired once for every + individual node change. The event arguments include the change type + (Add or Remove) and the extension node added or removed. + + NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + + + + + Unregister a listener of extension node changes. + + + Path of the node. + + + A handler method. + + + This method unregisters a delegate from the node change event of a path. + + + + + Register a listener of extension node changes. + + + Type defining the extension point + + + A handler method. + + + Hosts can call this method to be subscribed to an extension change + event for a specific type extension point. The event will be fired once for every + individual node change. The event arguments include the change type + (Add or Remove) and the extension node added or removed. + + NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + + + + + Unregister a listener of extension node changes. + + + Type defining the extension point + + + A handler method. + + + + + Gets whether the add-in engine has been initialized. + + + + + Gets the default add-in installer + + + The default installer is used by the CheckInstalled method to request + the installation of missing add-ins. + + + + + Gets the default localizer for this add-in engine + + + + + Gets the localizer for the add-in that is invoking this property + + + + + Gets a reference to the RuntimeAddin object for the add-in that is invoking this property + + + + + Gets the default add-in engine + + + + + Gets the add-in registry bound to the default add-in engine + + + + + Extension change event. + + + This event is fired when any extension point in the add-in system changes. + The event args object provides the path of the changed extension, although + it does not provide information about what changed. Hosts subscribing to + this event should get the new list of nodes using a query method such as + AddinManager.GetExtensionNodes() and then update whatever needs to be updated. + + + + + Add-in loading error event. + + + This event is fired when there is an error when loading the extension + of an add-in, or any other kind of error that may happen when querying extension points. + + + + + Add-in loaded event. + + + Fired after loading an add-in in memory. + + + + + Add-in unload event. + + + Fired when an add-in is unloaded from memory. It may happen an add-in is disabled or uninstalled. + + + + + An extension node definition. + + + + + Base class for add-in description definitions. + + + + + Gets the parent object. + + + The parent object. + + + + + Gets the parent add-in description. + + + The parent add-in description. + + + + + An extension node element. + + + A raw representation of an extension node. Contains the basic information + needed to create ExtensionNode instances. + + + + + Gets element attributes. + + + Name of the attribute + + + The value of the attribute + + + + + Name of the node element. + + + + + Gets all attributes defined in the element. + + + + + Gets child nodes of this node + + + + + Initializes a new instance of the class. + + + Node name. + + + + + Gets the type of the node. + + + The node type. + + + This method only works when the add-in description to which the node belongs has been + loaded from an add-in registry. + + + + + Gets the extension path under which this node is registered + + + The parent path. + + + For example, if the id of the node is 'ThisNode', and the node is a child of another node with id 'ParentNode', and + that parent node is defined in an extension with the path '/Core/MainExtension', then the parent path is 'Core/MainExtension/ParentNode'. + + + + + Gets the value of an attribute. + + + The value of the attribute, or an empty string if the attribute is not defined. + + + Name of the attribute. + + + + + Sets the value of an attribute. + + + Name of the attribute + + + The value. + + + + + Removes an attribute. + + + Name of the attribute to remove. + + + + + Gets or sets the name of the node. + + + The name of the node. + + + + + Gets or sets the identifier of the node. + + + The identifier. + + + + + Gets or sets the identifier of the node after which this node has to be inserted + + + The identifier of the reference node + + + + + Gets or sets the identifier of the node before which this node has to be inserted + + + The identifier of the reference node + + + + + Gets a value indicating whether this node is a condition. + + + true if this node is a condition; otherwise, false. + + + + + Gets the attributes of the node. + + + The attributes. + + + + + Gets the child nodes. + + + The child nodes. + + + + + An extension node type definition. + + + + + An extension node set definition. + + + Node sets allow grouping a set of extension node declarations and give an identifier to that group + (the node set). Once a node set is declared, it can be referenced from several extension points + which use the same extension node structure. Extension node sets also allow declaring recursive + extension nodes, that is, extension nodes with a tree structure. + + + + + Copies data from another node set + + + Node set from which to copy + + + + + Initializes a new instance of the class. + + + + + Gets all the allowed node types. + + + The allowed node types. + + + Gets all allowed node types, including those defined in included node sets. + This method only works for descriptions loaded from a registry. + + + + + Gets or sets the identifier of the node set. + + + The identifier. + + + + + Gets the node types allowed in this node set. + + + The node types. + + + + + Gets a list of other node sets included in this node set. + + + The node sets. + + + + + Initializes a new instance of the class. + + + + + Copies data from another node set + + + + + Type that implements the extension node. + + + The full name of the type. + + + + + Element name to be used when defining an extension in an XML manifest. The default name is "Type". + + + The name of the node. + + + + + Type of the object that the extension creates (only valid for TypeNodeExtension). + + + + + Name of the custom attribute that can be used to declare nodes of this type + + + + + Long description of the node type + + + + + Attributes supported by the extension node type. + + + + + An add-in description + + + This class represent an add-in manifest. It has properties for getting + all information, and methods for loading and saving files. + + + + + Adds an extension point. + + + The extension point. + + + Path that identifies the new extension point. + + + + + Saves the add-in description. + + + File name where to save this instance + + + Saves the add-in description to the specified file and sets the FileName property. + + + + + Saves the add-in description. + + + It is thrown if FileName is not set + + + The description is saved to the file specified in the FileName property. + + + + + Generates an XML representation of the add-in description + + + An XML manifest. + + + + + Load an add-in description from a file + + + The file. + + + + + Load an add-in description from a stream + + + The stream + + + The path to be used to resolve relative file paths. + + + + + Load an add-in description from a text reader + + + The text reader + + + The path to be used to resolve relative file paths. + + + + + Verify this instance. + + + This method checks all the definitions in the description and returns a list of errors. + If the returned list is empty, it means that the description is valid. + + + + + Gets or sets the path to the main addin file. + + + The addin file. + + + The add-in file can be either the main assembly of an add-in or an xml manifest. + + + + + Gets the addin identifier. + + + The addin identifier. + + + + + Gets or sets the local identifier. + + + The local identifier. + + + + + Gets or sets the namespace. + + + The namespace. + + + + + Gets or sets the display name of the add-in. + + + The name. + + + + + Gets or sets the version. + + + The version. + + + + + Gets or sets the version of the add-in with which this add-in is backwards compatible. + + + The compat version. + + + + + Gets or sets the author. + + + The author. + + + + + Gets or sets the Url where more information about the add-in can be found. + + + The URL. + + + + + Gets or sets the copyright. + + + The copyright. + + + + + Gets or sets the description of the add-in. + + + The description. + + + + + Gets or sets the category of the add-in. + + + The category. + + + + + Gets the base path for locating external files relative to the add-in. + + + The base path. + + + + + Gets or sets a value indicating whether this instance is an add-in root. + + + true if this instance is an add-in root; otherwise, false. + + + + + Gets or sets a value indicating whether this add-in is enabled by default. + + + true if enabled by default; otherwise, false. + + + + + Gets or sets the add-in flags. + + + The flags. + + + + + Gets a value indicating whether this add-in can be disabled. + + + true if this add-in can be disabled; otherwise, false. + + + + + Gets a value indicating whether this add-in can be uninstalled. + + + true if this instance can be uninstalled; otherwise, false. + + + + + Gets a value indicating whether this add-in is hidden. + + + true if this add-in is hidden; otherwise, false. + + + + + Gets all external files + + + All files. + + + External files are data files and assemblies explicitly referenced in the Runtime section of the add-in manifest. + + + + + Gets all paths to be ignored by the add-in scanner. + + + All paths to be ignored. + + + + + Gets the main module. + + + The main module. + + + + + Gets the optional modules. + + + The optional modules. + + + Optional modules can be used to declare extensions which will be registered only if some specified + add-in dependencies can be satisfied. Dependencies specified in optional modules are 'soft dependencies', + which means that they don't need to be satisfied in order to load the add-in. + + + + + Gets all modules (including the main module and all optional modules) + + + All modules. + + + + + Gets the extension node sets. + + + The extension node sets. + + + + + Gets the extension points. + + + The extension points. + + + + + Gets the condition types. + + + The condition types. + + + + + Gets or sets the add-in localizer. + + + The description of the add-in localizer for this add-in. + + + + + Custom properties specified in the add-in header + + + + + Gets or sets file where this description is stored + + + The file path. + + + + + Addin flags attribute. + + + + + Initializes the attribute + + + Add-in flags + + + + + Add-in flags + + + + + Delegate to be used in add-in engine events + + + + + Provides information about an add-in engine event. + + + + + Initializes a new instance of the class. + + + Add-in identifier. + + + + + Identifier of the add-in that generated the event. + + + + + Attribute of a NodeElement. + + + + + Name of the attribute. + + + + + Value of the attribute. + + + + + Allows finding assemblies in the file system + + + + + Locates an assembly + + + The full path to the assembly, or null if not found + + + Full name of the assembly + + + + + An add-in property. + + + + + Name of the property + + + + + Locale of the property. It is null if the property is not localized. + + + + + Value of the property. + + + + + A localizer factory. + + + + + Creates a localizer for an add-in. + + + The localizer. + + + The add-in for which to create the localizer. + + + Localizer parameters. + + + + + Definition of an add-in dependency. + + + + + Gets the display name of the dependency. + + + The name. + + + + + A collection of node sets. + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Gets the with the specified id. + + + Identifier. + + + + + An add-in engine. + + + This class allows hosting several independent add-in engines in a single application domain. + In general, applications use the AddinManager class to query and manage extensions. This class is static, + so the API is easily accessible. However, some kind applications may need to use several isolated + add-in engines, and in this case the AddinManager class can't be used, because it is bound to a single + add-in engine. Those applications can instead create several instances of the AddinEngine class. Each + add-in engine can be independently initialized with different add-in registries and extension models. + + + + + An extension context. + + + Extension contexts can be used to query the extension tree + using particular condition values. Extension points which + declare the availability of a condition type can only be + queryed using an extension context which provides an + evaluator for that condition. + + + + + Registers a new condition in the extension context. + + + Identifier of the condition. + + + Condition evaluator. + + + The registered condition will be particular to this extension context. + Any event that might be fired as a result of changes in the condition will + only be fired in this context. + + + + + Registers a new condition in the extension context. + + + Identifier of the condition. + + + Type of the condition evaluator. Must be a subclass of Mono.Addins.ConditionType. + + + The registered condition will be particular to this extension context. Any event + that might be fired as a result of changes in the condition will only be fired in this context. + + + + + Returns the extension node in a path + + + Location of the node. + + + The node, or null if not found. + + + + + Returns the extension node in a path + + + Location of the node. + + + The node, or null if not found. + + + + + Gets extension nodes registered in a path. + + + An extension path.> + + + All nodes registered in the provided path. + + + + + Gets extension nodes registered in a path. + + + An extension path. + + + A list of nodes + + + This method returns all nodes registered under the provided path. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + A list of nodes + + + This method returns all extension nodes bound to the provided type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + Expected extension node type + + + A list of nodes + + + This method returns all nodes registered for the provided type. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided node type. + + + + + Gets extension nodes for a type extension point + + + Type defining the extension point + + + A list of nodes + + + This method returns all nodes registered for the provided type. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the specified node type argument. + + + + + Gets extension nodes registered in a path. + + + An extension path. + + + Expected node type. + + + A list of nodes + + + This method returns all nodes registered under the provided path. + It will throw a InvalidOperationException if the type of one of + the registered nodes is not assignable to the provided type. + + + + + Gets extension objects registered for a type extension point. + + + Type defining the extension point + + + A list of objects + + + + + Gets extension objects registered for a type extension point. + + + A list of objects + + + The type argument of this generic method is the type that defines + the extension point. + + + + + Gets extension objects registered for a type extension point. + + + Type defining the extension point + + + When set to True, it will return instances created in previous calls. + + + A list of extension objects. + + + + + Gets extension objects registered for a type extension point. + + + When set to True, it will return instances created in previous calls. + + + A list of extension objects. + + + The type argument of this generic method is the type that defines + the extension point. + + + + + Gets extension objects registered in a path + + + An extension path. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true) + + + + + Gets extension objects registered in a path. + + + An extension path. + + + Type of the return array elements. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node. + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true). + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Gets extension objects registered in a path. + + + An extension path. + + + Type of the return array elements. + + + When set to True, it will return instances created in previous calls. + + + An array of objects registered in the path. + + + This method can only be used if all nodes in the provided extension path + are of type Mono.Addins.TypeExtensionNode. The returned array is composed + by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if + reuseCachedInstance is set to true). + + An InvalidOperationException exception is thrown if one of the found + objects is not a subclass of the provided type. + + + + + Register a listener of extension node changes. + + + Path of the node. + + + A handler method. + + + Hosts can call this method to be subscribed to an extension change + event for a specific path. The event will be fired once for every + individual node change. The event arguments include the change type + (Add or Remove) and the extension node added or removed. + + NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + + + + + Unregister a listener of extension node changes. + + + Path of the node. + + + A handler method. + + + This method unregisters a delegate from the node change event of a path. + + + + + Register a listener of extension node changes. + + + Type defining the extension point + + + A handler method. + + + Hosts can call this method to be subscribed to an extension change + event for a specific type extension point. The event will be fired once for every + individual node change. The event arguments include the change type + (Add or Remove) and the extension node added or removed. + + NOTE: The handler will be called for all nodes existing in the path at the moment of registration. + + + + + Unregister a listener of extension node changes. + + + Type defining the extension point + + + A handler method. + + + + + Extension change event. + + + This event is fired when any extension point in the add-in system changes. + The event args object provides the path of the changed extension, although + it does not provide information about what changed. Hosts subscribing to + this event should get the new list of nodes using a query method such as + AddinManager.GetExtensionNodes() and then update whatever needs to be updated. + + + + + Initializes a new instance of the class. + + + + + Initializes the add-in engine + + + Location of the add-in registry. + + The add-in engine needs to be initialized before doing any add-in operation. + When initialized with this method, it will look for add-in in the add-in registry + located in the specified path. + + + + + Initializes the add-in engine. + + + Location of the add-in registry. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + The add-in engine needs to be initialized before doing any add-in operation. + Configuration information about the add-in registry will be stored in the + provided location. The add-in engine will look for add-ins in the provided + 'addinsDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes the add-in engine. + + + Location of the add-in registry. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + Location of the add-in database. If the path is relative, it is considered to be relative + to the configDir directory. + + + The add-in engine needs to be initialized before doing any add-in operation. + Configuration information about the add-in registry will be stored in the + provided location. The add-in engine will look for add-ins in the provided + 'addinsDir' directory. Cached information about add-ins will be stored in + the 'databaseDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Finalizes the add-in engine. + + + + + Sets the default localizer to be used for this add-in engine + + + The add-in localizer + + + + + Checks if the provided add-ins are installed, and requests the installation of those + which aren't. + + + Message to show to the user when new add-ins have to be installed. + + + List of IDs of the add-ins to be checked. + + + This method checks if the specified add-ins are installed. + If some of the add-ins are not installed, it will use + the installer assigned to the DefaultAddinInstaller property + to install them. If the installation fails, or if DefaultAddinInstaller + is not set, an exception will be thrown. + + + + + Checks if an add-in has been loaded. + + + Full identifier of the add-in. + + + True if the add-in is loaded. + + + + + Forces the loading of an add-in. + + + Status monitor to keep track of the loading process. + + + Full identifier of the add-in to load. + + + This method loads all assemblies that belong to an add-in in memory. + All add-ins on which the specified add-in depends will also be loaded. + Notice that in general add-ins don't need to be explicitely loaded using + this method, since the add-in engine will load them on demand. + + + + + Creates a new extension context. + + + The new extension context. + + + Extension contexts can be used to query the extension model using particular condition values. + + + + + Raised when there is an error while loading an add-in + + + + + Raised when an add-in is loaded + + + + + Raised when an add-in is unloaded + + + + + Gets whether the add-in engine has been initialized. + + + + + Gets the default add-in installer + + + The default installer is used by the CheckInstalled method to request + the installation of missing add-ins. + + + + + Gets the default localizer for this add-in engine + + + + + Gets the localizer for the add-in that is invoking this property + + + + + Gets a reference to the RuntimeAddin object for the add-in that is invoking this property + + + + + Gets the add-in registry bound to this add-in engine + + + + + An assembly reflector + + + This interface can be implemented to provide a custom method for getting information about assemblies. + + + + + Called to initialize the assembly reflector + + + IAssemblyLocator instance which can be used to locate referenced assemblies. + + + + + Gets a list of custom attributes + + + The custom attributes. + + + An assembly, class or class member + + + Type of the attribute to be returned. It will always be one of the attribute types + defined in Mono.Addins. + + + 'true' if inherited attributes must be returned + + + + + Gets a list of custom attributes + + + The attributes. + + + An assembly, class or class member + + + Base type of the attribute to be returned + + + 'true' if inherited attributes must be returned + + + + + Loads an assembly. + + + The loaded assembly + + + Path of the assembly. + + + + + Loads the assembly specified in an assembly reference + + + The assembly + + + An assembly reference + + + + + Gets the names of all resources embedded in an assembly + + + The names of the resources + + + An assembly + + + + + Gets the data stream of a resource + + + The stream. + + + An assembly + + + The name of a resource + + + + + Gets all types defined in an assembly + + + The types + + + An assembly + + + + + Gets all assembly references of an assembly + + + A list of assembly references + + + An assembly + + + + + Looks for a type in an assembly + + + The type. + + + An assembly + + + Name of the type + + + + + Gets a custom attribute + + + The custom attribute. + + + An assembly, class or class member + + + Base type of the attribute to be returned. It will always be one of the attribute types + defined in Mono.Addins. + + + 'true' if inherited attributes must be returned + + + + + Gets the name of a type (not including namespace) + + + The type name. + + + A type + + + + + Gets the full name of a type (including namespace) + + + The full name of the type + + + A type + + + + + Gets the assembly qualified name of a type + + + The assembly qualified type name + + + A type + + + + + Gets a list of all base types (including interfaces) of a type + + + An enumeration of the full name of all base types of the type + + + A type + + + + + Checks if a type is assignable to another type + + + 'true' if the type is assignable + + + Expected base type. + + + A type. + + + + + Gets the fields of a type + + + The fields. + + + A type + + + + + Gets the name of a field. + + + The field name. + + + A field. + + + + + Gets the full name of the type of a field + + + The full type name + + + A field. + + + + + A custom attribute + + + + + Full name of the type of the custom attribute + + + + + Declares an extension point bound to a type + + + + + Initializes a new instance + + + + + Initializes a new instance + + + Path that identifies the extension point + + + + + Path that identifies the extension point + + + + + Description of the extension point. + + + + + Element name to be used when defining an extension in an XML manifest. The default name is "Type". + + + + + Display name of the extension point. + + + + + Type of the extension node to be created for extensions + + + + + Type of the custom attribute to be used to specify metadata for the extension point + + + + + Add-in flags + + + + + No flags + + + + + The add-in can't be uninstalled + + + + + The add-in can't be disabled + + + + + The add-in is not visible to end users + + + + + A collection of extensions + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Base class for extension nodes which create extension objects + + + + + A node of the extension model. + + + An extension node is an element registered by an add-in in an extension point. + A host can get nodes registered in an extension point using methods such as + AddinManager.GetExtensionNodes(string), which returns a collection of ExtensionNode objects. + + ExtensionNode will normally be used as a base class of more complex extension point types. + The most common subclass is Mono.Addins.TypeExtensionNode, which allows registering a class + implemented in an add-in. + + + + + Returns the child objects of a node. + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the + TypeExtensionNode.GetInstance() method for each node. + + + + + Returns the child objects of a node. + + + True if the method can reuse instances created in previous calls. + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true). + + + + + Returns the child objects of a node (with type check). + + + Type of the return array elements. + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the + TypeExtensionNode.GetInstance(Type) method for each node. + + An InvalidOperationException exception is thrown if one of the found child objects is not a + subclass of the provided type. + + + + + Returns the child objects of a node (casting to the specified type) + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the + TypeExtensionNode.GetInstance() method for each node. + + + + + Returns the child objects of a node (with type check). + + + Type of the return array elements. + + + True if the method can reuse instances created in previous calls. + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance(Type) + method for each node (or TypeExtensionNode.GetInstance(Type) if reuseCachedInstance is set to true). + + An InvalidOperationException exception will be thrown if one of the found child objects is not a subclass + of the provided type. + + + + + Returns the child objects of a node (casting to the specified type). + + + True if the method can reuse instances created in previous calls. + + + An array of child objects. + + + This method only works if all children of this node are of type Mono.Addins.TypeExtensionNode. + The returned array is composed by all objects created by calling the TypeExtensionNode.CreateInstance() + method for each node (or TypeExtensionNode.GetInstance() if reuseCachedInstance is set to true). + + + + + Reads the extension node data + + + The element containing the extension data + + + This method can be overriden to provide a custom method for reading extension node data from an element. + The default implementation reads the attributes if the element and assigns the values to the fields + and properties of the extension node that have the corresponding [NodeAttribute] decoration. + + + + + Called when the add-in that defined this extension node is actually loaded in memory. + + + + + Called when the add-in that defined this extension node is being + unloaded from memory. + + + + + Called when the children list of this node has changed. It may be due to add-ins + being loaded/unloaded, or to conditions being changed. + + + + + Called when a child node is added + + + Added node. + + + + + Called when a child node is removed + + + Removed node. + + + + + Identifier of the node. + + + It is not mandatory to specify an 'id' for a node. When none is provided, + the add-in manager will automatically generate an unique id for the node. + The ExtensionNode.HasId property can be used to know if the 'id' has been + specified by the developer or not. + + + + + Location of this node in the extension tree. + + + The node path is composed by the path of the extension point where it is defined, + the identifiers of its parent nodes, and its own identifier. + + + + + Parent node of this node. + + + + + Extension context to which this node belongs + + + + + Specifies whether the extension node has as an Id or not. + + + It is not mandatory to specify an 'id' for a node. When none is provided, + the add-in manager will automatically generate an unique id for the node. + This property will return true if an 'id' was provided for the node, and + false if the id was assigned by the add-in manager. + + + + + The add-in that registered this extension node. + + + This property provides access to the resources and types of the add-in that created this extension node. + + + + + Notifies that a child node of this node has been added or removed. + + + The first time the event is subscribed, the handler will be called for each existing node. + + + + + Child nodes of this extension node. + + + + + Gets the extension object declared by this node + + + Expected object type. An exception will be thrown if the object is not an instance of the specified type. + + + The extension object + + + The extension object is cached and the same instance will be returned at every call. + + + + + Gets the extension object declared by this node + + + The extension object + + + The extension object is cached and the same instance will be returned at every call. + + + + + Creates a new extension object + + + Expected object type. An exception will be thrown if the object is not an instance of the specified type. + + + The extension object + + + + + Creates a new extension object + + + The extension object + + + + + Declares a dependency on an add-in or add-in host + + + + + Initializes the attribute + + + Identifier of the add-in + + + Version of the add-in + + + + + Identifier of the add-in + + + + + Version of the add-in + + + + + An extension node with custom metadata + + + This is the default type for extension nodes bound to a custom extension attribute. + + + + + An extension node with custom metadata provided by an attribute + + + This interface is implemented by ExtensionNode<T> to provide non-generic access to the attribute instance. + + + + + The custom attribute containing the extension metadata + + + + + The custom attribute containing the extension metadata + + + + + Defines an add-in property + + + + + Initializes a new instance of the class. + + + Name of the property + + + Value of the property + + + + + Initializes a new instance of the class. + + + Name of the property + + + Locale of the property. It can be null if the property is not bound to a locale. + + + Value of the property + + + + + Name of the property + + + + + Locale of the property. It can be null if the property is not bound to a locale. + + + + + Value of the property + + + + + Base class for custon extension attributes. + + + Custom extension attributes can be used to declare extensions with custom metadata. + All custom extension attributes must subclass CustomExtensionAttribute. + + + + + Identifier of the node + + + + + Identifier of the node before which this node has to be placed + + + + + Identifier of the node after which this node has to be placed + + + + + Path of the extension point being extended. + + + This property is optional and useful only when there are several extension points which allow + using this custom attribute to define extensions. + + + + + The extension node bound to this attribute + + + + + The add-in that registered this extension node. + + + This property provides access to the resources and types of the add-in that created this extension node. + + + + + A localizer that supports localization of plural forms. + + + This interface can be implemented by add-in localizers which want to provide + support plural forms. + + + + + Gets a localized message which may contain plural forms. + + + The localized message. + + + Message identifier to use when the specified count is 1. + + + Default message identifier to use when the specified count is not 1. + + + The count that determines which plural form to use. + + + + + Converts message identifiers to localized messages. + + + + + Gets a localized message + + + Message identifier + + + The localized message + + + + + Gets a formatted and localized message + + + Message identifier (can contain string format placeholders) + + + Arguments for the string format operation + + + The formatted and localized string + + + + + Gets a formatted and localized message + + + Message identifier (can contain string format placeholders) + + + Arguments for the string format operation + + + The formatted and localized string + + + + + Gets a localized plural form for a message identifier + + + Message identifier for the singular form + + + Default result message for the plural form + + + Value count. Determines wether to use singular or plural form. + + + The localized message + + + + + Gets a localized and formatted plural form for a message identifier + + + Message identifier for the singular form (can contain string format placeholders) + + + Default result message for the plural form (can contain string format placeholders) + + + Value count. Determines whether to use singular or plural form. + + + Arguments for the string format operation + + + The localized message + + + + + Gets a localized and formatted plural form for a message identifier + + + Message identifier for the singular form (can contain string format placeholders) + + + Default result message for the plural form (can contain string format placeholders) + + + Value count. Determines whether to use singular or plural form. + + + Arguments for the string format operation + + + The localized message + + + + + A collection of extension point definitions. + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Gets the with the specified path. + + + Path. + + + + + Declares a Gettext-based localizer for an add-in + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + Name of the catalog which contains the strings. + + + + + Initializes a new instance of the class. + + + Name of the catalog which contains the strings. + + + Relative path to the location of the catalog. This path must be relative to the add-in location. + + + The location path must contain a directory structure like this: + + {language-id}/LC_MESSAGES/{Catalog}.mo + + For example, the catalog for spanish strings would be located at: + + locale/es/LC_MESSAGES/some-addin.mo + + + + + Name of the catalog which contains the strings. + + + + + Relative path to the location of the catalog. This path must be relative to the add-in location. + + + When not specified, the default value of this property is 'locale'. + The location path must contain a directory structure like this: + + {language-id}/LC_MESSAGES/{Catalog}.mo + + For example, the catalog for spanish strings would be located at: + + locale/es/LC_MESSAGES/some-addin.mo + + + + + Declares an author of the add-in + + + + + Initializes the attribute + + + Name of the author + + + + + Author name + + + + + A condition type definition. + + + + + Initializes a new instance of the class. + + + + + Copies data from another condition type definition + + + Condition from which to copy + + + + + Gets or sets the identifier of the condition type + + + The identifier. + + + + + Gets or sets the name of the type that implements the condition + + + The name of the type. + + + + + Gets or sets the description of the condition. + + + The description. + + + + + An extension point definition. + + + + + Initializes a new instance of the class. + + + + + Copies another extension point. + + + Extension point from which to copy. + + + + + Adds an extension node type. + + + The extension node type. + + + Name of the node + + + Name of the type that implements the extension node. + + + This method can be used to register a new allowed node type for the extension point. + + + + + Gets or sets the path that identifies the extension point. + + + The path. + + + + + Gets or sets the display name of the extension point. + + + The name. + + + + + Gets or sets the description of the extension point. + + + The description. + + + + + Gets a list of add-ins that extend this extension point. + + + This value is only available when the add-in description is loaded from an add-in registry. + + + + + A node set which specifies the node types allowed in this extension point. + + + The node set. + + + + + Gets the conditions available in this node set. + + + The conditions. + + + + + A condition evaluator. + + + Add-ins may use conditions to register nodes in an extension point which + are only visible under some contexts. For example, an add-in registering + a custom menu option to the main menu of a sample text editor might want + to make that option visible only for some kind of files. To allow add-ins + to do this kind of check, the host application needs to define a new condition. + + + + + Evaluates the condition. + + + Condition node information. + + + 'true' if the condition is satisfied. + + + + + Notifies that the condition has changed, and that it has to be re-evaluated. + + This method must be called when there is a change in the state that determines + the result of the evaluation. When this method is called, all node conditions + depending on it are reevaluated and the corresponding events for adding or + removing extension nodes are fired. + + + + + + A list of extension nodes. + + + + + Gets an enumerator which enumerates all nodes in the list + + + + + Copies all nodes to an array + + + The target array + + + Initial index where to copy to + + + + + Returns the node in the specified index. + + + The index. + + + + + Returns the node with the specified ID. + + + An id. + + + + + Number of nodes of the collection. + + + + + A list of extension nodes. + + + + + Gets an enumerator which enumerates all nodes in the list + + + + + Copies all nodes to an array + + + The target array + + + Initial index where to copy to + + + + + Returns the node in the specified index. + + + The index. + + + + + Returns the node with the specified ID. + + + An id. + + + + + Number of nodes of the collection. + + + + + Exception thrown when the add-in engine can't find a required add-in dependency + + + + + An extension node which specifies a type. + + + This class is a kind of Mono.Addins.ExtensionNode which can be used to register + types in an extension point. This is a very common case: a host application + defines an interface, and add-ins create classes that implement that interface. + The host will define an extension point which will use TypeExtensionNode as nodetext + type. Add-ins will register the classes they implement in that extension point. + + When the nodes of an extension point are of type TypeExtensionNode it is then + possible to use query methods such as AddinManager.GetExtensionObjects(string), + which will get all nodes in the provided extension path and will create an object + for each node. + + When declaring extension nodes in an add-in manifest, the class names can be + specified using the 'class' or 'type' attribute. If none of those attributes is + provided, the class name will be taken from the 'id' attribute. + + TypeExtensionNode is the default extension type used when no type is provided + in the definition of an extension point. + + + + + Reads the extension node data + + + The element containing the extension data + + + This method can be overriden to provide a custom method for reading extension node data from an element. + The default implementation reads the attributes if the element and assigns the values to the fields + and properties of the extension node that have the corresponding [NodeAttribute] decoration. + + + + + Creates a new extension object + + + The extension object + + + + + Type of the object that this node creates + + + + + Name of the type of the object that this node creates + + The name of the type. + + + + An extension node which specifies a type with custom extension metadata + + + This is the default type for type extension nodes bound to a custom extension attribute. + + + + + The custom attribute containing the extension metadata + + + + + Describes the purpose of an add-in or add-in root + + + + + Initializes a new instance of the class. + + + Description of the add-in + + + + + Initializes a new instance of the class. + + + Description of the add-in + + + Locale of the description (for example, 'en-US', or 'en') + + + + + Description of the add-in + + + + + Locale of the description (for example, 'en-US', or 'en') + + + + + Marks an assembly as being an add-in root. + + + An add-in root is an assemly which can be extended by add-ins. + + + + + Marks an assembly as being an add-in. + + + + + Initializes an add-in marker attribute + + + + + Initializes an add-in marker attribute + + + Identifier of the add-in + + + + + Initializes an add-in marker attribute + + + Identifier of the add-in + + + Version of the add-in + + + + + Identifier of the add-in. + + + + + Version of the add-in. + + + + + Version of the add-in with which this add-in is backwards compatible. + + + + + Namespace of the add-in + + + + + Category of the add-in + + + + + Url to a web page with more information about the add-in + + + + + When set to True, the add-in will be automatically enabled after installing. + It's True by default. + + + + + Add-in flags + + + + + Initializes a new instance + + + + + Initializes a new instance + + + Identifier of the add-in root + + + + + Initializes a new instance + + + Identifier of the add-in root + + + Version of the add-in root + + + + + An extension definition. + + + An Extension is a collection of nodes which have to be registered in an extension point. + The target extension point is specified in the .Path property. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + Path that identifies the extension point being extended + + + + + Gets the object extended by this extension + + + The extended object can be an or + an . + + + This method only works when the add-in description to which the extension belongs has been + loaded from an add-in registry. + + + + + Gets the node types allowed in this extension. + + + The allowed node types. + + + This method only works when the add-in description to which the extension belongs has been + loaded from an add-in registry. + + + + + Initializes a new instance of the class. + + + XML that describes the extension. + + + + + Gets or sets the path that identifies the extension point being extended. + + + The path. + + + + + Gets the extension nodes. + + + The extension nodes. + + + + + Delegate to be used in extension point subscriptions + + + + + Delegate to be used in extension point subscriptions + + + + + Arguments for extension events. + + + + + Creates a new instance. + + + Path of the extension node that has changed. + + + + + Checks if a path has changed. + + + An extension path. + + + 'true' if the path is affected by the extension change event. + + + Checks if the specified path or any of its children paths is affected by the extension change event. + + + + + Path of the extension node that has changed. + + + + + Arguments for extension node events. + + + + + Creates a new instance + + + Type of change. + + + Node that has been added or removed. + + + + + Path of the extension that changed. + + + + + Type of change. + + + + + Node that has been added or removed. + + + + + Extension object that has been added or removed. + + + + + Type of change in an extension change event. + + + + + An extension node has been added. + + + + + An extension node has been removed. + + + + + Sets the display name of an add-in + + + + + Initializes a new instance of the class. + + + Name of the add-in + + + + + Initializes a new instance of the class. + + + Name of the add-in + + + Locale of the name (for example, 'en-US', or 'en') + + + + + Name of the add-in + + + + + Locale of the name (for example, 'en-US', or 'en') + + + + + Type of the content of a string extension node attribute + + + + + Plain text + + + + + A class name + + + + + A resource name + + + + + A file name + + + + + An add-in file system extension. + + + File system extensions can override the behavior of the add-in scanner and provide custom rules for + locating and scanning assemblies. + + + + + Called when the add-in scan is about to start + + + + + Called when the add-in scan has finished + + + + + Checks if a directory exists + + + 'true' if the directory exists + + + Directory path + + + + + Checks if a file exists + + + 'true' if the file exists + + + Path to the file + + + + + Gets the files in a directory + + + The full path of the files in the directory + + + Directory path + + + + + Gets the subdirectories of a directory + + + The subdirectories. + + + The directory + + + + + Gets the last write time of a file + + + The last write time. + + + File path. + + + + + Opens a text file + + + The text file stream + + + File path. + + + + + Opens a file. + + + The file stream. + + + The file path. + + + + + Gets an assembly reflector for a file. + + + The reflector for the file. + + + An assembly locator + + + A file path + + + + + Gets a value indicating whether this needs to be isolated from the main execution process + + + true if requires isolation; otherwise, false. + + + + + Assigns an attribute value to an extension + + + This attribute can be used together with the [Extenion] attribute to specify + a value for an attribute of the extension. + + + + + Initializes a new instance of the class. + + + Name of the attribute + + + Value of the attribute + + + + + Initializes a new instance of the class. + + + Type of the extension for which the attribute value is being set + + + Name of the attribute + + + Value of the attribute + + + + + Initializes a new instance of the class. + + + Path of the extension for which the attribute value is being set + + + Name of the attribute + + + Value of the attribute + + + + + Name of the attribute + + + + + Value of the attribute + + + + + Path of the extension for which the attribute value is being set + + + + + Type of the extension for which the attribute value is being set + + + + + An add-in installation handler + + + + + Installs a set of add-ins + + + Registry where to install + + + Message to show to the user when new add-ins have to be installed. + + + List of IDs of the add-ins to be installed. + + + + + This attribute can be applied to an ExtensionNode subclass to specify the default name and description. + + + This information will be used when an extension point does not define a name or description for a node type. + + + + + Initializes the attribute + + + + + Initializes the attribute + + + Name of the node + + + + + Initializes the attribute + + + Name of the node + + + Description of the node + + + + + Default name of the extension node + + + + + Default description of the extension node type + + + + + Type of a custom attribute which can be used to specify metadata for this extension node type + + + + + A collection of dependency definitions. + + + + + Initializes a new instance of the class. + + + + + Adds a dependency to the collection + + + The dependency to add. + + + + + Remove the specified dependency. + + + Dependency to remove. + + + + + Gets the at the specified index. + + + The idnex. + + + + + Declares a type extension. + + + When applied to a class, specifies that the class is an extension + class to be registered in a matching extension point. + + + + + Initializes a new instance of the ExtensionAttribute class. + + + + + Initializes a new instance + + + Path of the extension point. + + The path is only required if there are several extension points defined for the same type. + + + + Initializes a new instance + + + Type defining the extension point being extended + + + This constructor can be used to explicitly specify the type that defines the extension point + to be extended. By default, Mono.Addins will try to find any extension point defined in any + of the base classes or interfaces. The type parameter can be used when there is more than one + base type providing an extension point. + + + + + Path of the extension point being extended + + + The path is only required if there are several extension points defined for the same type. + + + + + Name of the extension node + + + Extension points may require extensions to use a specific node name. + This is needed when an extension point may contain several different types of nodes. + + + + + Identifier of the extension node. + + + The ExtensionAttribute.InsertAfter and ExtensionAttribute.InsertBefore + properties can be used to specify the relative location of a node. The nodes + referenced in those properties must be defined either in the add-in host + being extended, or in any add-in on which this add-in depends. + + + + + Identifier of the extension node before which this node has to be added in the extension point. + + + The ExtensionAttribute.InsertAfter and ExtensionAttribute.InsertBefore + properties can be used to specify the relative location of a node. The nodes + referenced in those properties must be defined either in the add-in host + being extended, or in any add-in on which this add-in depends. + + + + + Identifier of the extension node after which this node has to be added in the extension point. + + + + + Type defining the extension point being extended + + + This property can be used to explicitly specify the type that defines the extension point + to be extended. By default, Mono.Addins will try to find any extension point defined in any + of the base classes or interfaces. This property can be used when there is more than one + base type providing an extension point. + + + + + Run-time representation of an add-in. + + + + + Returns a string that represents the current RuntimeAddin. + + + A string that represents the current RuntimeAddin. + + + + + Gets a resource string + + + Name of the resource + + + The value of the resource string, or null if the resource can't be found. + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a resource string + + + Name of the resource + + + When set to true, an exception will be thrown if the resource is not found. + + + The value of the resource string + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a resource string + + + Name of the resource + + + When set to true, an exception will be thrown if the resource is not found. + + + Culture of the resource + + + The value of the resource string + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a resource object + + + Name of the resource + + + Value of the resource + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a resource object + + + Name of the resource + + + When set to true, an exception will be thrown if the resource is not found. + + + Value of the resource + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a resource object + + + Name of the resource + + + When set to true, an exception will be thrown if the resource is not found. + + + Culture of the resource + + + Value of the resource + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets a type defined in the add-in + + + Full name of the type + + + A type. + + + The type will be looked up in the assemblies that implement the add-in, + and recursivelly in all add-ins on which it depends. + + This method throws an InvalidOperationException if the type can't be found. + + + + + Gets a type defined in the add-in + + + Full name of the type + + + Indicates whether the method should throw an exception if the type can't be found. + + + A + + + The type will be looked up in the assemblies that implement the add-in, + and recursivelly in all add-ins on which it depends. + + If the type can't be found, this method throw a InvalidOperationException if + 'throwIfNotFound' is 'true', or 'null' otherwise. + + + + + Creates an instance of a type defined in the add-in + + + Name of the type. + + + A new instance of the type + + + The type will be looked up in the assemblies that implement the add-in, + and recursivelly in all add-ins on which it depends. + + This method throws an InvalidOperationException if the type can't be found. + + The specified type must have a default constructor. + + + + + Creates an instance of a type defined in the add-in + + + Name of the type. + + + Indicates whether the method should throw an exception if the type can't be found. + + + A new instance of the type + + + The type will be looked up in the assemblies that implement the add-in, + and recursivelly in all add-ins on which it depends. + + If the type can't be found, this method throw a InvalidOperationException if + 'throwIfNotFound' is 'true', or 'null' otherwise. + + The specified type must have a default constructor. + + + + + Gets the path of an add-in file + + + Relative path of the file + + + Full path of the file + + + This method can be used to get the full path of a data file deployed together with the add-in. + + + + + Gets the path of an add-in file + + + Components of the file path + + + Full path of the file + + + This method can be used to get the full path of a data file deployed together with the add-in. + + + + + Gets the content of a resource + + + Name of the resource + + + Content of the resource, or null if not found + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Gets the content of a resource + + + Name of the resource + + + When set to true, an exception will be thrown if the resource is not found. + + + Content of the resource. + + + The add-in engine will look for resources in the main add-in assembly and in all included add-in assemblies. + + + + + Identifier of the add-in. + + + + + Version of the add-in. + + + + + Path to a directory where add-ins can store private configuration or status data + + + + + Localizer which can be used to localize strings defined in this add-in + + + + + Declares an add-in assembly import + + + An add-in may be composed by several assemblies and data files. + Assemblies must be declared in the main assembly using this attribute, or in the XML manifest. + + It is important to properly declare all files used by an add-in. + For example, when a type from the add-in is required (e.g. an ICommand implementation), + only properly declared assemblies will be checked. + This information is also used by setup tools to know exactly what needs to be packaged when creating + an add-in package, or to know what needs to be deleted when removing an add-in. + + + + + Initializes a new instance + + + Path to the assembly. Must be relative to the assembly declaring this attribute. + + + + + Path to the assembly. Must be relative to the assembly declaring this attribute. + + + + + When set to true (the default), the included assembly will be scanned + looking for extension point declarations. + + + + + Delegate to be used in add-in error subscriptions + + + + + Provides information about an add-in loading error. + + + + + Initializes a new instance of the class. + + + Error message + + + Add-in identifier. + + + Exception that caused the error. + + + + + Exception that caused the error. + + + + + Error message + + + + + An add-in. + + + + + Checks version compatibility. + + + An add-in version. + + + True if the provided version is compatible with this add-in. + + + This method checks the CompatVersion property to know if the provided version is compatible with the version of this add-in. + + + + + Returns a that represents the current . + + + A that represents the current . + + + + + Compares two add-in versions + + + -1 if v1 is greater than v2, 0 if v1 == v2, 1 if v1 less than v2 + + + A version + + + A version + + + + + Returns the identifier of an add-in + + + The full identifier. + + + Namespace of the add-in + + + Name of the add-in + + + Version of the add-in + + + + + Given a full add-in identifier, returns the namespace and name of the add-in (it removes the version number) + + + Add-in identifier. + + + + + Given a full add-in identifier, returns the version the add-in + + + The version. + + + + + Splits a full add-in identifier in name and version + + + Add-in identifier. + + + The resulting name + + + The resulting version + + + + + Full identifier of the add-in, including namespace and version. + + + + + Namespace of the add-in. + + + + + Identifier of the add-in (without namespace) + + + + + Version of the add-in + + + + + Display name of the add-in + + + + + Custom properties specified in the add-in header + + + + + Gets or sets the enabled status of the add-in. + + + This property can be used to enable or disable an add-in. + The enabled status of an add-in is stored in the add-in registry, + so when an add-in is disabled, it will be disabled for all applications + sharing the same registry. + When an add-in is enabled or disabled, the extension points currently loaded + in memory will be properly updated to include or exclude extensions from the add-in. + + + + + Returns 'true' if the add-in is installed in the user's personal folder + + + + + Path to the add-in file (it can be an assembly or a standalone XML manifest) + + + + + Description of the add-in + + + + + A collection of condition types + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + Index. + + + The condition. + + + + + A collection of module descriptions + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + A collection of node attributes + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Definition of a dependency of an add-in on an assembly. + + + + + Initializes a new instance of the class. + + + + + Gets or sets the full name of the assembly + + + The full name of the assembly. + + + + + Gets or sets the name of the package that provides the assembly. + + + The name of the package that provides the assembly. + + + + + Display name of the dependency + + + The name. + + + + + Declares an optional add-in module + + + + + Initializes the instance. + + + Relative path to the assembly that implements the optional module + + + + + Relative path to the assembly that implements the optional module + + + + + An add-in registry. + + + An add-in registry is a data structure used by the add-in engine to locate add-ins to load. + + A registry can be configured to look for add-ins in several directories. However, add-ins + copied to those directories won't be detected until an explicit add-in scan is requested. + The registry can be updated by an application by calling Registry.Update(), or by a user by + running the 'mautil' add-in setup tool. + + The registry has information about the location of every add-in and a timestamp of the last + check, so the Update method will only scan new or modified add-ins. An application can + add a call to Registry.Update() in the Main method to detect all new add-ins every time the + app is started. + + Every add-in added to the registry is parsed and validated, and if there is any error it + will be rejected. The registry is also in charge of scanning the add-in assemblies and look + for extensions and other information declared using custom attributes. That information is + merged with the manifest information (if there is one) to create a complete add-in + description ready to be used at run-time. + + Mono.Addins allows sharing an add-in registry among several applications. In this context, + all applications sharing the registry share the same extension point model, and it is + possible to implement add-ins which extend several hosts. + + + + + Initializes a new instance. + + + Location of the add-in registry. + + + Creates a new add-in registry located in the provided path. + The add-in registry will look for add-ins in an 'addins' + subdirectory of the provided registryPath. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes a new instance. + + + Location of the add-in registry. + + + Location of the application. + + + Creates a new add-in registry located in the provided path. + The add-in registry will look for add-ins in an 'addins' + subdirectory of the provided registryPath. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes a new instance of the class. + + + Location of the add-in registry. + + + Location of the application. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + Creates a new add-in registry located in the provided path. + Configuration information about the add-in registry will be stored in + 'registryPath'. The add-in registry will look for add-ins in the provided + 'addinsDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Initializes a new instance of the class. + + + Location of the add-in registry. + + + Location of the application. + + + Add-ins directory. If the path is relative, it is considered to be relative + to the configDir directory. + + + Location of the add-in database. If the path is relative, it is considered to be relative + to the configDir directory. + + + Creates a new add-in registry located in the provided path. + Configuration information about the add-in registry will be stored in + 'registryPath'. The add-in registry will look for add-ins in the provided + 'addinsDir' directory. Cached information about add-ins will be stored in + the 'databaseDir' directory. + + When specifying a path, it is possible to use a special folder name as root. + For example: [Personal]/.config/MyApp. In this case, [Personal] will be replaced + by the location of the Environment.SpecialFolder.Personal folder. Any value + of the Environment.SpecialFolder enumeration can be used (always between square + brackets) + + + + + Gets the global registry. + + + The global registry + + + The global add-in registry is created in "~/.config/mono.addins", + and it is the default registry used when none is specified. + + + + + Disposes the add-in engine. + + + + + Returns an add-in from the registry. + + + Identifier of the add-in. + + + The add-in, or 'null' if not found. + + + The add-in identifier may optionally include a version number, for example: "TextEditor.Xml,1.2" + + + + + Returns an add-in from the registry. + + + Identifier of the add-in. + + + 'true' if the exact add-in version must be found. + + + The add-in, or 'null' if not found. + + + The add-in identifier may optionally include a version number, for example: "TextEditor.Xml,1.2". + In this case, if the exact version is not found and exactVersionMatch is 'false', it will + return one than is compatible with the required version. + + + + + Gets all add-ins or add-in roots registered in the registry. + + + The addins. + + + Flags. + + + + + Gets all add-ins registered in the registry. + + + Add-ins registered in the registry. + + + + + Gets all add-in roots registered in the registry. + + + Descriptions of all add-in roots. + + + + + Loads an add-in description + + + Progress tracker. + + + Name of the file to load + + + An add-in description + + + This method loads an add-in description from a file. The file can be an XML manifest or an + assembly that implements an add-in. + + + + + Reads an XML add-in manifest + + + Path to the XML file + + + An add-in description + + + + + Reads an XML add-in manifest + + + Reader that contains the XML + + + Base path to use to discover add-in files + + + An add-in description + + + + + Checks whether an add-in is enabled. + + + Identifier of the add-in. + + + 'true' if the add-in is enabled. + + + + + Enables an add-in. + + + Identifier of the add-in + + + If the enabled add-in depends on other add-ins which are disabled, + those will automatically be enabled too. + + + + + Disables an add-in. + + + Identifier of the add-in. + + + When an add-in is disabled, all extension points it defines will be ignored + by the add-in engine. Other add-ins which depend on the disabled add-in will + also automatically be disabled. + + + + + Registers a set of add-ins for uninstallation. + + + Identifier of the add-in + + + Files to be uninstalled + + + This method can be used to instruct the add-in manager to uninstall + an add-in the next time the registry is updated. This is useful + when an add-in manager can't delete an add-in because if it is + loaded. + + + + + Determines whether an add-in is registered for uninstallation + + + true if the add-in is registered for uninstallation + + + Identifier of the add-in + + + + + Internal use only + + + + + Resets the configuration files of the registry + + + + + Updates the add-in registry. + + + This method must be called after modifying, installing or uninstalling add-ins. + + When calling Update, every add-in added to the registry is parsed and validated, + and if there is any error it will be rejected. It will also cache add-in information + needed at run-time. + + If during the update operation the registry finds new add-ins or detects that some + add-ins have been deleted, the loaded extension points will be updated to include + or exclude extension nodes from those add-ins. + + + + + Updates the add-in registry. + + + Progress monitor to keep track of the update operation. + + + This method must be called after modifying, installing or uninstalling add-ins. + + When calling Update, every add-in added to the registry is parsed and validated, + and if there is any error it will be rejected. It will also cache add-in information + needed at run-time. + + If during the update operation the registry finds new add-ins or detects that some + add-ins have been deleted, the loaded extension points will be updated to include + or exclude extension nodes from those add-ins. + + + + + Regenerates the cached data of the add-in registry. + + + Progress monitor to keep track of the rebuild operation. + + + + + Registers an extension. Only AddinFileSystemExtension extensions are supported right now. + + + The extension to register + + + + + Unregisters an extension. + + + The extension to unregister + + + + + Location of the add-in registry. + + + + + Gets a value indicating whether there are pending add-ins to be uninstalled installed + + + + + Gets the default add-ins folder of the registry. + + + For every add-in registry there is an add-in folder where the registry will look for add-ins by default. + This folder is an "addins" subdirectory of the directory where the repository is located. In most cases, + this folder will only contain .addins files referencing other more convenient locations for add-ins. + + + + + Addin search flags. + + + + + Add-ins are included in the search + + + + + Add-in roots are included in the search + + + + + Both add-in and add-in roots are included in the search + + + + + Only the latest version of every add-in or add-in root is included in the search + + + + + An IProgressStatus class which writes output to the console. + + + + + Progress status listener. + + + + + Sets the description of the current operation. + + + A message + + + This method is called by the add-in engine to show a description of the operation being monitorized. + + + + + Sets the progress of the operation. + + + A number between 0 and 1. 0 means no progress, 1 means operation completed. + + + This method is called by the add-in engine to show the progress of the operation being monitorized. + + + + + Writes text to the log. + + + Message to write + + + + + Reports a warning. + + + Warning message + + + This method is called by the add-in engine to report a warning in the operation being monitorized. + + + + + Reports an error. + + + Error message + + + Exception that caused the error. It can be null. + + + This method is called by the add-in engine to report an error occurred while executing the operation being monitorized. + + + + + Cancels the operation being montorized. + + + + + Log level requested by the user: 0: no log, 1: normal log, >1 verbose log + + + + + Returns True when the user requested to cancel this operation + + + + + Initializes a new instance + + + Set to true to enabled verbose log + + + + + Initializes a new instance + + + Verbosity level. 0: not verbose, 1: normal, >1 extra verbose + + + + + Sets the description of the current operation. + + + A message + + + This method is called by the add-in engine to show a description of the operation being monitorized. + + + + + Sets the progress of the operation. + + + A number between 0 and 1. 0 means no progress, 1 means operation completed. + + + This method is called by the add-in engine to show the progress of the operation being monitorized. + + + + + Writes text to the log. + + + Message to write + + + + + Reports a warning. + + + Warning message + + + This method is called by the add-in engine to report a warning in the operation being monitorized. + + + + + Reports an error. + + + Error message + + + Exception that caused the error. It can be null. + + + This method is called by the add-in engine to report an error occurred while executing the operation being monitorized. + + + + + Cancels the operation being montorized. + + + + + Returns True when the user requested to cancel this operation + + + + + Log level requested by the user: 0: no log, 1: normal log, >1 verbose log + + + + + Definition of a dependency of an add-in on another add-in. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class. + + + Full identifier of the add-in (includes version) + + + + + Initializes a new instance of the class. + + + Identifier of the add-in. + + + Version of the add-in. + + + + + Gets the full addin identifier. + + + The full addin identifier. + + + Includes namespace and version number. For example: MonoDevelop.TextEditor,1.0 + + + + + Gets or sets the addin identifier. + + + The addin identifier. + + + + + Gets or sets the version. + + + The version. + + + + + Display name of the dependency. + + + The name. + + + + + Indicates that a field or property is bound to a node attribute + + + + + Initializes a new instance + + + + + Initializes a new instance + + + XML name of the attribute. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Description of the attribute. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Indicates whether the attribute is required or not. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Indicates whether the attribute is required or not. + + + Description of the attribute. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Type of the extension node attribute. + + + The type of the attribute is only required when applying this attribute at class level. + It is not required when it is applied to a field, since the attribute type will be the type of the field. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Type of the extension node attribute. + + + Description of the attribute. + + + The type of the attribute is only required when applying this attribute at class level. + It is not required when it is applied to a field, since the attribute type will be the type of the field. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Type of the extension node attribute. + + + Indicates whether the attribute is required or not. + + + The type of the attribute is only required when applying this attribute at class level. + It is not required when it is applied to a field, since the attribute type will be the type of the field. + + + + + Initializes a new instance + + + XML name of the attribute. + + + Type of the extension node attribute. + + + Indicates whether the attribute is required or not. + + + Description of the attribute. + + + The type of the attribute is only required when applying this attribute at class level. + It is not required when it is applied to a field, since the attribute type will be the type of the field. + + + + + XML name of the attribute. + + + If the name is not specified, the field name to which the [NodeAttribute] + is applied will be used as name. Providing a name is mandatory when applying + [NodeAttribute] at class level. + + + + + Indicates whether the attribute is required or not. + + + + + Type of the extension node attribute. + + + To be used only when applying [NodeAttribute] at class level. It is not required when it + is applied to a field, since the attribute type will be the type of the field. + + + + + Description of the attribute. + + + To be used in the extension point documentation. + + + + + When set to True, the value of the field or property is expected to be a string id which + will be localized by the add-in engine + + + + + Gets or sets the type of the content. + + + Allows specifying the type of the content of a string attribute. + This value is for documentation purposes only. + + + + + A collection of add-in properties + + + + + Gets the value of a property + + + The property value. + + + Name of the property. + + + If the property is localized, it will return the value for the current language if exists, or the + default value if it doesn't. + + + + + Gets the value of a property + + + The property value. + + + Name of the property. + + + Locale for which the value must be returned. + + + + + Sets the value of a property + + + Name of the property + + + New value. + + + + + Sets the value of a property for a specific locale + + + Name of the property. + + + New value. + + + Locale of the property to be set. + + + + + Removes a property. + + + Name of the property. + + + This method only removes properties which have no locale set. + + + + + Removes a property with a specified locale + + + Name of the property + + + Locale of the property + + + + + Declares an add-in file import + + + An add-in may be composed by several assemblies and data files. + Data files must be declared in the main assembly using this attribute, or in the XML manifest. + + It is important to properly declare all files used by an add-in. + This information is used by setup tools to know exactly what needs to be packaged when creating + an add-in package, or to know what needs to be deleted when removing an add-in. + + + + + Initializes a new instance + + + Path to the file. Must be relative to the assembly declaring this attribute. + + + + + Path to the file. Must be relative to the assembly declaring this attribute. + + + + + A collection of node set identifiers + + + + + Gets the collection enumerator. + + + The enumerator. + + + + + Add the specified node set identifier. + + + Node set identifier. + + + + + Remove a node set identifier + + + Node set identifier. + + + + + Clears the collection + + + + + Checks if the specified identifier is present in the collection + + + true if the node set identifier is present. + + + + + Returns the index of the specified node set identifier + + + The index. + + + A node set identifier. + + + + + Gets the node set identifier at the specified index. + + + An index. + + + + + Gets the item count. + + + The count. + + + + + A module definition. + + + Optional modules can be used to declare extensions which will be registered only if some + specified add-in dependencies can be satisfied. + + + + + Initializes a new instance of the class. + + + + + Checks if this module depends on the specified add-in. + + + true if there is a dependency. + + + Identifier of the add-in + + + + + Adds an extension node to the module. + + + The extension node. + + + Path that identifies the extension point. + + + Node name. + + + This method creates a new Extension object for the provided path if none exist. + + + + + Gets an extension instance. + + + The extension instance. + + + Path that identifies the extension point that the extension extends. + + + This method creates a new Extension object for the provided path if none exist. + + + + + Adds an add-in reference (there is a typo in the method name) + + + Identifier of the add-in. + + + Version of the add-in. + + + + + Gets the list of paths to be ignored by the add-in scanner. + + + + + Gets all external files + + + All files. + + + External files are data files and assemblies explicitly referenced in the Runtime section of the add-in manifest. + + + + + Gets the list of external assemblies used by this module. + + + + + Gets the list of external data files used by this module + + + + + Gets the dependencies of this module + + + + + Gets the extensions of this module + + + + + Addin category attribute. + + + + + Initializes the attribute + + + The category to which the add-in belongs + + + + + The category to which the add-in belongs + + + + + A collection of node types. + + + + + Initializes a new instance of the class. + + + + + Gets the at the specified index. + + + The index. + + + + + Gets the with the specified id. + + + Identifier. + + + + + Description of the attribute of a node type. + + + + + Initializes a new instance of the class. + + + + + Copies data from another node attribute. + + + The attribute from which to copy. + + + + + Gets or sets the name of the attribute. + + + The name. + + + + + Gets or sets a value indicating whether this is required. + + + true if required; otherwise, false. + + + + + Gets or sets a value indicating whether this is localizable. + + + true if localizable; otherwise, false. + + + + + Gets or sets the type of the attribute. + + + The type. + + + + + Gets or sets the description of the attribute. + + + The description. + + + + + Gets or sets the type of the content. + + + Allows specifying the type of the content of a string attribute. + The value of this property is only informative, and it doesn't + have any effect on how add-ins are packaged or loaded. + + + + diff --git a/bin/Mono.Data.Sqlite.dll b/bin/Mono.Data.Sqlite.dll new file mode 100755 index 0000000000..4f69e0d4af Binary files /dev/null and b/bin/Mono.Data.Sqlite.dll differ diff --git a/bin/Mono.Data.Sqlite.dll.config b/bin/Mono.Data.Sqlite.dll.config new file mode 100644 index 0000000000..e66d1b7406 --- /dev/null +++ b/bin/Mono.Data.Sqlite.dll.config @@ -0,0 +1,5 @@ + + + + + diff --git a/bin/Mono.Data.SqliteClient.dll b/bin/Mono.Data.SqliteClient.dll new file mode 100755 index 0000000000..657fbf47b7 Binary files /dev/null and b/bin/Mono.Data.SqliteClient.dll differ diff --git a/bin/MySql.Data.dll b/bin/MySql.Data.dll new file mode 100755 index 0000000000..992aa5621c Binary files /dev/null and b/bin/MySql.Data.dll differ diff --git a/bin/NDesk.Options.dll b/bin/NDesk.Options.dll new file mode 100755 index 0000000000..df45878957 Binary files /dev/null and b/bin/NDesk.Options.dll differ diff --git a/bin/Nini.dll b/bin/Nini.dll new file mode 100755 index 0000000000..2d16d95aca Binary files /dev/null and b/bin/Nini.dll differ diff --git a/bin/Npgsql.dll b/bin/Npgsql.dll new file mode 100755 index 0000000000..24ca4bde64 Binary files /dev/null and b/bin/Npgsql.dll differ diff --git a/bin/Npgsql.xml b/bin/Npgsql.xml new file mode 100644 index 0000000000..a51252d119 --- /dev/null +++ b/bin/Npgsql.xml @@ -0,0 +1,4120 @@ + + + + Npgsql + + + + + This class represents a parameter to a command that will be sent to server + + + + + Initializes a new instance of the NpgsqlParameter class. + + + + + Initializes a new instance of the NpgsqlParameter + class with the parameter m_Name and a value of the new NpgsqlParameter. + + The m_Name of the parameter to map. + An Object that is the value of the NpgsqlParameter. + +

When you specify an Object + in the value parameter, the DbType is + inferred from the .NET Framework type of the Object.

+

When using this constructor, you must be aware of a possible misuse of the constructor which takes a DbType parameter. + This happens when calling this constructor passing an int 0 and the compiler thinks you are passing a value of DbType. + Use Convert.ToInt32(value) for example to have compiler calling the correct constructor.

+
+
+ + + Initializes a new instance of the NpgsqlParameter + class with the parameter m_Name and the data type. + + The m_Name of the parameter to map. + One of the DbType values. + + + + Initializes a new instance of the NpgsqlParameter + class with the parameter m_Name, the DbType, and the size. + + The m_Name of the parameter to map. + One of the DbType values. + The length of the parameter. + + + + Initializes a new instance of the NpgsqlParameter + class with the parameter m_Name, the DbType, the size, + and the source column m_Name. + + The m_Name of the parameter to map. + One of the DbType values. + The length of the parameter. + The m_Name of the source column. + + + + Initializes a new instance of the NpgsqlParameter + class with the parameter m_Name, the DbType, the size, + the source column m_Name, a ParameterDirection, + the precision of the parameter, the scale of the parameter, a + DataRowVersion to use, and the + value of the parameter. + + The m_Name of the parameter to map. + One of the DbType values. + The length of the parameter. + The m_Name of the source column. + One of the ParameterDirection values. + true if the value of the field can be null, otherwise false. + The total number of digits to the left and right of the decimal point to which + Value is resolved. + The total number of decimal places to which + Value is resolved. + One of the DataRowVersion values. + An Object that is the value + of the NpgsqlParameter. + + + + Creates a new NpgsqlParameter that + is a copy of the current instance. + + A new NpgsqlParameter that is a copy of this instance. + + + + Gets or sets the maximum number of digits used to represent the + Value property. + + The maximum number of digits used to represent the + Value property. + The default value is 0, which indicates that the data provider + sets the precision for Value. + + + + Gets or sets the number of decimal places to which + Value is resolved. + + The number of decimal places to which + Value is resolved. The default is 0. + + + + Gets or sets the maximum size, in bytes, of the data within the column. + + The maximum size, in bytes, of the data within the column. + The default value is inferred from the parameter value. + + + + Gets or sets the DbType of the parameter. + + One of the DbType values. The default is String. + + + + Gets or sets the DbType of the parameter. + + One of the DbType values. The default is String. + + + + Gets or sets a value indicating whether the parameter is input-only, + output-only, bidirectional, or a stored procedure return value parameter. + + One of the ParameterDirection + values. The default is Input. + + + + Gets or sets a value indicating whether the parameter accepts null values. + + true if null values are accepted; otherwise, false. The default is false. + + + + Gets or sets the m_Name of the NpgsqlParameter. + + The m_Name of the NpgsqlParameter. + The default is an empty string. + + + + The m_Name scrubbed of any optional marker + + + + + Gets or sets the m_Name of the source column that is mapped to the + DataSet and used for loading or + returning the Value. + + The m_Name of the source column that is mapped to the + DataSet. The default is an empty string. + + + + Gets or sets the DataRowVersion + to use when loading Value. + + One of the DataRowVersion values. + The default is Current. + + + + Gets or sets the value of the parameter. + + An Object that is the value of the parameter. + The default value is null. + + + + Gets or sets the value of the parameter. + + An Object that is the value of the parameter. + The default value is null. + + + + This class represents the Parse message sent to PostgreSQL + server. + + + + + + For classes representing messages sent from the client to the server. + + + + + Writes given objects into a stream for PostgreSQL COPY in default copy format (not CSV or BINARY). + + + + + Return an exact copy of this NpgsqlConnectionString. + + + + + This function will set value for known key, both private member and base[key]. + + + + + + + The function will modify private member only, not base[key]. + + + + + + + Clear the member and assign them to the default value. + + + + + Compatibilty version. When possible, behaviour caused by breaking changes will be preserved + if this version is less than that where the breaking change was introduced. + + + + + Case insensative accessor for indivual connection string values. + + + + + Common base class for all derived MD5 implementations. + + + + + Called from constructor of derived class. + + + + + Finalizer for HashAlgorithm + + + + + Computes the entire hash of all the bytes in the byte array. + + + + + When overridden in a derived class, drives the hashing function. + + + + + + + + When overridden in a derived class, this pads and hashes whatever data might be left in the buffers and then returns the hash created. + + + + + When overridden in a derived class, initializes the object to prepare for hashing. + + + + + Used for stream chaining. Computes hash as data passes through it. + + The buffer from which to grab the data to be copied. + The offset into the input buffer to start reading at. + The number of bytes to be copied. + The buffer to write the copied data to. + At what point in the outputBuffer to write the data at. + + + + Used for stream chaining. Computes hash as data passes through it. Finishes off the hash. + + The buffer from which to grab the data to be copied. + The offset into the input buffer to start reading at. + The number of bytes to be copied. + + + + Get whether or not the hash can transform multiple blocks at a time. + Note: MUST be overriden if descendant can transform multiple block + on a single call! + + + + + Gets the previously computed hash. + + + + + Returns the size in bits of the hash. + + + + + Must be overriden if not 1 + + + + + Must be overriden if not 1 + + + + + Called from constructor of derived class. + + + + + Creates the default derived class. + + + + + Given a join expression and a projection, fetch all columns in the projection + that reference columns in the join. + + + + + Given an InputExpression append all from names (including nested joins) to the list. + + + + + Get new ColumnExpression that will be used in projection that had it's existing columns moved. + These should be simple references to the inner column + + + + + Every property accessed in the list of columns must be adjusted for a new scope + + + + + This class provides many util methods to handle + reading and writing of PostgreSQL protocol messages. + + + + + This method takes a ProtocolVersion and returns an integer + version number that the Postgres backend will recognize in a + startup packet. + + + + + This method takes a version string as returned by SELECT VERSION() and returns + a valid version string ("7.2.2" for example). + This is only needed when running protocol version 2. + This does not do any validity checks. + + + + + This method gets a C NULL terminated string from the network stream. + It keeps reading a byte in each time until a NULL byte is returned. + It returns the resultant string of bytes read. + This string is sent from backend. + + + + + Reads requested number of bytes from stream with retries until Stream.Read returns 0 or count is reached. + + Stream to read + byte buffer to fill + starting position to fill the buffer + number of bytes to read + The number of bytes read. May be less than count if no more bytes are available. + + + + This method writes a C NULL terminated string to the network stream. + It appends a NULL terminator to the end of the String. + + + This method writes a C NULL terminated string to the network stream. + It appends a NULL terminator to the end of the String. + + + + + This method writes a set of bytes to the stream. It also enables logging of them. + + + + + This method writes a C NULL terminated string limited in length to the + backend server. + It pads the string with null bytes to the size specified. + + + + + Write a 32-bit integer to the given stream in the correct byte order. + + + + + Read a 32-bit integer from the given stream in the correct byte order. + + + + + Write a 16-bit integer to the given stream in the correct byte order. + + + + + Read a 16-bit integer from the given stream in the correct byte order. + + + + + Represent the frontend/backend protocol version. + + + + + Represent the backend server version. + As this class offers no functionality beyond that offered by it has been + deprecated in favour of that class. + + + + + + Returns the string representation of this version in three place dot notation (Major.Minor.Patch). + + + + + Server version major number. + + + + + Server version minor number. + + + + + Server version patch level number. + + + + + Represents a PostgreSQL COPY TO STDOUT operation with a corresponding SQL statement + to execute against a PostgreSQL database + and an associated stream used to write results to (if provided by user) + or for reading the results (when generated by driver). + Eg. new NpgsqlCopyOut("COPY (SELECT * FROM mytable) TO STDOUT", connection, streamToWrite).Start(); + + + + + Creates NpgsqlCommand to run given query upon Start(), after which CopyStream provides data from database as requested in the query. + + + + + Given command is run upon Start(), after which CopyStream provides data from database as requested in the query. + + + + + Given command is executed upon Start() and all requested copy data is written to toStream immediately. + + + + + Returns true if this operation is currently active and field at given location is in binary format. + + + + + Command specified upon creation is executed as a non-query. + If CopyStream is set upon creation, all copy data from server will be written to it, and operation will be finished immediately. + Otherwise the CopyStream member can be used for reading copy data from server until no more data is available. + + + + + Flush generated CopyStream at once. Effectively reads and discard all the rest of copy data from server. + + + + + Returns true if the connection is currently reserved for this operation. + + + + + The stream provided by user or generated upon Start() + + + + + The Command used to execute this copy operation. + + + + + Returns true if this operation is currently active and in binary format. + + + + + Returns number of fields if this operation is currently active, otherwise -1 + + + + + Faster alternative to using the generated CopyStream. + + + + + This class manages all connector objects, pooled AND non-pooled. + + + + Unique static instance of the connector pool + mamager. + + + Map of index to unused pooled connectors, avaliable to the + next RequestConnector() call. + This hashmap will be indexed by connection string. + This key will hold a list of queues of pooled connectors available to be used. + + + Timer for tracking unused connections in pools. + + + + Searches the shared and pooled connector lists for a + matching connector object or creates a new one. + + The NpgsqlConnection that is requesting + the connector. Its ConnectionString will be used to search the + pool for available connectors. + A connector object. + + + + Find a pooled connector. Handle locking and timeout here. + + + + + Find a pooled connector. Handle shared/non-shared here. + + + + + Releases a connector, possibly back to the pool for future use. + + + Pooled connectors will be put back into the pool if there is room. + Shared connectors should just have their use count decremented + since they always stay in the shared pool. + + The connector to release. + + + + Release a pooled connector. Handle locking here. + + + + + Release a pooled connector. Handle shared/non-shared here. + + + + + Create a connector without any pooling functionality. + + + + + Find an available pooled connector in the non-shared pool, or create + a new one if none found. + + + + + This method is only called when NpgsqlConnection.Dispose(false) is called which means a + finalization. This also means, an NpgsqlConnection was leak. We clear pool count so that + client doesn't end running out of connections from pool. When the connection is finalized, its underlying + socket is closed. + + + + + Close the connector. + + + Connector to release + + + + Put a pooled connector into the pool queue. + + Connector to pool + + + + A queue with an extra Int32 for keeping track of busy connections. + + + + + Connections available to the end user + + + + + Connections currently in use + + + + + This class represents a BackEndKeyData message received + from PostgreSQL + + + + + Used when a connection is closed + + + + + Summary description for NpgsqlQuery + + + + + Represents the method that handles the Notice events. + + A NpgsqlNoticeEventArgs that contains the event data. + + + + Represents the method that handles the Notification events. + + The source of the event. + A NpgsqlNotificationEventArgs that contains the event data. + + + + This class represents a connection to a + PostgreSQL server. + + + + + Initializes a new instance of the + NpgsqlConnection class. + + + + + Initializes a new instance of the + NpgsqlConnection class + and sets the ConnectionString. + + The connection used to open the PostgreSQL database. + + + + Begins a database transaction with the specified isolation level. + + The isolation level under which the transaction should run. + An DbTransaction + object representing the new transaction. + + Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. + There's no support for nested transactions. + + + + + Begins a database transaction. + + A NpgsqlTransaction + object representing the new transaction. + + Currently there's no support for nested transactions. + + + + + Begins a database transaction with the specified isolation level. + + The isolation level under which the transaction should run. + A NpgsqlTransaction + object representing the new transaction. + + Currently the IsolationLevel ReadCommitted and Serializable are supported by the PostgreSQL backend. + There's no support for nested transactions. + + + + + Opens a database connection with the property settings specified by the + ConnectionString. + + + + + This method changes the current database by disconnecting from the actual + database and connecting to the specified. + + The name of the database to use in place of the current database. + + + + Releases the connection to the database. If the connection is pooled, it will be + made available for re-use. If it is non-pooled, the actual connection will be shutdown. + + + + + Creates and returns a DbCommand + object associated with the IDbConnection. + + A DbCommand object. + + + + Creates and returns a NpgsqlCommand + object associated with the NpgsqlConnection. + + A NpgsqlCommand object. + + + + Releases all resources used by the + NpgsqlConnection. + + true when called from Dispose(); + false when being called from the finalizer. + + + + Create a new connection based on this one. + + A new NpgsqlConnection object. + + + + Create a new connection based on this one. + + A new NpgsqlConnection object. + + + + Default SSL CertificateSelectionCallback implementation. + + + + + Default SSL CertificateValidationCallback implementation. + + + + + Default SSL PrivateKeySelectionCallback implementation. + + + + + Default SSL ProvideClientCertificatesCallback implementation. + + + + + Write each key/value pair in the connection string to the log. + + + + + Returns the supported collections + + + + + Returns the schema collection specified by the collection name. + + The collection name. + The collection specified. + + + + Returns the schema collection specified by the collection name filtered by the restrictions. + + The collection name. + + The restriction values to filter the results. A description of the restrictions is contained + in the Restrictions collection. + + The collection specified. + + + + Occurs on NoticeResponses from the PostgreSQL backend. + + + + + Occurs on NotificationResponses from the PostgreSQL backend. + + + + + Called to provide client certificates for SSL handshake. + + + + + Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate. + + + + + Mono.Security.Protocol.Tls.CertificateValidationCallback delegate. + + + + + Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate. + + + + + Gets or sets the string used to connect to a PostgreSQL database. + Valid values are: +
    +
  • + Server: Address/Name of Postgresql Server; +
  • +
  • + Port: Port to connect to; +
  • +
  • + Protocol: Protocol version to use, instead of automatic; Integer 2 or 3; +
  • +
  • + Database: Database name. Defaults to user name if not specified; +
  • +
  • + User Id: User name; +
  • +
  • + Password: Password for clear text authentication; +
  • +
  • + SSL: True or False. Controls whether to attempt a secure connection. Default = False; +
  • +
  • + Pooling: True or False. Controls whether connection pooling is used. Default = True; +
  • +
  • + MinPoolSize: Min size of connection pool; +
  • +
  • + MaxPoolSize: Max size of connection pool; +
  • +
  • + Timeout: Time to wait for connection open in seconds. Default is 15. +
  • +
  • + CommandTimeout: Time to wait for command to finish execution before throw an exception. In seconds. Default is 20. +
  • +
  • + Sslmode: Mode for ssl connection control. Can be Prefer, Require, Allow or Disable. Default is Disable. Check user manual for explanation of values. +
  • +
  • + ConnectionLifeTime: Time to wait before closing unused connections in the pool in seconds. Default is 15. +
  • +
  • + SyncNotification: Specifies if Npgsql should use synchronous notifications. +
  • +
  • + SearchPath: Changes search path to specified and public schemas. +
  • +
+
+ The connection string that includes the server name, + the database name, and other parameters needed to establish + the initial connection. The default value is an empty string. + +
+ + + Backend server host name. + + + + + Backend server port. + + + + + If true, the connection will attempt to use SSL. + + + + + Gets the time to wait while trying to establish a connection + before terminating the attempt and generating an error. + + The time (in seconds) to wait for a connection to open. The default value is 15 seconds. + + + + Gets the time to wait while trying to execute a command + before terminating the attempt and generating an error. + + The time (in seconds) to wait for a command to complete. The default value is 20 seconds. + + + + Gets the time to wait before closing unused connections in the pool if the count + of all connections exeeds MinPoolSize. + + + If connection pool contains unused connections for ConnectionLifeTime seconds, + the half of them will be closed. If there will be unused connections in a second + later then again the half of them will be closed and so on. + This strategy provide smooth change of connection count in the pool. + + The time (in seconds) to wait. The default value is 15 seconds. + + + + Gets the name of the current database or the database to be used after a connection is opened. + + The name of the current database or the name of the database to be + used after a connection is opened. The default value is the empty string. + + + + Whether datareaders are loaded in their entirety (for compatibility with earlier code). + + + + + Gets the database server name. + + + + + Gets flag indicating if we are using Synchronous notification or not. + The default value is false. + + + + + Gets the current state of the connection. + + A bitwise combination of the ConnectionState values. The default is Closed. + + + + Gets whether the current state of the connection is Open or Closed + + ConnectionState.Open or ConnectionState.Closed + + + + Version of the PostgreSQL backend. + This can only be called when there is an active connection. + + + + + Protocol version in use. + This can only be called when there is an active connection. + + + + + Process id of backend server. + This can only be called when there is an active connection. + + + + + The connector object connected to the backend. + + + + + Gets the NpgsqlConnectionStringBuilder containing the parsed connection string values. + + + + + User name. + + + + + Password. + + + + + Determine if connection pooling will be used for this connection. + + + + + This class represents the CancelRequest message sent to PostgreSQL + server. + + + + + + + + + + + + + + + + + + + A time period expressed in 100ns units. + + + A time period expressed in a + + + Number of 100ns units. + + + Number of seconds. + + + Number of milliseconds. + + + Number of milliseconds. + + + Number of milliseconds. + + + A d with the given number of ticks. + + + A d with the given number of microseconds. + + + A d with the given number of milliseconds. + + + A d with the given number of seconds. + + + A d with the given number of minutes. + + + A d with the given number of hours. + + + A d with the given number of days. + + + A d with the given number of months. + + + An whose values are the sums of the two instances. + + + An whose values are the differences of the two instances. + + + An whose value is the negated value of this instance. + + + An whose value is the absolute value of this instance. + + + + An based on this one, but with any days converted to multiples of ±24hours. + + + + An based on this one, but with any months converted to multiples of ±30days. + + + + An based on this one, but with any months converted to multiples of ±30days and then any days converted to multiples of ±24hours; + + + + An eqivalent, canonical, . + + + An equivalent . + + + + + + An signed integer. + + + + The argument is not an . + + + The string was not in a format that could be parsed to produce an . + + + true if the parsing succeeded, false otherwise. + + + The representation. + + + An whose values are the sum of the arguments. + + + An whose values are the difference of the arguments + + + true if the two arguments are exactly the same, false otherwise. + + + false if the two arguments are exactly the same, true otherwise. + + + true if the first is less than second, false otherwise. + + + true if the first is less than or equivalent to second, false otherwise. + + + true if the first is greater than second, false otherwise. + + + true if the first is greater than or equivalent to the second, false otherwise. + + + The argument. + + + The negation of the argument. + + + + + + + + + + + + + + + + + + + + This time, normalised + + + + + + + + + This time, normalised + + + An integer which is 0 if they are equal, < 0 if this is the smaller and > 0 if this is the larger. + + + + + + + + + A class to handle everything associated with SSPI authentication + + + + + Simplified SecBufferDesc struct with only one SecBuffer + + + + + This class represents the Parse message sent to PostgreSQL + server. + + + + + + EventArgs class to send Notice parameters, which are just NpgsqlError's in a lighter context. + + + + + Notice information. + + + + + This class represents the ErrorResponse and NoticeResponse + message sent from PostgreSQL server. + + + + + Return a string representation of this error object. + + + + + Severity code. All versions. + + + + + Error code. PostgreSQL 7.4 and up. + + + + + Terse error message. All versions. + + + + + Detailed error message. PostgreSQL 7.4 and up. + + + + + Suggestion to help resolve the error. PostgreSQL 7.4 and up. + + + + + Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. + + + + + Position (one based) within the query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up. + + + + + Internal query string where the error was encounterd. This position refers to an internal command executed for example inside a PL/pgSQL function. PostgreSQL 7.4 and up. + + + + + Trace back information. PostgreSQL 7.4 and up. + + + + + Source file (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + String containing the sql sent which produced this error. + + + + + Backend protocol version in use. + + + + + Represents an ongoing COPY TO STDOUT operation. + Provides methods to read data from server or end the operation. + + + + This class represents the base class for the state pattern design pattern + implementation. + + + + + + This method is used by the states to change the state of the context. + + + + + This method is responsible to handle all protocol messages sent from the backend. + It holds all the logic to do it. + To exchange data, it uses a Mediator object from which it reads/writes information + to handle backend requests. + + + + + + This method is responsible to handle all protocol messages sent from the backend. + It holds all the logic to do it. + To exchange data, it uses a Mediator object from which it reads/writes information + to handle backend requests. + + + + + + Called from NpgsqlState.ProcessBackendResponses upon CopyOutResponse. + If CopyStream is already set, it is used to write data received from server, after which the copy ends. + Otherwise CopyStream is set to a readable NpgsqlCopyOutStream that receives data from server. + + + + + Called from NpgsqlOutStream.Read to read copy data from server. + + + + + Copy format information returned from server. + + + + + Handles serialisation of .NET array or IEnumeration to pg format. + Arrays of arrays, enumerations of enumerations, arrays of enumerations etc. + are treated as multi-dimensional arrays (in much the same manner as an array of arrays + is used to emulate multi-dimensional arrays in languages that lack native support for them). + If such an enumeration of enumerations is "jagged" (as opposed to rectangular, cuboid, + hypercuboid, hyperhypercuboid, etc) then this class will "correctly" serialise it, but pg + will raise an error as it doesn't allow jagged arrays. + + + + + Create an ArrayNativeToBackendTypeConverter with the element converter passed + + The that would be used to serialise the element type. + + + + Serialise the enumeration or array. + + + + + Handles parsing of pg arrays into .NET arrays. + + + + + Takes a string representation of a pg 1-dimensional array + (or a 1-dimensional row within an n-dimensional array) + and allows enumeration of the string represenations of each items. + + + + + Takes a string representation of a pg n-dimensional array + and allows enumeration of the string represenations of the next + lower level of rows (which in turn can be taken as (n-1)-dimensional arrays. + + + + + Takes an ArrayList which may be an ArrayList of ArrayLists, an ArrayList of ArrayLists of ArrayLists + and so on and enumerates the items that aren't ArrayLists (the leaf nodes if we think of the ArrayList + passed as a tree). Simply uses the ArrayLists' own IEnumerators to get that of the next, + pushing them onto a stack until we hit something that isn't an ArrayList. + ArrayList to enumerate + IEnumerable + + + + + Create a new ArrayBackendToNativeTypeConverter + + for the element type. + + + + Creates an array from pg representation. + + + + + Creates an array list from pg represenation of an array. + Multidimensional arrays are treated as ArrayLists of ArrayLists + + + + + Creates an n-dimensional array from an ArrayList of ArrayLists or + a 1-dimensional array from something else. + + to convert + produced. + + + + Takes an array of ints and treats them like the limits of a set of counters. + Retains a matching set of ints that is set to all zeros on the first ++ + On a ++ it increments the "right-most" int. If that int reaches it's + limit it is set to zero and the one before it is incremented, and so on. + + Making this a more general purpose class is pretty straight-forward, but we'll just put what we need here. + + + + + This class represents the ParameterStatus message sent from PostgreSQL + server. + + + + + + This class is responsible for serving as bridge between the backend + protocol handling and the core classes. It is used as the mediator for + exchanging data generated/sent from/to backend. + + + + + + This class is responsible to create database commands for automatic insert, update and delete operations. + + + + + + This method is reponsible to derive the command parameter list with values obtained from function definition. + It clears the Parameters collection of command. Also, if there is any parameter type which is not supported by Npgsql, an InvalidOperationException will be thrown. + Parameters name will be parameter1, parameter2, ... + For while, only parameter name and NpgsqlDbType are obtained. + + NpgsqlCommand whose function parameters will be obtained. + + + + Represents a completed response message. + + + + + + Marker interface which identifies a class which may take possession of a stream for the duration of + it's lifetime (possibly temporarily giving that possession to another class for part of that time. + + It inherits from IDisposable, since any such class must make sure it leaves the stream in a valid state. + + The most important such class is that compiler-generated from ProcessBackendResponsesEnum. Of course + we can't make that inherit from this interface, alas. + + + + + The exception that is thrown when the PostgreSQL backend reports errors. + + + + + Construct a backend error exception based on a list of one or more + backend errors. The basic Exception.Message will be built from the + first (usually the only) error in the list. + + + + + Format a .NET style exception string. + Include all errors in the list, including any hints. + + + + + Append a line to the given Stream, first checking for zero-length. + + + + + Provide access to the entire list of errors provided by the PostgreSQL backend. + + + + + Severity code. All versions. + + + + + Error code. PostgreSQL 7.4 and up. + + + + + Basic error message. All versions. + + + + + Detailed error message. PostgreSQL 7.4 and up. + + + + + Suggestion to help resolve the error. PostgreSQL 7.4 and up. + + + + + Position (one based) within the query string where the error was encounterd. PostgreSQL 7.4 and up. + + + + + Trace back information. PostgreSQL 7.4 and up. + + + + + Source file (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + Source file line number (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + Source routine (in backend) reporting the error. PostgreSQL 7.4 and up. + + + + + String containing the sql sent which produced this error. + + + + + Returns the entire list of errors provided by the PostgreSQL backend. + + + + + The level of verbosity of the NpgsqlEventLog + + + + + Don't log at all + + + + + Only log the most common issues + + + + + Log everything + + + + + This class handles all the Npgsql event and debug logging + + + + + Writes a string to the Npgsql event log if msglevel is bigger then NpgsqlEventLog.Level + + + This method is obsolete and should no longer be used. + It is likely to be removed in future versions of Npgsql + + The message to write to the event log + The minimum LogLevel for which this message should be logged. + + + + Writes a string to the Npgsql event log if msglevel is bigger then NpgsqlEventLog.Level + + The ResourceManager to get the localized resources + The name of the resource that should be fetched by the ResourceManager + The minimum LogLevel for which this message should be logged. + The additional parameters that shall be included into the log-message (must be compatible with the string in the resource): + + + + Writes the default log-message for the action of calling the Get-part of an Indexer to the log file. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Indexer + The parameter given to the Indexer + + + + Writes the default log-message for the action of calling the Set-part of an Indexer to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Indexer + The parameter given to the Indexer + The value the Indexer is set to + + + + Writes the default log-message for the action of calling the Get-part of a Property to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Property + The name of the Property + + + + Writes the default log-message for the action of calling the Set-part of a Property to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Property + The name of the Property + The value the Property is set to + + + + Writes the default log-message for the action of calling a Method without Arguments to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Method + The name of the Method + + + + Writes the default log-message for the action of calling a Method with one Argument to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Method + The name of the Method + The value of the Argument of the Method + + + + Writes the default log-message for the action of calling a Method with two Arguments to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Method + The name of the Method + The value of the first Argument of the Method + The value of the second Argument of the Method + + + + Writes the default log-message for the action of calling a Method with three Arguments to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Method + The name of the Method + The value of the first Argument of the Method + The value of the second Argument of the Method + The value of the third Argument of the Method + + + + Writes the default log-message for the action of calling a Method with more than three Arguments to the logfile. + + The minimum LogLevel for which this message should be logged. + The name of the class that contains the Method + The name of the Method + A Object-Array with zero or more Ojects that are Arguments of the Method. + + + + Sets/Returns the level of information to log to the logfile. + + The current LogLevel + + + + Sets/Returns the filename to use for logging. + + The filename of the current Log file. + + + + Sets/Returns whether Log messages should be echoed to the console + + true if Log messages are echoed to the console, otherwise false + + + + This class represents the Parse message sent to PostgreSQL + server. + + + + + + Represents a PostgreSQL COPY FROM STDIN operation with a corresponding SQL statement + to execute against a PostgreSQL database + and an associated stream used to read data from (if provided by user) + or for writing it (when generated by driver). + Eg. new NpgsqlCopyIn("COPY mytable FROM STDIN", connection, streamToRead).Start(); + + + + + Creates NpgsqlCommand to run given query upon Start(). Data for the requested COPY IN operation can then be written to CopyData stream followed by a call to End() or Cancel(). + + + + + Given command is run upon Start(). Data for the requested COPY IN operation can then be written to CopyData stream followed by a call to End() or Cancel(). + + + + + Given command is executed upon Start() and all data from fromStream is passed to it as copy data. + + + + + Returns true if this operation is currently active and field at given location is in binary format. + + + + + Command specified upon creation is executed as a non-query. + If CopyStream is set upon creation, it will be flushed to server as copy data, and operation will be finished immediately. + Otherwise the CopyStream member can be used for writing copy data to server and operation finished with a call to End() or Cancel(). + + + + + Called after writing all data to CopyStream to successfully complete this copy operation. + + + + + Withdraws an already started copy operation. The operation will fail with given error message. + Will do nothing if current operation is not active. + + + + + Returns true if the connection is currently reserved for this operation. + + + + + The stream provided by user or generated upon Start(). + User may provide a stream to constructor; it is used to pass to server all data read from it. + Otherwise, call to Start() sets this to a writable NpgsqlCopyInStream that passes all data written to it to server. + In latter case this is only available while the copy operation is active and null otherwise. + + + + + Returns true if this operation is currently active and in binary format. + + + + + Returns number of fields expected on each input row if this operation is currently active, otherwise -1 + + + + + The Command used to execute this copy operation. + + + + + Set before a COPY IN query to define size of internal buffer for reading from given CopyStream. + + + + + Represents information about COPY operation data transfer format as returned by server. + + + + + Only created when a CopyInResponse or CopyOutResponse is received by NpgsqlState.ProcessBackendResponses() + + + + + Returns true if this operation is currently active and field at given location is in binary format. + + + + + Returns true if this operation is currently active and in binary format. + + + + + Returns number of fields if this operation is currently active, otherwise -1 + + + + + + + + + Provide event handlers to convert all native supported basic data types from their backend + text representation to a .NET object. + + + + + Binary data. + + + + + Convert a postgresql boolean to a System.Boolean. + + + + + Convert a postgresql bit to a System.Boolean. + + + + + Convert a postgresql datetime to a System.DateTime. + + + + + Convert a postgresql date to a System.DateTime. + + + + + Convert a postgresql time to a System.DateTime. + + + + + Convert a postgresql money to a System.Decimal. + + + + + Provide event handlers to convert the basic native supported data types from + native form to backend representation. + + + + + Binary data. + + + + + Convert to a postgresql boolean. + + + + + Convert to a postgresql bit. + + + + + Convert to a postgresql timestamp. + + + + + Convert to a postgresql date. + + + + + Convert to a postgresql time. + + + + + Convert to a postgres money. + + + + + Convert to a postgres double with maximum precision. + + + + + Provide event handlers to convert extended native supported data types from their backend + text representation to a .NET object. + + + + + Convert a postgresql point to a System.NpgsqlPoint. + + + + + Convert a postgresql point to a System.RectangleF. + + + + + LDeg. + + + + + Path. + + + + + Polygon. + + + + + Circle. + + + + + Inet. + + + + + MAC Address. + + + + + interval + + + + + Provide event handlers to convert extended native supported data types from + native form to backend representation. + + + + + Point. + + + + + Box. + + + + + LSeg. + + + + + Open path. + + + + + Polygon. + + + + + Convert to a postgres MAC Address. + + + + + Circle. + + + + + Convert to a postgres inet. + + + + + Convert to a postgres interval + + + + + EventArgs class to send Notification parameters. + + + + + Process ID of the PostgreSQL backend that sent this notification. + + + + + Condition that triggered that notification. + + + + + Additional Information From Notifiying Process (for future use, currently postgres always sets this to an empty string) + + + + + Resolve a host name or IP address. + This is needed because if you call Dns.Resolve() with an IP address, it will attempt + to resolve it as a host name, when it should just convert it to an IP address. + + + + + + This class represents a RowDescription message sent from + the PostgreSQL. + + + + + + This struct represents the internal data of the RowDescription message. + + + + + This class represents the Parse message sent to PostgreSQL + server. + + + + + + A factory to create instances of various Npgsql objects. + + + + + Creates an NpgsqlCommand object. + + + + + This class represents the Parse message sent to PostgreSQL + server. + + + + + + Represents the method that handles the RowUpdated events. + + The source of the event. + A NpgsqlRowUpdatedEventArgs that contains the event data. + + + + Represents the method that handles the RowUpdating events. + + The source of the event. + A NpgsqlRowUpdatingEventArgs that contains the event data. + + + + This class represents an adapter from many commands: select, update, insert and delete to fill Datasets. + + + + + Stream for reading data from a table or select on a PostgreSQL version 7.4 or newer database during an active COPY TO STDOUT operation. + Passes data exactly as provided by the server. + + + + + Created only by NpgsqlCopyOutState.StartCopy() + + + + + Discards copy data as long as server pushes it. Returns after operation is finished. + Does nothing if this stream is not the active copy operation reader. + + + + + Not writable. + + + + + Not flushable. + + + + + Copies data read from server to given byte buffer. + Since server returns data row by row, length will differ each time, but it is only zero once the operation ends. + Can be mixed with calls to the more efficient NpgsqlCopyOutStream.Read() : byte[] though that would not make much sense. + + + + + Not seekable + + + + + Not supported + + + + + Returns a whole row of data from server without extra work. + If standard Stream.Read(...) has been called before, it's internal buffers remains are returned. + + + + + True while this stream can be used to read copy data from server + + + + + True + + + + + False + + + + + False + + + + + Number of bytes read so far + + + + + Number of bytes read so far; can not be set. + + + + + This class represents the Bind message sent to PostgreSQL + server. + + + + + + Summary description for LargeObjectManager. + + + + + Represents a transaction to be made in a PostgreSQL database. This class cannot be inherited. + + + + + Commits the database transaction. + + + + + Rolls back a transaction from a pending state. + + + + + Rolls back a transaction from a pending savepoint state. + + + + + Creates a transaction save point. + + + + + Cancel the transaction without telling the backend about it. This is + used to make the transaction go away when closing a connection. + + + + + Gets the NpgsqlConnection + object associated with the transaction, or a null reference if the + transaction is no longer valid. + + The NpgsqlConnection + object associated with the transaction. + + + + Specifies the IsolationLevel for this transaction. + + The IsolationLevel for this transaction. + The default is ReadCommitted. + + + + This class represents a StartupPacket message of PostgreSQL + protocol. + + + + + + Provides a means of reading a forward-only stream of rows from a PostgreSQL backend. This class cannot be inherited. + + + + + Return the data type name of the column at index . + + + + + Return the data type of the column at index . + + + + + Return the Npgsql specific data type of the column at requested ordinal. + + column position + Appropriate Npgsql type for column. + + + + Return the column name of the column at index . + + + + + Return the data type OID of the column at index . + + FIXME: Why this method returns String? + + + + Return the column name of the column named . + + + + + Return the data DbType of the column at index . + + + + + Return the data NpgsqlDbType of the column at index . + + + + + Get the value of a column as a . + If the differences between and + in handling of days and months is not important to your application, use + instead. + + Index of the field to find. + value of the field. + + + + Gets the value of a column converted to a Guid. + + + + + Gets the value of a column as Int16. + + + + + Gets the value of a column as Int32. + + + + + Gets the value of a column as Int64. + + + + + Gets the value of a column as Single. + + + + + Gets the value of a column as Double. + + + + + Gets the value of a column as String. + + + + + Gets the value of a column as Decimal. + + + + + Gets the value of a column as TimeSpan. + + + + + Copy values from each column in the current row into . + + The number of column values copied. + + + + Copy values from each column in the current row into . + + An array appropriately sized to store values from all columns. + The number of column values copied. + + + + Gets the value of a column as Boolean. + + + + + Gets the value of a column as Byte. Not implemented. + + + + + Gets the value of a column as Char. + + + + + Gets the value of a column as DateTime. + + + + + Returns a System.Data.DataTable that describes the column metadata of the DataReader. + + + + + This methods parses the command text and tries to get the tablename + from it. + + + + + Is raised whenever Close() is called. + + + + + Gets the number of columns in the current row. + + + + + Gets the value of a column in its native format. + + + + + Gets the value of a column in its native format. + + + + + Gets a value indicating the depth of nesting for the current row. Always returns zero. + + + + + Gets a value indicating whether the data reader is closed. + + + + + Contains the column names as the keys + + + + + Contains all unique columns + + + + + This is the primary implementation of NpgsqlDataReader. It is the one used in normal cases (where the + preload-reader option is not set in the connection string to resolve some potential backwards-compatibility + issues), the only implementation used internally, and in cases where CachingDataReader is used, it is still + used to do the actual "leg-work" of turning a response stream from the server into a datareader-style + object - with CachingDataReader then filling it's cache from here. + + + + + Iterate through the objects returned through from the server. + If it's a CompletedResponse the rowsaffected count is updated appropriately, + and we iterate again, otherwise we return it (perhaps updating our cache of pending + rows if appropriate). + + The next we will deal with. + + + + Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. + + True if the reader was advanced, otherwise false. + + + + Releases the resources used by the NpgsqlCommand. + + + + + Closes the data reader object. + + + + + Advances the data reader to the next result, when multiple result sets were returned by the PostgreSQL backend. + + True if the reader was advanced, otherwise false. + + + + Advances the data reader to the next row. + + True if the reader was advanced, otherwise false. + + + + Return the value of the column at index . + + + + + Gets raw data from a column. + + + + + Gets raw data from a column. + + + + + Report whether the value in a column is DBNull. + + + + + Gets the number of rows changed, inserted, or deleted by execution of the SQL statement. + + + + + Indicates if NpgsqlDatareader has rows to be read. + + + + + Provides an implementation of NpgsqlDataReader in which all data is pre-loaded into memory. + This operates by first creating a ForwardsOnlyDataReader as usual, and then loading all of it's + Rows into memory. There is a general principle that when there is a trade-off between a class design that + is more efficient and/or scalable on the one hand and one that is less efficient but has more functionality + (in this case the internal-only functionality of caching results) that one can build the less efficent class + from the most efficient without significant extra loss in efficiency, but not the other way around. The relationship + between ForwardsOnlyDataReader and CachingDataReader is an example of this). + Since the interface presented to the user is still forwards-only, queues are used to + store this information, so that dequeueing as we go we give the garbage collector the best opportunity + possible to reclaim any memory that is no longer in use. + ForwardsOnlyDataReader being used to actually + obtain the information from the server means that the "leg-work" is still only done (and need only be + maintained) in one place. + This class exists to allow for certain potential backwards-compatibility issues to be resolved + with little effort on the part of affected users. It is considerably less efficient than ForwardsOnlyDataReader + and hence never used internally. + + + + + Represents the method that allows the application to provide a certificate collection to be used for SSL clien authentication + + A X509CertificateCollection to be filled with one or more client certificates. + + + + !!! Helper class, for compilation only. + Connector implements the logic for the Connection Objects to + access the physical connection to the database, and isolate + the application developer from connection pooling internals. + + + + + Constructor. + + Controls whether the connector can be shared. + + + + This method checks if the connector is still ok. + We try to send a simple query text, select 1 as ConnectionTest; + + + + + This method is responsible for releasing all resources associated with this Connector. + + + + + This method is responsible to release all portals used by this Connector. + + + + + Default SSL CertificateSelectionCallback implementation. + + + + + Default SSL CertificateValidationCallback implementation. + + + + + Default SSL PrivateKeySelectionCallback implementation. + + + + + Default SSL ProvideClientCertificatesCallback implementation. + + + + + This method is required to set all the version dependent features flags. + SupportsPrepare means the server can use prepared query plans (7.3+) + + + + + Opens the physical connection to the server. + + Usually called by the RequestConnector + Method of the connection pool manager. + + + + Closes the physical connection to the server. + + + + + Returns next portal index. + + + + + Returns next plan index. + + + + + Occurs on NoticeResponses from the PostgreSQL backend. + + + + + Occurs on NotificationResponses from the PostgreSQL backend. + + + + + Called to provide client certificates for SSL handshake. + + + + + Mono.Security.Protocol.Tls.CertificateSelectionCallback delegate. + + + + + Mono.Security.Protocol.Tls.CertificateValidationCallback delegate. + + + + + Mono.Security.Protocol.Tls.PrivateKeySelectionCallback delegate. + + + + + Gets the current state of the connection. + + + + + Return Connection String. + + + + + Version of backend server this connector is connected to. + + + + + Backend protocol version in use by this connector. + + + + + The physical connection stream to the backend. + + + + + The physical connection socket to the backend. + + + + + Reports if this connector is fully connected. + + + + + The connection mediator. + + + + + Report if the connection is in a transaction. + + + + + Report whether the current connection can support prepare functionality. + + + + + This class contains helper methods for type conversion between + the .Net type system and postgresql. + + + + + A cache of basic datatype mappings keyed by server version. This way we don't + have to load the basic type mappings for every connection. + + + + + Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + of the given NpgsqlDbType. + + + + + Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + of the given NpgsqlDbType. + + + + + Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + of the given DbType. + + + + + Find a NpgsqlNativeTypeInfo in the default types map that can handle objects + of the given System.Type. + + + + + This method is responsible to convert the string received from the backend + to the corresponding NpgsqlType. + The given TypeInfo is called upon to do the conversion. + If no TypeInfo object is provided, no conversion is performed. + + + + + Create the one and only native to backend type map. + This map is used when formatting native data + types to backend representations. + + + + + This method creates (or retrieves from cache) a mapping between type and OID + of all natively supported postgresql data types. + This is needed as from one version to another, this mapping can be changed and + so we avoid hardcoding them. + + NpgsqlTypeMapping containing all known data types. The mapping must be + cloned before it is modified because it is cached; changes made by one connection may + effect another connection. + + + + Attempt to map types by issuing a query against pg_type. + This function takes a list of NpgsqlTypeInfo and attempts to resolve the OID field + of each by querying pg_type. If the mapping is found, the type info object is + updated (OID) and added to the provided NpgsqlTypeMapping object. + + NpgsqlConnector to send query through. + Mapping object to add types too. + List of types that need to have OID's mapped. + + + + Delegate called to convert the given backend data to its native representation. + + + + + Delegate called to convert the given native data to its backand representation. + + + + + Represents a backend data type. + This class can be called upon to convert a backend field representation to a native object. + + + + + Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. + + Type OID provided by the backend server. + Type name provided by the backend server. + NpgsqlDbType + System type to convert fields of this type to. + Data conversion handler. + + + + Perform a data conversion from a backend representation to + a native object. + + Data sent from the backend. + Type modifier field sent from the backend. + + + + Type OID provided by the backend server. + + + + + Type name provided by the backend server. + + + + + NpgsqlDbType. + + + + + NpgsqlDbType. + + + + + Provider type to convert fields of this type to. + + + + + System type to convert fields of this type to. + + + + + Represents a backend data type. + This class can be called upon to convert a native object to its backend field representation, + + + + + Returns an NpgsqlNativeTypeInfo for an array where the elements are of the type + described by the NpgsqlNativeTypeInfo supplied. + + + + + Construct a new NpgsqlTypeInfo with the given attributes and conversion handlers. + + Type name provided by the backend server. + NpgsqlDbType + Data conversion handler. + + + + Perform a data conversion from a native object to + a backend representation. + DBNull and null values are handled differently depending if a plain query is used + When + + Native .NET object to be converted. + Flag indicating if the conversion has to be done for + plain queries or extended queries + + + + Type name provided by the backend server. + + + + + NpgsqlDbType. + + + + + DbType. + + + + + Apply quoting. + + + + + Use parameter size information. + + + + + Provide mapping between type OID, type name, and a NpgsqlBackendTypeInfo object that represents it. + + + + + Construct an empty mapping. + + + + + Copy constuctor. + + + + + Add the given NpgsqlBackendTypeInfo to this mapping. + + + + + Add a new NpgsqlBackendTypeInfo with the given attributes and conversion handlers to this mapping. + + Type OID provided by the backend server. + Type name provided by the backend server. + NpgsqlDbType + System type to convert fields of this type to. + Data conversion handler. + + + + Make a shallow copy of this type mapping. + + + + + Determine if a NpgsqlBackendTypeInfo with the given backend type OID exists in this mapping. + + + + + Determine if a NpgsqlBackendTypeInfo with the given backend type name exists in this mapping. + + + + + Get the number of type infos held. + + + + + Retrieve the NpgsqlBackendTypeInfo with the given backend type OID, or null if none found. + + + + + Retrieve the NpgsqlBackendTypeInfo with the given backend type name, or null if none found. + + + + + Provide mapping between type Type, NpgsqlDbType and a NpgsqlNativeTypeInfo object that represents it. + + + + + Add the given NpgsqlNativeTypeInfo to this mapping. + + + + + Add a new NpgsqlNativeTypeInfo with the given attributes and conversion handlers to this mapping. + + Type name provided by the backend server. + NpgsqlDbType + Data conversion handler. + + + + Retrieve the NpgsqlNativeTypeInfo with the given NpgsqlDbType. + + + + + Retrieve the NpgsqlNativeTypeInfo with the given DbType. + + + + + Retrieve the NpgsqlNativeTypeInfo with the given Type. + + + + + Determine if a NpgsqlNativeTypeInfo with the given backend type name exists in this mapping. + + + + + Determine if a NpgsqlNativeTypeInfo with the given NpgsqlDbType exists in this mapping. + + + + + Determine if a NpgsqlNativeTypeInfo with the given Type name exists in this mapping. + + + + + Get the number of type infos held. + + + + + Implements for version 3 of the protocol. + + + + + Reads a row, field by field, allowing a DataRow to be built appropriately. + + + + + Reads part of a field, as needed (for + and + + + + + Adds further functionality to stream that is dependant upon the type of data read. + + + + + Completes the implementation of Streamer for char data. + + + + + Completes the implementation of Streamer for byte data. + + + + + Implements for version 2 of the protocol. + + + + + Encapsulates the null mapping bytes sent at the start of a version 2 + datarow message, and the process of identifying the nullity of the data + at a particular index + + + + + Provides the underlying mechanism for reading schema information. + + + + + Creates an NpgsqlSchema that can read schema information from the database. + + An open database connection for reading metadata. + + + + Returns the MetaDataCollections that lists all possible collections. + + The MetaDataCollections + + + + Returns the Restrictions that contains the meaning and position of the values in the restrictions array. + + The Restrictions + + + + Returns the Databases that contains a list of all accessable databases. + + The restrictions to filter the collection. + The Databases + + + + Returns the Tables that contains table and view names and the database and schema they come from. + + The restrictions to filter the collection. + The Tables + + + + Returns the Columns that contains information about columns in tables. + + The restrictions to filter the collection. + The Columns. + + + + Returns the Views that contains view names and the database and schema they come from. + + The restrictions to filter the collection. + The Views + + + + Returns the Users containing user names and the sysid of those users. + + The restrictions to filter the collection. + The Users. + + + + This is the abstract base class for NpgsqlAsciiRow and NpgsqlBinaryRow. + + + + + Implements a bit string; a collection of zero or more bits which can each be 1 or 0. + BitString's behave as a list of bools, though like most strings and unlike most collections the position + tends to be of as much significance as the value. + BitStrings are often used as masks, and are commonly cast to and from other values. + + + + + Represents the empty string. + + + + + Create a BitString from an enumeration of boolean values. The BitString will contain + those booleans in the order they came in. + + The boolean values. + + + + Creates a BitString filled with a given number of true or false values. + + The value to fill the string with. + The number of bits to fill. + + + + Creats a bitstring from a string. + The string to copy from. + + + + + + Creates a single-bit element from a boolean value. + + The bool value which determines whether + the bit is 1 or 0. + + + + Creates a bitstring from an unsigned integer value. The string will be the shortest required to + contain the integer (e.g. 1 bit for 0 or 1, 2 for 2 or 3, 3 for 4-7, and so on). + + The integer. + This method is not CLS Compliant, and may not be available to some languages. + + + + Creates a bitstring from an integer value. The string will be the shortest required to + contain the integer (e.g. 1 bit for 0 or 1, 2 for 2 or 3, 3 for 4-7, and so on). + + The integer. + + + + Finds the first instance of a given value + + The value - whether true or false - to search for. + The index of the value found, or -1 if none are present. + + + + True if there is at least one bit with the value looked for. + + The value - true or false - to detect. + True if at least one bit was the same as item, false otherwise. + + + + Copies the bitstring to an array of bools. + + The boolean array to copy to. + The index in the array to start copying from. + + + + Returns an enumerator that enumerates through the string. + + The enumerator. + + + + Creats a bitstring by concatenating another onto this one. + + The string to append to this one. + The combined strings. + + + + Returns a substring of this string. + + The position to start from, must be between 0 and the length of the string. + The length of the string to return, must be greater than zero, and may not be + so large that the start + length exceeds the bounds of this instance. + The Bitstring identified + + + + Returns a substring of this string. + + The position to start from, must be between 0 and the length of the string, + the rest of the string is returned. + The Bitstring identified + + + + A logical and between this string and another. The two strings must be the same length. + + Another BitString to AND with this one. + A bitstring with 1 where both BitStrings had 1 and 0 otherwise. + + + + A logical or between this string and another. The two strings must be the same length. + + Another BitString to OR with this one. + A bitstring with 1 where either BitString had 1 and 0 otherwise. + + + + A logical xor between this string and another. The two strings must be the same length. + + Another BitString to XOR with this one. + A bitstring with 1 where one BitStrings and the other had 0, + and 0 where they both had 1 or both had 0. + + + + A bitstring that is the logical inverse of this one. + + A bitstring of the same length as this with 1 where this has 0 and vice-versa. + + + + Shifts the string operand bits to the left, filling with zeros to produce a + string of the same length. + + The number of bits to shift to the left. + A left-shifted bitstring. + The behaviour of LShift is closer to what one would expect from dealing + with PostgreSQL bit-strings than in using the same operations on integers in .NET + In particular, negative operands result in a right-shift, and operands greater than + the length of the string will shift it entirely, resulting in a zero-filled string. + + + + + Shifts the string operand bits to the right, filling with zeros to produce a + string of the same length. + + The number of bits to shift to the right. + A right-shifted bitstring. + The behaviour of RShift is closer to what one would expect from dealing + with PostgreSQL bit-strings than in using the same operations on integers in .NET + In particular, negative operands result in a left-shift, and operands greater than + the length of the string will shift it entirely, resulting in a zero-filled string. It also performs + a logical shift, rather than an arithmetic shift, so it always sets the vacated bit positions to zero + (like PostgreSQL and like .NET for unsigned integers but not for signed integers). + + + + + Returns true if the this string is identical to the argument passed. + + + + + Compares two strings. Strings are compared as strings, so while 0 being less than 1 will + mean a comparison between two strings of the same size is the same as treating them as numbers, + in the case of two strings of differing lengths the comparison starts at the right-most (most significant) + bit, and if all bits of the shorter string are exhausted without finding a comparison, then the larger + string is deemed to be greater than the shorter (0010 is greater than 0001 but less than 00100). + + Another string to compare with this one. + A value if the two strings are identical, an integer less + than zero if this is less than the argument, and an integer greater + than zero otherwise. + + + + Compares the string with another object. + + The object to compare with. + If the object is null then this string is considered greater. If the object is another BitString + then they are compared as in the explicit comparison for BitStrings + in any other case a is thrown. + + + + Compares this BitString with an object for equality. + + + + + Returns a code for use in hashing operations. + + + + + Returns a string representation of the BitString. + + + A string which can contain a letter and optionally a number which sets a minimum size for the string + returned. In each case using the lower-case form of the letter will result in a lower-case string + being returned. + + + B + A string of 1s and 0s. + + + X + An hexadecimal string (will result in an error unless the string's length is divisible by 4). + + + G + A string of 1s and 0s in single-quotes preceded by 'B' (Postgres bit string literal syntax). + + Y + An hexadecimal string in single-quotes preceded by 'X' (Postgres bit literal syntax, will result in an error unless the string's length is divisible by 4. + + C + The format produced by format-string "Y" if legal, otherwise that produced by format-string "G". + E + The most compact safe representation for Postgres. If single bit will be either a 0 or a 1. Otherwise if it + can be that produce by format string "Y" it will, otherwise if there are less than 9bits in length it will be that + produced by format-string "G". For longer strings that cannot be represented in hexadecimal it will be a string + representing the first part of the string in format "Y" followed by the PostgreSQL concatenation operator, followed + by the final bits in the format "G". E.g. "X'13DCE'||B'110'" + If format is empty or null, it is treated as if "B" had been passed (the default repreesentation, and that + generally used by PostgreSQL for display). + + The formatted string. + + + + Returns a string representation for the Bitstring + + A string containing '0' and '1' characters. + + + + Returns the same string as . formatProvider is ignored. + + + + + Parses a string to produce a BitString. Most formats that can be produced by + can be accepted, but hexadecimal + can be interpreted with the preceding X' to mark the following characters as + being hexadecimal rather than binary. + + + + + Performs a logical AND on the two operands. + + + + + Performs a logcial OR on the two operands. + + + + + Perofrms a logical EXCLUSIVE-OR on the two operands + + + + + Performs a logical NOT on the operand. + + + + + Concatenates the operands. + + + + + Left-shifts the string BitString. + + + + + Right-shifts the string BitString. + + + + + Compares the two operands. + + + + + Compares the two operands. + + + + + Compares the two operands. + + + + + Compares the two operands. + + + + + Compares the two operands. + + + + + Compares the two operands. + + + + + Interprets the bitstring as a series of bits in an encoded character string, + encoded according to the Encoding passed, and returns that string. + The bitstring must contain a whole number of octets(bytes) and also be + valid according to the Encoding passed. + + The to use in producing the string. + The string that was encoded in the BitString. + + + + Interprets the bitstring as a series of octets (bytes) and returns those octets. Fails + if the Bitstring does not contain a whole number of octets (its length is not evenly + divisible by 8). + + + + + Interprets the bitstring as a series of signed octets (bytes) and returns those octets. Fails + if the Bitstring does not contain a whole number of octets (its length is not evenly + divisible by 8). + This method is not CLS-Compliant and may not be available to languages that cannot + handle signed bytes. + + + + + Interprets the bitstring as a series of unsigned 16-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 16. + This method is not CLS-Compliant and may not be available to languages that cannot + handle unsigned integers. + + + + + Interprets the bitstring as a series of 16-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 16. + + + + + Interprets the bitstring as a series of unsigned 32-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 32. + This method is not CLS-Compliant and may not be available to languages that cannot + handle unsigned integers. + + + + + Interprets the bitstring as a series of signed 32-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 32. + + + + + Interprets the bitstring as a series of unsigned 64-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 64. + This method is not CLS-Compliant and may not be available to languages that cannot + handle unsigned integers. + + + + + Interprets the bitstring as a series of signed 64-bit integers and returns those integers. + Fails if the Bitstring's length is not evenly divisible by 64. + + + + + The length of the string. + + + + + Retrieves the value of the bit at the given index. + + + + + C# implementation of the MD5 cryptographic hash function. + + + + + Creates a new MD5CryptoServiceProvider. + + + + + Drives the hashing function. + + Byte array containing the data to hash. + Where in the input buffer to start. + Size in bytes of the data in the buffer to hash. + + + + This finalizes the hash. Takes the data from the chaining variables and returns it. + + + + + Resets the class after use. Called automatically after hashing is done. + + + + + This is the meat of the hash function. It is what processes each block one at a time. + + Byte array to process data from. + Where in the byte array to start processing. + + + + Pads and then processes the final block. + + Buffer to grab data from. + Position in buffer in bytes to get data from. + How much data in bytes in the buffer to use. + + + + Stream for writing data to a table on a PostgreSQL version 7.4 or newer database during an active COPY FROM STDIN operation. + Passes data exactly as is and when given, so see to it that you use server encoding, correct format and reasonably sized writes! + + + + + Created only by NpgsqlCopyInState.StartCopy() + + + + + Successfully completes copying data to server. Returns after operation is finished. + Does nothing if this stream is not the active copy operation writer. + + + + + Withdraws an already started copy operation. The operation will fail with given error message. + Does nothing if this stream is not the active copy operation writer. + + + + + Writes given bytes to server. + Fails if this stream is not the active copy operation writer. + + + + + Flushes stream contents to server. + Fails if this stream is not the active copy operation writer. + + + + + Not readable + + + + + Not seekable + + + + + Not supported + + + + + True while this stream can be used to write copy data to server + + + + + False + + + + + True + + + + + False + + + + + Number of bytes written so far + + + + + Number of bytes written so far; not settable + + + + + Represents a SQL statement or function (stored procedure) to execute + against a PostgreSQL database. This class cannot be inherited. + + + + + Initializes a new instance of the NpgsqlCommand class. + + + + + Initializes a new instance of the NpgsqlCommand class with the text of the query. + + The text of the query. + + + + Initializes a new instance of the NpgsqlCommand class with the text of the query and a NpgsqlConnection. + + The text of the query. + A NpgsqlConnection that represents the connection to a PostgreSQL server. + + + + Initializes a new instance of the NpgsqlCommand class with the text of the query, a NpgsqlConnection, and the NpgsqlTransaction. + + The text of the query. + A NpgsqlConnection that represents the connection to a PostgreSQL server. + The NpgsqlTransaction in which the NpgsqlCommand executes. + + + + Used to execute internal commands. + + + + + Attempts to cancel the execution of a NpgsqlCommand. + + This Method isn't implemented yet. + + + + Create a new command based on this one. + + A new NpgsqlCommand object. + + + + Create a new command based on this one. + + A new NpgsqlCommand object. + + + + Creates a new instance of an DbParameter object. + + An DbParameter object. + + + + Creates a new instance of a NpgsqlParameter object. + + A NpgsqlParameter object. + + + + Slightly optimised version of ExecuteNonQuery() for internal ues in cases where the number + of affected rows is of no interest. + + + + + Executes a SQL statement against the connection and returns the number of rows affected. + + The number of rows affected if known; -1 otherwise. + + + + Sends the CommandText to + the Connection and builds a + NpgsqlDataReader + using one of the CommandBehavior values. + + One of the CommandBehavior values. + A NpgsqlDataReader object. + + + + Sends the CommandText to + the Connection and builds a + NpgsqlDataReader. + + A NpgsqlDataReader object. + + + + Sends the CommandText to + the Connection and builds a + NpgsqlDataReader + using one of the CommandBehavior values. + + One of the CommandBehavior values. + A NpgsqlDataReader object. + Currently the CommandBehavior parameter is ignored. + + + + This method binds the parameters from parameters collection to the bind + message. + + + + + Executes the query, and returns the first column of the first row + in the result set returned by the query. Extra columns or rows are ignored. + + The first column of the first row in the result set, + or a null reference if the result set is empty. + + + + Creates a prepared version of the command on a PostgreSQL server. + + + + + This method checks the connection state to see if the connection + is set or it is open. If one of this conditions is not met, throws + an InvalidOperationException + + + + + This method substitutes the Parameters, if exist, in the command + to their actual values. + The parameter name format is :ParameterName. + + A version of CommandText with the Parameters inserted. + + + + Gets or sets the SQL statement or function (stored procedure) to execute at the data source. + + The Transact-SQL statement or stored procedure to execute. The default is an empty string. + + + + Gets or sets the wait time before terminating the attempt + to execute a command and generating an error. + + The time (in seconds) to wait for the command to execute. + The default is 20 seconds. + + + + Gets or sets a value indicating how the + CommandText property is to be interpreted. + + One of the CommandType values. The default is CommandType.Text. + + + + Gets or sets the NpgsqlConnection + used by this instance of the NpgsqlCommand. + + The connection to a data source. The default value is a null reference. + + + + Gets the NpgsqlParameterCollection. + + The parameters of the SQL statement or function (stored procedure). The default is an empty collection. + + + + Gets or sets the NpgsqlTransaction + within which the NpgsqlCommand executes. + + The NpgsqlTransaction. + The default value is a null reference. + + + + Gets or sets how command results are applied to the DataRow + when used by the Update + method of the DbDataAdapter. + + One of the UpdateRowSource values. + + + + Returns oid of inserted row. This is only updated when using executenonQuery and when command inserts just a single row. If table is created without oids, this will always be 0. + + + + + Represents a collection of parameters relevant to a NpgsqlCommand + as well as their respective mappings to columns in a DataSet. + This class cannot be inherited. + + + + + Initializes a new instance of the NpgsqlParameterCollection class. + + + + + Adds the specified NpgsqlParameter object to the NpgsqlParameterCollection. + + The NpgsqlParameter to add to the collection. + The index of the new NpgsqlParameter object. + + + + Adds a NpgsqlParameter to the NpgsqlParameterCollection given the specified parameter name and value. + + The name of the NpgsqlParameter. + The Value of the NpgsqlParameter to add to the collection. + The index of the new NpgsqlParameter object. + + Use caution when using this overload of the + Add method to specify integer parameter values. + Because this overload takes a value of type Object, + you must convert the integral value to an Object + type when the value is zero, as the following C# example demonstrates. + parameters.Add(":pname", Convert.ToInt32(0)); + If you do not perform this conversion, the compiler will assume you + are attempting to call the NpgsqlParameterCollection.Add(string, DbType) overload. + + + + + Adds a NpgsqlParameter to the NpgsqlParameterCollection given the parameter name and the data type. + + The name of the parameter. + One of the DbType values. + The index of the new NpgsqlParameter object. + + + + Adds a NpgsqlParameter to the NpgsqlParameterCollection with the parameter name, the data type, and the column length. + + The name of the parameter. + One of the DbType values. + The length of the column. + The index of the new NpgsqlParameter object. + + + + Adds a NpgsqlParameter to the NpgsqlParameterCollection with the parameter name, the data type, the column length, and the source column name. + + The name of the parameter. + One of the DbType values. + The length of the column. + The name of the source column. + The index of the new NpgsqlParameter object. + + + + Removes the specified NpgsqlParameter from the collection using the parameter name. + + The name of the NpgsqlParameter object to retrieve. + + + + Gets a value indicating whether a NpgsqlParameter with the specified parameter name exists in the collection. + + The name of the NpgsqlParameter object to find. + true if the collection contains the parameter; otherwise, false. + + + + Gets the location of the NpgsqlParameter in the collection with a specific parameter name. + + The name of the NpgsqlParameter object to find. + The zero-based location of the NpgsqlParameter in the collection. + + + + Removes the specified NpgsqlParameter from the collection using a specific index. + + The zero-based index of the parameter. + + + + Inserts a NpgsqlParameter into the collection at the specified index. + + The zero-based index where the parameter is to be inserted within the collection. + The NpgsqlParameter to add to the collection. + + + + Removes the specified NpgsqlParameter from the collection. + + The NpgsqlParameter to remove from the collection. + + + + Gets a value indicating whether a NpgsqlParameter exists in the collection. + + The value of the NpgsqlParameter object to find. + true if the collection contains the NpgsqlParameter object; otherwise, false. + + + + Gets a value indicating whether a NpgsqlParameter with the specified parameter name exists in the collection. + + The name of the NpgsqlParameter object to find. + A reference to the requested parameter is returned in this out param if it is found in the list. This value is null if the parameter is not found. + true if the collection contains the parameter and param will contain the parameter; otherwise, false. + + + + Removes all items from the collection. + + + + + Gets the location of a NpgsqlParameter in the collection. + + The value of the NpgsqlParameter object to find. + The zero-based index of the NpgsqlParameter object in the collection. + + + + Adds the specified NpgsqlParameter object to the NpgsqlParameterCollection. + + The NpgsqlParameter to add to the collection. + The zero-based index of the new NpgsqlParameter object. + + + + Copies NpgsqlParameter objects from the NpgsqlParameterCollection to the specified array. + + An Array to which to copy the NpgsqlParameter objects in the collection. + The starting index of the array. + + + + Returns an enumerator that can iterate through the collection. + + An IEnumerator that can be used to iterate through the collection. + + + + In methods taking an object as argument this method is used to verify + that the argument has the type NpgsqlParameter + + The object to verify + + + + Gets the NpgsqlParameter with the specified name. + + The name of the NpgsqlParameter to retrieve. + The NpgsqlParameter with the specified name, or a null reference if the parameter is not found. + + + + Gets the NpgsqlParameter at the specified index. + + The zero-based index of the NpgsqlParameter to retrieve. + The NpgsqlParameter at the specified index. + + + + Gets the number of NpgsqlParameter objects in the collection. + + The number of NpgsqlParameter objects in the collection. + + + + Represents an ongoing COPY FROM STDIN operation. + Provides methods to push data to server and end or cancel the operation. + + + + + Called from NpgsqlState.ProcessBackendResponses upon CopyInResponse. + If CopyStream is already set, it is used to read data to push to server, after which the copy is completed. + Otherwise CopyStream is set to a writable NpgsqlCopyInStream that calls SendCopyData each time it is written to. + + + + + Sends given packet to server as a CopyData message. + Does not check for notifications! Use another thread for that. + + + + + Sends CopyDone message to server. Handles responses, ie. may throw an exception. + + + + + Sends CopyFail message to server. Handles responses, ie. should always throw an exception: + in CopyIn state the server responds to CopyFail with an error response; + outside of a CopyIn state the server responds to CopyFail with an error response; + without network connection or whatever, there's going to eventually be a failure, timeout or user intervention. + + + + + Copy format information returned from server. + + + + + Represents a PostgreSQL Point type + + + + + Represents a PostgreSQL Line Segment type. + + + + + Represents a PostgreSQL Path type. + + + + + Represents a PostgreSQL Polygon type. + + + + + Represents a PostgreSQL Circle type. + + + + + Represents a PostgreSQL inet type. + + + + + Represents a PostgreSQL MacAddress type. + + + + + + + The macAddr parameter must contain a string that can only consist of numbers + and upper-case letters as hexadecimal digits. (See PhysicalAddress.Parse method on MSDN) + + + + This class represents a PasswordPacket message sent to backend + PostgreSQL. + + +
+
diff --git a/bin/Ode.NET.dll b/bin/Ode.NET.dll new file mode 100755 index 0000000000..fcf17b1b9d Binary files /dev/null and b/bin/Ode.NET.dll differ diff --git a/bin/Ode.NET.dll.config b/bin/Ode.NET.dll.config new file mode 100644 index 0000000000..c72c28119e --- /dev/null +++ b/bin/Ode.NET.dll.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bin/OpenMetaverse.Rendering.Meshmerizer.dll b/bin/OpenMetaverse.Rendering.Meshmerizer.dll new file mode 100755 index 0000000000..4f55376b0b Binary files /dev/null and b/bin/OpenMetaverse.Rendering.Meshmerizer.dll differ diff --git a/bin/OpenMetaverse.StructuredData.XML b/bin/OpenMetaverse.StructuredData.XML new file mode 100644 index 0000000000..8f0dd817a3 --- /dev/null +++ b/bin/OpenMetaverse.StructuredData.XML @@ -0,0 +1,349 @@ + + + + OpenMetaverse.StructuredData + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Uses reflection to create an SDMap from all of the SD + serializable types in an object + + Class or struct containing serializable types + An SDMap holding the serialized values from the + container object + + + + Uses reflection to deserialize member variables in an object from + an SDMap + + Reference to an object to fill with deserialized + values + Serialized values to put in the target + object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Deserializes binary LLSD + + Serialized data + OSD containting deserialized data + + + + Deserializes binary LLSD + + Stream to read the data from + OSD containting deserialized data + + + + Serializes OSD to binary format. It does no prepend header + + OSD to serialize + Serialized data + + + + Serializes OSD to binary format + + OSD to serialize + + Serialized data + + + + Serializes OSD to binary format. It does no prepend header + + OSD to serialize + Serialized data + + + + Serializes OSD to binary format + + OSD to serialize + + Serialized data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/OpenMetaverse.StructuredData.dll b/bin/OpenMetaverse.StructuredData.dll new file mode 100755 index 0000000000..8c72174e68 Binary files /dev/null and b/bin/OpenMetaverse.StructuredData.dll differ diff --git a/bin/OpenMetaverse.XML b/bin/OpenMetaverse.XML new file mode 100644 index 0000000000..36e5b9272b --- /dev/null +++ b/bin/OpenMetaverse.XML @@ -0,0 +1,36240 @@ + + + + OpenMetaverse + + + + + Permission request flags, asked when a script wants to control an Avatar + + + + Placeholder for empty values, shouldn't ever see this + + + Script wants ability to take money from you + + + Script wants to take camera controls for you + + + Script wants to remap avatars controls + + + Script wants to trigger avatar animations + This function is not implemented on the grid + + + Script wants to attach or detach the prim or primset to your avatar + + + Script wants permission to release ownership + This function is not implemented on the grid + The concept of "public" objects does not exist anymore. + + + Script wants ability to link/delink with other prims + + + Script wants permission to change joints + This function is not implemented on the grid + + + Script wants permissions to change permissions + This function is not implemented on the grid + + + Script wants to track avatars camera position and rotation + + + Script wants to control your camera + + + Script wants the ability to teleport you + + + + Special commands used in Instant Messages + + + + Indicates a regular IM from another agent + + + Simple notification box with an OK button + + + You've been invited to join a group. + + + Inventory offer + + + Accepted inventory offer + + + Declined inventory offer + + + Group vote + + + An object is offering its inventory + + + Accept an inventory offer from an object + + + Decline an inventory offer from an object + + + Unknown + + + Start a session, or add users to a session + + + Start a session, but don't prune offline users + + + Start a session with your group + + + Start a session without a calling card (finder or objects) + + + Send a message to a session + + + Leave a session + + + Indicates that the IM is from an object + + + Sent an IM to a busy user, this is the auto response + + + Shows the message in the console and chat history + + + Send a teleport lure + + + Response sent to the agent which inititiated a teleport invitation + + + Response sent to the agent which inititiated a teleport invitation + + + Only useful if you have Linden permissions + + + Request a teleport lure + + + IM to tell the user to go to an URL + + + IM for help + + + IM sent automatically on call for help, sends a lure + to each Helper reached + + + Like an IM but won't go to email + + + IM from a group officer to all group members + + + Unknown + + + Unknown + + + Accept a group invitation + + + Decline a group invitation + + + Unknown + + + An avatar is offering you friendship + + + An avatar has accepted your friendship offer + + + An avatar has declined your friendship offer + + + Indicates that a user has started typing + + + Indicates that a user has stopped typing + + + + Flag in Instant Messages, whether the IM should be delivered to + offline avatars as well + + + + Only deliver to online avatars + + + If the avatar is offline the message will be held until + they login next, and possibly forwarded to their e-mail account + + + + Conversion type to denote Chat Packet types in an easier-to-understand format + + + + Whisper (5m radius) + + + Normal chat (10/20m radius), what the official viewer typically sends + + + Shouting! (100m radius) + + + Event message when an Avatar has begun to type + + + Event message when an Avatar has stopped typing + + + Send the message to the debug channel + + + Event message when an object uses llOwnerSay + + + Special value to support llRegionSay, never sent to the client + + + + Identifies the source of a chat message + + + + Chat from the grid or simulator + + + Chat from another avatar + + + Chat from an object + + + + + + + + + + + + + + + + + + Effect type used in ViewerEffect packets + + + + + + + + + + + + + + + + + + + + + + + + + Project a beam from a source to a destination, such as + the one used when editing an object + + + + + + + + + + + + Create a swirl of particles around an object + + + + + + + + + Cause an avatar to look at an object + + + Cause an avatar to point at an object + + + + The action an avatar is doing when looking at something, used in + ViewerEffect packets for the LookAt effect + + + + + + + + + + + + + + + + + + + + + + Deprecated + + + + + + + + + + + + + + + + The action an avatar is doing when pointing at something, used in + ViewerEffect packets for the PointAt effect + + + + + + + + + + + + + + + + + Money transaction types + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Flags sent when a script takes or releases a control + + NOTE: (need to verify) These might be a subset of the ControlFlags enum in Movement, + + + No Flags set + + + Forward (W or up Arrow) + + + Back (S or down arrow) + + + Move left (shift+A or left arrow) + + + Move right (shift+D or right arrow) + + + Up (E or PgUp) + + + Down (C or PgDown) + + + Rotate left (A or left arrow) + + + Rotate right (D or right arrow) + + + Left Mouse Button + + + Left Mouse button in MouseLook + + + + Currently only used to hide your group title + + + + No flags set + + + Hide your group title + + + + Action state of the avatar, which can currently be typing and + editing + + + + + + + + + + + + + + Current teleport status + + + + Unknown status + + + Teleport initialized + + + Teleport in progress + + + Teleport failed + + + Teleport completed + + + Teleport cancelled + + + + + + + + No flags set, or teleport failed + + + Set when newbie leaves help island for first time + + + + + + Via Lure + + + Via Landmark + + + Via Location + + + Via Home + + + Via Telehub + + + Via Login + + + Linden Summoned + + + Linden Forced me + + + + + + Agent Teleported Home via Script + + + + + + + + + + + + forced to new location for example when avatar is banned or ejected + + + Teleport Finished via a Lure + + + Finished, Sim Changed + + + Finished, Same Sim + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Type of mute entry + + + + Object muted by name + + + Muted residet + + + Object muted by UUID + + + Muted group + + + Muted external entry + + + + Flags of mute entry + + + + No exceptions + + + Don't mute text chat + + + Don't mute voice chat + + + Don't mute particles + + + Don't mute sounds + + + Don't mute + + + + Instant Message + + + + Key of sender + + + Name of sender + + + Key of destination avatar + + + ID of originating estate + + + Key of originating region + + + Coordinates in originating region + + + Instant message type + + + Group IM session toggle + + + Key of IM session, for Group Messages, the groups UUID + + + Timestamp of the instant message + + + Instant message text + + + Whether this message is held for offline avatars + + + Context specific packed data + + + Print the struct data as a string + A string containing the field name, and field value + + + Represents muted object or resident + + + Type of the mute entry + + + UUID of the mute etnry + + + Mute entry name + + + Mute flags + + + Transaction detail sent with MoneyBalanceReply message + + + Type of the transaction + + + UUID of the transaction source + + + Is the transaction source a group + + + UUID of the transaction destination + + + Is transaction destination a group + + + Transaction amount + + + Transaction description + + + + Manager class for our own avatar + + + + The event subscribers. null if no subcribers + + + Raises the ChatFromSimulator event + A ChatEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ScriptDialog event + A SctriptDialogEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ScriptQuestion event + A ScriptQuestionEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the LoadURL event + A LoadUrlEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the MoneyBalance event + A BalanceEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the MoneyBalanceReply event + A MoneyBalanceReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the IM event + A InstantMessageEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the TeleportProgress event + A TeleportEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AgentDataReply event + A AgentDataReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AnimationsChanged event + A AnimationsChangedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the MeanCollision event + A MeanCollisionEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the RegionCrossed event + A RegionCrossedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupChatJoined event + A GroupChatJoinedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AlertMessage event + A AlertMessageEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ScriptControlChange event + A ScriptControlEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the CameraConstraint event + A CameraConstraintEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ScriptSensorReply event + A ScriptSensorReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AvatarSitResponse event + A AvatarSitResponseEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ChatSessionMemberAdded event + A ChatSessionMemberAddedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ChatSessionMemberLeft event + A ChatSessionMemberLeftEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SetDisplayNameReply Event + A SetDisplayNameReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the MuteListUpdated event + A EventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + Reference to the GridClient instance + + + Used for movement and camera tracking + + + Currently playing animations for the agent. Can be used to + check the current movement status such as walking, hovering, aiming, + etc. by checking against system animations found in the Animations class + + + Dictionary containing current Group Chat sessions and members + + + Dictionary containing mute list keyead on mute name and key + + + Various abilities and preferences sent by the grid + + + + Constructor, setup callbacks for packets related to our avatar + + A reference to the Class + + + + Send a text message from the Agent to the Simulator + + A containing the message + The channel to send the message on, 0 is the public channel. Channels above 0 + can be used however only scripts listening on the specified channel will see the message + Denotes the type of message being sent, shout, whisper, etc. + + + + Request any instant messages sent while the client was offline to be resent. + + + + + Send an Instant Message to another Avatar + + The recipients + A containing the message to send + + + + Send an Instant Message to an existing group chat or conference chat + + The recipients + A containing the message to send + IM session ID (to differentiate between IM windows) + + + + Send an Instant Message + + The name this IM will show up as being from + Key of Avatar + Text message being sent + IM session ID (to differentiate between IM windows) + IDs of sessions for a conference + + + + Send an Instant Message + + The name this IM will show up as being from + Key of Avatar + Text message being sent + IM session ID (to differentiate between IM windows) + Type of instant message to send + Whether to IM offline avatars as well + Senders Position + RegionID Sender is In + Packed binary data that is specific to + the dialog type + + + + Send an Instant Message to a group + + of the group to send message to + Text Message being sent. + + + + Send an Instant Message to a group the agent is a member of + + The name this IM will show up as being from + of the group to send message to + Text message being sent + + + + Send a request to join a group chat session + + of Group to leave + + + + Exit a group chat session. This will stop further Group chat messages + from being sent until session is rejoined. + + of Group chat session to leave + + + + Reply to script dialog questions. + + Channel initial request came on + Index of button you're "clicking" + Label of button you're "clicking" + of Object that sent the dialog request + + + + + Accept invite for to a chatterbox session + + of session to accept invite to + + + + Start a friends conference + + List of UUIDs to start a conference with + the temportary session ID returned in the callback> + + + + Start a particle stream between an agent and an object + + Key of the source agent + Key of the target object + + The type from the enum + A unique for this effect + + + + Start a particle stream between an agent and an object + + Key of the source agent + Key of the target object + A representing the beams offset from the source + A which sets the avatars lookat animation + of the Effect + + + + Create a particle beam between an avatar and an primitive + + The ID of source avatar + The ID of the target primitive + global offset + A object containing the combined red, green, blue and alpha + color values of particle beam + a float representing the duration the parcicle beam will last + A Unique ID for the beam + + + + + Create a particle swirl around a target position using a packet + + global offset + A object containing the combined red, green, blue and alpha + color values of particle beam + a float representing the duration the parcicle beam will last + A Unique ID for the beam + + + + Sends a request to sit on the specified object + + of the object to sit on + Sit at offset + + + + Follows a call to to actually sit on the object + + + + Stands up from sitting on a prim or the ground + true of AgentUpdate was sent + + + + Does a "ground sit" at the avatar's current position + + + + + Starts or stops flying + + True to start flying, false to stop flying + + + + Starts or stops crouching + + True to start crouching, false to stop crouching + + + + Starts a jump (begin holding the jump key) + + + + + Use the autopilot sim function to move the avatar to a new + position. Uses double precision to get precise movements + + The z value is currently not handled properly by the simulator + Global X coordinate to move to + Global Y coordinate to move to + Z coordinate to move to + + + + Use the autopilot sim function to move the avatar to a new position + + The z value is currently not handled properly by the simulator + Integer value for the global X coordinate to move to + Integer value for the global Y coordinate to move to + Floating-point value for the Z coordinate to move to + + + + Use the autopilot sim function to move the avatar to a new position + + The z value is currently not handled properly by the simulator + Integer value for the local X coordinate to move to + Integer value for the local Y coordinate to move to + Floating-point value for the Z coordinate to move to + + + Macro to cancel autopilot sim function + Not certain if this is how it is really done + true if control flags were set and AgentUpdate was sent to the simulator + + + + Grabs an object + + an unsigned integer of the objects ID within the simulator + + + + + Overload: Grab a simulated object + + an unsigned integer of the objects ID within the simulator + + The texture coordinates to grab + The surface coordinates to grab + The face of the position to grab + The region coordinates of the position to grab + The surface normal of the position to grab (A normal is a vector perpindicular to the surface) + The surface binormal of the position to grab (A binormal is a vector tangen to the surface + pointing along the U direction of the tangent space + + + + Drag an object + + of the object to drag + Drag target in region coordinates + + + + Overload: Drag an object + + of the object to drag + Drag target in region coordinates + + The texture coordinates to grab + The surface coordinates to grab + The face of the position to grab + The region coordinates of the position to grab + The surface normal of the position to grab (A normal is a vector perpindicular to the surface) + The surface binormal of the position to grab (A binormal is a vector tangen to the surface + pointing along the U direction of the tangent space + + + + Release a grabbed object + + The Objects Simulator Local ID + + + + + + + Release a grabbed object + + The Objects Simulator Local ID + The texture coordinates to grab + The surface coordinates to grab + The face of the position to grab + The region coordinates of the position to grab + The surface normal of the position to grab (A normal is a vector perpindicular to the surface) + The surface binormal of the position to grab (A binormal is a vector tangen to the surface + pointing along the U direction of the tangent space + + + + Touches an object + + an unsigned integer of the objects ID within the simulator + + + + + Request the current L$ balance + + + + + Give Money to destination Avatar + + UUID of the Target Avatar + Amount in L$ + + + + Give Money to destination Avatar + + UUID of the Target Avatar + Amount in L$ + Description that will show up in the + recipients transaction history + + + + Give L$ to an object + + object to give money to + amount of L$ to give + name of object + + + + Give L$ to a group + + group to give money to + amount of L$ to give + + + + Give L$ to a group + + group to give money to + amount of L$ to give + description of transaction + + + + Pay texture/animation upload fee + + + + + Pay texture/animation upload fee + + description of the transaction + + + + Give Money to destination Object or Avatar + + UUID of the Target Object/Avatar + Amount in L$ + Reason (Optional normally) + The type of transaction + Transaction flags, mostly for identifying group + transactions + + + + Plays a gesture + + Asset of the gesture + + + + Mark gesture active + + Inventory of the gesture + Asset of the gesture + + + + Mark gesture inactive + + Inventory of the gesture + + + + Send an AgentAnimation packet that toggles a single animation on + + The of the animation to start playing + Whether to ensure delivery of this packet or not + + + + Send an AgentAnimation packet that toggles a single animation off + + The of a + currently playing animation to stop playing + Whether to ensure delivery of this packet or not + + + + Send an AgentAnimation packet that will toggle animations on or off + + A list of animation s, and whether to + turn that animation on or off + Whether to ensure delivery of this packet or not + + + + Teleports agent to their stored home location + + true on successful teleport to home location + + + + Teleport agent to a landmark + + of the landmark to teleport agent to + true on success, false on failure + + + + Attempt to look up a simulator name and teleport to the discovered + destination + + Region name to look up + Position to teleport to + True if the lookup and teleport were successful, otherwise + false + + + + Attempt to look up a simulator name and teleport to the discovered + destination + + Region name to look up + Position to teleport to + Target to look at + True if the lookup and teleport were successful, otherwise + false + + + + Teleport agent to another region + + handle of region to teleport agent to + position in destination sim to teleport to + true on success, false on failure + This call is blocking + + + + Teleport agent to another region + + handle of region to teleport agent to + position in destination sim to teleport to + direction in destination sim agent will look at + true on success, false on failure + This call is blocking + + + + Request teleport to a another simulator + + handle of region to teleport agent to + position in destination sim to teleport to + + + + Request teleport to a another simulator + + handle of region to teleport agent to + position in destination sim to teleport to + direction in destination sim agent will look at + + + + Teleport agent to a landmark + + of the landmark to teleport agent to + + + + Send a teleport lure to another avatar with default "Join me in ..." invitation message + + target avatars to lure + + + + Send a teleport lure to another avatar with custom invitation message + + target avatars to lure + custom message to send with invitation + + + + Respond to a teleport lure by either accepting it and initiating + the teleport, or denying it + + of the avatar sending the lure + IM session of the incoming lure request + true to accept the lure, false to decline it + + + + Update agent profile + + struct containing updated + profile information + + + + Update agents profile interests + + selection of interests from struct + + + + Set the height and the width of the client window. This is used + by the server to build a virtual camera frustum for our avatar + + New height of the viewer window + New width of the viewer window + + + + Request the list of muted objects and avatars for this agent + + + + + Mute an object, resident, etc. + + Mute type + Mute UUID + Mute name + + + + Mute an object, resident, etc. + + Mute type + Mute UUID + Mute name + Mute flags + + + + Unmute an object, resident, etc. + + Mute UUID + Mute name + + + + Sets home location to agents current position + + will fire an AlertMessage () with + success or failure message + + + + Move an agent in to a simulator. This packet is the last packet + needed to complete the transition in to a new simulator + + Object + + + + Reply to script permissions request + + Object + of the itemID requesting permissions + of the taskID requesting permissions + list of permissions to allow + + + + Respond to a group invitation by either accepting or denying it + + UUID of the group (sent in the AgentID field of the invite message) + IM Session ID from the group invitation message + Accept the group invitation or deny it + + + + Requests script detection of objects and avatars + + name of the object/avatar to search for + UUID of the object or avatar to search for + Type of search from ScriptSensorTypeFlags + range of scan (96 max?) + the arc in radians to search within + an user generated ID to correlate replies with + Simulator to perform search in + + + + Create or update profile pick + + UUID of the pick to update, or random UUID to create a new pick + Is this a top pick? (typically false) + UUID of the parcel (UUID.Zero for the current parcel) + Name of the pick + Global position of the pick landmark + UUID of the image displayed with the pick + Long description of the pick + + + + Delete profile pick + + UUID of the pick to delete + + + + Create or update profile Classified + + UUID of the classified to update, or random UUID to create a new classified + Defines what catagory the classified is in + UUID of the image displayed with the classified + Price that the classified will cost to place for a week + Global position of the classified landmark + Name of the classified + Long description of the classified + if true, auto renew classified after expiration + + + + Create or update profile Classified + + UUID of the classified to update, or random UUID to create a new classified + Defines what catagory the classified is in + UUID of the image displayed with the classified + Price that the classified will cost to place for a week + Name of the classified + Long description of the classified + if true, auto renew classified after expiration + + + + Delete a classified ad + + The classified ads ID + + + + Fetches resource usage by agents attachmetns + + Called when the requested information is collected + + + + Initates request to set a new display name + + Previous display name + Desired new display name + + + + Tells the sim what UI language is used, and if it's ok to share that with scripts + + Two letter language code + Share language info with scripts + + + + Sets agents maturity access level + + PG, M or A + + + + Sets agents maturity access level + + PG, M or A + Callback function + + + + Take an incoming ImprovedInstantMessage packet, auto-parse, and if + OnInstantMessage is defined call that with the appropriate arguments + + The sender + The EventArgs object containing the packet data + + + + Take an incoming Chat packet, auto-parse, and if OnChat is defined call + that with the appropriate arguments. + + The sender + The EventArgs object containing the packet data + + + + Used for parsing llDialogs + + The sender + The EventArgs object containing the packet data + + + + Used for parsing llRequestPermissions dialogs + + The sender + The EventArgs object containing the packet data + + + + Handles Script Control changes when Script with permissions releases or takes a control + + The sender + The EventArgs object containing the packet data + + + + Used for parsing llLoadURL Dialogs + + The sender + The EventArgs object containing the packet data + + + + Update client's Position, LookAt and region handle from incoming packet + + The sender + The EventArgs object containing the packet data + This occurs when after an avatar moves into a new sim + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + EQ Message fired with the result of SetDisplayName request + + The message key + the IMessage object containing the deserialized data sent from the simulator + The which originated the packet + + + + Process TeleportFailed message sent via EventQueue, informs agent its last teleport has failed and why. + + The Message Key + An IMessage object Deserialized from the recieved message event + The simulator originating the event message + + + + Process TeleportFinish from Event Queue and pass it onto our TeleportHandler + + The message system key for this event + IMessage object containing decoded data from OSD + The simulator originating the event message + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + Crossed region handler for message that comes across the EventQueue. Sent to an agent + when the agent crosses a sim border into a new region. + + The message key + the IMessage object containing the deserialized data sent from the simulator + The which originated the packet + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + This packet is now being sent via the EventQueue + + + + Group Chat event handler + + The capability Key + IMessage object containing decoded data from OSD + + + + + Response from request to join a group chat + + + IMessage object containing decoded data from OSD + + + + + Someone joined or left group chat + + + IMessage object containing decoded data from OSD + + + + + Handle a group chat Invitation + + Caps Key + IMessage object containing decoded data from OSD + Originating Simulator + + + + Moderate a chat session + + the of the session to moderate, for group chats this will be the groups UUID + the of the avatar to moderate + Either "voice" to moderate users voice, or "text" to moderate users text session + true to moderate (silence user), false to allow avatar to speak + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when a scripted object or agent within range sends a public message + + + Raised when a scripted object sends a dialog box containing possible + options an agent can respond to + + + Raised when an object requests a change in the permissions an agent has permitted + + + Raised when a script requests an agent open the specified URL + + + Raised when an agents currency balance is updated + + + Raised when a transaction occurs involving currency such as a land purchase + + + Raised when an ImprovedInstantMessage packet is recieved from the simulator, this is used for everything from + private messaging to friendship offers. The Dialog field defines what type of message has arrived + + + Raised when an agent has requested a teleport to another location, or when responding to a lure. Raised multiple times + for each teleport indicating the progress of the request + + + Raised when a simulator sends agent specific information for our avatar. + + + Raised when our agents animation playlist changes + + + Raised when an object or avatar forcefully collides with our agent + + + Raised when our agent crosses a region border into another region + + + Raised when our agent succeeds or fails to join a group chat session + + + Raised when a simulator sends an urgent message usually indication the recent failure of + another action we have attempted to take such as an attempt to enter a parcel where we are denied access + + + Raised when a script attempts to take or release specified controls for our agent + + + Raised when the simulator detects our agent is trying to view something + beyond its limits + + + Raised when a script sensor reply is received from a simulator + + + Raised in response to a request + + + Raised when an avatar enters a group chat session we are participating in + + + Raised when an agent exits a group chat session we are participating in + + + Raised when the simulator sends us data containing + the details of display name change + + + Raised when a scripted object or agent within range sends a public message + + + Your (client) avatars + "client", "agent", and "avatar" all represent the same thing + + + Temporary assigned to this session, used for + verifying our identity in packets + + + Shared secret that is never sent over the wire + + + Your (client) avatar ID, local to the current region/sim + + + Where the avatar started at login. Can be "last", "home" + or a login + + + The access level of this agent, usually M, PG or A + + + The CollisionPlane of Agent + + + An representing the velocity of our agent + + + An representing the acceleration of our agent + + + A which specifies the angular speed, and axis about which an Avatar is rotating. + + + Position avatar client will goto when login to 'home' or during + teleport request to 'home' region. + + + LookAt point saved/restored with HomePosition + + + Avatar First Name (i.e. Philip) + + + Avatar Last Name (i.e. Linden) + + + LookAt point received with the login response message + + + Avatar Full Name (i.e. Philip Linden) + + + Gets the health of the agent + + + Gets the current balance of the agent + + + Gets the local ID of the prim the agent is sitting on, + zero if the avatar is not currently sitting + + + Gets the of the agents active group. + + + Gets the Agents powers in the currently active group + + + Current status message for teleporting + + + Current position of the agent as a relative offset from + the simulator, or the parent object if we are sitting on something + + + Current rotation of the agent as a relative rotation from + the simulator, or the parent object if we are sitting on something + + + Current position of the agent in the simulator + + + + A representing the agents current rotation + + + + Returns the global grid position of the avatar + + + + Called once attachment resource usage information has been collected + + Indicates if operation was successfull + Attachment resource usage information + + + + Agent movement and camera control + + Agent movement is controlled by setting specific + After the control flags are set, An AgentUpdate is required to update the simulator of the specified flags + This is most easily accomplished by setting one or more of the AgentMovement properties + + Movement of an avatar is always based on a compass direction, for example AtPos will move the + agent from West to East or forward on the X Axis, AtNeg will of course move agent from + East to West or backward on the X Axis, LeftPos will be South to North or forward on the Y Axis + The Z axis is Up, finer grained control of movements can be done using the Nudge properties + + + + Agent camera controls + + + Currently only used for hiding your group title + + + Action state of the avatar, which can currently be + typing and editing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Timer for sending AgentUpdate packets + + + Default constructor + + + + Send an AgentUpdate with the camera set at the current agent + position and pointing towards the heading specified + + Camera rotation in radians + Whether to send the AgentUpdate reliable + or not + + + + Rotates the avatar body and camera toward a target position. + This will also anchor the camera position on the avatar + + Region coordinates to turn toward + + + + Rotates the avatar body and camera toward a target position. + This will also anchor the camera position on the avatar + + Region coordinates to turn toward + whether to send update or not + + + + Send new AgentUpdate packet to update our current camera + position and rotation + + + + + Send new AgentUpdate packet to update our current camera + position and rotation + + Whether to require server acknowledgement + of this packet + + + + Send new AgentUpdate packet to update our current camera + position and rotation + + Whether to require server acknowledgement + of this packet + Simulator to send the update to + + + + Builds an AgentUpdate packet entirely from parameters. This + will not touch the state of Self.Movement or + Self.Movement.Camera in any way + + + + + + + + + + + + + + + + Sends update of Field of Vision vertical angle to the simulator + + Angle in radians + + + Move agent positive along the X axis + + + Move agent negative along the X axis + + + Move agent positive along the Y axis + + + Move agent negative along the Y axis + + + Move agent positive along the Z axis + + + Move agent negative along the Z axis + + + + + + + + + + + + + + + + + + + + + + + + Causes simulator to make agent fly + + + Stop movement + + + Finish animation + + + Stand up from a sit + + + Tells simulator to sit agent on ground + + + Place agent into mouselook mode + + + Nudge agent positive along the X axis + + + Nudge agent negative along the X axis + + + Nudge agent positive along the Y axis + + + Nudge agent negative along the Y axis + + + Nudge agent positive along the Z axis + + + Nudge agent negative along the Z axis + + + + + + + + + Tell simulator to mark agent as away + + + + + + + + + + + + + + + + Returns "always run" value, or changes it by sending a SetAlwaysRunPacket + + + + The current value of the agent control flags + + + Gets or sets the interval in milliseconds at which + AgentUpdate packets are sent to the current simulator. Setting + this to a non-zero value will also enable the packet sending if + it was previously off, and setting it to zero will disable + + + Gets or sets whether AgentUpdate packets are sent to + the current simulator + + + Reset movement controls every time we send an update + + + + Camera controls for the agent, mostly a thin wrapper around + CoordinateFrame. This class is only responsible for state + tracking and math, it does not send any packets + + + + + + + The camera is a local frame of reference inside of + the larger grid space. This is where the math happens + + + + Default constructor + + + + + + + + + + + + + + + + + Used to specify movement actions for your agent + + + + Empty flag + + + Move Forward (SL Keybinding: W/Up Arrow) + + + Move Backward (SL Keybinding: S/Down Arrow) + + + Move Left (SL Keybinding: Shift-(A/Left Arrow)) + + + Move Right (SL Keybinding: Shift-(D/Right Arrow)) + + + Not Flying: Jump/Flying: Move Up (SL Keybinding: E) + + + Not Flying: Croutch/Flying: Move Down (SL Keybinding: C) + + + Unused + + + Unused + + + Unused + + + Unused + + + ORed with AGENT_CONTROL_AT_* if the keyboard is being used + + + ORed with AGENT_CONTROL_LEFT_* if the keyboard is being used + + + ORed with AGENT_CONTROL_UP_* if the keyboard is being used + + + Fly + + + + + + Finish our current animation + + + Stand up from the ground or a prim seat + + + Sit on the ground at our current location + + + Whether mouselook is currently enabled + + + Legacy, used if a key was pressed for less than a certain amount of time + + + Legacy, used if a key was pressed for less than a certain amount of time + + + Legacy, used if a key was pressed for less than a certain amount of time + + + Legacy, used if a key was pressed for less than a certain amount of time + + + Legacy, used if a key was pressed for less than a certain amount of time + + + Legacy, used if a key was pressed for less than a certain amount of time + + + + + + + + + Set when the avatar is idled or set to away. Note that the away animation is + activated separately from setting this flag + + + + + + + + + + + + + + + + Class for sending info on the success of the opration + of setting the maturity access level + + + + + Creates new instance of the EventArgs class + + Has setting new maturty access level succeeded + New maturity access level as returned by the simulator + + + + New maturity accesss level returned from the sim + + + + + True if setting the new maturity access level has succedded + + + + + + + + + + Construct a new instance of the ChatEventArgs object + + Sim from which the message originates + The message sent + The audible level of the message + The type of message sent: whisper, shout, etc + The source type of the message sender + The name of the agent or object sending the message + The ID of the agent or object sending the message + The ID of the object owner, or the agent ID sending the message + The position of the agent or object sending the message + + + Get the simulator sending the message + + + Get the message sent + + + Get the audible level of the message + + + Get the type of message sent: whisper, shout, etc + + + Get the source type of the message sender + + + Get the name of the agent or object sending the message + + + Get the ID of the agent or object sending the message + + + Get the ID of the object owner, or the agent ID sending the message + + + Get the position of the agent or object sending the message + + + Contains the data sent when a primitive opens a dialog with this agent + + + + Construct a new instance of the ScriptDialogEventArgs + + The dialog message + The name of the object that sent the dialog request + The ID of the image to be displayed + The ID of the primitive sending the dialog + The first name of the senders owner + The last name of the senders owner + The communication channel the dialog was sent on + The string labels containing the options presented in this dialog + UUID of the scritped object owner + + + Get the dialog message + + + Get the name of the object that sent the dialog request + + + Get the ID of the image to be displayed + + + Get the ID of the primitive sending the dialog + + + Get the first name of the senders owner + + + Get the last name of the senders owner + + + Get the communication channel the dialog was sent on, responses + should also send responses on this same channel + + + Get the string labels containing the options presented in this dialog + + + UUID of the scritped object owner + + + Contains the data sent when a primitive requests debit or other permissions + requesting a YES or NO answer + + + + Construct a new instance of the ScriptQuestionEventArgs + + The simulator containing the object sending the request + The ID of the script making the request + The ID of the primitive containing the script making the request + The name of the primitive making the request + The name of the owner of the object making the request + The permissions being requested + + + Get the simulator containing the object sending the request + + + Get the ID of the script making the request + + + Get the ID of the primitive containing the script making the request + + + Get the name of the primitive making the request + + + Get the name of the owner of the object making the request + + + Get the permissions being requested + + + Contains the data sent when a primitive sends a request + to an agent to open the specified URL + + + + Construct a new instance of the LoadUrlEventArgs + + The name of the object sending the request + The ID of the object sending the request + The ID of the owner of the object sending the request + True if the object is owned by a group + The message sent with the request + The URL the object sent + + + Get the name of the object sending the request + + + Get the ID of the object sending the request + + + Get the ID of the owner of the object sending the request + + + True if the object is owned by a group + + + Get the message sent with the request + + + Get the URL the object sent + + + The date received from an ImprovedInstantMessage + + + + Construct a new instance of the InstantMessageEventArgs object + + the InstantMessage object + the simulator where the InstantMessage origniated + + + Get the InstantMessage object + + + Get the simulator where the InstantMessage origniated + + + Contains the currency balance + + + + Construct a new BalanceEventArgs object + + The currenct balance + + + + Get the currenct balance + + + + Contains the transaction summary when an item is purchased, + money is given, or land is purchased + + + + Construct a new instance of the MoneyBalanceReplyEventArgs object + + The ID of the transaction + True of the transaction was successful + The current currency balance + The meters credited + The meters comitted + A brief description of the transaction + Transaction info + + + Get the ID of the transaction + + + True of the transaction was successful + + + Get the remaining currency balance + + + Get the meters credited + + + Get the meters comitted + + + Get the description of the transaction + + + Detailed transaction information + + + Data sent from the simulator containing information about your agent and active group information + + + + Construct a new instance of the AgentDataReplyEventArgs object + + The agents first name + The agents last name + The agents active group ID + The group title of the agents active group + The combined group powers the agent has in the active group + The name of the group the agent has currently active + + + Get the agents first name + + + Get the agents last name + + + Get the active group ID of your agent + + + Get the active groups title of your agent + + + Get the combined group powers of your agent + + + Get the active group name of your agent + + + Data sent by the simulator to indicate the active/changed animations + applied to your agent + + + + Construct a new instance of the AnimationsChangedEventArgs class + + The dictionary that contains the changed animations + + + Get the dictionary that contains the changed animations + + + + Data sent from a simulator indicating a collision with your agent + + + + + Construct a new instance of the MeanCollisionEventArgs class + + The type of collision that occurred + The ID of the agent or object that perpetrated the agression + The ID of the Victim + The strength of the collision + The Time the collision occurred + + + Get the Type of collision + + + Get the ID of the agent or object that collided with your agent + + + Get the ID of the agent that was attacked + + + A value indicating the strength of the collision + + + Get the time the collision occurred + + + Data sent to your agent when it crosses region boundaries + + + + Construct a new instance of the RegionCrossedEventArgs class + + The simulator your agent just left + The simulator your agent is now in + + + Get the simulator your agent just left + + + Get the simulator your agent is now in + + + Data sent from the simulator when your agent joins a group chat session + + + + Construct a new instance of the GroupChatJoinedEventArgs class + + The ID of the session + The name of the session + A temporary session id used for establishing new sessions + True of your agent successfully joined the session + + + Get the ID of the group chat session + + + Get the name of the session + + + Get the temporary session ID used for establishing new sessions + + + True if your agent successfully joined the session + + + Data sent by the simulator containing urgent messages + + + + Construct a new instance of the AlertMessageEventArgs class + + The alert message + + + Get the alert message + + + Data sent by a script requesting to take or release specified controls to your agent + + + + Construct a new instance of the ScriptControlEventArgs class + + The controls the script is attempting to take or release to the agent + True if the script is passing controls back to the agent + True if the script is requesting controls be released to the script + + + Get the controls the script is attempting to take or release to the agent + + + True if the script is passing controls back to the agent + + + True if the script is requesting controls be released to the script + + + + Data sent from the simulator to an agent to indicate its view limits + + + + + Construct a new instance of the CameraConstraintEventArgs class + + The collision plane + + + Get the collision plane + + + + Data containing script sensor requests which allow an agent to know the specific details + of a primitive sending script sensor requests + + + + + Construct a new instance of the ScriptSensorReplyEventArgs + + The ID of the primitive sending the sensor + The ID of the group associated with the primitive + The name of the primitive sending the sensor + The ID of the primitive sending the sensor + The ID of the owner of the primitive sending the sensor + The position of the primitive sending the sensor + The range the primitive specified to scan + The rotation of the primitive sending the sensor + The type of sensor the primitive sent + The velocity of the primitive sending the sensor + + + Get the ID of the primitive sending the sensor + + + Get the ID of the group associated with the primitive + + + Get the name of the primitive sending the sensor + + + Get the ID of the primitive sending the sensor + + + Get the ID of the owner of the primitive sending the sensor + + + Get the position of the primitive sending the sensor + + + Get the range the primitive specified to scan + + + Get the rotation of the primitive sending the sensor + + + Get the type of sensor the primitive sent + + + Get the velocity of the primitive sending the sensor + + + Contains the response data returned from the simulator in response to a + + + Construct a new instance of the AvatarSitResponseEventArgs object + + + Get the ID of the primitive the agent will be sitting on + + + True if the simulator Autopilot functions were involved + + + Get the camera offset of the agent when seated + + + Get the camera eye offset of the agent when seated + + + True of the agent will be in mouselook mode when seated + + + Get the position of the agent when seated + + + Get the rotation of the agent when seated + + + Data sent when an agent joins a chat session your agent is currently participating in + + + + Construct a new instance of the ChatSessionMemberAddedEventArgs object + + The ID of the chat session + The ID of the agent joining + + + Get the ID of the chat session + + + Get the ID of the agent that joined + + + Data sent when an agent exits a chat session your agent is currently participating in + + + + Construct a new instance of the ChatSessionMemberLeftEventArgs object + + The ID of the chat session + The ID of the Agent that left + + + Get the ID of the chat session + + + Get the ID of the agent that left + + + Event arguments with the result of setting display name operation + + + Default constructor + + + Status code, 200 indicates settign display name was successful + + + Textual description of the status + + + Details of the newly set display name + + + + Throttles the network traffic for various different traffic types. + Access this class through GridClient.Throttle + + + + + Default constructor, uses a default high total of 1500 KBps (1536000) + + + + + Constructor that decodes an existing AgentThrottle packet in to + individual values + + Reference to the throttle data in an AgentThrottle + packet + Offset position to start reading at in the + throttle data + This is generally not needed in clients as the server will + never send a throttle packet to the client + + + + Send an AgentThrottle packet to the current server using the + current values + + + + + Send an AgentThrottle packet to the specified server using the + current values + + + + + Convert the current throttle values to a byte array that can be put + in an AgentThrottle packet + + Byte array containing all the throttle values + + + Maximum bits per second for resending unacknowledged packets + + + Maximum bits per second for LayerData terrain + + + Maximum bits per second for LayerData wind data + + + Maximum bits per second for LayerData clouds + + + Unknown, includes object data + + + Maximum bits per second for textures + + + Maximum bits per second for downloaded assets + + + Maximum bits per second the entire connection, divided up + between invidiual streams using default multipliers + + + + Static pre-defined animations available to all agents + + + + Agent with afraid expression on face + + + Agent aiming a bazooka (right handed) + + + Agent aiming a bow (left handed) + + + Agent aiming a hand gun (right handed) + + + Agent aiming a rifle (right handed) + + + Agent with angry expression on face + + + Agent hunched over (away) + + + Agent doing a backflip + + + Agent laughing while holding belly + + + Agent blowing a kiss + + + Agent with bored expression on face + + + Agent bowing to audience + + + Agent brushing himself/herself off + + + Agent in busy mode + + + Agent clapping hands + + + Agent doing a curtsey bow + + + Agent crouching + + + Agent crouching while walking + + + Agent crying + + + Agent unanimated with arms out (e.g. setting appearance) + + + Agent re-animated after set appearance finished + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent dancing + + + Agent on ground unanimated + + + Agent boozing it up + + + Agent with embarassed expression on face + + + Agent with afraid expression on face + + + Agent with angry expression on face + + + Agent with bored expression on face + + + Agent crying + + + Agent showing disdain (dislike) for something + + + Agent with embarassed expression on face + + + Agent with frowning expression on face + + + Agent with kissy face + + + Agent expressing laughgter + + + Agent with open mouth + + + Agent with repulsed expression on face + + + Agent expressing sadness + + + Agent shrugging shoulders + + + Agent with a smile + + + Agent expressing surprise + + + Agent sticking tongue out + + + Agent with big toothy smile + + + Agent winking + + + Agent expressing worry + + + Agent falling down + + + Agent walking (feminine version) + + + Agent wagging finger (disapproval) + + + I'm not sure I want to know + + + Agent in superman position + + + Agent in superman position + + + Agent greeting another + + + Agent holding bazooka (right handed) + + + Agent holding a bow (left handed) + + + Agent holding a handgun (right handed) + + + Agent holding a rifle (right handed) + + + Agent throwing an object (right handed) + + + Agent in static hover + + + Agent hovering downward + + + Agent hovering upward + + + Agent being impatient + + + Agent jumping + + + Agent jumping with fervor + + + Agent point to lips then rear end + + + Agent landing from jump, finished flight, etc + + + Agent laughing + + + Agent landing from jump, finished flight, etc + + + Agent sitting on a motorcycle + + + + + + Agent moving head side to side + + + Agent moving head side to side with unhappy expression + + + Agent taunting another + + + + + + Agent giving peace sign + + + Agent pointing at self + + + Agent pointing at another + + + Agent preparing for jump (bending knees) + + + Agent punching with left hand + + + Agent punching with right hand + + + Agent acting repulsed + + + Agent trying to be Chuck Norris + + + Rocks, Paper, Scissors 1, 2, 3 + + + Agent with hand flat over other hand + + + Agent with fist over other hand + + + Agent with two fingers spread over other hand + + + Agent running + + + Agent appearing sad + + + Agent saluting + + + Agent shooting bow (left handed) + + + Agent cupping mouth as if shouting + + + Agent shrugging shoulders + + + Agent in sit position + + + Agent in sit position (feminine) + + + Agent in sit position (generic) + + + Agent sitting on ground + + + Agent sitting on ground + + + + + + Agent sleeping on side + + + Agent smoking + + + Agent inhaling smoke + + + + + + Agent taking a picture + + + Agent standing + + + Agent standing up + + + Agent standing + + + Agent standing + + + Agent standing + + + Agent standing + + + Agent stretching + + + Agent in stride (fast walk) + + + Agent surfing + + + Agent acting surprised + + + Agent striking with a sword + + + Agent talking (lips moving) + + + Agent throwing a tantrum + + + Agent throwing an object (right handed) + + + Agent trying on a shirt + + + Agent turning to the left + + + Agent turning to the right + + + Agent typing + + + Agent walking + + + Agent whispering + + + Agent whispering with fingers in mouth + + + Agent winking + + + Agent winking + + + Agent worried + + + Agent nodding yes + + + Agent nodding yes with happy face + + + Agent floating with legs and arms crossed + + + + A dictionary containing all pre-defined animations + + A dictionary containing the pre-defined animations, + where the key is the animations ID, and the value is a string + containing a name to identify the purpose of the animation + + + + Index of TextureEntry slots for avatar appearances + + + + + Bake layers for avatar appearance + + + + + Appearance Flags, introdued with server side baking, currently unused + + + + Maximum number of concurrent downloads for wearable assets and textures + + + Maximum number of concurrent uploads for baked textures + + + Timeout for fetching inventory listings + + + Timeout for fetching a single wearable, or receiving a single packet response + + + Timeout for fetching a single texture + + + Timeout for uploading a single baked texture + + + Number of times to retry bake upload + + + When changing outfit, kick off rebake after + 20 seconds has passed since the last change + + + Total number of wearables for each avatar + + + Total number of baked textures on each avatar + + + Total number of wearables per bake layer + + + Mask for multiple attachments + + + Mapping between BakeType and AvatarTextureIndex + + + Map of what wearables are included in each bake + + + Magic values to finalize the cache check hashes for each + bake + + + Default avatar texture, used to detect when a custom + texture is not set for a face + + + The event subscribers. null if no subcribers + + + Raises the AgentWearablesReply event + An AgentWearablesReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the CachedBakesReply event + An AgentCachedBakesReplyEventArgs object containing the + data returned from the data server AgentCachedTextureResponse + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AppearanceSet event + An AppearanceSetEventArgs object indicating if the operatin was successfull + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the RebakeAvatarRequested event + An RebakeAvatarTexturesEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + Visual parameters last sent to the sim + + + Textures about this client sent to the sim + + + A cache of wearables currently being worn + + + A cache of textures currently being worn + + + Incrementing serial number for AgentCachedTexture packets + + + Incrementing serial number for AgentSetAppearance packets + + + Indicates if WearablesRequest succeeded + + + Indicates whether or not the appearance thread is currently + running, to prevent multiple appearance threads from running + simultaneously + + + Reference to our agent + + + + Timer used for delaying rebake on changing outfit + + + + + Main appearance thread + + + + + Is server baking complete. It needs doing only once + + + + + Default constructor + + A reference to our agent + + + + Obsolete method for setting appearance. This function no longer does anything. + Use RequestSetAppearance() to manually start the appearance thread + + + + + Obsolete method for setting appearance. This function no longer does anything. + Use RequestSetAppearance() to manually start the appearance thread + + Unused parameter + + + + Starts the appearance setting thread + + + + + Starts the appearance setting thread + + True to force rebaking, otherwise false + + + + Check if current region supports server side baking + + True if server side baking support is detected + + + + Ask the server what textures our agent is currently wearing + + + + + Build hashes out of the texture assetIDs for each baking layer to + ask the simulator whether it has cached copies of each baked texture + + + + + Returns the AssetID of the asset that is currently being worn in a + given WearableType slot + + WearableType slot to get the AssetID for + The UUID of the asset being worn in the given slot, or + UUID.Zero if no wearable is attached to the given slot or wearables + have not been downloaded yet + + + + Add a wearable to the current outfit and set appearance + + Wearable to be added to the outfit + + + + Add a wearable to the current outfit and set appearance + + Wearable to be added to the outfit + Should existing item on the same point or of the same type be replaced + + + + Add a list of wearables to the current outfit and set appearance + + List of wearable inventory items to + be added to the outfit + Should existing item on the same point or of the same type be replaced + + + + Add a list of wearables to the current outfit and set appearance + + List of wearable inventory items to + be added to the outfit + Should existing item on the same point or of the same type be replaced + + + + Remove a wearable from the current outfit and set appearance + + Wearable to be removed from the outfit + + + + Removes a list of wearables from the current outfit and set appearance + + List of wearable inventory items to + be removed from the outfit + + + + Replace the current outfit with a list of wearables and set appearance + + List of wearable inventory items that + define a new outfit + + + + Replace the current outfit with a list of wearables and set appearance + + List of wearable inventory items that + define a new outfit + Check if we have all body parts, set this to false only + if you know what you're doing + + + + Checks if an inventory item is currently being worn + + The inventory item to check against the agent + wearables + The WearableType slot that the item is being worn in, + or WearbleType.Invalid if it is not currently being worn + + + + Returns a copy of the agents currently worn wearables + + A copy of the agents currently worn wearables + Avoid calling this function multiple times as it will make + a copy of all of the wearable data each time + + + + Calls either or + depending on the value of + replaceItems + + List of wearable inventory items to add + to the outfit or become a new outfit + True to replace existing items with the + new list of items, false to add these items to the existing outfit + + + + Adds a list of attachments to our agent + + A List containing the attachments to add + If true, tells simulator to remove existing attachment + first + + + + Adds a list of attachments to our agent + + A List containing the attachments to add + If true, tells simulator to remove existing attachment + If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments) + first + + + + Attach an item to our agent at a specific attach point + + A to attach + the on the avatar + to attach the item to + + + + Attach an item to our agent at a specific attach point + + A to attach + the on the avatar + If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments) + to attach the item to + + + + Attach an item to our agent specifying attachment details + + The of the item to attach + The attachments owner + The name of the attachment + The description of the attahment + The to apply when attached + The of the attachment + The on the agent + to attach the item to + + + + Attach an item to our agent specifying attachment details + + The of the item to attach + The attachments owner + The name of the attachment + The description of the attahment + The to apply when attached + The of the attachment + The on the agent + If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments) + to attach the item to + + + + Detach an item from our agent using an object + + An object + + + + Detach an item from our agent + + The inventory itemID of the item to detach + + + + Inform the sim which wearables are part of our current outfit + + + + + Replaces the Wearables collection with a list of new wearable items + + Wearable items to replace the Wearables collection with + + + + Calculates base color/tint for a specific wearable + based on its params + + All the color info gathered from wearable's VisualParams + passed as list of ColorParamInfo tuples + Base color/tint for the wearable + + + + Blocking method to populate the Wearables dictionary + + True on success, otherwise false + + + + Blocking method to populate the Textures array with cached bakes + + True on success, otherwise false + + + + Populates textures and visual params from a decoded asset + + Wearable to decode + + Populates textures and visual params from a decoded asset + + Wearable to decode + + + + Blocking method to download and parse currently worn wearable assets + + True on success, otherwise false + + + + Get a list of all of the textures that need to be downloaded for a + single bake layer + + Bake layer to get texture AssetIDs for + A list of texture AssetIDs to download + + + + Helper method to lookup the TextureID for a single layer and add it + to a list if it is not already present + + + + + + + Blocking method to download all of the textures needed for baking + the given bake layers + + A list of layers that need baking + No return value is given because the baking will happen + whether or not all textures are successfully downloaded + + + + Blocking method to create and upload baked textures for all of the + missing bakes + + True on success, otherwise false + + + + Blocking method to create and upload a baked texture for a single + bake layer + + Layer to bake + True on success, otherwise false + + + + Blocking method to upload a baked texture + + Five channel JPEG2000 texture data to upload + UUID of the newly created asset on success, otherwise UUID.Zero + + + + Creates a dictionary of visual param values from the downloaded wearables + + A dictionary of visual param indices mapping to visual param + values for our agent that can be fed to the Baker class + + + + Initate server baking process + + True if the server baking was successful + + + + Get the latest version of COF + + Current Outfit Folder (or null if getting the data failed) + + + + Create an AgentSetAppearance packet from Wearables data and the + Textures array and send it + + + + + Converts a WearableType to a bodypart or clothing WearableType + + A WearableType + AssetType.Bodypart or AssetType.Clothing or AssetType.Unknown + + + + Converts a BakeType to the corresponding baked texture slot in AvatarTextureIndex + + A BakeType + The AvatarTextureIndex slot that holds the given BakeType + + + + Gives the layer number that is used for morph mask + + >A BakeType + Which layer number as defined in BakeTypeToTextures is used for morph mask + + + + Converts a BakeType to a list of the texture slots that make up that bake + + A BakeType + A list of texture slots that are inputs for the given bake + + + Triggered when an AgentWearablesUpdate packet is received, + telling us what our avatar is currently wearing + request. + + + Raised when an AgentCachedTextureResponse packet is + received, giving a list of cached bakes that were found on the + simulator + request. + + + + Raised when appearance data is sent to the simulator, also indicates + the main appearance thread is finished. + + request. + + + + Triggered when the simulator requests the agent rebake its appearance. + + + + + + Returns true if AppearanceManager is busy and trying to set or change appearance will fail + + + + + Contains information about a wearable inventory item + + + + Inventory ItemID of the wearable + + + AssetID of the wearable asset + + + WearableType of the wearable + + + AssetType of the wearable + + + Asset data for the wearable + + + + Data collected from visual params for each wearable + needed for the calculation of the color + + + + + Holds a texture assetID and the data needed to bake this layer into + an outfit texture. Used to keep track of currently worn textures + and baking data + + + + A texture AssetID + + + Asset data for the texture + + + Collection of alpha masks that needs applying + + + Tint that should be applied to the texture + + + Where on avatar does this texture belong + + + Contains the Event data returned from the data server from an AgentWearablesRequest + + + Construct a new instance of the AgentWearablesReplyEventArgs class + + + Contains the Event data returned from the data server from an AgentCachedTextureResponse + + + Construct a new instance of the AgentCachedBakesReplyEventArgs class + + + Contains the Event data returned from an AppearanceSetRequest + + + + Triggered when appearance data is sent to the sim and + the main appearance thread is done. + Indicates whether appearance setting was successful + + + Indicates whether appearance setting was successful + + + Contains the Event data returned from the data server from an RebakeAvatarTextures + + + + Triggered when the simulator sends a request for this agent to rebake + its appearance + + The ID of the Texture Layer to bake + + + The ID of the Texture Layer to bake + + + + Class that handles the local asset cache + + + + + Default constructor + + A reference to the GridClient object + + + + Disposes cleanup timer + + + + + Only create timer when needed + + + + + Return bytes read from the local asset cache, null if it does not exist + + UUID of the asset we want to get + Raw bytes of the asset, or null on failure + + + + Returns ImageDownload object of the + image from the local image cache, null if it does not exist + + UUID of the image we want to get + ImageDownload object containing the image, or null on failure + + + + Constructs a file name of the cached asset + + UUID of the asset + String with the file name of the cahced asset + + + + Constructs a file name of the static cached asset + + UUID of the asset + String with the file name of the static cached asset + + + + Saves an asset to the local cache + + UUID of the asset + Raw bytes the asset consists of + Weather the operation was successfull + + + + Get the file name of the asset stored with gived UUID + + UUID of the asset + Null if we don't have that UUID cached on disk, file name if found in the cache folder + + + + Checks if the asset exists in the local cache + + UUID of the asset + True is the asset is stored in the cache, otherwise false + + + + Wipes out entire cache + + + + + Brings cache size to the 90% of the max size + + + + + Asynchronously brings cache size to the 90% of the max size + + + + + Adds up file sizes passes in a FileInfo array + + + + + Checks whether caching is enabled + + + + + Periodically prune the cache + + + + + Nicely formats file sizes + + Byte size we want to output + String with humanly readable file size + + + + Allows setting weather to periodicale prune the cache if it grows too big + Default is enabled, when caching is enabled + + + + + How long (in ms) between cache checks (default is 5 min.) + + + + + Helper class for sorting files by their last accessed time + + + + + + + + + OK + + + Transfer completed + + + + + + + + + Unknown error occurred + + + Equivalent to a 404 error + + + Client does not have permission for that resource + + + Unknown status + + + + + + + + + + + Unknown + + + Virtually all asset transfers use this channel + + + + + + + + + + + Asset from the asset server + + + Inventory item + + + Estate asset, such as an estate covenant + + + + + + + + + + + + + + + + + + When requesting image download, type of the image requested + + + + Normal in-world object texture + + + Avatar texture + + + Server baked avatar texture + + + + Image file format + + + + + + + + + Number of milliseconds passed since the last transfer + packet was received + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Number of milliseconds to wait for a transfer header packet if out of order data was received + + + The event subscribers. null if no subcribers + + + Raises the XferReceived event + A XferReceivedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the AssetUploaded event + A AssetUploadedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the UploadProgress event + A UploadProgressEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the InitiateDownload event + A InitiateDownloadEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ImageReceiveProgress event + A ImageReceiveProgressEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + Texture download cache + + + + Default constructor + + A reference to the GridClient object + + + + Request an asset download + + Asset UUID + Asset type, must be correct for the transfer to succeed + Whether to give this transfer an elevated priority + The callback to fire when the simulator responds with the asset data + + + + Request an asset download + + Asset UUID + Asset type, must be correct for the transfer to succeed + Whether to give this transfer an elevated priority + Source location of the requested asset + The callback to fire when the simulator responds with the asset data + + + + Request an asset download + + Asset UUID + Asset type, must be correct for the transfer to succeed + Whether to give this transfer an elevated priority + Source location of the requested asset + UUID of the transaction + The callback to fire when the simulator responds with the asset data + + + + Request an asset download + + Asset UUID + Asset type, must be correct for the transfer to succeed + Whether to give this transfer an elevated priority + Source location of the requested asset + UUID of the transaction + The callback to fire when the simulator responds with the asset data + + + + Request an asset download through the almost deprecated Xfer system + + Filename of the asset to request + Whether or not to delete the asset + off the server after it is retrieved + Use large transfer packets or not + UUID of the file to request, if filename is + left empty + Asset type of vFileID, or + AssetType.Unknown if filename is not empty + Sets the FilePath in the request to Cache + (4) if true, otherwise Unknown (0) is used + + + + + + + Use UUID.Zero if you do not have the + asset ID but have all the necessary permissions + The item ID of this asset in the inventory + Use UUID.Zero if you are not requesting an + asset from an object inventory + The owner of this asset + Asset type + Whether to prioritize this asset download or not + + + + + Used to force asset data into the PendingUpload property, ie: for raw terrain uploads + + An AssetUpload object containing the data to upload to the simulator + + + + Request an asset be uploaded to the simulator + + The Object containing the asset data + If True, the asset once uploaded will be stored on the simulator + in which the client was connected in addition to being stored on the asset server + The of the transfer, can be used to correlate the upload with + events being fired + + + + Request an asset be uploaded to the simulator + + The of the asset being uploaded + A byte array containing the encoded asset data + If True, the asset once uploaded will be stored on the simulator + in which the client was connected in addition to being stored on the asset server + The of the transfer, can be used to correlate the upload with + events being fired + + + + Request an asset be uploaded to the simulator + + + Asset type to upload this data as + A byte array containing the encoded asset data + If True, the asset once uploaded will be stored on the simulator + in which the client was connected in addition to being stored on the asset server + The of the transfer, can be used to correlate the upload with + events being fired + + + + Initiate an asset upload + + The ID this asset will have if the + upload succeeds + Asset type to upload this data as + Raw asset data to upload + Whether to store this asset on the local + simulator or the grid-wide asset server + The tranaction id for the upload + The transaction ID of this transfer + + + + Request a texture asset from the simulator using the system to + manage the requests and re-assemble the image from the packets received from the simulator + + The of the texture asset to download + The of the texture asset. + Use for most textures, or for baked layer texture assets + A float indicating the requested priority for the transfer. Higher priority values tell the simulator + to prioritize the request before lower valued requests. An image already being transferred using the can have + its priority changed by resending the request with the new priority value + Number of quality layers to discard. + This controls the end marker of the data sent. Sending with value -1 combined with priority of 0 cancels an in-progress + transfer. + A bug exists in the Linden Simulator where a -1 will occasionally be sent with a non-zero priority + indicating an off-by-one error. + The packet number to begin the request at. A value of 0 begins the request + from the start of the asset texture + The callback to fire when the image is retrieved. The callback + will contain the result of the request and the texture asset data + If true, the callback will be fired for each chunk of the downloaded image. + The callback asset parameter will contain all previously received chunks of the texture asset starting + from the beginning of the request + + Request an image and fire a callback when the request is complete + + Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished); + + private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset) + { + if(state == TextureRequestState.Finished) + { + Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded", + asset.AssetID, + asset.AssetData.Length); + } + } + + Request an image and use an inline anonymous method to handle the downloaded texture data + + Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, delegate(TextureRequestState state, AssetTexture asset) + { + if(state == TextureRequestState.Finished) + { + Console.WriteLine("Texture {0} ({1} bytes) has been successfully downloaded", + asset.AssetID, + asset.AssetData.Length); + } + } + ); + + Request a texture, decode the texture to a bitmap image and apply it to a imagebox + + Client.Assets.RequestImage(UUID.Parse("c307629f-e3a1-4487-5e88-0d96ac9d4965"), ImageType.Normal, TextureDownloader_OnDownloadFinished); + + private void TextureDownloader_OnDownloadFinished(TextureRequestState state, AssetTexture asset) + { + if(state == TextureRequestState.Finished) + { + ManagedImage imgData; + Image bitmap; + + if (state == TextureRequestState.Finished) + { + OpenJPEG.DecodeToImage(assetTexture.AssetData, out imgData, out bitmap); + picInsignia.Image = bitmap; + } + } + } + + + + + + Overload: Request a texture asset from the simulator using the system to + manage the requests and re-assemble the image from the packets received from the simulator + + The of the texture asset to download + The callback to fire when the image is retrieved. The callback + will contain the result of the request and the texture asset data + + + + Overload: Request a texture asset from the simulator using the system to + manage the requests and re-assemble the image from the packets received from the simulator + + The of the texture asset to download + The of the texture asset. + Use for most textures, or for baked layer texture assets + The callback to fire when the image is retrieved. The callback + will contain the result of the request and the texture asset data + + + + Overload: Request a texture asset from the simulator using the system to + manage the requests and re-assemble the image from the packets received from the simulator + + The of the texture asset to download + The of the texture asset. + Use for most textures, or for baked layer texture assets + The callback to fire when the image is retrieved. The callback + will contain the result of the request and the texture asset data + If true, the callback will be fired for each chunk of the downloaded image. + The callback asset parameter will contain all previously received chunks of the texture asset starting + from the beginning of the request + + + + Cancel a texture request + + The texture assets + + + + Requests download of a mesh asset + + UUID of the mesh asset + Callback when the request completes + + + + Fetach avatar texture on a grid capable of server side baking + + ID of the avatar + ID of the texture + Name of the part of the avatar texture applies to + Callback invoked on operation completion + + + + Lets TexturePipeline class fire the progress event + + The texture ID currently being downloaded + the number of bytes transferred + the total number of bytes expected + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator responds sends + + + Raised during upload completes + + + Raised during upload with progres update + + + Fired when the simulator sends an InitiateDownloadPacket, used to download terrain .raw files + + + Fired when a texture is in the process of being downloaded by the TexturePipeline class + + + + Callback used for various asset download requests + + Transfer information + Downloaded asset, null on fail + + + + Callback used upon competition of baked texture upload + + Asset UUID of the newly uploaded baked texture + + + + A callback that fires upon the completition of the RequestMesh call + + Was the download successfull + Resulting mesh or null on problems + + + Xfer data + + + Upload data + + + Filename used on the simulator + + + Filename used by the client + + + UUID of the image that is in progress + + + Number of bytes received so far + + + Image size in bytes + + + + Avatar profile flags + + + + + Represents an avatar (other than your own) + + + + + Particle system specific enumerators, flags and methods. + + + + + Current version of the media data for the prim + + + + + Array of media entries indexed by face number + + + + + + + + + + + + + + + + + + + + + + + + + Foliage type for this primitive. Only applicable if this + primitive is foliage + + + Unknown + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Identifies the owner if audio or a particle system is + active + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Objects physics engine propertis + + + Extra data about primitive + + + Indicates if prim is attached to an avatar + + + Number of clients referencing this prim + + + + Default constructor + + + + + Packs PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew + parameters in to signed eight bit values + + Floating point parameter to pack + Signed eight bit value containing the packed parameter + + + + Unpacks PathTwist, PathTwistBegin, PathRadiusOffset, and PathSkew + parameters from signed eight bit integers to floating point values + + Signed eight bit value to unpack + Unpacked floating point value + + + + + + + + + Uses basic heuristics to estimate the primitive shape + + + + Complete structure for the particle system + + + + Particle Flags + There appears to be more data packed in to this area + for many particle systems. It doesn't appear to be flag values + and serialization breaks unless there is a flag for every + possible bit so it is left as an unsigned integer + + + pattern of particles + + + A representing the maximimum age (in seconds) particle will be displayed + Maximum value is 30 seconds + + + A representing the number of seconds, + from when the particle source comes into view, + or the particle system's creation, that the object will emits particles; + after this time period no more particles are emitted + + + A in radians that specifies where particles will not be created + + + A in radians that specifies where particles will be created + + + A representing the number of seconds between burts. + + + A representing the number of meters + around the center of the source where particles will be created. + + + A representing in seconds, the minimum speed between bursts of new particles + being emitted + + + A representing in seconds the maximum speed of new particles being emitted. + + + A representing the maximum number of particles emitted per burst + + + A which represents the velocity (speed) from the source which particles are emitted + + + A which represents the Acceleration from the source which particles are emitted + + + The Key of the texture displayed on the particle + + + The Key of the specified target object or avatar particles will follow + + + Flags of particle from + + + Max Age particle system will emit particles for + + + The the particle has at the beginning of its lifecycle + + + The the particle has at the ending of its lifecycle + + + A that represents the starting X size of the particle + Minimum value is 0, maximum value is 4 + + + A that represents the starting Y size of the particle + Minimum value is 0, maximum value is 4 + + + A that represents the ending X size of the particle + Minimum value is 0, maximum value is 4 + + + A that represents the ending Y size of the particle + Minimum value is 0, maximum value is 4 + + + A that represents the start glow value + Minimum value is 0, maximum value is 1 + + + A that represents the end glow value + Minimum value is 0, maximum value is 1 + + + OpenGL blend function to use at particle source + + + OpenGL blend function to use at particle destination + + + + Can this particle system be packed in a legacy compatible way + + True if the particle system doesn't use new particle system features + + + + Decodes a byte[] array into a ParticleSystem Object + + ParticleSystem object + Start position for BitPacker + + + + Generate byte[] array from particle data + + Byte array + + + + Particle source pattern + + + + None + + + Drop particles from source position with no force + + + "Explode" particles in all directions + + + Particles shoot across a 2D area + + + Particles shoot across a 3D Cone + + + Inverse of AngleCone (shoot particles everywhere except the 3D cone defined + + + + Particle Data Flags + + + + None + + + Interpolate color and alpha from start to end + + + Interpolate scale from start to end + + + Bounce particles off particle sources Z height + + + velocity of particles is dampened toward the simulators wind + + + Particles follow the source + + + Particles point towards the direction of source's velocity + + + Target of the particles + + + Particles are sent in a straight line + + + Particles emit a glow + + + used for point/grab/touch + + + continuous ribbon particle + + + particle data contains glow + + + particle data contains blend functions + + + + Particle Flags Enum + + + + None + + + Acceleration and velocity for particles are + relative to the object rotation + + + Particles use new 'correct' angle parameters + + + + Parameters used to construct a visual representation of a primitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Calculdates hash code for prim construction data + + The has + + + Attachment point to an avatar + + + + + + + + + + + + + + + + Information on the flexible properties of a primitive + + + + + + + + + + + + + + + + + + + + + + + Default constructor + + + + + + + + + + + + + + + + + + + + + + + + Information on the light properties of a primitive + + + + + + + + + + + + + + + + + + + + Default constructor + + + + + + + + + + + + + + + + + + + + + + + + Information on the light properties of a primitive as texture map + + + + + + + + + + + Default constructor + + + + + + + + + + + + + + + + + + + + + + + + Information on the sculpt properties of a sculpted primitive + + + + + Default constructor + + + + + + + + + + + + Render inside out (inverts the normals). + + + + + Render an X axis mirror of the sculpty. + + + + + Extended properties to describe an object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default constructor + + + + + Set the properties that are set in an ObjectPropertiesFamily packet + + that has + been partially filled by an ObjectPropertiesFamily packet + + + + Describes physics attributes of the prim + + + + Primitive's local ID + + + Density (1000 for normal density) + + + Friction + + + Gravity multiplier (1 for normal gravity) + + + Type of physics representation of this primitive in the simulator + + + Restitution + + + + Creates PhysicsProperties from OSD + + OSDMap with incoming data + Deserialized PhysicsProperties object + + + + Serializes PhysicsProperties to OSD + + OSDMap with serialized PhysicsProperties data + + + + Texture animation mode + + + + Disable texture animation + + + Enable texture animation + + + Loop when animating textures + + + Animate in reverse direction + + + Animate forward then reverse + + + Slide texture smoothly instead of frame-stepping + + + Rotate texture instead of using frames + + + Scale texture instead of using frames + + + + A single textured face. Don't instantiate this class yourself, use the + methods in TextureEntry + + + + + Contains the definition for individual faces + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + In the future this will specify whether a webpage is + attached to this face + + + + + + + + + + Represents all of the texturable faces for an object + + Grid objects have infinite faces, with each face + using the properties of the default face unless set otherwise. So if + you have a TextureEntry with a default texture uuid of X, and face 18 + has a texture UUID of Y, every face would be textured with X except for + face 18 that uses Y. In practice however, primitives utilize a maximum + of nine faces + + + + + + + + + + Constructor that takes a default texture UUID + + Texture UUID to use as the default texture + + + + Constructor that takes a TextureEntryFace for the + default face + + Face to use as the default face + + + + Constructor that creates the TextureEntry class from a byte array + + Byte array containing the TextureEntry field + Starting position of the TextureEntry field in + the byte array + Length of the TextureEntry field, in bytes + + + + This will either create a new face if a custom face for the given + index is not defined, or return the custom face for that index if + it already exists + + The index number of the face to create or + retrieve + A TextureEntryFace containing all the properties for that + face + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Controls the texture animation of a particular prim + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Groups that this avatar is a member of + + + Positive and negative ratings + + + Avatar properties including about text, profile URL, image IDs and + publishing settings + + + Avatar interests including spoken languages, skills, and "want to" + choices + + + Movement control flags for avatars. Typically not set or used by + clients. To move your avatar, use Client.Self.Movement instead + + + + Contains the visual parameters describing the deformation of the avatar + + + + + Appearance version. Value greater than 0 indicates using server side baking + + + + + Version of the Current Outfit Folder that the appearance is based on + + + + + Appearance flags. Introduced with server side baking, currently unused. + + + + + List of current avatar animations + + + + + Default constructor + + + + First name + + + Last name + + + Full name + + + Active group + + + + Positive and negative ratings + + + + Positive ratings for Behavior + + + Negative ratings for Behavior + + + Positive ratings for Appearance + + + Negative ratings for Appearance + + + Positive ratings for Building + + + Negative ratings for Building + + + Positive ratings given by this avatar + + + Negative ratings given by this avatar + + + + Avatar properties including about text, profile URL, image IDs and + publishing settings + + + + First Life about text + + + First Life image ID + + + + + + + + + + + + + + + Profile image ID + + + Flags of the profile + + + Web URL for this profile + + + Should this profile be published on the web + + + Avatar Online Status + + + Is this a mature profile + + + + + + + + + + Avatar interests including spoken languages, skills, and "want to" + choices + + + + Languages profile field + + + + + + + + + + + + + + + Information about agents display name + + + Agent UUID + + + Username + + + Display name + + + First name (legacy) + + + Last name (legacy) + + + Is display name default display name + + + Cache display name until + + + Last updated timestamp + + + + Creates AgentDisplayName object from OSD + + Incoming OSD data + AgentDisplayName object + + + + Return object as OSD map + + OSD containing agent's display name data + + + Full name (legacy) + + + + Holds group information for Avatars such as those you might find in a profile + + + + true of Avatar accepts group notices + + + Groups Key + + + Texture Key for groups insignia + + + Name of the group + + + Powers avatar has in the group + + + Avatars Currently selected title + + + true of Avatar has chosen to list this in their profile + + + + Contains an animation currently being played by an agent + + + + The ID of the animation asset + + + A number to indicate start order of currently playing animations + On Linden Grids this number is unique per region, with OpenSim it is per client + + + + + + + Holds group information on an individual profile pick + + + + + Retrieve friend status notifications, and retrieve avatar names and + profiles + + + + The event subscribers, null of no subscribers + + + Raises the AvatarAnimation Event + An AvatarAnimationEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarAppearance Event + A AvatarAppearanceEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the UUIDNameReply Event + A UUIDNameReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarInterestsReply Event + A AvatarInterestsReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarPropertiesReply Event + A AvatarPropertiesReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarGroupsReply Event + A AvatarGroupsReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarPickerReply Event + A AvatarPickerReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ViewerEffectPointAt Event + A ViewerEffectPointAtEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ViewerEffectLookAt Event + A ViewerEffectLookAtEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ViewerEffect Event + A ViewerEffectEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarPicksReply Event + A AvatarPicksReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the PickInfoReply Event + A PickInfoReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarClassifiedReply Event + A AvatarClassifiedReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ClassifiedInfoReply Event + A ClassifiedInfoReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the DisplayNameUpdate Event + A DisplayNameUpdateEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + + Represents other avatars + + + + + Tracks the specified avatar on your map + Avatar ID to track + + + + Request a single avatar name + + The avatar key to retrieve a name for + + + + Request a list of avatar names + + The avatar keys to retrieve names for + + + + Check if Display Names functionality is available + + True if Display name functionality is available + + + + Request retrieval of display names (max 90 names per request) + + List of UUIDs to lookup + Callback to report result of the operation + + + + Start a request for Avatar Properties + + + + + + Search for an avatar (first name, last name) + + The name to search for + An ID to associate with this query + + + + Start a request for Avatar Picks + + UUID of the avatar + + + + Start a request for Avatar Classifieds + + UUID of the avatar + + + + Start a request for details of a specific profile pick + + UUID of the avatar + UUID of the profile pick + + + + Start a request for details of a specific profile classified + + UUID of the avatar + UUID of the profile classified + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + EQ Message fired when someone nearby changes their display name + + The message key + the IMessage object containing the deserialized data sent from the simulator + The which originated the packet + + + + Crossed region handler for message that comes across the EventQueue. Sent to an agent + when the agent crosses a sim border into a new region. + + The message key + the IMessage object containing the deserialized data sent from the simulator + The which originated the packet + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends us data containing + an agents animation playlist + + + Raised when the simulator sends us data containing + the appearance information for an agent + + + Raised when the simulator sends us data containing + agent names/id values + + + Raised when the simulator sends us data containing + the interests listed in an agents profile + + + Raised when the simulator sends us data containing + profile property information for an agent + + + Raised when the simulator sends us data containing + the group membership an agent is a member of + + + Raised when the simulator sends us data containing + name/id pair + + + Raised when the simulator sends us data containing + the objects and effect when an agent is pointing at + + + Raised when the simulator sends us data containing + the objects and effect when an agent is looking at + + + Raised when the simulator sends us data containing + an agents viewer effect information + + + Raised when the simulator sends us data containing + the top picks from an agents profile + + + Raised when the simulator sends us data containing + the Pick details + + + Raised when the simulator sends us data containing + the classified ads an agent has placed + + + Raised when the simulator sends us data containing + the details of a classified ad + + + Raised when the simulator sends us data containing + the details of display name change + + + + Callback giving results when fetching display names + + If the request was successful + Array of display names + Array of UUIDs that could not be fetched + + + Provides data for the event + The event occurs when the simulator sends + the animation playlist for an agent + + The following code example uses the and + properties to display the animation playlist of an avatar on the window. + + // subscribe to the event + Client.Avatars.AvatarAnimation += Avatars_AvatarAnimation; + + private void Avatars_AvatarAnimation(object sender, AvatarAnimationEventArgs e) + { + // create a dictionary of "known" animations from the Animations class using System.Reflection + Dictionary<UUID, string> systemAnimations = new Dictionary<UUID, string>(); + Type type = typeof(Animations); + System.Reflection.FieldInfo[] fields = type.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static); + foreach (System.Reflection.FieldInfo field in fields) + { + systemAnimations.Add((UUID)field.GetValue(type), field.Name); + } + + // find out which animations being played are known animations and which are assets + foreach (Animation animation in e.Animations) + { + if (systemAnimations.ContainsKey(animation.AnimationID)) + { + Console.WriteLine("{0} is playing {1} ({2}) sequence {3}", e.AvatarID, + systemAnimations[animation.AnimationID], animation.AnimationSequence); + } + else + { + Console.WriteLine("{0} is playing {1} (Asset) sequence {2}", e.AvatarID, + animation.AnimationID, animation.AnimationSequence); + } + } + } + + + + + + Construct a new instance of the AvatarAnimationEventArgs class + + The ID of the agent + The list of animations to start + + + Get the ID of the agent + + + Get the list of animations to start + + + Provides data for the event + The event occurs when the simulator sends + the appearance data for an avatar + + The following code example uses the and + properties to display the selected shape of an avatar on the window. + + // subscribe to the event + Client.Avatars.AvatarAppearance += Avatars_AvatarAppearance; + + // handle the data when the event is raised + void Avatars_AvatarAppearance(object sender, AvatarAppearanceEventArgs e) + { + Console.WriteLine("The Agent {0} is using a {1} shape.", e.AvatarID, (e.VisualParams[31] > 0) : "male" ? "female") + } + + + + + + Construct a new instance of the AvatarAppearanceEventArgs class + + The simulator request was from + The ID of the agent + true of the agent is a trial account + The default agent texture + The agents appearance layer textures + The for the agent + + + Get the Simulator this request is from of the agent + + + Get the ID of the agent + + + true if the agent is a trial account + + + Get the default agent texture + + + Get the agents appearance layer textures + + + Get the for the agent + + + Version of the appearance system used. + Value greater than 0 indicates that server side baking is used + + + Version of the Current Outfit Folder the appearance is based on + + + Appearance flags, introduced with server side baking, currently unused + + + Represents the interests from the profile of an agent + + + Get the ID of the agent + + + The properties of an agent + + + Get the ID of the agent + + + Get the ID of the agent + + + Get the ID of the agent + + + Get the ID of the avatar + + + + Event args class for display name notification messages + + + + + Wrapper around a byte array that allows bit to be packed and unpacked + one at a time or by a variable amount. Useful for very tightly packed + data like LayerData packets + + + + + + + + Default constructor, initialize the bit packer / bit unpacker + with a byte array and starting position + + Byte array to pack bits in to or unpack from + Starting position in the byte array + + + + Pack a floating point value in to the data + + Floating point value to pack + + + + Pack part or all of an integer in to the data + + Integer containing the data to pack + Number of bits of the integer to pack + + + + Pack part or all of an unsigned integer in to the data + + Unsigned integer containing the data to pack + Number of bits of the integer to pack + + + + Pack a single bit in to the data + + Bit to pack + + + + + + + + + + + + + + + + + + + + + + + + + Unpacking a floating point value from the data + + Unpacked floating point value + + + + Unpack a variable number of bits from the data in to integer format + + Number of bits to unpack + An integer containing the unpacked bits + This function is only useful up to 32 bits + + + + Unpack a variable number of bits from the data in to unsigned + integer format + + Number of bits to unpack + An unsigned integer containing the unpacked bits + This function is only useful up to 32 bits + + + + Unpack a 16-bit signed integer + + 16-bit signed integer + + + + Unpack a 16-bit unsigned integer + + 16-bit unsigned integer + + + + Unpack a 32-bit signed integer + + 32-bit signed integer + + + + Unpack a 32-bit unsigned integer + + 32-bit unsigned integer + + + + + + + + + + Reads in a byte array of an Animation Asset created by the SecondLife(tm) client. + + + + + Rotation Keyframe count (used internally) + + + + + Position Keyframe count (used internally) + + + + + Animation Priority + + + + + The animation length in seconds. + + + + + Expression set in the client. Null if [None] is selected + + + + + The time in seconds to start the animation + + + + + The time in seconds to end the animation + + + + + Loop the animation + + + + + Meta data. Ease in Seconds. + + + + + Meta data. Ease out seconds. + + + + + Meta Data for the Hand Pose + + + + + Number of joints defined in the animation + + + + + Contains an array of joints + + + + + Searialize an animation asset into it's joints/keyframes/meta data + + + + + + Variable length strings seem to be null terminated in the animation asset.. but.. + use with caution, home grown. + advances the index. + + The animation asset byte array + The offset to start reading + a string + + + + Read in a Joint from an animation asset byte array + Variable length Joint fields, yay! + Advances the index + + animation asset byte array + Byte Offset of the start of the joint + The Joint data serialized into the binBVHJoint structure + + + + Read Keyframes of a certain type + advance i + + Animation Byte array + Offset in the Byte Array. Will be advanced + Number of Keyframes + Scaling Min to pass to the Uint16ToFloat method + Scaling Max to pass to the Uint16ToFloat method + + + + + Determines whether the specified is equal to the current . + + + true if the specified is equal to the current ; otherwise, false. + + The to compare with the current . + The parameter is null. + 2 + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + 2 + + + + A Joint and it's associated meta data and keyframes + + + + + Indicates whether this instance and a specified object are equal. + + + true if and this instance are the same type and represent the same value; otherwise, false. + + Another object to compare to. + 2 + + + + Returns the hash code for this instance. + + + A 32-bit signed integer that is the hash code for this instance. + + 2 + + + + Name of the Joint. Matches the avatar_skeleton.xml in client distros + + + + + Joint Animation Override? Was the same as the Priority in testing.. + + + + + Array of Rotation Keyframes in order from earliest to latest + + + + + Array of Position Keyframes in order from earliest to latest + This seems to only be for the Pelvis? + + + + + Custom application data that can be attached to a joint + + + + + A Joint Keyframe. This is either a position or a rotation. + + + + + Either a Vector3 position or a Vector3 Euler rotation + + + + + Poses set in the animation metadata for the hands. + + + + + Capabilities is the name of the bi-directional HTTP REST protocol + used to communicate non real-time transactions such as teleporting or + group messaging + + + + Reference to the simulator this system is connected to + + + + Default constructor + + + + + + + Request the URI of a named capability + + Name of the capability to request + The URI of the requested capability, or String.Empty if + the capability does not exist + + + + Process any incoming events, check to see if we have a message created for the event, + + + + + + Capabilities URI this system was initialized with + + + Whether the capabilities event queue is connected and + listening for incoming events + + + + Triggered when an event is received via the EventQueueGet + capability + + Event name + Decoded event data + The simulator that generated the event + + + + Attempts to convert an LLSD structure to a known Packet type + + Event name, this must match an actual + packet name for a Packet to be successfully built + LLSD to convert to a Packet + A Packet on success, otherwise null + + + + + + Looking direction, must be a normalized vector + Up direction, must be a normalized vector + + + + Align the coordinate frame X and Y axis with a given rotation + around the Z axis in radians + + Absolute rotation around the Z axis in + radians + + + Origin position of this coordinate frame + + + X axis of this coordinate frame, or Forward/At in grid terms + + + Y axis of this coordinate frame, or Left in grid terms + + + Z axis of this coordinate frame, or Up in grid terms + + + + Access to the data server which allows searching for land, events, people, etc + + + + The event subscribers. null if no subcribers + + + Raises the EventInfoReply event + An EventInfoReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirEventsReply event + An DirEventsReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the PlacesReply event + A PlacesReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirPlacesReply event + A DirPlacesReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirClassifiedsReply event + A DirClassifiedsReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirGroupsReply event + A DirGroupsReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirPeopleReply event + A DirPeopleReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the DirLandReply event + A DirLandReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + + Constructs a new instance of the DirectoryManager class + + An instance of GridClient + + + + Query the data server for a list of classified ads containing the specified string. + Defaults to searching for classified placed in any category, and includes PG, Adult and Mature + results. + + Responses are sent 16 per response packet, there is no way to know how many results a query reply will contain however assuming + the reply packets arrived ordered, a response with less than 16 entries would indicate all results have been received + + The event is raised when a response is received from the simulator + + A string containing a list of keywords to search for + A UUID to correlate the results when the event is raised + + + + Query the data server for a list of classified ads which contain specified keywords (Overload) + + The event is raised when a response is received from the simulator + + A string containing a list of keywords to search for + The category to search + A set of flags which can be ORed to modify query options + such as classified maturity rating. + A UUID to correlate the results when the event is raised + + Search classified ads containing the key words "foo" and "bar" in the "Any" category that are either PG or Mature + + UUID searchID = StartClassifiedSearch("foo bar", ClassifiedCategories.Any, ClassifiedQueryFlags.PG | ClassifiedQueryFlags.Mature); + + + + Responses are sent 16 at a time, there is no way to know how many results a query reply will contain however assuming + the reply packets arrived ordered, a response with less than 16 entries would indicate all results have been received + + + + + Starts search for places (Overloaded) + + The event is raised when a response is received from the simulator + + Search text + Each request is limited to 100 places + being returned. To get the first 100 result entries of a request use 0, + from 100-199 use 1, 200-299 use 2, etc. + A UUID to correlate the results when the event is raised + + + + Queries the dataserver for parcels of land which are flagged to be shown in search + + The event is raised when a response is received from the simulator + + A string containing a list of keywords to search for separated by a space character + A set of flags which can be ORed to modify query options + such as classified maturity rating. + The category to search + Each request is limited to 100 places + being returned. To get the first 100 result entries of a request use 0, + from 100-199 use 1, 200-299 use 2, etc. + A UUID to correlate the results when the event is raised + + Search places containing the key words "foo" and "bar" in the "Any" category that are either PG or Adult + + UUID searchID = StartDirPlacesSearch("foo bar", DirFindFlags.DwellSort | DirFindFlags.IncludePG | DirFindFlags.IncludeAdult, ParcelCategory.Any, 0); + + + + Additional information on the results can be obtained by using the ParcelManager.InfoRequest method + + + + + Starts a search for land sales using the directory + + The event is raised when a response is received from the simulator + + What type of land to search for. Auction, + estate, mainland, "first land", etc + The OnDirLandReply event handler must be registered before + calling this function. There is no way to determine how many + results will be returned, or how many times the callback will be + fired other than you won't get more than 100 total parcels from + each query. + + + + Starts a search for land sales using the directory + + The event is raised when a response is received from the simulator + + What type of land to search for. Auction, + estate, mainland, "first land", etc + Maximum price to search for + Maximum area to search for + Each request is limited to 100 parcels + being returned. To get the first 100 parcels of a request use 0, + from 100-199 use 1, 200-299 use 2, etc. + The OnDirLandReply event handler must be registered before + calling this function. There is no way to determine how many + results will be returned, or how many times the callback will be + fired other than you won't get more than 100 total parcels from + each query. + + + + Send a request to the data server for land sales listings + + + Flags sent to specify query options + + Available flags: + Specify the parcel rating with one or more of the following: + IncludePG IncludeMature IncludeAdult + + Specify the field to pre sort the results with ONLY ONE of the following: + PerMeterSort NameSort AreaSort PricesSort + + Specify the order the results are returned in, if not specified the results are pre sorted in a Descending Order + SortAsc + + Specify additional filters to limit the results with one or both of the following: + LimitByPrice LimitByArea + + Flags can be combined by separating them with the | (pipe) character + + Additional details can be found in + + What type of land to search for. Auction, + Estate or Mainland + Maximum price to search for when the + DirFindFlags.LimitByPrice flag is specified in findFlags + Maximum area to search for when the + DirFindFlags.LimitByArea flag is specified in findFlags + Each request is limited to 100 parcels + being returned. To get the first 100 parcels of a request use 0, + from 100-199 use 100, 200-299 use 200, etc. + The event will be raised with the response from the simulator + + There is no way to determine how many results will be returned, or how many times the callback will be + fired other than you won't get more than 100 total parcels from + each reply. + + Any land set for sale to either anybody or specific to the connected agent will be included in the + results if the land is included in the query + + + // request all mainland, any maturity rating that is larger than 512 sq.m + StartLandSearch(DirFindFlags.SortAsc | DirFindFlags.PerMeterSort | DirFindFlags.LimitByArea | DirFindFlags.IncludePG | DirFindFlags.IncludeMature | DirFindFlags.IncludeAdult, SearchTypeFlags.Mainland, 0, 512, 0); + + + + + Search for Groups + + The name or portion of the name of the group you wish to search for + Start from the match number + + + + + Search for Groups + + The name or portion of the name of the group you wish to search for + Start from the match number + Search flags + + + + + Search the People directory for other avatars + + The name or portion of the name of the avatar you wish to search for + + + + + + Search Places for parcels of land you personally own + + + + + Searches Places for land owned by the specified group + + ID of the group you want to recieve land list for (You must be a member of the group) + Transaction (Query) ID which can be associated with results from your request. + + + + Search the Places directory for parcels that are listed in search and contain the specified keywords + + A string containing the keywords to search for + Transaction (Query) ID which can be associated with results from your request. + + + + Search Places - All Options + + One of the Values from the DirFindFlags struct, ie: AgentOwned, GroupOwned, etc. + One of the values from the SearchCategory Struct, ie: Any, Linden, Newcomer + A string containing a list of keywords to search for separated by a space character + String Simulator Name to search in + LLUID of group you want to recieve results for + Transaction (Query) ID which can be associated with results from your request. + Transaction (Query) ID which can be associated with results from your request. + + + + Search All Events with specifid searchText in all categories, includes PG, Mature and Adult + + A string containing a list of keywords to search for separated by a space character + Each request is limited to 100 entries + being returned. To get the first group of entries of a request use 0, + from 100-199 use 100, 200-299 use 200, etc. + UUID of query to correlate results in callback. + + + + Search Events + + A string containing a list of keywords to search for separated by a space character + One or more of the following flags: DateEvents, IncludePG, IncludeMature, IncludeAdult + from the Enum + + Multiple flags can be combined by separating the flags with the | (pipe) character + "u" for in-progress and upcoming events, -or- number of days since/until event is scheduled + For example "0" = Today, "1" = tomorrow, "2" = following day, "-1" = yesterday, etc. + Each request is limited to 100 entries + being returned. To get the first group of entries of a request use 0, + from 100-199 use 100, 200-299 use 200, etc. + EventCategory event is listed under. + UUID of query to correlate results in callback. + + + Requests Event Details + ID of Event returned from the method + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming event message + The Unique Capabilities Key + The event message containing the data + The simulator the message originated from + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming event message + The Unique Capabilities Key + The event message containing the data + The simulator the message originated from + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Classified Ad categories + + + Classified is listed in the Any category + + + Classified is shopping related + + + Classified is + + + + + + + + + + + + + + + + + + + + + + + + Event Categories + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Query Flags used in many of the DirectoryManager methods to specify which query to execute and how to return the results. + + Flags can be combined using the | (pipe) character, not all flags are available in all queries + + + + Query the People database + + + + + + + + + Query the Groups database + + + Query the Events database + + + Query the land holdings database for land owned by the currently connected agent + + + + + + Query the land holdings database for land which is owned by a Group + + + Specifies the query should pre sort the results based upon traffic + when searching the Places database + + + + + + + + + + + + + + + Specifies the query should pre sort the results in an ascending order when searching the land sales database. + This flag is only used when searching the land sales database + + + Specifies the query should pre sort the results using the SalePrice field when searching the land sales database. + This flag is only used when searching the land sales database + + + Specifies the query should pre sort the results by calculating the average price/sq.m (SalePrice / Area) when searching the land sales database. + This flag is only used when searching the land sales database + + + Specifies the query should pre sort the results using the ParcelSize field when searching the land sales database. + This flag is only used when searching the land sales database + + + Specifies the query should pre sort the results using the Name field when searching the land sales database. + This flag is only used when searching the land sales database + + + When set, only parcels less than the specified Price will be included when searching the land sales database. + This flag is only used when searching the land sales database + + + When set, only parcels greater than the specified Size will be included when searching the land sales database. + This flag is only used when searching the land sales database + + + + + + + + + Include PG land in results. This flag is used when searching both the Groups, Events and Land sales databases + + + Include Mature land in results. This flag is used when searching both the Groups, Events and Land sales databases + + + Include Adult land in results. This flag is used when searching both the Groups, Events and Land sales databases + + + + + + + Land types to search dataserver for + + + + Search Auction, Mainland and Estate + + + Land which is currently up for auction + + + Parcels which are on the mainland (Linden owned) continents + + + Parcels which are on privately owned simulators + + + + The content rating of the event + + + + Event is PG + + + Event is Mature + + + Event is Adult + + + + Classified Ad Options + + There appear to be two formats the flags are packed in. + This set of flags is for the newer style + + + + + + + + + + + + + + + + + + + Classified ad query options + + + + Include all ads in results + + + Include PG ads in results + + + Include Mature ads in results + + + Include Adult ads in results + + + + The For Sale flag in PlacesReplyData + + + + Parcel is not listed for sale + + + Parcel is For Sale + + + + A classified ad on the grid + + + + UUID for this ad, useful for looking up detailed + information about it + + + The title of this classified ad + + + Flags that show certain options applied to the classified + + + Creation date of the ad + + + Expiration date of the ad + + + Price that was paid for this ad + + + Print the struct data as a string + A string containing the field name, and field value + + + + A parcel retrieved from the dataserver such as results from the + "For-Sale" listings or "Places" Search + + + + The unique dataserver parcel ID + This id is used to obtain additional information from the entry + by using the method + + + A string containing the name of the parcel + + + The size of the parcel + This field is not returned for Places searches + + + The price of the parcel + This field is not returned for Places searches + + + If True, this parcel is flagged to be auctioned + + + If true, this parcel is currently set for sale + + + Parcel traffic + + + Print the struct data as a string + A string containing the field name, and field value + + + + An Avatar returned from the dataserver + + + + Online status of agent + This field appears to be obsolete and always returns false + + + The agents first name + + + The agents last name + + + The agents + + + Print the struct data as a string + A string containing the field name, and field value + + + + Response to a "Groups" Search + + + + The Group ID + + + The name of the group + + + The current number of members + + + Print the struct data as a string + A string containing the field name, and field value + + + + Parcel information returned from a request + + Represents one of the following: + A parcel of land on the grid that has its Show In Search flag set + A parcel of land owned by the agent making the request + A parcel of land owned by a group the agent making the request is a member of + + + In a request for Group Land, the First record will contain an empty record + + Note: This is not the same as searching the land for sale data source + + + + The ID of the Agent of Group that owns the parcel + + + The name + + + The description + + + The Size of the parcel + + + The billable Size of the parcel, for mainland + parcels this will match the ActualArea field. For Group owned land this will be 10 percent smaller + than the ActualArea. For Estate land this will always be 0 + + + Indicates the ForSale status of the parcel + + + The Gridwide X position + + + The Gridwide Y position + + + The Z position of the parcel, or 0 if no landing point set + + + The name of the Region the parcel is located in + + + The Asset ID of the parcels Snapshot texture + + + The calculated visitor traffic + + + The billing product SKU + Known values are: + + 023Mainland / Full Region + 024Estate / Full Region + 027Estate / Openspace + 029Estate / Homestead + 129Mainland / Homestead (Linden Owned) + + + + + No longer used, will always be 0 + + + Get a SL URL for the parcel + A string, containing a standard SLURL + + + Print the struct data as a string + A string containing the field name, and field value + + + + An "Event" Listing summary + + + + The ID of the event creator + + + The name of the event + + + The events ID + + + A string containing the short date/time the event will begin + + + The event start time in Unixtime (seconds since epoch) + + + The events maturity rating + + + Print the struct data as a string + A string containing the field name, and field value + + + + The details of an "Event" + + + + The events ID + + + The ID of the event creator + + + The name of the event + + + The category + + + The events description + + + The short date/time the event will begin + + + The event start time in Unixtime (seconds since epoch) UTC adjusted + + + The length of the event in minutes + + + 0 if no cover charge applies + + + The cover charge amount in L$ if applicable + + + The name of the region where the event is being held + + + The gridwide location of the event + + + The maturity rating + + + Get a SL URL for the parcel where the event is hosted + A string, containing a standard SLURL + + + Print the struct data as a string + A string containing the field name, and field value + + + Contains the Event data returned from the data server from an EventInfoRequest + + + Construct a new instance of the EventInfoReplyEventArgs class + A single EventInfo object containing the details of an event + + + + A single EventInfo object containing the details of an event + + + + Contains the "Event" detail data returned from the data server + + + Construct a new instance of the DirEventsReplyEventArgs class + The ID of the query returned by the data server. + This will correlate to the ID returned by the method + A list containing the "Events" returned by the search query + + + The ID returned by + + + A list of "Events" returned by the data server + + + Contains the "Event" list data returned from the data server + + + Construct a new instance of PlacesReplyEventArgs class + The ID of the query returned by the data server. + This will correlate to the ID returned by the method + A list containing the "Places" returned by the data server query + + + The ID returned by + + + A list of "Places" returned by the data server + + + Contains the places data returned from the data server + + + Construct a new instance of the DirPlacesReplyEventArgs class + The ID of the query returned by the data server. + This will correlate to the ID returned by the method + A list containing land data returned by the data server + + + The ID returned by + + + A list containing Places data returned by the data server + + + Contains the classified data returned from the data server + + + Construct a new instance of the DirClassifiedsReplyEventArgs class + A list of classified ad data returned from the data server + + + A list containing Classified Ads returned by the data server + + + Contains the group data returned from the data server + + + Construct a new instance of the DirGroupsReplyEventArgs class + The ID of the query returned by the data server. + This will correlate to the ID returned by the method + A list of groups data returned by the data server + + + The ID returned by + + + A list containing Groups data returned by the data server + + + Contains the people data returned from the data server + + + Construct a new instance of the DirPeopleReplyEventArgs class + The ID of the query returned by the data server. + This will correlate to the ID returned by the method + A list of people data returned by the data server + + + The ID returned by + + + A list containing People data returned by the data server + + + Contains the land sales data returned from the data server + + + Construct a new instance of the DirLandReplyEventArgs class + A list of parcels for sale returned by the data server + + + A list containing land forsale data returned by the data server + + + + Represends individual HTTP Download request + + + + URI of the item to fetch + + + Timout specified in milliseconds + + + Download progress callback + + + Download completed callback + + + Accept the following content type + + + How many times will this request be retried + + + Current fetch attempt + + + Default constructor + + + Constructor + + + + Manages async HTTP downloads with a limit on maximum + concurrent downloads + + + + Default constructor + + + Cleanup method + + + Setup http download request + + + Check the queue for pending work + + + Enqueue a new HTTP download + + + Maximum number of parallel downloads from a single endpoint + + + Client certificate + + + Describes tasks returned in LandStatReply + + + + Estate level administration and utilities + + + + Textures for each of the four terrain height levels + + + Upper/lower texture boundaries for each corner of the sim + + + + Constructor for EstateTools class + + + + + The event subscribers. null if no subcribers + + + Raises the TopCollidersReply event + A TopCollidersReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the TopScriptsReply event + A TopScriptsReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateUsersReply event + A EstateUsersReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateGroupsReply event + A EstateGroupsReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateManagersReply event + A EstateManagersReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateBansReply event + A EstateBansReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateCovenantReply event + A EstateCovenantReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the EstateUpdateInfoReply event + A EstateUpdateInfoReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + + Requests estate information such as top scripts and colliders + + + + + + + + Requests estate settings, including estate manager and access/ban lists + + + Requests the "Top Scripts" list for the current region + + + Requests the "Top Colliders" list for the current region + + + + Set several estate specific configuration variables + + The Height of the waterlevel over the entire estate. Defaults to 20 + The maximum height change allowed above the baked terrain. Defaults to 4 + The minimum height change allowed below the baked terrain. Defaults to -4 + true to use + if True forces the sun position to the position in SunPosition + The current position of the sun on the estate, or when FixedSun is true the static position + the sun will remain. 6.0 = Sunrise, 30.0 = Sunset + + + + Request return of objects owned by specified avatar + + The Agents owning the primitives to return + specify the coverage and type of objects to be included in the return + true to perform return on entire estate + + + + + + + + + Used for setting and retrieving various estate panel settings + + EstateOwnerMessage Method field + List of parameters to include + + + + Kick an avatar from an estate + + Key of Agent to remove + + + + Ban an avatar from an estate + Key of Agent to remove + Ban user from this estate and all others owned by the estate owner + + + Unban an avatar from an estate + Key of Agent to remove + /// Unban user from this estate and all others owned by the estate owner + + + + Send a message dialog to everyone in an entire estate + + Message to send all users in the estate + + + + Send a message dialog to everyone in a simulator + + Message to send all users in the simulator + + + + Send an avatar back to their home location + + Key of avatar to send home + + + + Begin the region restart process + + + + + Cancels a region restart + + + + Estate panel "Region" tab settings + + + Estate panel "Debug" tab settings + + + Used for setting the region's terrain textures for its four height levels + + + + + + + Used for setting sim terrain texture heights + + + Requests the estate covenant + + + + Upload a terrain RAW file + + A byte array containing the encoded terrain data + The name of the file being uploaded + The Id of the transfer request + + + + Teleports all users home in current Estate + + + + + Remove estate manager + Key of Agent to Remove + removes manager to this estate and all others owned by the estate owner + + + + Add estate manager + Key of Agent to Add + Add agent as manager to this estate and all others owned by the estate owner + + + + Add's an agent to the estate Allowed list + Key of Agent to Add + Add agent as an allowed reisdent to All estates if true + + + + Removes an agent from the estate Allowed list + Key of Agent to Remove + Removes agent as an allowed reisdent from All estates if true + + + + + Add's a group to the estate Allowed list + Key of Group to Add + Add Group as an allowed group to All estates if true + + + + + Removes a group from the estate Allowed list + Key of Group to Remove + Removes Group as an allowed Group from All estates if true + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Raised when the data server responds to a request. + + + Used in the ReportType field of a LandStatRequest + + + Used by EstateOwnerMessage packets + + + Used by EstateOwnerMessage packets + + + + + + + + No flags set + + + Only return targets scripted objects + + + Only return targets objects if on others land + + + Returns target's scripted objects and objects on other parcels + + + Ground texture settings for each corner of the region + + + Used by GroundTextureHeightSettings + + + The high and low texture thresholds for each corner of the sim + + + Raised on LandStatReply when the report type is for "top colliders" + + + Construct a new instance of the TopCollidersReplyEventArgs class + The number of returned items in LandStatReply + Dictionary of Object UUIDs to tasks returned in LandStatReply + + + + The number of returned items in LandStatReply + + + + + A Dictionary of Object UUIDs to tasks returned in LandStatReply + + + + Raised on LandStatReply when the report type is for "top Scripts" + + + Construct a new instance of the TopScriptsReplyEventArgs class + The number of returned items in LandStatReply + Dictionary of Object UUIDs to tasks returned in LandStatReply + + + + The number of scripts returned in LandStatReply + + + + + A Dictionary of Object UUIDs to tasks returned in LandStatReply + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateBansReplyEventArgs class + The estate's identifier on the grid + The number of returned items in LandStatReply + User UUIDs banned + + + + The identifier of the estate + + + + + The number of returned itmes + + + + + List of UUIDs of Banned Users + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateUsersReplyEventArgs class + The estate's identifier on the grid + The number of users + Allowed users UUIDs + + + + The identifier of the estate + + + + + The number of returned items + + + + + List of UUIDs of Allowed Users + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateGroupsReplyEventArgs class + The estate's identifier on the grid + The number of Groups + Allowed Groups UUIDs + + + + The identifier of the estate + + + + + The number of returned items + + + + + List of UUIDs of Allowed Groups + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateManagersReplyEventArgs class + The estate's identifier on the grid + The number of Managers + Managers UUIDs + + + + The identifier of the estate + + + + + The number of returned items + + + + + List of UUIDs of the Estate's Managers + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateCovenantReplyEventArgs class + The Covenant ID + The timestamp + The estate's name + The Estate Owner's ID (can be a GroupID) + + + + The Covenant + + + + + The timestamp + + + + + The Estate name + + + + + The Estate Owner's ID (can be a GroupID) + + + + Returned, along with other info, upon a successful .RequestInfo() + + + Construct a new instance of the EstateUpdateInfoReplyEventArgs class + The estate's name + The Estate Owners ID (can be a GroupID) + The estate's identifier on the grid + + + + + The estate's name + + + + + The Estate Owner's ID (can be a GroupID) + + + + + The identifier of the estate on the grid + + + + + + + + Registers, unregisters, and fires events generated by incoming packets + + + + Reference to the GridClient object + + + + Default constructor + + + + + + Register an event handler + + Use PacketType.Default to fire this event on every + incoming packet + Packet type to register the handler for + Callback to be fired + True if this callback should be ran + asynchronously, false to run it synchronous + + + + Unregister an event handler + + Packet type to unregister the handler for + Callback to be unregistered + + + + Fire the events registered for this packet type + + Incoming packet type + Incoming packet + Simulator this packet was received from + + + + Object that is passed to worker threads in the ThreadPool for + firing packet callbacks + + + + Callback to fire for this packet + + + Reference to the simulator that this packet came from + + + The packet that needs to be processed + + + + Registers, unregisters, and fires events generated by the Capabilities + event queue + + + + Reference to the GridClient object + + + + Default constructor + + Reference to the GridClient object + + + + Register an new event handler for a capabilities event sent via the EventQueue + + Use String.Empty to fire this event on every CAPS event + Capability event name to register the + handler for + Callback to fire + + + + Unregister a previously registered capabilities handler + + Capability event name unregister the + handler for + Callback to unregister + + + + Fire the events registered for this event type synchronously + + Capability name + Decoded event body + Reference to the simulator that + generated this event + + + + Fire the events registered for this event type asynchronously + + Capability name + Decoded event body + Reference to the simulator that + generated this event + + + + Object that is passed to worker threads in the ThreadPool for + firing CAPS callbacks + + + + Callback to fire for this packet + + + Name of the CAPS event + + + Strongly typed decoded data + + + Reference to the simulator that generated this event + + + + + + + + The avatar has no rights + + + The avatar can see the online status of the target avatar + + + The avatar can see the location of the target avatar on the map + + + The avatar can modify the ojects of the target avatar + + + + This class holds information about an avatar in the friends list. There are two ways + to interface to this class. The first is through the set of boolean properties. This is the typical + way clients of this class will use it. The second interface is through two bitflag properties, + TheirFriendsRights and MyFriendsRights + + + + + Used internally when building the initial list of friends at login time + + System ID of the avatar being prepesented + Rights the friend has to see you online and to modify your objects + Rights you have to see your friend online and to modify their objects + + + + FriendInfo represented as a string + + A string reprentation of both my rights and my friends rights + + + + System ID of the avatar + + + + + full name of the avatar + + + + + True if the avatar is online + + + + + True if the friend can see if I am online + + + + + True if the friend can see me on the map + + + + + True if the freind can modify my objects + + + + + True if I can see if my friend is online + + + + + True if I can see if my friend is on the map + + + + + True if I can modify my friend's objects + + + + + My friend's rights represented as bitmapped flags + + + + + My rights represented as bitmapped flags + + + + + This class is used to add and remove avatars from your friends list and to manage their permission. + + + + The event subscribers. null if no subcribers + + + Raises the FriendOnline event + A FriendInfoEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendOffline event + A FriendInfoEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendRightsUpdate event + A FriendInfoEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendNames event + A FriendNamesEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendshipOffered event + A FriendshipOfferedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendshipResponse event + A FriendshipResponseEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendshipTerminated event + A FriendshipTerminatedEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the FriendFoundReply event + A FriendFoundReplyEventArgs object containing the + data returned from the data server + + + Thread sync lock object + + + + A dictionary of key/value pairs containing known friends of this avatar. + + The Key is the of the friend, the value is a + object that contains detailed information including permissions you have and have given to the friend + + + + + A Dictionary of key/value pairs containing current pending frienship offers. + + The key is the of the avatar making the request, + the value is the of the request which is used to accept + or decline the friendship offer + + + + + Internal constructor + + A reference to the GridClient Object + + + + Accept a friendship request + + agentID of avatatar to form friendship with + imSessionID of the friendship request message + + + + Decline a friendship request + + of friend + imSessionID of the friendship request message + + + + Overload: Offer friendship to an avatar. + + System ID of the avatar you are offering friendship to + + + + Offer friendship to an avatar. + + System ID of the avatar you are offering friendship to + A message to send with the request + + + + Terminate a friendship with an avatar + + System ID of the avatar you are terminating the friendship with + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + Change the rights of a friend avatar. + + the of the friend + the new rights to give the friend + This method will implicitly set the rights to those passed in the rights parameter. + + + + Use to map a friends location on the grid. + + Friends UUID to find + + + + + Use to track a friends movement on the grid + + Friends Key + + + + Ask for a notification of friend's online status + + Friend's UUID + + + + This handles the asynchronous response of a RequestAvatarNames call. + + + names cooresponding to the the list of IDs sent the the RequestAvatarNames call. + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + Populate FriendList with data from the login reply + + true if login was successful + true if login request is requiring a redirect + A string containing the response to the login request + A string containing the reason for the request + A object containing the decoded + reply from the login server + + + Raised when the simulator sends notification one of the members in our friends list comes online + + + Raised when the simulator sends notification one of the members in our friends list goes offline + + + Raised when the simulator sends notification one of the members in our friends list grants or revokes permissions + + + Raised when the simulator sends us the names on our friends list + + + Raised when the simulator sends notification another agent is offering us friendship + + + Raised when a request we sent to friend another agent is accepted or declined + + + Raised when the simulator sends notification one of the members in our friends list has terminated + our friendship + + + Raised when the simulator sends the location of a friend we have + requested map location info for + + + Contains information on a member of our friends list + + + + Construct a new instance of the FriendInfoEventArgs class + + The FriendInfo + + + Get the FriendInfo + + + Contains Friend Names + + + + Construct a new instance of the FriendNamesEventArgs class + + A dictionary where the Key is the ID of the Agent, + and the Value is a string containing their name + + + A dictionary where the Key is the ID of the Agent, + and the Value is a string containing their name + + + Sent when another agent requests a friendship with our agent + + + + Construct a new instance of the FriendshipOfferedEventArgs class + + The ID of the agent requesting friendship + The name of the agent requesting friendship + The ID of the session, used in accepting or declining the + friendship offer + + + Get the ID of the agent requesting friendship + + + Get the name of the agent requesting friendship + + + Get the ID of the session, used in accepting or declining the + friendship offer + + + A response containing the results of our request to form a friendship with another agent + + + + Construct a new instance of the FriendShipResponseEventArgs class + + The ID of the agent we requested a friendship with + The name of the agent we requested a friendship with + true if the agent accepted our friendship offer + + + Get the ID of the agent we requested a friendship with + + + Get the name of the agent we requested a friendship with + + + true if the agent accepted our friendship offer + + + Contains data sent when a friend terminates a friendship with us + + + + Construct a new instance of the FrindshipTerminatedEventArgs class + + The ID of the friend who terminated the friendship with us + The name of the friend who terminated the friendship with us + + + Get the ID of the agent that terminated the friendship with us + + + Get the name of the agent that terminated the friendship with us + + + + Data sent in response to a request which contains the information to allow us to map the friends location + + + + + Construct a new instance of the FriendFoundReplyEventArgs class + + The ID of the agent we have requested location information for + The region handle where our friend is located + The simulator local position our friend is located + + + Get the ID of the agent we have received location information for + + + Get the region handle where our mapped friend is located + + + Get the simulator local position where our friend is located + + + + Main class to expose grid functionality to clients. All of the + classes needed for sending and receiving data are accessible through + this class. + + + + // Example minimum code required to instantiate class and + // connect to a simulator. + using System; + using System.Collections.Generic; + using System.Text; + using OpenMetaverse; + + namespace FirstBot + { + class Bot + { + public static GridClient Client; + static void Main(string[] args) + { + Client = new GridClient(); // instantiates the GridClient class + // to the global Client object + // Login to Simulator + Client.Network.Login("FirstName", "LastName", "Password", "FirstBot", "1.0"); + // Wait for a Keypress + Console.ReadLine(); + // Logout of simulator + Client.Network.Logout(); + } + } + } + + + + + Networking subsystem + + + Settings class including constant values and changeable + parameters for everything + + + Parcel (subdivided simulator lots) subsystem + + + Our own avatars subsystem + + + Other avatars subsystem + + + Estate subsystem + + + Friends list subsystem + + + Grid (aka simulator group) subsystem + + + Object subsystem + + + Group subsystem + + + Asset subsystem + + + Appearance subsystem + + + Inventory subsystem + + + Directory searches including classifieds, people, land + sales, etc + + + Handles land, wind, and cloud heightmaps + + + Handles sound-related networking + + + Throttling total bandwidth usage, or allocating bandwidth + for specific data stream types + + + + Default constructor + + + + + Return the full name of this instance + + Client avatars full name + + + + Map layer request type + + + + Objects and terrain are shown + + + Only the terrain is shown, no objects + + + Overlay showing land for sale and for auction + + + + Type of grid item, such as telehub, event, populator location, etc. + + + + Telehub + + + PG rated event + + + Mature rated event + + + Popular location + + + Locations of avatar groups in a region + + + Land for sale + + + Classified ad + + + Adult rated event + + + Adult land for sale + + + + Information about a region on the grid map + + + + Sim X position on World Map + + + Sim Y position on World Map + + + Sim Name (NOTE: In lowercase!) + + + + + + Appears to always be zero (None) + + + Sim's defined Water Height + + + + + + UUID of the World Map image + + + Unique identifier for this region, a combination of the X + and Y position + + + + + + + + + + + + + + + + + + + + + + + Visual chunk of the grid map + + + + + Base class for Map Items + + + + The Global X position of the item + + + The Global Y position of the item + + + Get the Local X position of the item + + + Get the Local Y position of the item + + + Get the Handle of the region + + + + Represents an agent or group of agents location + + + + + Represents a Telehub location + + + + + Represents a non-adult parcel of land for sale + + + + + Represents an Adult parcel of land for sale + + + + + Represents a PG Event + + + + + Represents a Mature event + + + + + Represents an Adult event + + + + + Manages grid-wide tasks such as the world map + + + + The event subscribers. null if no subcribers + + + Raises the CoarseLocationUpdate event + A CoarseLocationUpdateEventArgs object containing the + data sent by simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GridRegion event + A GridRegionEventArgs object containing the + data sent by simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GridLayer event + A GridLayerEventArgs object containing the + data sent by simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GridItems event + A GridItemEventArgs object containing the + data sent by simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the RegionHandleReply event + A RegionHandleReplyEventArgs object containing the + data sent by simulator + + + Thread sync lock object + + + A dictionary of all the regions, indexed by region name + + + A dictionary of all the regions, indexed by region handle + + + + Constructor + + Instance of GridClient object to associate with this GridManager instance + + + + + + + + + + Request a map layer + + The name of the region + The type of layer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Request data for all mainland (Linden managed) simulators + + + + + Request the region handle for the specified region UUID + + UUID of the region to look up + + + + Get grid region information using the region name, this function + will block until it can find the region or gives up + + Name of sim you're looking for + Layer that you are requesting + Will contain a GridRegion for the sim you're + looking for if successful, otherwise an empty structure + True if the GridRegion was successfully fetched, otherwise + false + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends a + containing the location of agents in the simulator + + + Raised when the simulator sends a Region Data in response to + a Map request + + + Raised when the simulator sends GridLayer object containing + a map tile coordinates and texture information + + + Raised when the simulator sends GridItems object containing + details on events, land sales at a specific location + + + Raised in response to a Region lookup + + + Unknown + + + Current direction of the sun + + + Current angular velocity of the sun + + + Microseconds since the start of SL 4-hour day + + + + Avatar group management + + + + Key of Group Member + + + Total land contribution + + + Online status information + + + Abilities that the Group Member has + + + Current group title + + + Is a group owner + + + + Role manager for a group + + + + Key of the group + + + Key of Role + + + Name of Role + + + Group Title associated with Role + + + Description of Role + + + Abilities Associated with Role + + + Returns the role's title + The role's title + + + + Class to represent Group Title + + + + Key of the group + + + ID of the role title belongs to + + + Group Title + + + Whether title is Active + + + Returns group title + + + + Represents a group on the grid + + + + Key of Group + + + Key of Group Insignia + + + Key of Group Founder + + + Key of Group Role for Owners + + + Name of Group + + + Text of Group Charter + + + Title of "everyone" role + + + Is the group open for enrolement to everyone + + + Will group show up in search + + + + + + + + + + + + Is the group Mature + + + Cost of group membership + + + + + + + + + The total number of current members this group has + + + The number of roles this group has configured + + + Show this group in agent's profile + + + Returns the name of the group + A string containing the name of the group + + + + A group Vote + + + + Key of Avatar who created Vote + + + Text of the Vote proposal + + + Total number of votes + + + + A group proposal + + + + The Text of the proposal + + + The minimum number of members that must vote before proposal passes or failes + + + The required ration of yes/no votes required for vote to pass + The three options are Simple Majority, 2/3 Majority, and Unanimous + TODO: this should be an enum + + + The duration in days votes are accepted + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Struct representing a group notice + + + + + + + + + + + + + + + + + + + + + + + Struct representing a group notice list entry + + + + Notice ID + + + Creation timestamp of notice + + + Agent name who created notice + + + Notice subject + + + Is there an attachment? + + + Attachment Type + + + + Struct representing a member of a group chat session and their settings + + + + The of the Avatar + + + True if user has voice chat enabled + + + True of Avatar has moderator abilities + + + True if a moderator has muted this avatars chat + + + True if a moderator has muted this avatars voice + + + + Role update flags + + + + + + + + + + + + + + + + + + + + + + + + + Can send invitations to groups default role + + + Can eject members from group + + + Can toggle 'Open Enrollment' and change 'Signup fee' + + + Member is visible in the public member list + + + Can create new roles + + + Can delete existing roles + + + Can change Role names, titles and descriptions + + + Can assign other members to assigners role + + + Can assign other members to any role + + + Can remove members from roles + + + Can assign and remove abilities in roles + + + Can change group Charter, Insignia, 'Publish on the web' and which + members are publicly visible in group member listings + + + Can buy land or deed land to group + + + Can abandon group owned land to Governor Linden on mainland, or Estate owner for + private estates + + + Can set land for-sale information on group owned parcels + + + Can subdivide and join parcels + + + Can join group chat sessions + + + Can use voice chat in Group Chat sessions + + + Can moderate group chat sessions + + + Can toggle "Show in Find Places" and set search category + + + Can change parcel name, description, and 'Publish on web' settings + + + Can set the landing point and teleport routing on group land + + + Can change music and media settings + + + Can toggle 'Edit Terrain' option in Land settings + + + Can toggle various About Land > Options settings + + + Can always terraform land, even if parcel settings have it turned off + + + Can always fly while over group owned land + + + Can always rez objects on group owned land + + + Can always create landmarks for group owned parcels + + + Can set home location on any group owned parcel + + + Can modify public access settings for group owned parcels + + + Can manager parcel ban lists on group owned land + + + Can manage pass list sales information + + + Can eject and freeze other avatars on group owned land + + + Can return objects set to group + + + Can return non-group owned/set objects + + + Can return group owned objects + + + Can landscape using Linden plants + + + Can deed objects to group + + + Can move group owned objects + + + Can set group owned objects for-sale + + + Pay group liabilities and receive group dividends + + + List and Host group events + + + Can send group notices + + + Can receive group notices + + + Can create group proposals + + + Can vote on group proposals + + + + Ban actions available for group members + + + + Ban agent from joining a group + + + Remove restriction on agent jointing a group + + + + Handles all network traffic related to reading and writing group + information + + + + The event subscribers. null if no subcribers + + + Raises the CurrentGroups event + A CurrentGroupsEventArgs object containing the + data sent from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupNamesReply event + A GroupNamesEventArgs object containing the + data response from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupProfile event + An GroupProfileEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupMembers event + A GroupMembersEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupRolesDataReply event + A GroupRolesDataReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupRoleMembersReply event + A GroupRolesRoleMembersReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupTitlesReply event + A GroupTitlesReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupAccountSummary event + A GroupAccountSummaryReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupCreated event + An GroupCreatedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupJoined event + A GroupOperationEventArgs object containing the + result of the operation returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupLeft event + A GroupOperationEventArgs object containing the + result of the operation returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupDropped event + An GroupDroppedEventArgs object containing the + the group your agent left + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupMemberEjected event + An GroupMemberEjectedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupNoticesListReply event + An GroupNoticesListReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the GroupInvitation event + An GroupInvitationEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the BannedAgents event + An BannedAgentsEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + A reference to the current instance + + + Currently-active group members requests + + + Currently-active group roles requests + + + Currently-active group role-member requests + + + Dictionary keeping group members while request is in progress + + + Dictionary keeping mebmer/role mapping while request is in progress + + + Dictionary keeping GroupRole information while request is in progress + + + Caches group name lookups + + + + Construct a new instance of the GroupManager class + + A reference to the current instance + + + + Request a current list of groups the avatar is a member of. + + CAPS Event Queue must be running for this to work since the results + come across CAPS. + + + + Lookup name of group based on groupID + + groupID of group to lookup name for. + + + + Request lookup of multiple group names + + List of group IDs to request. + + + Lookup group profile data such as name, enrollment, founder, logo, etc + Subscribe to OnGroupProfile event to receive the results. + group ID (UUID) + + + Request a list of group members. + Subscribe to OnGroupMembers event to receive the results. + group ID (UUID) + UUID of the request, use to index into cache + + + Request group roles + Subscribe to OnGroupRoles event to receive the results. + group ID (UUID) + UUID of the request, use to index into cache + + + Request members (members,role) role mapping for a group. + Subscribe to OnGroupRolesMembers event to receive the results. + group ID (UUID) + UUID of the request, use to index into cache + + + Request a groups Titles + Subscribe to OnGroupTitles event to receive the results. + group ID (UUID) + UUID of the request, use to index into cache + + + Begin to get the group account summary + Subscribe to the OnGroupAccountSummary event to receive the results. + group ID (UUID) + How long of an interval + Which interval (0 for current, 1 for last) + + + Invites a user to a group + The group to invite to + A list of roles to invite a person to + Key of person to invite + + + Set a group as the current active group + group ID (UUID) + + + Change the role that determines your active title + Group ID to use + Role ID to change to + + + Set this avatar's tier contribution + Group ID to change tier in + amount of tier to donate + + + + Save wheather agent wants to accept group notices and list this group in their profile + + Group + Accept notices from this group + List this group in the profile + + + Request to join a group + Subscribe to OnGroupJoined event for confirmation. + group ID (UUID) to join. + + + + Request to create a new group. If the group is successfully + created, L$100 will automatically be deducted + + Subscribe to OnGroupCreated event to receive confirmation. + Group struct containing the new group info + + + Update a group's profile and other information + Groups ID (UUID) to update. + Group struct to update. + + + Eject a user from a group + Group ID to eject the user from + Avatar's key to eject + + + Update role information + Modified role to be updated + + + Create a new group role + Group ID to update + Role to create + + + Delete a group role + Group ID to update + Role to delete + + + Remove an avatar from a role + Group ID to update + Role ID to be removed from + Avatar's Key to remove + + + Assign an avatar to a role + Group ID to update + Role ID to assign to + Avatar's ID to assign to role + + + Request the group notices list + Group ID to fetch notices for + + + Request a group notice by key + ID of group notice + + + Send out a group notice + Group ID to update + GroupNotice structure containing notice data + + + Start a group proposal (vote) + The Group ID to send proposal to + GroupProposal structure containing the proposal + + + Request to leave a group + Subscribe to OnGroupLeft event to receive confirmation + The group to leave + + + + Gets the URI of the cpability for handling group bans + + Group ID + null, if the feature is not supported, or URI of the capability + + + + Request a list of residents banned from joining a group + + UUID of the group + + + + Request a list of residents banned from joining a group + + UUID of the group + Callback on request completition + + + + Request that group of agents be banned or unbanned from the group + + Group ID + Ban/Unban action + Array of agents UUIDs to ban + + + + Request that group of agents be banned or unbanned from the group + + Group ID + Ban/Unban action + Array of agents UUIDs to ban + Callback + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends us data containing + our current group membership + + + Raised when the simulator responds to a RequestGroupName + or RequestGroupNames request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when a response to a RequestGroupAccountSummary is returned + by the simulator + + + Raised when a request to create a group is successful + + + Raised when a request to join a group either + fails or succeeds + + + Raised when a request to leave a group either + fails or succeeds + + + Raised when A group is removed from the group server + + + Raised when a request to eject a member from a group either + fails or succeeds + + + Raised when the simulator sends us group notices + + + + Raised when another agent invites our avatar to join a group + + + Raised when another agent invites our avatar to join a group + + + Contains the current groups your agent is a member of + + + Construct a new instance of the CurrentGroupsEventArgs class + The current groups your agent is a member of + + + Get the current groups your agent is a member of + + + A Dictionary of group names, where the Key is the groups ID and the value is the groups name + + + Construct a new instance of the GroupNamesEventArgs class + The Group names dictionary + + + Get the Group Names dictionary + + + Represents the members of a group + + + + Construct a new instance of the GroupMembersReplyEventArgs class + + The ID of the request + The ID of the group + The membership list of the group + + + Get the ID as returned by the request to correlate + this result set and the request + + + Get the ID of the group + + + Get the dictionary of members + + + Represents the roles associated with a group + + + Construct a new instance of the GroupRolesDataReplyEventArgs class + The ID as returned by the request to correlate + this result set and the request + The ID of the group + The dictionary containing the roles + + + Get the ID as returned by the request to correlate + this result set and the request + + + Get the ID of the group + + + Get the dictionary containing the roles + + + Represents the Role to Member mappings for a group + + + Construct a new instance of the GroupRolesMembersReplyEventArgs class + The ID as returned by the request to correlate + this result set and the request + The ID of the group + The member to roles map + + + Get the ID as returned by the request to correlate + this result set and the request + + + Get the ID of the group + + + Get the member to roles map + + + Represents the titles for a group + + + Construct a new instance of the GroupTitlesReplyEventArgs class + The ID as returned by the request to correlate + this result set and the request + The ID of the group + The titles + + + Get the ID as returned by the request to correlate + this result set and the request + + + Get the ID of the group + + + Get the titles + + + Represents the summary data for a group + + + Construct a new instance of the GroupAccountSummaryReplyEventArgs class + The ID of the group + The summary data + + + Get the ID of the group + + + Get the summary data + + + A response to a group create request + + + Construct a new instance of the GroupCreatedReplyEventArgs class + The ID of the group + the success or faulure of the request + A string containing additional information + + + Get the ID of the group + + + true of the group was created successfully + + + A string containing the message + + + Represents a response to a request + + + Construct a new instance of the GroupOperationEventArgs class + The ID of the group + true of the request was successful + + + Get the ID of the group + + + true of the request was successful + + + Represents your agent leaving a group + + + Construct a new instance of the GroupDroppedEventArgs class + The ID of the group + + + Get the ID of the group + + + Represents a list of active group notices + + + Construct a new instance of the GroupNoticesListReplyEventArgs class + The ID of the group + The list containing active notices + + + Get the ID of the group + + + Get the notices list + + + Represents the profile of a group + + + Construct a new instance of the GroupProfileEventArgs class + The group profile + + + Get the group profile + + + + Provides notification of a group invitation request sent by another Avatar + + The invitation is raised when another avatar makes an offer for our avatar + to join a group. + + + The ID of the Avatar sending the group invitation + + + The name of the Avatar sending the group invitation + + + A message containing the request information which includes + the name of the group, the groups charter and the fee to join details + + + The Simulator + + + Set to true to accept invitation, false to decline + + + + Result of the request for list of agents banned from a group + + + + Indicates if list of banned agents for a group was successfully retrieved + + + Indicates if list of banned agents for a group was successfully retrieved + + + Array containing a list of UUIDs of the agents banned from a group + + + + Static helper functions and global variables + + + + This header flag signals that ACKs are appended to the packet + + + This header flag signals that this packet has been sent before + + + This header flags signals that an ACK is expected for this packet + + + This header flag signals that the message is compressed using zerocoding + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Given an X/Y location in absolute (grid-relative) terms, a region + handle is returned along with the local X/Y location in that region + + The absolute X location, a number such as + 255360.35 + The absolute Y location, a number such as + 255360.35 + The sim-local X position of the global X + position, a value from 0.0 to 256.0 + The sim-local Y position of the global Y + position, a value from 0.0 to 256.0 + A 64-bit region handle that can be used to teleport to + + + + Converts a floating point number to a terse string format used for + transmitting numbers in wearable asset files + + Floating point number to convert to a string + A terse string representation of the input number + + + + Convert a variable length field (byte array) to a string, with a + field name prepended to each line of the output + + If the byte array has unprintable characters in it, a + hex dump will be written instead + The StringBuilder object to write to + The byte array to convert to a string + A field name to prepend to each line of output + + + + Decode a zerocoded byte array, used to decompress packets marked + with the zerocoded flag + + Any time a zero is encountered, the next byte is a count + of how many zeroes to expand. One zero is encoded with 0x00 0x01, + two zeroes is 0x00 0x02, three zeroes is 0x00 0x03, etc. The + first four bytes are copied directly to the output buffer. + + The byte array to decode + The length of the byte array to decode. This + would be the length of the packet up to (but not including) any + appended ACKs + The output byte array to decode to + The length of the output buffer + + + + Encode a byte array with zerocoding. Used to compress packets marked + with the zerocoded flag. Any zeroes in the array are compressed down + to a single zero byte followed by a count of how many zeroes to expand + out. A single zero becomes 0x00 0x01, two zeroes becomes 0x00 0x02, + three zeroes becomes 0x00 0x03, etc. The first four bytes are copied + directly to the output buffer. + + The byte array to encode + The length of the byte array to encode + The output byte array to encode to + The length of the output buffer + + + + Calculates the CRC (cyclic redundancy check) needed to upload inventory. + + Creation date + Sale type + Inventory type + Type + Asset ID + Group ID + Sale price + Owner ID + Creator ID + Item ID + Folder ID + Everyone mask (permissions) + Flags + Next owner mask (permissions) + Group mask (permissions) + Owner mask (permissions) + The calculated CRC + + + + Attempts to load a file embedded in the assembly + + The filename of the resource to load + A Stream for the requested file, or null if the resource + was not successfully loaded + + + + Attempts to load a file either embedded in the assembly or found in + a given search path + + The filename of the resource to load + An optional path that will be searched if + the asset is not found embedded in the assembly + A Stream for the requested file, or null if the resource + was not successfully loaded + + + + Converts a list of primitives to an object that can be serialized + with the LLSD system + + Primitives to convert to a serializable object + An object that can be serialized with LLSD + + + + Deserializes OSD in to a list of primitives + + Structure holding the serialized primitive list, + must be of the SDMap type + A list of deserialized primitives + + + + Converts a struct or class object containing fields only into a key value separated string + + The struct object + A string containing the struct fields as the keys, and the field value as the value separated + + + // Add the following code to any struct or class containing only fields to override the ToString() + // method to display the values of the passed object + + /// Print the struct data as a string + ///A string containing the field name, and field value + public override string ToString() + { + return Helpers.StructToString(this); + } + + + + + + Passed to Logger.Log() to identify the severity of a log entry + + + + No logging information will be output + + + Non-noisy useful information, may be helpful in + debugging a problem + + + A non-critical error occurred. A warning will not + prevent the rest of the library from operating as usual, + although it may be indicative of an underlying issue + + + A critical error has occurred. Generally this will + be followed by the network layer shutting down, although the + stability of the library after an error is uncertain + + + Used for internal testing, this logging level can + generate very noisy (long and/or repetitive) messages. Don't + pass this to the Log() function, use DebugLog() instead. + + + + + The InternalDictionary class is used through the library for storing key/value pairs. + It is intended to be a replacement for the generic Dictionary class and should + be used in its place. It contains several methods for allowing access to the data from + outside the library that are read only and thread safe. + + + Key + Value + + + Internal dictionary that this class wraps around. Do not + modify or enumerate the contents of this dictionary without locking + on this member + + + + Initializes a new instance of the Class + with the specified key/value, has the default initial capacity. + + + + // initialize a new InternalDictionary named testDict with a string as the key and an int as the value. + public InternalDictionary<string, int> testDict = new InternalDictionary<string, int>(); + + + + + + Initializes a new instance of the Class + with the specified key/value, has its initial valies copied from the specified + + + + to copy initial values from + + + // initialize a new InternalDictionary named testAvName with a UUID as the key and an string as the value. + // populates with copied values from example KeyNameCache Dictionary. + + // create source dictionary + Dictionary<UUID, string> KeyNameCache = new Dictionary<UUID, string>(); + KeyNameCache.Add("8300f94a-7970-7810-cf2c-fc9aa6cdda24", "Jack Avatar"); + KeyNameCache.Add("27ba1e40-13f7-0708-3e98-5819d780bd62", "Jill Avatar"); + + // Initialize new dictionary. + public InternalDictionary<UUID, string> testAvName = new InternalDictionary<UUID, string>(KeyNameCache); + + + + + + Initializes a new instance of the Class + with the specified key/value, With its initial capacity specified. + + Initial size of dictionary + + + // initialize a new InternalDictionary named testDict with a string as the key and an int as the value, + // initially allocated room for 10 entries. + public InternalDictionary<string, int> testDict = new InternalDictionary<string, int>(10); + + + + + + Try to get entry from with specified key + + Key to use for lookup + Value returned + if specified key exists, if not found + + + // find your avatar using the Simulator.ObjectsAvatars InternalDictionary: + Avatar av; + if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(Client.Self.AgentID, out av)) + Console.WriteLine("Found Avatar {0}", av.Name); + + + + + + + Finds the specified match. + + The match. + Matched value + + + // use a delegate to find a prim in the ObjectsPrimitives InternalDictionary + // with the ID 95683496 + uint findID = 95683496; + Primitive findPrim = sim.ObjectsPrimitives.Find( + delegate(Primitive prim) { return prim.ID == findID; }); + + + + + Find All items in an + return matching items. + a containing found items. + + Find All prims within 20 meters and store them in a List + + int radius = 20; + List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( + delegate(Primitive prim) { + Vector3 pos = prim.Position; + return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); + } + ); + + + + + Find All items in an + return matching keys. + a containing found keys. + + Find All keys which also exist in another dictionary + + List<UUID> matches = myDict.FindAll( + delegate(UUID id) { + return myOtherDict.ContainsKey(id); + } + ); + + + + + Perform an on each entry in an + to perform + + + // Iterates over the ObjectsPrimitives InternalDictionary and prints out some information. + Client.Network.CurrentSim.ObjectsPrimitives.ForEach( + delegate(Primitive prim) + { + if (prim.Text != null) + { + Console.WriteLine("NAME={0} ID = {1} TEXT = '{2}'", + prim.PropertiesFamily.Name, prim.ID, prim.Text); + } + }); + + + + + Perform an on each key of an + to perform + + + + Perform an on each KeyValuePair of an + + to perform + + + Check if Key exists in Dictionary + Key to check for + if found, otherwise + + + Check if Value exists in Dictionary + Value to check for + if found, otherwise + + + + Adds the specified key to the dictionary, dictionary locking is not performed, + + + The key + The value + + + + Removes the specified key, dictionary locking is not performed + + The key. + if successful, otherwise + + + + Gets the number of Key/Value pairs contained in the + + + + + Indexer for the dictionary + + The key + The value + + + + Exception class to identify inventory exceptions + + + + + Responsible for maintaining inventory structure. Inventory constructs nodes + and manages node children as is necessary to maintain a coherant hirarchy. + Other classes should not manipulate or create InventoryNodes explicitly. When + A node's parent changes (when a folder is moved, for example) simply pass + Inventory the updated InventoryFolder and it will make the appropriate changes + to its internal representation. + + + + The event subscribers, null of no subscribers + + + Raises the InventoryObjectUpdated Event + A InventoryObjectUpdatedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the InventoryObjectRemoved Event + A InventoryObjectRemovedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the InventoryObjectAdded Event + A InventoryObjectAddedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + + Returns the contents of the specified folder + + A folder's UUID + The contents of the folder corresponding to folder + When folder does not exist in the inventory + + + + Updates the state of the InventoryNode and inventory data structure that + is responsible for the InventoryObject. If the item was previously not added to inventory, + it adds the item, and updates structure accordingly. If it was, it updates the + InventoryNode, changing the parent node if item.parentUUID does + not match node.Parent.Data.UUID. + + You can not set the inventory root folder using this method + + The InventoryObject to store + + + + Removes the InventoryObject and all related node data from Inventory. + + The InventoryObject to remove. + + + + Used to find out if Inventory contains the InventoryObject + specified by uuid. + + The UUID to check. + true if inventory contains uuid, false otherwise + + + + Saves the current inventory structure to a cache file + + Name of the cache file to save to + + + + Loads in inventory cache file into the inventory structure. Note only valid to call after login has been successful. + + Name of the cache file to load + The number of inventory items sucessfully reconstructed into the inventory node tree + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + + The root folder of this avatars inventory + + + + + The default shared library folder + + + + + The root node of the avatars inventory + + + + + The root node of the default shared library + + + + + By using the bracket operator on this class, the program can get the + InventoryObject designated by the specified uuid. If the value for the corresponding + UUID is null, the call is equivelant to a call to RemoveNodeFor(this[uuid]). + If the value is non-null, it is equivelant to a call to UpdateNodeFor(value), + the uuid parameter is ignored. + + The UUID of the InventoryObject to get or set, ignored if set to non-null value. + The InventoryObject corresponding to uuid. + + + Sort by name + + + Sort by date + + + Sort folders by name, regardless of whether items are + sorted by name or date + + + Place system folders at the top + + + + Possible destinations for DeRezObject request + + + + + + + Copy from in-world to agent inventory + + + Derez to TaskInventory + + + + + + Take Object + + + + + + Delete Object + + + Put an avatar attachment into agent inventory + + + + + + Return an object back to the owner's inventory + + + Return a deeded object back to the last owner's inventory + + + + Upper half of the Flags field for inventory items + + + + Indicates that the NextOwner permission will be set to the + most restrictive set of permissions found in the object set + (including linkset items and object inventory items) on next rez + + + Indicates that the object sale information has been + changed + + + If set, and a slam bit is set, indicates BaseMask will be overwritten on Rez + + + If set, and a slam bit is set, indicates OwnerMask will be overwritten on Rez + + + If set, and a slam bit is set, indicates GroupMask will be overwritten on Rez + + + If set, and a slam bit is set, indicates EveryoneMask will be overwritten on Rez + + + If set, and a slam bit is set, indicates NextOwnerMask will be overwritten on Rez + + + Indicates whether this object is composed of multiple + items or not + + + Indicates that the asset is only referenced by this + inventory item. If this item is deleted or updated to reference a + new assetID, the asset can be deleted + + + + Base Class for Inventory Items + + + + of item/folder + + + of parent folder + + + Name of item/folder + + + Item/Folder Owners + + + + Constructor, takes an itemID as a parameter + + The of the item + + + + + + + + + + + + + + + + Generates a number corresponding to the value of the object to support the use of a hash table, + suitable for use in hashing algorithms and data structures such as a hash table + + A Hashcode of all the combined InventoryBase fields + + + + Determine whether the specified object is equal to the current object + + InventoryBase object to compare against + true if objects are the same + + + + Determine whether the specified object is equal to the current object + + InventoryBase object to compare against + true if objects are the same + + + + Convert inventory to OSD + + OSD representation + + + + An Item in Inventory + + + + The of this item + + + The combined of this item + + + The type of item from + + + The type of item from the enum + + + The of the creator of this item + + + A Description of this item + + + The s this item is set to or owned by + + + If true, item is owned by a group + + + The price this item can be purchased for + + + The type of sale from the enum + + + Combined flags from + + + Time and date this inventory item was created, stored as + UTC (Coordinated Universal Time) + + + Used to update the AssetID in requests sent to the server + + + The of the previous owner of the item + + + + Construct a new InventoryItem object + + The of the item + + + + Construct a new InventoryItem object of a specific Type + + The type of item from + of the item + + + + Indicates inventory item is a link + + True if inventory item is a link to another inventory item + + + + + + + + + + + + + + + + Generates a number corresponding to the value of the object to support the use of a hash table. + Suitable for use in hashing algorithms and data structures such as a hash table + + A Hashcode of all the combined InventoryItem fields + + + + Compares an object + + The object to compare + true if comparison object matches + + + + Determine whether the specified object is equal to the current object + + The object to compare against + true if objects are the same + + + + Determine whether the specified object is equal to the current object + + The object to compare against + true if objects are the same + + + + Create InventoryItem from OSD + + OSD Data that makes up InventoryItem + Inventory item created + + + + Convert InventoryItem to OSD + + OSD representation of InventoryItem + + + + InventoryTexture Class representing a graphical image + + + + + + Construct an InventoryTexture object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryTexture object from a serialization stream + + + + + InventorySound Class representing a playable sound + + + + + Construct an InventorySound object + + A which becomes the + objects AssetUUID + + + + Construct an InventorySound object from a serialization stream + + + + + InventoryCallingCard Class, contains information on another avatar + + + + + Construct an InventoryCallingCard object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryCallingCard object from a serialization stream + + + + + InventoryLandmark Class, contains details on a specific location + + + + + Construct an InventoryLandmark object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryLandmark object from a serialization stream + + + + + Landmarks use the InventoryItemFlags struct and will have a flag of 1 set if they have been visited + + + + + InventoryObject Class contains details on a primitive or coalesced set of primitives + + + + + Construct an InventoryObject object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryObject object from a serialization stream + + + + + Gets or sets the upper byte of the Flags value + + + + + Gets or sets the object attachment point, the lower byte of the Flags value + + + + + InventoryNotecard Class, contains details on an encoded text document + + + + + Construct an InventoryNotecard object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryNotecard object from a serialization stream + + + + + InventoryCategory Class + + TODO: Is this even used for anything? + + + + Construct an InventoryCategory object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryCategory object from a serialization stream + + + + + InventoryLSL Class, represents a Linden Scripting Language object + + + + + Construct an InventoryLSL object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryLSL object from a serialization stream + + + + + InventorySnapshot Class, an image taken with the viewer + + + + + Construct an InventorySnapshot object + + A which becomes the + objects AssetUUID + + + + Construct an InventorySnapshot object from a serialization stream + + + + + InventoryAttachment Class, contains details on an attachable object + + + + + Construct an InventoryAttachment object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryAttachment object from a serialization stream + + + + + Get the last AttachmentPoint this object was attached to + + + + + InventoryWearable Class, details on a clothing item or body part + + + + + Construct an InventoryWearable object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryWearable object from a serialization stream + + + + + The , Skin, Shape, Skirt, Etc + + + + + InventoryAnimation Class, A bvh encoded object which animates an avatar + + + + + Construct an InventoryAnimation object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryAnimation object from a serialization stream + + + + + InventoryGesture Class, details on a series of animations, sounds, and actions + + + + + Construct an InventoryGesture object + + A which becomes the + objects AssetUUID + + + + Construct an InventoryGesture object from a serialization stream + + + + + A folder contains s and has certain attributes specific + to itself + + + + The Preferred for a folder. + + + The Version of this folder + + + Number of child items this folder contains. + + + + Constructor + + UUID of the folder + + + + + + + + + + Get Serilization data for this InventoryFolder object + + + + + Construct an InventoryFolder object from a serialization stream + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create InventoryFolder from OSD + + OSD Data that makes up InventoryFolder + Inventory folder created + + + + Convert InventoryItem to OSD + + OSD representation of InventoryItem + + + + Tools for dealing with agents inventory + + + + Used for converting shadow_id to asset_id + + + The event subscribers, null of no subscribers + + + Raises the ItemReceived Event + A ItemReceivedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the FolderUpdated Event + A FolderUpdatedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the InventoryObjectOffered Event + A InventoryObjectOfferedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the TaskItemReceived Event + A TaskItemReceivedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the FindObjectByPath Event + A FindObjectByPathEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the TaskInventoryReply Event + A TaskInventoryReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SaveAssetToInventory Event + A SaveAssetToInventoryEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ScriptRunningReply Event + A ScriptRunningReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + Partial mapping of AssetTypes to folder names + + + + Default constructor + + Reference to the GridClient object + + + + Fetch an inventory item from the dataserver + + The items + The item Owners + a integer representing the number of milliseconds to wait for results + An object on success, or null if no item was found + Items will also be sent to the event + + + + Request A single inventory item + + The items + The item Owners + + + + + Request inventory items + + Inventory items to request + Owners of the inventory items + + + + + Request inventory items via Capabilities + + Inventory items to request + Owners of the inventory items + + + + + Get contents of a folder + + The of the folder to search + The of the folders owner + true to retrieve folders + true to retrieve items + sort order to return results in + a integer representing the number of milliseconds to wait for results + A list of inventory items matching search criteria within folder + + InventoryFolder.DescendentCount will only be accurate if both folders and items are + requested + + + + Request the contents of an inventory folder + + The folder to search + The folder owners + true to return s contained in folder + true to return s containd in folder + the sort order to return items in + + + + + Request the contents of an inventory folder using HTTP capabilities + + The folder to search + The folder owners + true to return s contained in folder + true to return s containd in folder + the sort order to return items in + + + + + Returns the UUID of the folder (category) that defaults to + containing 'type'. The folder is not necessarily only for that + type + + This will return the root folder if one does not exist + + The UUID of the desired folder if found, the UUID of the RootFolder + if not found, or UUID.Zero on failure + + + + Find an object in inventory using a specific path to search + + The folder to begin the search in + The object owners + A string path to search + milliseconds to wait for a reply + Found items or if + timeout occurs or item is not found + + + + Find inventory items by path + + The folder to begin the search in + The object owners + A string path to search, folders/objects separated by a '/' + Results are sent to the event + + + + Search inventory Store object for an item or folder + + The folder to begin the search in + An array which creates a path to search + Number of levels below baseFolder to conduct searches + if True, will stop searching after first match is found + A list of inventory items found + + + + Move an inventory item or folder to a new location + + The item or folder to move + The to move item or folder to + + + + Move an inventory item or folder to a new location and change its name + + The item or folder to move + The to move item or folder to + The name to change the item or folder to + + + + Move and rename a folder + + The source folders + The destination folders + The name to change the folder to + + + + Update folder properties + + of the folder to update + Sets folder's parent to + Folder name + Folder type + + + + Move a folder + + The source folders + The destination folders + + + + Move multiple folders, the keys in the Dictionary parameter, + to a new parents, the value of that folder's key. + + A Dictionary containing the + of the source as the key, and the + of the destination as the value + + + + Move an inventory item to a new folder + + The of the source item to move + The of the destination folder + + + + Move and rename an inventory item + + The of the source item to move + The of the destination folder + The name to change the folder to + + + + Move multiple inventory items to new locations + + A Dictionary containing the + of the source item as the key, and the + of the destination folder as the value + + + + Remove descendants of a folder + + The of the folder + + + + Remove a single item from inventory + + The of the inventory item to remove + + + + Remove a folder from inventory + + The of the folder to remove + + + + Remove multiple items or folders from inventory + + A List containing the s of items to remove + A List containing the s of the folders to remove + + + + Empty the Lost and Found folder + + + + + Empty the Trash folder + + + + + + + + + + + Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here. + + + + + + + + + + + + + Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here. + + + + + + + + Creates a new inventory folder + + ID of the folder to put this folder in + Name of the folder to create + The UUID of the newly created folder + + + + Creates a new inventory folder + + ID of the folder to put this folder in + Name of the folder to create + Sets this folder as the default folder + for new assets of the specified type. Use AssetType.Unknown + to create a normal folder, otherwise it will likely create a + duplicate of an existing folder type + The UUID of the newly created folder + If you specify a preferred type of AsseType.Folder + it will create a new root folder which may likely cause all sorts + of strange problems + + + + Create an inventory item and upload asset data + + Asset data + Inventory item name + Inventory item description + Asset type + Inventory type + Put newly created inventory in this folder + Delegate that will receive feedback on success or failure + + + + Create an inventory item and upload asset data + + Asset data + Inventory item name + Inventory item description + Asset type + Inventory type + Put newly created inventory in this folder + Permission of the newly created item + (EveryoneMask, GroupMask, and NextOwnerMask of Permissions struct are supported) + Delegate that will receive feedback on success or failure + + + + Creates inventory link to another inventory item or folder + + Put newly created link in folder with this UUID + Inventory item or folder + Method to call upon creation of the link + + + + Creates inventory link to another inventory item + + Put newly created link in folder with this UUID + Original inventory item + Method to call upon creation of the link + + + + Creates inventory link to another inventory folder + + Put newly created link in folder with this UUID + Original inventory folder + Method to call upon creation of the link + + + + Creates inventory link to another inventory item or folder + + Put newly created link in folder with this UUID + Original item's UUID + Name + Description + Asset Type + Inventory Type + Transaction UUID + Method to call upon creation of the link + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Request a copy of an asset embedded within a notecard + + Usually UUID.Zero for copying an asset from a notecard + UUID of the notecard to request an asset from + Target folder for asset to go to in your inventory + UUID of the embedded asset + callback to run when item is copied to inventory + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Save changes to notecard embedded in object contents + + Encoded notecard asset data + Notecard UUID + Object's UUID + Called upon finish of the upload with status information + + + + Upload new gesture asset for an inventory gesture item + + Encoded gesture asset + Gesture inventory UUID + Callback whick will be called when upload is complete + + + + Update an existing script in an agents Inventory + + A byte[] array containing the encoded scripts contents + the itemID of the script + if true, sets the script content to run on the mono interpreter + + + + + Update an existing script in an task Inventory + + A byte[] array containing the encoded scripts contents + the itemID of the script + UUID of the prim containting the script + if true, sets the script content to run on the mono interpreter + if true, sets the script to running + + + + + Rez an object from inventory + + Simulator to place object in + Rotation of the object when rezzed + Vector of where to place object + InventoryItem object containing item details + + + + Rez an object from inventory + + Simulator to place object in + Rotation of the object when rezzed + Vector of where to place object + InventoryItem object containing item details + UUID of group to own the object + + + + Rez an object from inventory + + Simulator to place object in + Rotation of the object when rezzed + Vector of where to place object + InventoryItem object containing item details + UUID of group to own the object + User defined queryID to correlate replies + If set to true, the CreateSelected flag + will be set on the rezzed object + + + + Rez an object from inventory + + Simulator to place object in + TaskID object when rezzed + Rotation of the object when rezzed + Vector of where to place object + InventoryItem object containing item details + UUID of group to own the object + User defined queryID to correlate replies + If set to true, the CreateSelected flag + will be set on the rezzed object + + + + DeRez an object from the simulator to the agents Objects folder in the agents Inventory + + The simulator Local ID of the object + If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed + + + + DeRez an object from the simulator and return to inventory + + The simulator Local ID of the object + The type of destination from the enum + The destination inventory folders -or- + if DeRezzing object to a tasks Inventory, the Tasks + The transaction ID for this request which + can be used to correlate this request with other packets + If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed + + + + Rez an item from inventory to its previous simulator location + + + + + + + + + Give an inventory item to another avatar + + The of the item to give + The name of the item + The type of the item from the enum + The of the recipient + true to generate a beameffect during transfer + + + + Give an inventory Folder with contents to another avatar + + The of the Folder to give + The name of the folder + The type of the item from the enum + The of the recipient + true to generate a beameffect during transfer + + + + Copy or move an from agent inventory to a task (primitive) inventory + + The target object + The item to copy or move from inventory + + For items with copy permissions a copy of the item is placed in the tasks inventory, + for no-copy items the object is moved to the tasks inventory + + + + Retrieve a listing of the items contained in a task (Primitive) + + The tasks + The tasks simulator local ID + milliseconds to wait for reply from simulator + A list containing the inventory items inside the task or null + if a timeout occurs + This request blocks until the response from the simulator arrives + or timeoutMS is exceeded + + + + Request the contents of a tasks (primitives) inventory from the + current simulator + + The LocalID of the object + + + + + Request the contents of a tasks (primitives) inventory + + The simulator Local ID of the object + A reference to the simulator object that contains the object + + + + + Move an item from a tasks (Primitive) inventory to the specified folder in the avatars inventory + + LocalID of the object in the simulator + UUID of the task item to move + The ID of the destination folder in this agents inventory + Simulator Object + Raises the event + + + + Remove an item from an objects (Prim) Inventory + + LocalID of the object in the simulator + UUID of the task item to remove + Simulator Object + You can confirm the removal by comparing the tasks inventory serial before and after the + request with the request combined with + the event + + + + Copy an InventoryScript item from the Agents Inventory into a primitives task inventory + + An unsigned integer representing a primitive being simulated + An which represents a script object from the agents inventory + true to set the scripts running state to enabled + A Unique Transaction ID + + The following example shows the basic steps necessary to copy a script from the agents inventory into a tasks inventory + and assumes the script exists in the agents inventory. + + uint primID = 95899503; // Fake prim ID + UUID scriptID = UUID.Parse("92a7fe8a-e949-dd39-a8d8-1681d8673232"); // Fake Script UUID in Inventory + + Client.Inventory.FolderContents(Client.Inventory.FindFolderForType(AssetType.LSLText), Client.Self.AgentID, + false, true, InventorySortOrder.ByName, 10000); + + Client.Inventory.RezScript(primID, (InventoryItem)Client.Inventory.Store[scriptID]); + + + + + + Request the running status of a script contained in a task (primitive) inventory + + The ID of the primitive containing the script + The ID of the script + The event can be used to obtain the results of the + request + + + + + Send a request to set the running state of a script contained in a task (primitive) inventory + + The ID of the primitive containing the script + The ID of the script + true to set the script running, false to stop a running script + To verify the change you can use the method combined + with the event + + + + Create a CRC from an InventoryItem + + The source InventoryItem + A uint representing the source InventoryItem as a CRC + + + + Reverses a cheesy XORing with a fixed UUID to convert a shadow_id to an asset_id + + Obfuscated shadow_id value + Deobfuscated asset_id value + + + + Does a cheesy XORing with a fixed UUID to convert an asset_id to a shadow_id + + asset_id value to obfuscate + Obfuscated shadow_id value + + + + Wrapper for creating a new object + + The type of item from the enum + The of the newly created object + An object with the type and id passed + + + + Parse the results of a RequestTaskInventory() response + + A string which contains the data from the task reply + A List containing the items contained within the tasks inventory + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + UpdateCreateInventoryItem packets are received when a new inventory item + is created. This may occur when an object that's rezzed in world is + taken into inventory, when an item is created using the CreateInventoryItem + packet, or when an object is purchased + + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + an inventory object sent by another avatar or primitive + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + + Get this agents Inventory data + + + + + Callback for inventory item creation finishing + + Whether the request to create an inventory + item succeeded or not + Inventory item being created. If success is + false this will be null + + + + Callback for an inventory item being create from an uploaded asset + + true if inventory item creation was successful + + + + + + + + + + + + + Reply received when uploading an inventory asset + + Has upload been successful + Error message if upload failed + Inventory asset UUID + New asset UUID + + + + Delegate that is invoked when script upload is completed + + Has upload succeded (note, there still might be compile errors) + Upload status message + Is compilation successful + If compilation failed, list of error messages, null on compilation success + Script inventory UUID + Script's new asset UUID + + + Set to true to accept offer, false to decline it + + + The folder to accept the inventory into, if null default folder for will be used + + + + Callback when an inventory object is accepted and received from a + task inventory. This is the callback in which you actually get + the ItemID, as in ObjectOfferedCallback it is null when received + from a task. + + + + + + + + + + + + + + + + De-serialization constructor for the InventoryNode Class + + + + + Serialization handler for the InventoryNode Class + + + + + De-serialization handler for the InventoryNode Class + + + + + + + + + + + + + User data + + + + + + + + + + + + + For inventory folder nodes specifies weather the folder needs to be + refreshed from the server + + + + + Singleton logging class for the entire library + + + + log4net logging engine + + + + Default constructor + + + + + Send a log message to the logging engine + + The log message + The severity of the log entry + + + + Send a log message to the logging engine + + The log message + The severity of the log entry + Instance of the client + + + + Send a log message to the logging engine + + The log message + The severity of the log entry + Exception that was raised + + + + Send a log message to the logging engine + + The log message + The severity of the log entry + Instance of the client + Exception that was raised + + + + If the library is compiled with DEBUG defined, an event will be + fired if an OnLogMessage handler is registered and the + message will be sent to the logging engine + + The message to log at the DEBUG level to the + current logging engine + + + + If the library is compiled with DEBUG defined and + GridClient.Settings.DEBUG is true, an event will be + fired if an OnLogMessage handler is registered and the + message will be sent to the logging engine + + The message to log at the DEBUG level to the + current logging engine + Instance of the client + + + Triggered whenever a message is logged. If this is left + null, log messages will go to the console + + + + Callback used for client apps to receive log messages from + the library + + Data being logged + The severity of the log entry from + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Status of the last application run. + Used for error reporting to the grid login service for statistical purposes. + + + + Application exited normally + + + Application froze + + + Application detected error and exited abnormally + + + Other crash + + + Application froze during logout + + + Application crashed during logout + + + + Login Request Parameters + + + + The URL of the Login Server + + + The number of milliseconds to wait before a login is considered + failed due to timeout + + + The request method + login_to_simulator is currently the only supported method + + + The Agents First name + + + The Agents Last name + + + A md5 hashed password + plaintext password will be automatically hashed + + + The agents starting location once logged in + Either "last", "home", or a string encoded URI + containing the simulator name and x/y/z coordinates e.g: uri:hooper&128&152&17 + + + A string containing the client software channel information + Second Life Release + + + The client software version information + The official viewer uses: Second Life Release n.n.n.n + where n is replaced with the current version of the viewer + + + A string containing the platform information the agent is running on + + + A string hash of the network cards Mac Address + + + Unknown or deprecated + + + A string hash of the first disk drives ID used to identify this clients uniqueness + + + A string containing the viewers Software, this is not directly sent to the login server but + instead is used to generate the Version string + + + A string representing the software creator. This is not directly sent to the login server but + is used by the library to generate the Version information + + + If true, this agent agrees to the Terms of Service of the grid its connecting to + + + Unknown + + + Status of the last application run sent to the grid login server for statistical purposes + + + An array of string sent to the login server to enable various options + + + A randomly generated ID to distinguish between login attempts. This value is only used + internally in the library and is never sent over the wire + + + + Default constuctor, initializes sane default values + + + + + Instantiates new LoginParams object and fills in the values + + Instance of GridClient to read settings from + Login first name + Login last name + Password + Login channnel (application name) + Client version, should be application name + version number + + + + Instantiates new LoginParams object and fills in the values + + Instance of GridClient to read settings from + Login first name + Login last name + Password + Login channnel (application name) + Client version, should be application name + version number + URI of the login server + + + + The decoded data returned from the login server after a successful login + + + + true, false, indeterminate + + + Login message of the day + + + M or PG, also agent_region_access and agent_access_max + + + + Parse LLSD Login Reply Data + + An + contaning the login response data + XML-RPC logins do not require this as XML-RPC.NET + automatically populates the struct properly using attributes + + + + Login Routines + + + NetworkManager is responsible for managing the network layer of + OpenMetaverse. It tracks all the server connections, serializes + outgoing traffic and deserializes incoming traffic, and provides + instances of delegates for network-related events. + + + + The event subscribers, null of no subscribers + + + Raises the LoginProgress Event + A LoginProgressEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + Seed CAPS URL returned from the login server + + + Maximum number of groups an agent can belong to, -1 for unlimited + + + Server side baking service URL + + + Parsed login response data + + + A list of packets obtained during the login process which + networkmanager will log but not process + + + + Generate sane default values for a login request + + Account first name + Account last name + Account password + Client application name (channel) + Client application name + version + A populated struct containing + sane defaults + + + + Simplified login that takes the most common and required fields + + Account first name + Account last name + Account password + Client application name (channel) + Client application name + version + Whether the login was successful or not. On failure the + LoginErrorKey string will contain the error code and LoginMessage + will contain a description of the error + + + + Simplified login that takes the most common fields along with a + starting location URI, and can accept an MD5 string instead of a + plaintext password + + Account first name + Account last name + Account password or MD5 hash of the password + such as $1$1682a1e45e9f957dcdf0bb56eb43319c + Client application name (channel) + Starting location URI that can be built with + StartLocation() + Client application name + version + Whether the login was successful or not. On failure the + LoginErrorKey string will contain the error code and LoginMessage + will contain a description of the error + + + + Login that takes a struct of all the values that will be passed to + the login server + + The values that will be passed to the login + server, all fields must be set even if they are String.Empty + Whether the login was successful or not. On failure the + LoginErrorKey string will contain the error code and LoginMessage + will contain a description of the error + + + + Build a start location URI for passing to the Login function + + Name of the simulator to start in + X coordinate to start at + Y coordinate to start at + Z coordinate to start at + String with a URI that can be used to login to a specified + location + + + + LoginParams and the initial login XmlRpcRequest were made on a remote machine. + This method now initializes libomv with the results. + + + + + Handles response from XML-RPC login replies + + + + + Handles response from XML-RPC login replies with already parsed LoginResponseData + + + + + Handle response from LLSD login replies + + + + + + + + Get current OS + + Either "Win" or "Linux" + + + + Get clients default Mac Address + + A string containing the first found Mac Address + + + The event subscribers, null of no subscribers + + + Raises the PacketSent Event + A PacketSentEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the LoggedOut Event + A LoggedOutEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SimConnecting Event + A SimConnectingEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SimConnected Event + A SimConnectedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SimDisconnected Event + A SimDisconnectedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the Disconnected Event + A DisconnectedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SimChanged Event + A SimChangedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the EventQueueRunning Event + A EventQueueRunningEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + All of the simulators we are currently connected to + + + Handlers for incoming capability events + + + Handlers for incoming packets + + + Incoming packets that are awaiting handling + + + Outgoing packets that are awaiting handling + + + + Default constructor + + Reference to the GridClient object + + + + Register an event handler for a packet. This is a low level event + interface and should only be used if you are doing something not + supported in the library + + Packet type to trigger events for + Callback to fire when a packet of this type + is received + + + + Register an event handler for a packet. This is a low level event + interface and should only be used if you are doing something not + supported in the library + + Packet type to trigger events for + Callback to fire when a packet of this type + is received + True if the callback should be ran + asynchronously. Only set this to false (synchronous for callbacks + that will always complete quickly) + If any callback for a packet type is marked as + asynchronous, all callbacks for that packet type will be fired + asynchronously + + + + Unregister an event handler for a packet. This is a low level event + interface and should only be used if you are doing something not + supported in the library + + Packet type this callback is registered with + Callback to stop firing events for + + + + Register a CAPS event handler. This is a low level event interface + and should only be used if you are doing something not supported in + the library + + Name of the CAPS event to register a handler for + Callback to fire when a CAPS event is received + + + + Unregister a CAPS event handler. This is a low level event interface + and should only be used if you are doing something not supported in + the library + + Name of the CAPS event this callback is + registered with + Callback to stop firing events for + + + + Send a packet to the simulator the avatar is currently occupying + + Packet to send + + + + Send a packet to a specified simulator + + Packet to send + Simulator to send the packet to + + + + Connect to a simulator + + IP address to connect to + Port to connect to + Handle for this simulator, to identify its + location in the grid + Whether to set CurrentSim to this new + connection, use this if the avatar is moving in to this simulator + URL of the capabilities server to use for + this sim connection + A Simulator object on success, otherwise null + + + + Connect to a simulator + + IP address and port to connect to + Handle for this simulator, to identify its + location in the grid + Whether to set CurrentSim to this new + connection, use this if the avatar is moving in to this simulator + URL of the capabilities server to use for + this sim connection + A Simulator object on success, otherwise null + + + + Initiate a blocking logout request. This will return when the logout + handshake has completed or when Settings.LOGOUT_TIMEOUT + has expired and the network layer is manually shut down + + + + + Initiate the logout process. Check if logout succeeded with the + OnLogoutReply event, and if this does not fire the + Shutdown() function needs to be manually called + + + + + Close a connection to the given simulator + + + + + + + Shutdown will disconnect all the sims except for the current sim + first, and then kill the connection to CurrentSim. This should only + be called if the logout process times out on RequestLogout + + Type of shutdown + + + + Shutdown will disconnect all the sims except for the current sim + first, and then kill the connection to CurrentSim. This should only + be called if the logout process times out on RequestLogout + + Type of shutdown + Shutdown message + + + + Searches through the list of currently connected simulators to find + one attached to the given IPEndPoint + + IPEndPoint of the Simulator to search for + A Simulator reference on success, otherwise null + + + + Fire an event when an event queue connects for capabilities + + Simulator the event queue is attached to + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends us data containing + ... + + + Called when a reply is received from the login server, the + login sequence will block until this event returns + + + Current state of logging in + + + Upon login failure, contains a short string key for the + type of login error that occurred + + + The raw XML-RPC reply from the login server, exactly as it + was received (minus the HTTP header) + + + During login this contains a descriptive version of + LoginStatusCode. After a successful login this will contain the + message of the day, and after a failed login a descriptive error + message will be returned + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Unique identifier associated with our connections to + simulators + + + The simulator that the logged in avatar is currently + occupying + + + Shows whether the network layer is logged in to the + grid or not + + + Number of packets in the incoming queue + + + Number of packets in the outgoing queue + + + + + + + + + + + + + + Explains why a simulator or the grid disconnected from us + + + + The client requested the logout or simulator disconnect + + + The server notified us that it is disconnecting + + + Either a socket was closed or network traffic timed out + + + The last active simulator shut down + + + + Holds a simulator reference and a decoded packet, these structs are put in + the packet inbox for event handling + + + + Reference to the simulator that this packet came from + + + Packet that needs to be processed + + + + Holds a simulator reference and a serialized packet, these structs are put in + the packet outbox for sending + + + + Reference to the simulator this packet is destined for + + + Packet that needs to be sent + + + Sequence number of the wrapped packet + + + Number of times this packet has been resent + + + Environment.TickCount when this packet was last sent over the wire + + + Type of the packet + + + + A Name Value pair with additional settings, used in the protocol + primarily to transmit avatar names and active group in object packets + + + + + + + + + + + + + + + + + + + + Constructor that takes all the fields as parameters + + + + + + + + + + Constructor that takes a single line from a NameValue field + + + + + Type of the value + + + Unknown + + + String value + + + + + + + + + + + + + + + Deprecated + + + String value, but designated as an asset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No report + + + Unknown report type + + + Bug report + + + Complaint report + + + Customer service report + + + + Bitflag field for ObjectUpdateCompressed data blocks, describing + which options are present for each object + + + + Unknown + + + Whether the object has a TreeSpecies + + + Whether the object has floating text ala llSetText + + + Whether the object has an active particle system + + + Whether the object has sound attached to it + + + Whether the object is attached to a root object or not + + + Whether the object has texture animation settings + + + Whether the object has an angular velocity + + + Whether the object has a name value pairs string + + + Whether the object has a Media URL set + + + + Specific Flags for MultipleObjectUpdate requests + + + + None + + + Change position of prims + + + Change rotation of prims + + + Change size of prims + + + Perform operation on link set + + + Scale prims uniformly, same as selecing ctrl+shift in the + viewer. Used in conjunction with Scale + + + + Special values in PayPriceReply. If the price is not one of these + literal value of the price should be use + + + + + Indicates that this pay option should be hidden + + + + + Indicates that this pay option should have the default value + + + + + Contains the variables sent in an object update packet for objects. + Used to track position and movement of prims and avatars + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Handles all network traffic related to prims and avatar positions and + movement. + + + + The event subscribers, null of no subscribers + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ObjectProperties Event + A ObjectPropertiesEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ObjectPropertiesUpdated Event + A ObjectPropertiesUpdatedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ObjectPropertiesFamily Event + A ObjectPropertiesFamilyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarUpdate Event + A AvatarUpdateEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the ObjectDataBlockUpdate Event + A ObjectDataBlockUpdateEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the KillObject Event + A KillObjectEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the KillObjects Event + A KillObjectsEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AvatarSitChanged Event + A AvatarSitChangedEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the PayPriceReply Event + A PayPriceReplyEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the PhysicsProperties Event + A PhysicsPropertiesEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + Reference to the GridClient object + + + Does periodic dead reckoning calculation to convert + velocity and acceleration to new positions for objects + + + + Construct a new instance of the ObjectManager class + + A reference to the instance + + + + Request information for a single object from a + you are currently connected to + + The the object is located + The Local ID of the object + + + + Request information for multiple objects contained in + the same simulator + + The the objects are located + An array containing the Local IDs of the objects + + + + Attempt to purchase an original object, a copy, or the contents of + an object + + The the object is located + The Local ID of the object + Whether the original, a copy, or the object + contents are on sale. This is used for verification, if the this + sale type is not valid for the object the purchase will fail + Price of the object. This is used for + verification, if it does not match the actual price the purchase + will fail + Group ID that will be associated with the new + purchase + Inventory folder UUID where the object or objects + purchased should be placed + + + BuyObject(Client.Network.CurrentSim, 500, SaleType.Copy, + 100, UUID.Zero, Client.Self.InventoryRootFolderUUID); + + + + + + Request prices that should be displayed in pay dialog. This will triggger the simulator + to send us back a PayPriceReply which can be handled by OnPayPriceReply event + + The the object is located + The ID of the object + The result is raised in the event + + + + Select a single object. This will cause the to send us + an which will raise the event + + The the object is located + The Local ID of the object + + + + + Select a single object. This will cause the to send us + an which will raise the event + + The the object is located + The Local ID of the object + if true, a call to is + made immediately following the request + + + + + Select multiple objects. This will cause the to send us + an which will raise the event + + The the objects are located + An array containing the Local IDs of the objects + Should objects be deselected immediately after selection + + + + + Select multiple objects. This will cause the to send us + an which will raise the event + + The the objects are located + An array containing the Local IDs of the objects + + + + + Update the properties of an object + + The the object is located + The Local ID of the object + true to turn the objects physical property on + true to turn the objects temporary property on + true to turn the objects phantom property on + true to turn the objects cast shadows property on + + + + Update the properties of an object + + The the object is located + The Local ID of the object + true to turn the objects physical property on + true to turn the objects temporary property on + true to turn the objects phantom property on + true to turn the objects cast shadows property on + Type of the represetnation prim will have in the physics engine + Density - normal value 1000 + Friction - normal value 0.6 + Restitution - standard value 0.5 + Gravity multiplier - standar value 1.0 + + + + Sets the sale properties of a single object + + The the object is located + The Local ID of the object + One of the options from the enum + The price of the object + + + + Sets the sale properties of multiple objects + + The the objects are located + An array containing the Local IDs of the objects + One of the options from the enum + The price of the object + + + + Deselect a single object + + The the object is located + The Local ID of the object + + + + Deselect multiple objects. + + The the objects are located + An array containing the Local IDs of the objects + + + + Perform a click action on an object + + The the object is located + The Local ID of the object + + + + Perform a click action (Grab) on a single object + + The the object is located + The Local ID of the object + The texture coordinates to touch + The surface coordinates to touch + The face of the position to touch + The region coordinates of the position to touch + The surface normal of the position to touch (A normal is a vector perpindicular to the surface) + The surface binormal of the position to touch (A binormal is a vector tangen to the surface + pointing along the U direction of the tangent space + + + + Create (rez) a new prim object in a simulator + + A reference to the object to place the object in + Data describing the prim object to rez + Group ID that this prim will be set to, or UUID.Zero if you + do not want the object to be associated with a specific group + An approximation of the position at which to rez the prim + Scale vector to size this prim + Rotation quaternion to rotate this prim + Due to the way client prim rezzing is done on the server, + the requested position for an object is only close to where the prim + actually ends up. If you desire exact placement you'll need to + follow up by moving the object after it has been created. This + function will not set textures, light and flexible data, or other + extended primitive properties + + + + Create (rez) a new prim object in a simulator + + A reference to the object to place the object in + Data describing the prim object to rez + Group ID that this prim will be set to, or UUID.Zero if you + do not want the object to be associated with a specific group + An approximation of the position at which to rez the prim + Scale vector to size this prim + Rotation quaternion to rotate this prim + Specify the + Due to the way client prim rezzing is done on the server, + the requested position for an object is only close to where the prim + actually ends up. If you desire exact placement you'll need to + follow up by moving the object after it has been created. This + function will not set textures, light and flexible data, or other + extended primitive properties + + + + Rez a Linden tree + + A reference to the object where the object resides + The size of the tree + The rotation of the tree + The position of the tree + The Type of tree + The of the group to set the tree to, + or UUID.Zero if no group is to be set + true to use the "new" Linden trees, false to use the old + + + + Rez grass and ground cover + + A reference to the object where the object resides + The size of the grass + The rotation of the grass + The position of the grass + The type of grass from the enum + The of the group to set the tree to, + or UUID.Zero if no group is to be set + + + + Set the textures to apply to the faces of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The texture data to apply + + + + Set the textures to apply to the faces of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The texture data to apply + A media URL (not used) + + + + Set the Light data on an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + A object containing the data to set + + + + Set the flexible data on an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + A object containing the data to set + + + + Set the sculptie texture and data on an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + A object containing the data to set + + + + Unset additional primitive parameters on an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The extra parameters to set + + + + Link multiple prims into a linkset + + A reference to the object where the objects reside + An array which contains the IDs of the objects to link + The last object in the array will be the root object of the linkset TODO: Is this true? + + + + Delink/Unlink multiple prims from a linkset + + A reference to the object where the objects reside + An array which contains the IDs of the objects to delink + + + + Change the rotation of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new rotation of the object + + + + Set the name of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + A string containing the new name of the object + + + + Set the name of multiple objects + + A reference to the object where the objects reside + An array which contains the IDs of the objects to change the name of + An array which contains the new names of the objects + + + + Set the description of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + A string containing the new description of the object + + + + Set the descriptions of multiple objects + + A reference to the object where the objects reside + An array which contains the IDs of the objects to change the description of + An array which contains the new descriptions of the objects + + + + Attach an object to this avatar + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The point on the avatar the object will be attached + The rotation of the attached object + + + + Drop an attached object from this avatar + + A reference to the + object where the objects reside. This will always be the simulator the avatar is currently in + + The object's ID which is local to the simulator the object is in + + + + Detach an object from yourself + + A reference to the + object where the objects reside + + This will always be the simulator the avatar is currently in + + An array which contains the IDs of the objects to detach + + + + Change the position of an object, Will change position of entire linkset + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new position of the object + + + + Change the position of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new position of the object + if true, will change position of (this) child prim only, not entire linkset + + + + Change the Scale (size) of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new scale of the object + If true, will change scale of this prim only, not entire linkset + True to resize prims uniformly + + + + Change the Rotation of an object that is either a child or a whole linkset + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new scale of the object + If true, will change rotation of this prim only, not entire linkset + + + + Send a Multiple Object Update packet to change the size, scale or rotation of a primitive + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new rotation, size, or position of the target object + The flags from the Enum + + + + Deed an object (prim) to a group, Object must be shared with group which + can be accomplished with SetPermissions() + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The of the group to deed the object to + + + + Deed multiple objects (prims) to a group, Objects must be shared with group which + can be accomplished with SetPermissions() + + A reference to the object where the object resides + An array which contains the IDs of the objects to deed + The of the group to deed the object to + + + + Set the permissions on multiple objects + + A reference to the object where the objects reside + An array which contains the IDs of the objects to set the permissions on + The new Who mask to set + Which permission to modify + The new state of permission + + + + Request additional properties for an object + + A reference to the object where the object resides + + + + + Request additional properties for an object + + A reference to the object where the object resides + Absolute UUID of the object + Whether to require server acknowledgement of this request + + + + Set the ownership of a list of objects to the specified group + + A reference to the object where the objects reside + An array which contains the IDs of the objects to set the group id on + The Groups ID + + + + Update current URL of the previously set prim media + + UUID of the prim + Set current URL to this + Prim face number + Simulator in which prim is located + + + + Set object media + + UUID of the prim + Array the length of prims number of faces. Null on face indexes where there is + no media, on faces which contain the media + Simulatior in which prim is located + + + + Retrieve information about object media + + UUID of the primitive + Simulator where prim is located + Call this callback when done + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + A terse object update, used when a transformation matrix or + velocity/acceleration for an object changes but nothing else + (scale/position/rotation/acceleration/velocity) + + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + + + + + + + + + + Setup construction data for a basic primitive shape + + Primitive shape to construct + Construction data that can be plugged into a + + + + + + + + + + + + + + + + + + + + Set the Shape data of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + Data describing the prim shape + + + + Set the Material data of an object + + A reference to the object where the object resides + The objects ID which is local to the simulator the object is in + The new material of the object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Raised when the simulator sends us data containing + A , Foliage or Attachment + + + + + Raised when the simulator sends us data containing + additional information + + + + + Raised when the simulator sends us data containing + Primitive.ObjectProperties for an object we are currently tracking + + + Raised when the simulator sends us data containing + additional and details + + + + Raised when the simulator sends us data containing + updated information for an + + + Raised when the simulator sends us data containing + and movement changes + + + Raised when the simulator sends us data containing + updates to an Objects DataBlock + + + Raised when the simulator informs us an + or is no longer within view + + + Raised when the simulator informs us when a group of + or is no longer within view + + + Raised when the simulator sends us data containing + updated sit information for our + + + Raised when the simulator sends us data containing + purchase price information for a + + + Raised when the simulator sends us data containing + additional information + + + + + + Callback for getting object media data via CAP + + Indicates if the operation was succesfull + Object media version string + Array indexed on prim face of media entry data + + + Provides data for the event + The event occurs when the simulator sends + an containing a Primitive, Foliage or Attachment data + Note 1: The event will not be raised when the object is an Avatar + Note 2: It is possible for the to be + raised twice for the same object if for example the primitive moved to a new simulator, then returned to the current simulator or + if an Avatar crosses the border into a new simulator and returns to the current simulator + + + The following code example uses the , , and + properties to display new Primitives and Attachments on the window. + + // Subscribe to the event that gives us prim and foliage information + Client.Objects.ObjectUpdate += Objects_ObjectUpdate; + + + private void Objects_ObjectUpdate(object sender, PrimEventArgs e) + { + Console.WriteLine("Primitive {0} {1} in {2} is an attachment {3}", e.Prim.ID, e.Prim.LocalID, e.Simulator.Name, e.IsAttachment); + } + + + + + + + + + Construct a new instance of the PrimEventArgs class + + The simulator the object originated from + The Primitive + The simulator time dilation + The prim was not in the dictionary before this update + true if the primitive represents an attachment to an agent + + + Get the simulator the originated from + + + Get the details + + + true if the did not exist in the dictionary before this update (always true if object tracking has been disabled) + + + true if the is attached to an + + + Get the simulator Time Dilation + + + Provides data for the event + The event occurs when the simulator sends + an containing Avatar data + Note 1: The event will not be raised when the object is an Avatar + Note 2: It is possible for the to be + raised twice for the same avatar if for example the avatar moved to a new simulator, then returned to the current simulator + + + The following code example uses the property to make a request for the top picks + using the method in the class to display the names + of our own agents picks listings on the window. + + // subscribe to the AvatarUpdate event to get our information + Client.Objects.AvatarUpdate += Objects_AvatarUpdate; + Client.Avatars.AvatarPicksReply += Avatars_AvatarPicksReply; + + private void Objects_AvatarUpdate(object sender, AvatarUpdateEventArgs e) + { + // we only want our own data + if (e.Avatar.LocalID == Client.Self.LocalID) + { + // Unsubscribe from the avatar update event to prevent a loop + // where we continually request the picks every time we get an update for ourselves + Client.Objects.AvatarUpdate -= Objects_AvatarUpdate; + // make the top picks request through AvatarManager + Client.Avatars.RequestAvatarPicks(e.Avatar.ID); + } + } + + private void Avatars_AvatarPicksReply(object sender, AvatarPicksReplyEventArgs e) + { + // we'll unsubscribe from the AvatarPicksReply event since we now have the data + // we were looking for + Client.Avatars.AvatarPicksReply -= Avatars_AvatarPicksReply; + // loop through the dictionary and extract the names of the top picks from our profile + foreach (var pickName in e.Picks.Values) + { + Console.WriteLine(pickName); + } + } + + + + + + + + Construct a new instance of the AvatarUpdateEventArgs class + + The simulator the packet originated from + The data + The simulator time dilation + The avatar was not in the dictionary before this update + + + Get the simulator the object originated from + + + Get the data + + + Get the simulator time dilation + + + true if the did not exist in the dictionary before this update (always true if avatar tracking has been disabled) + + + Provides additional primitive data for the event + The event occurs when the simulator sends + an containing additional details for a Primitive, Foliage data or Attachment data + The event is also raised when a request is + made. + + + The following code example uses the , and + + properties to display new attachments and send a request for additional properties containing the name of the + attachment then display it on the window. + + // Subscribe to the event that provides additional primitive details + Client.Objects.ObjectProperties += Objects_ObjectProperties; + + // handle the properties data that arrives + private void Objects_ObjectProperties(object sender, ObjectPropertiesEventArgs e) + { + Console.WriteLine("Primitive Properties: {0} Name is {1}", e.Properties.ObjectID, e.Properties.Name); + } + + + + + + Construct a new instance of the ObjectPropertiesEventArgs class + + The simulator the object is located + The primitive Properties + + + Get the simulator the object is located + + + Get the primitive properties + + + Provides additional primitive data for the event + The event occurs when the simulator sends + an containing additional details for a Primitive or Foliage data that is currently + being tracked in the dictionary + The event is also raised when a request is + made and is enabled + + + + + Construct a new instance of the ObjectPropertiesUpdatedEvenrArgs class + + The simulator the object is located + The Primitive + The primitive Properties + + + Get the primitive details + + + Provides additional primitive data, permissions and sale info for the event + The event occurs when the simulator sends + an containing additional details for a Primitive, Foliage data or Attachment. This includes + Permissions, Sale info, and other basic details on an object + The event is also raised when a request is + made, the viewer equivalent is hovering the mouse cursor over an object + + + + Get the simulator the object is located + + + + + + + + + Provides primitive data containing updated location, velocity, rotation, textures for the event + The event occurs when the simulator sends updated location, velocity, rotation, etc + + + + Get the simulator the object is located + + + Get the primitive details + + + + + + + + + + + + + + Get the simulator the object is located + + + Get the primitive details + + + + + + + + + + + + + + + Provides notification when an Avatar, Object or Attachment is DeRezzed or moves out of the avatars view for the + event + + + Get the simulator the object is located + + + The LocalID of the object + + + Provides notification when an Avatar, Object or Attachment is DeRezzed or moves out of the avatars view for the + event + + + Get the simulator the object is located + + + The LocalID of the object + + + + Provides updates sit position data + + + + Get the simulator the object is located + + + + + + + + + + + + + + + + + Get the simulator the object is located + + + + + + + + + + + + + Indicates if the operation was successful + + + + + Media version string + + + + + Array of media entries indexed by face number + + + + + Set when simulator sends us infomation on primitive's physical properties + + + + Simulator where the message originated + + + Updated physical properties + + + + Constructor + + Simulator where the message originated + Updated physical properties + + + Size of the byte array used to store raw packet data + + + Raw packet data buffer + + + Length of the data to transmit + + + EndPoint of the remote host + + + + Create an allocated UDP packet buffer for receiving a packet + + + + + Create an allocated UDP packet buffer for sending a packet + + EndPoint of the remote host + + + + Create an allocated UDP packet buffer for sending a packet + + EndPoint of the remote host + Size of the buffer to allocate for packet data + + + + Object pool for packet buffers. This is used to allocate memory for all + incoming and outgoing packets, and zerocoding buffers for those packets + + + + + Creates a new instance of the ObjectPoolBase class. Initialize MUST be called + after using this constructor. + + + + + Creates a new instance of the ObjectPool Base class. + + The object pool is composed of segments, which + are allocated whenever the size of the pool is exceeded. The number of items + in a segment should be large enough that allocating a new segmeng is a rare + thing. For example, on a server that will have 10k people logged in at once, + the receive buffer object pool should have segment sizes of at least 1000 + byte arrays per segment. + + The minimun number of segments that may exist. + Perform a full GC.Collect whenever a segment is allocated, and then again after allocation to compact the heap. + The frequency which segments are checked to see if they're eligible for cleanup. + + + + Forces the segment cleanup algorithm to be run. This method is intended + primarly for use from the Unit Test libraries. + + + + + Responsible for allocate 1 instance of an object that will be stored in a segment. + + An instance of whatever objec the pool is pooling. + + + + Checks in an instance of T owned by the object pool. This method is only intended to be called + by the WrappedObject class. + + The segment from which the instance is checked out. + The instance of T to check back into the segment. + + + + Checks an instance of T from the pool. If the pool is not sufficient to + allow the checkout, a new segment is created. + + A WrappedObject around the instance of T. To check + the instance back into the segment, be sureto dispose the WrappedObject + when finished. + + + + The total number of segments created. Intended to be used by the Unit Tests. + + + + + The number of items that are in a segment. Items in a segment + are all allocated at the same time, and are hopefully close to + each other in the managed heap. + + + + + The minimum number of segments. When segments are reclaimed, + this number of segments will always be left alone. These + segments are allocated at startup. + + + + + The age a segment must be before it's eligible for cleanup. + This is used to prevent thrash, and typical values are in + the 5 minute range. + + + + + The frequence which the cleanup thread runs. This is typically + expected to be in the 5 minute range. + + + + + Initialize the object pool in client mode + + Server to connect to + + + + + + Initialize the object pool in server mode + + + + + + + Returns a packet buffer with EndPoint set if the buffer is in + client mode, or with EndPoint set to null in server mode + + Initialized UDPPacketBuffer object + + + + Default constructor + + + + + Check a packet buffer out of the pool + + A packet buffer object + + + + Checks the instance back into the object pool + + + + + Returns an instance of the class that has been checked out of the Object Pool. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The ObservableDictionary class is used for storing key/value pairs. It has methods for firing + events to subscribers when items are added, removed, or changed. + + Key + Value + + + + A dictionary of callbacks to fire when specified action occurs + + + + + Register a callback to be fired when an action occurs + + The action + The callback to fire + + + + Unregister a callback + + The action + The callback to fire + + + + + + + + + + Internal dictionary that this class wraps around. Do not + modify or enumerate the contents of this dictionary without locking + + + + Initializes a new instance of the Class + with the specified key/value, has the default initial capacity. + + + + // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value. + public ObservableDictionary<string, int> testDict = new ObservableDictionary<string, int>(); + + + + + + Initializes a new instance of the Class + with the specified key/value, With its initial capacity specified. + + Initial size of dictionary + + + // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value, + // initially allocated room for 10 entries. + public ObservableDictionary<string, int> testDict = new ObservableDictionary<string, int>(10); + + + + + + Try to get entry from the with specified key + + Key to use for lookup + Value returned + if specified key exists, if not found + + + // find your avatar using the Simulator.ObjectsAvatars ObservableDictionary: + Avatar av; + if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(Client.Self.AgentID, out av)) + Console.WriteLine("Found Avatar {0}", av.Name); + + + + + + + Finds the specified match. + + The match. + Matched value + + + // use a delegate to find a prim in the ObjectsPrimitives ObservableDictionary + // with the ID 95683496 + uint findID = 95683496; + Primitive findPrim = sim.ObjectsPrimitives.Find( + delegate(Primitive prim) { return prim.ID == findID; }); + + + + + Find All items in an + return matching items. + a containing found items. + + Find All prims within 20 meters and store them in a List + + int radius = 20; + List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( + delegate(Primitive prim) { + Vector3 pos = prim.Position; + return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); + } + ); + + + + + Find All items in an + return matching keys. + a containing found keys. + + Find All keys which also exist in another dictionary + + List<UUID> matches = myDict.FindAll( + delegate(UUID id) { + return myOtherDict.ContainsKey(id); + } + ); + + + + + Check if Key exists in Dictionary + Key to check for + if found, otherwise + + + Check if Value exists in Dictionary + Value to check for + if found, otherwise + + + + Adds the specified key to the dictionary, dictionary locking is not performed, + + + The key + The value + + + + Removes the specified key, dictionary locking is not performed + + The key. + if successful, otherwise + + + + Clear the contents of the dictionary + + + + + Enumerator for iterating dictionary entries + + + + + + Gets the number of Key/Value pairs contained in the + + + + + Indexer for the dictionary + + The key + The value + + + + Add a custom decoder callback + + The key of the field to decode + The custom decode handler + + + + Remove a custom decoder callback + + The key of the field to decode + The custom decode handler + + + + Creates a formatted string containing the values of a Packet + + The Packet + A formatted string of values of the nested items in the Packet object + + + + Decode an IMessage object into a beautifully formatted string + + The IMessage object + Recursion level (used for indenting) + A formatted string containing the names and values of the source object + + + + A custom decoder callback + + The key of the object + the data to decode + A string represending the fieldData + + + + Provides helper methods for parallelizing loops + + + + + Executes a for loop in which iterations may run in parallel + + The loop will be started at this index + The loop will be terminated before this index is reached + Method body to run for each iteration of the loop + + + + Executes a for loop in which iterations may run in parallel + + The number of concurrent execution threads to run + The loop will be started at this index + The loop will be terminated before this index is reached + Method body to run for each iteration of the loop + + + + Executes a foreach loop in which iterations may run in parallel + + Object type that the collection wraps + An enumerable collection to iterate over + Method body to run for each object in the collection + + + + Executes a foreach loop in which iterations may run in parallel + + Object type that the collection wraps + The number of concurrent execution threads to run + An enumerable collection to iterate over + Method body to run for each object in the collection + + + + Executes a series of tasks in parallel + + A series of method bodies to execute + + + + Executes a series of tasks in parallel + + The number of concurrent execution threads to run + A series of method bodies to execute + + + + Type of return to use when returning objects from a parcel + + + + + + + Return objects owned by parcel owner + + + Return objects set to group + + + Return objects not owned by parcel owner or set to group + + + Return a specific list of objects on parcel + + + Return objects that are marked for-sale + + + + Blacklist/Whitelist flags used in parcels Access List + + + + Agent is denied access + + + Agent is granted access + + + + The result of a request for parcel properties + + + + No matches were found for the request + + + Request matched a single parcel + + + Request matched multiple parcels + + + + Flags used in the ParcelAccessListRequest packet to specify whether + we want the access list (whitelist), ban list (blacklist), or both + + + + Request the access list + + + Request the ban list + + + Request both White and Black lists + + + + Sequence ID in ParcelPropertiesReply packets (sent when avatar + tries to cross a parcel border) + + + + Parcel is currently selected + + + Parcel restricted to a group the avatar is not a + member of + + + Avatar is banned from the parcel + + + Parcel is restricted to an access list that the + avatar is not on + + + Response to hovering over a parcel + + + + The tool to use when modifying terrain levels + + + + Level the terrain + + + Raise the terrain + + + Lower the terrain + + + Smooth the terrain + + + Add random noise to the terrain + + + Revert terrain to simulator default + + + + The tool size to use when changing terrain levels + + + + Small + + + Medium + + + Large + + + + Reasons agent is denied access to a parcel on the simulator + + + + Agent is not denied, access is granted + + + Agent is not a member of the group set for the parcel, or which owns the parcel + + + Agent is not on the parcels specific allow list + + + Agent is on the parcels ban list + + + Unknown + + + Agent is not age verified and parcel settings deny access to non age verified avatars + + + + Parcel overlay type. This is used primarily for highlighting and + coloring which is why it is a single integer instead of a set of + flags + + These values seem to be poorly thought out. The first three + bits represent a single value, not flags. For example Auction (0x05) is + not a combination of OwnedByOther (0x01) and ForSale(0x04). However, + the BorderWest and BorderSouth values are bit flags that get attached + to the value stored in the first three bits. Bits four, five, and six + are unused + + + Public land + + + Land is owned by another avatar + + + Land is owned by a group + + + Land is owned by the current avatar + + + Land is for sale + + + Land is being auctioned + + + Land is private + + + To the west of this area is a parcel border + + + To the south of this area is a parcel border + + + + Various parcel properties + + + + No flags set + + + Allow avatars to fly (a client-side only restriction) + + + Allow foreign scripts to run + + + This parcel is for sale + + + Allow avatars to create a landmark on this parcel + + + Allows all avatars to edit the terrain on this parcel + + + Avatars have health and can take damage on this parcel. + If set, avatars can be killed and sent home here + + + Foreign avatars can create objects here + + + All objects on this parcel can be purchased + + + Access is restricted to a group + + + Access is restricted to a whitelist + + + Ban blacklist is enabled + + + Unknown + + + List this parcel in the search directory + + + Allow personally owned parcels to be deeded to group + + + If Deeded, owner contributes required tier to group parcel is deeded to + + + Restrict sounds originating on this parcel to the + parcel boundaries + + + Objects on this parcel are sold when the land is + purchsaed + + + Allow this parcel to be published on the web + + + The information for this parcel is mature content + + + The media URL is an HTML page + + + The media URL is a raw HTML string + + + Restrict foreign object pushes + + + Ban all non identified/transacted avatars + + + Allow group-owned scripts to run + + + Allow object creation by group members or group + objects + + + Allow all objects to enter this parcel + + + Only allow group and owner objects to enter this parcel + + + Voice Enabled on this parcel + + + Use Estate Voice channel for Voice on this parcel + + + Deny Age Unverified Users + + + + Parcel ownership status + + + + Placeholder + + + Parcel is leased (owned) by an avatar or group + + + Parcel is in process of being leased (purchased) by an avatar or group + + + Parcel has been abandoned back to Governor Linden + + + + Category parcel is listed in under search + + + + No assigned category + + + Linden Infohub or public area + + + Adult themed area + + + Arts and Culture + + + Business + + + Educational + + + Gaming + + + Hangout or Club + + + Newcomer friendly + + + Parks and Nature + + + Residential + + + Shopping + + + Not Used? + + + Other + + + Not an actual category, only used for queries + + + + Type of teleport landing for a parcel + + + + Unset, simulator default + + + Specific landing point set for this parcel + + + No landing point set, direct teleports enabled for + this parcel + + + + Parcel Media Command used in ParcelMediaCommandMessage + + + + Stop the media stream and go back to the first frame + + + Pause the media stream (stop playing but stay on current frame) + + + Start the current media stream playing and stop when the end is reached + + + Start the current media stream playing, + loop to the beginning when the end is reached and continue to play + + + Specifies the texture to replace with video + If passing the key of a texture, it must be explicitly typecast as a key, + not just passed within double quotes. + + + Specifies the movie URL (254 characters max) + + + Specifies the time index at which to begin playing + + + Specifies a single agent to apply the media command to + + + Unloads the stream. While the stop command sets the texture to the first frame of the movie, + unload resets it to the real texture that the movie was replacing. + + + Turn on/off the auto align feature, similar to the auto align checkbox in the parcel media properties + (NOT to be confused with the "align" function in the textures view of the editor!) Takes TRUE or FALSE as parameter. + + + Allows a Web page or image to be placed on a prim (1.19.1 RC0 and later only). + Use "text/html" for HTML. + + + Resizes a Web page to fit on x, y pixels (1.19.1 RC0 and later only). + This might still not be working + + + Sets a description for the media being displayed (1.19.1 RC0 and later only). + + + + Some information about a parcel of land returned from a DirectoryManager search + + + + Global Key of record + + + Parcel Owners + + + Name field of parcel, limited to 128 characters + + + Description field of parcel, limited to 256 characters + + + Total Square meters of parcel + + + Total area billable as Tier, for group owned land this will be 10% less than ActualArea + + + True of parcel is in Mature simulator + + + Grid global X position of parcel + + + Grid global Y position of parcel + + + Grid global Z position of parcel (not used) + + + Name of simulator parcel is located in + + + Texture of parcels display picture + + + Float representing calculated traffic based on time spent on parcel by avatars + + + Sale price of parcel (not used) + + + Auction ID of parcel + + + + Parcel Media Information + + + + A byte, if 0x1 viewer should auto scale media to fit object + + + A boolean, if true the viewer should loop the media + + + The Asset UUID of the Texture which when applied to a + primitive will display the media + + + A URL which points to any Quicktime supported media type + + + A description of the media + + + An Integer which represents the height of the media + + + An integer which represents the width of the media + + + A string which contains the mime type of the media + + + + Parcel of land, a portion of virtual real estate in a simulator + + + + The total number of contiguous 4x4 meter blocks your agent owns within this parcel + + + The total number of contiguous 4x4 meter blocks contained in this parcel owned by a group or agent other than your own + + + Deprecated, Value appears to always be 0 + + + Simulator-local ID of this parcel + + + UUID of the owner of this parcel + + + Whether the land is deeded to a group or not + + + + + + Date land was claimed + + + Appears to always be zero + + + This field is no longer used + + + Minimum corner of the axis-aligned bounding box for this + parcel + + + Maximum corner of the axis-aligned bounding box for this + parcel + + + Bitmap describing land layout in 4x4m squares across the + entire region + + + Total parcel land area + + + + + + Maximum primitives across the entire simulator owned by the same agent or group that owns this parcel that can be used + + + Total primitives across the entire simulator calculated by combining the allowed prim counts for each parcel + owned by the agent or group that owns this parcel + + + Maximum number of primitives this parcel supports + + + Total number of primitives on this parcel + + + For group-owned parcels this indicates the total number of prims deeded to the group, + for parcels owned by an individual this inicates the number of prims owned by the individual + + + Total number of primitives owned by the parcel group on + this parcel, or for parcels owned by an individual with a group set the + total number of prims set to that group. + + + Total number of prims owned by other avatars that are not set to group, or not the parcel owner + + + A bonus multiplier which allows parcel prim counts to go over times this amount, this does not affect + the max prims per simulator. e.g: 117 prim parcel limit x 1.5 bonus = 175 allowed + + + Autoreturn value in minutes for others' objects + + + + + + Sale price of the parcel, only useful if ForSale is set + The SalePrice will remain the same after an ownership + transfer (sale), so it can be used to see the purchase price after + a sale if the new owner has not changed it + + + Parcel Name + + + Parcel Description + + + URL For Music Stream + + + + + + Price for a temporary pass + + + How long is pass valid for + + + + + + Key of authorized buyer + + + Key of parcel snapshot + + + The landing point location + + + The landing point LookAt + + + The type of landing enforced from the enum + + + + + + + + + + + + Access list of who is whitelisted on this + parcel + + + Access list of who is blacklisted on this + parcel + + + TRUE of region denies access to age unverified users + + + true to obscure (hide) media url + + + true to obscure (hide) music url + + + A struct containing media details + + + + Displays a parcel object in string format + + string containing key=value pairs of a parcel object + + + + Defalt constructor + + Local ID of this parcel + + + + Update the simulator with any local changes to this Parcel object + + Simulator to send updates to + Whether we want the simulator to confirm + the update with a reply packet or not + + + + Set Autoreturn time + + Simulator to send the update to + + + + Parcel (subdivided simulator lots) subsystem + + + + The event subscribers. null if no subcribers + + + Raises the ParcelDwellReply event + A ParcelDwellReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelInfoReply event + A ParcelInfoReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelProperties event + A ParcelPropertiesEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelAccessListReply event + A ParcelAccessListReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelObjectOwnersReply event + A ParcelObjectOwnersReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the SimParcelsDownloaded event + A SimParcelsDownloadedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ForceSelectObjectsReply event + A ForceSelectObjectsReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelMediaUpdateReply event + A ParcelMediaUpdateReplyEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + The event subscribers. null if no subcribers + + + Raises the ParcelMediaCommand event + A ParcelMediaCommandEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + + Default constructor + + A reference to the GridClient object + + + + Request basic information for a single parcel + + Simulator-local ID of the parcel + + + + Request properties of a single parcel + + Simulator containing the parcel + Simulator-local ID of the parcel + An arbitrary integer that will be returned + with the ParcelProperties reply, useful for distinguishing between + multiple simultaneous requests + + + + Request the access list for a single parcel + + Simulator containing the parcel + Simulator-local ID of the parcel + An arbitrary integer that will be returned + with the ParcelAccessList reply, useful for distinguishing between + multiple simultaneous requests + + + + + Request properties of parcels using a bounding box selection + + Simulator containing the parcel + Northern boundary of the parcel selection + Eastern boundary of the parcel selection + Southern boundary of the parcel selection + Western boundary of the parcel selection + An arbitrary integer that will be returned + with the ParcelProperties reply, useful for distinguishing between + different types of parcel property requests + A boolean that is returned with the + ParcelProperties reply, useful for snapping focus to a single + parcel + + + + Request all simulator parcel properties (used for populating the Simulator.Parcels + dictionary) + + Simulator to request parcels from (must be connected) + + + + Request all simulator parcel properties (used for populating the Simulator.Parcels + dictionary) + + Simulator to request parcels from (must be connected) + If TRUE, will force a full refresh + Number of milliseconds to pause in between each request + + + + Request the dwell value for a parcel + + Simulator containing the parcel + Simulator-local ID of the parcel + + + + Send a request to Purchase a parcel of land + + The Simulator the parcel is located in + The parcels region specific local ID + true if this parcel is being purchased by a group + The groups + true to remove tier contribution if purchase is successful + The parcels size + The purchase price of the parcel + + + + + Reclaim a parcel of land + + The simulator the parcel is in + The parcels region specific local ID + + + + Deed a parcel to a group + + The simulator the parcel is in + The parcels region specific local ID + The groups + + + + Request prim owners of a parcel of land. + + Simulator parcel is in + The parcels region specific local ID + + + + Return objects from a parcel + + Simulator parcel is in + The parcels region specific local ID + the type of objects to return, + A list containing object owners s to return + + + + Subdivide (split) a parcel + + + + + + + + + + Join two parcels of land creating a single parcel + + + + + + + + + + Get a parcels LocalID + + Simulator parcel is in + Vector3 position in simulator (Z not used) + 0 on failure, or parcel LocalID on success. + A call to Parcels.RequestAllSimParcels is required to populate map and + dictionary. + + + + Terraform (raise, lower, etc) an area or whole parcel of land + + Simulator land area is in. + LocalID of parcel, or -1 if using bounding box + From Enum, Raise, Lower, Level, Smooth, Etc. + Size of area to modify + true on successful request sent. + Settings.STORE_LAND_PATCHES must be true, + Parcel information must be downloaded using RequestAllSimParcels() + + + + Terraform (raise, lower, etc) an area or whole parcel of land + + Simulator land area is in. + west border of area to modify + south border of area to modify + east border of area to modify + north border of area to modify + From Enum, Raise, Lower, Level, Smooth, Etc. + Size of area to modify + true on successful request sent. + Settings.STORE_LAND_PATCHES must be true, + Parcel information must be downloaded using RequestAllSimParcels() + + + + Terraform (raise, lower, etc) an area or whole parcel of land + + Simulator land area is in. + LocalID of parcel, or -1 if using bounding box + west border of area to modify + south border of area to modify + east border of area to modify + north border of area to modify + From Enum, Raise, Lower, Level, Smooth, Etc. + Size of area to modify + How many meters + or - to lower, 1 = 1 meter + true on successful request sent. + Settings.STORE_LAND_PATCHES must be true, + Parcel information must be downloaded using RequestAllSimParcels() + + + + Terraform (raise, lower, etc) an area or whole parcel of land + + Simulator land area is in. + LocalID of parcel, or -1 if using bounding box + west border of area to modify + south border of area to modify + east border of area to modify + north border of area to modify + From Enum, Raise, Lower, Level, Smooth, Etc. + Size of area to modify + How many meters + or - to lower, 1 = 1 meter + Height at which the terraform operation is acting at + + + + Sends a request to the simulator to return a list of objects owned by specific owners + + Simulator local ID of parcel + Owners, Others, Etc + List containing keys of avatars objects to select; + if List is null will return Objects of type selectType + Response data is returned in the event + + + + Eject and optionally ban a user from a parcel + + target key of avatar to eject + true to also ban target + + + + Freeze or unfreeze an avatar over your land + + target key to freeze + true to freeze, false to unfreeze + + + + Abandon a parcel of land + + Simulator parcel is in + Simulator local ID of parcel + + + + Requests the UUID of the parcel in a remote region at a specified location + + Location of the parcel in the remote region + Remote region handle + Remote region UUID + If successful UUID of the remote parcel, UUID.Zero otherwise + + + + Retrieves information on resources used by the parcel + + UUID of the parcel + Should per object resource usage be requested + Callback invoked when the request is complete + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + Raises the event + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a request + + + Raised when the simulator responds to a Parcel Update request + + + Raised when the parcel your agent is located sends a ParcelMediaCommand + + + + Parcel Accesslist + + + + Agents + + + + + + Flags for specific entry in white/black lists + + + + Owners of primitives on parcel + + + + Prim Owners + + + True of owner is group + + + Total count of prims owned by OwnerID + + + true of OwnerID is currently online and is not a group + + + The date of the most recent prim left by OwnerID + + + + Called once parcel resource usage information has been collected + + Indicates if operation was successfull + Parcel resource usage information + + + Contains a parcels dwell data returned from the simulator in response to an + + + + Construct a new instance of the ParcelDwellReplyEventArgs class + + The global ID of the parcel + The simulator specific ID of the parcel + The calculated dwell for the parcel + + + Get the global ID of the parcel + + + Get the simulator specific ID of the parcel + + + Get the calculated dwell + + + Contains basic parcel information data returned from the + simulator in response to an request + + + + Construct a new instance of the ParcelInfoReplyEventArgs class + + The object containing basic parcel info + + + Get the object containing basic parcel info + + + Contains basic parcel information data returned from the simulator in response to an request + + + + Construct a new instance of the ParcelPropertiesEventArgs class + + The object containing the details + The object containing the details + The result of the request + The number of primitieves your agent is + currently selecting and or sitting on in this parcel + The user assigned ID used to correlate a request with + these results + TODO: + + + Get the simulator the parcel is located in + + + Get the object containing the details + If Result is NoData, this object will not contain valid data + + + Get the result of the request + + + Get the number of primitieves your agent is + currently selecting and or sitting on in this parcel + + + Get the user assigned ID used to correlate a request with + these results + + + TODO: + + + Contains blacklist and whitelist data returned from the simulator in response to an request + + + + Construct a new instance of the ParcelAccessListReplyEventArgs class + + The simulator the parcel is located in + The user assigned ID used to correlate a request with + these results + The simulator specific ID of the parcel + TODO: + The list containing the white/blacklisted agents for the parcel + + + Get the simulator the parcel is located in + + + Get the user assigned ID used to correlate a request with + these results + + + Get the simulator specific ID of the parcel + + + TODO: + + + Get the list containing the white/blacklisted agents for the parcel + + + Contains blacklist and whitelist data returned from the + simulator in response to an request + + + + Construct a new instance of the ParcelObjectOwnersReplyEventArgs class + + The simulator the parcel is located in + The list containing prim ownership counts + + + Get the simulator the parcel is located in + + + Get the list containing prim ownership counts + + + Contains the data returned when all parcel data has been retrieved from a simulator + + + + Construct a new instance of the SimParcelsDownloadedEventArgs class + + The simulator the parcel data was retrieved from + The dictionary containing the parcel data + The multidimensional array containing a x,y grid mapped + to each 64x64 parcel's LocalID. + + + Get the simulator the parcel data was retrieved from + + + A dictionary containing the parcel data where the key correlates to the ParcelMap entry + + + Get the multidimensional array containing a x,y grid mapped + to each 64x64 parcel's LocalID. + + + Contains the data returned when a request + + + + Construct a new instance of the ForceSelectObjectsReplyEventArgs class + + The simulator the parcel data was retrieved from + The list of primitive IDs + true if the list is clean and contains the information + only for a given request + + + Get the simulator the parcel data was retrieved from + + + Get the list of primitive IDs + + + true if the list is clean and contains the information + only for a given request + + + Contains data when the media data for a parcel the avatar is on changes + + + + Construct a new instance of the ParcelMediaUpdateReplyEventArgs class + + the simulator the parcel media data was updated in + The updated media information + + + Get the simulator the parcel media data was updated in + + + Get the updated media information + + + Contains the media command for a parcel the agent is currently on + + + + Construct a new instance of the ParcelMediaCommandEventArgs class + + The simulator the parcel media command was issued in + + + The media command that was sent + + + + Get the simulator the parcel media command was issued in + + + + + + + + + Get the media command that was sent + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class for controlling various system settings. + + Some values are readonly because they affect things that + happen when the GridClient object is initialized, so changing them at + runtime won't do any good. Non-readonly values may affect things that + happen at login or dynamically + + + Main grid login server + + + Beta grid login server + + + + InventoryManager requests inventory information on login, + GridClient initializes an Inventory store for main inventory. + + + + + InventoryManager requests library information on login, + GridClient initializes an Inventory store for the library. + + + + Number of milliseconds between sending pings to each sim + + + Number of milliseconds between sending camera updates + + + Number of milliseconds between updating the current + positions of moving, non-accelerating and non-colliding objects + + + Millisecond interval between ticks, where all ACKs are + sent out and the age of unACKed packets is checked + + + The initial size of the packet inbox, where packets are + stored before processing + + + Maximum size of packet that we want to send over the wire + + + The maximum value of a packet sequence number before it + rolls over back to one + + + The relative directory where external resources are kept + + + Login server to connect to + + + IP Address the client will bind to + + + Use XML-RPC Login or LLSD Login, default is XML-RPC Login + + + + Maximum number of HTTP connections to open to a particular endpoint. + + + An endpoint is defined as a commbination of network address and port. This is used for Caps. + This is a static variable which applies to all instances. + + + + + Use Caps for fetching inventory where available + + + + Number of milliseconds before an asset transfer will time + out + + + Number of milliseconds before a teleport attempt will time + out + + + Number of milliseconds before NetworkManager.Logout() will + time out + + + Number of milliseconds before a CAPS call will time out + Setting this too low will cause web requests time out and + possibly retry repeatedly + + + Number of milliseconds for xml-rpc to timeout + + + Milliseconds before a packet is assumed lost and resent + + + Milliseconds without receiving a packet before the + connection to a simulator is assumed lost + + + Milliseconds to wait for a simulator info request through + the grid interface + + + The maximum size of the sequence number archive, used to + check for resent and/or duplicate packets + + + Maximum number of queued ACKs to be sent before SendAcks() + is forced + + + Network stats queue length (seconds) + + + + Primitives will be reused when falling in/out of interest list (and shared between clients) + prims returning to interest list do not need re-requested + Helps also in not re-requesting prim.Properties for code that checks for a Properties == null per client + + + + + Pool parcel data between clients (saves on requesting multiple times when all clients may need it) + + + + + How long to preserve cached data when no client is connected to a simulator + The reason for setting it to something like 2 minutes is in case a client + is running back and forth between region edges or a sim is comming and going + + + + Enable/disable storing terrain heightmaps in the + TerrainManager + + + Enable/disable sending periodic camera updates + + + Enable/disable automatically setting agent appearance at + login and after sim crossing + + + Enable/disable automatically setting the bandwidth throttle + after connecting to each simulator + The default throttle uses the equivalent of the maximum + bandwidth setting in the official client. If you do not set a + throttle your connection will by default be throttled well below + the minimum values and you may experience connection problems + + + Enable/disable the sending of pings to monitor lag and + packet loss + + + Should we connect to multiple sims? This will allow + viewing in to neighboring simulators and sim crossings + (Experimental) + + + If true, all object update packets will be decoded in to + native objects. If false, only updates for our own agent will be + decoded. Registering an event handler will force objects for that + type to always be decoded. If this is disabled the object tracking + will have missing or partial prim and avatar information + + + If true, when a cached object check is received from the + server the full object info will automatically be requested + + + Whether to establish connections to HTTP capabilities + servers for simulators + + + Whether to decode sim stats + + + The capabilities servers are currently designed to + periodically return a 502 error which signals for the client to + re-establish a connection. Set this to true to log those 502 errors + + + If true, any reference received for a folder or item + the library is not aware of will automatically be fetched + + + If true, and SEND_AGENT_UPDATES is true, + AgentUpdate packets will continuously be sent out to give the bot + smoother movement and autopiloting + + + If true, currently visible avatars will be stored + in dictionaries inside Simulator.ObjectAvatars. + If false, a new Avatar or Primitive object will be created + each time an object update packet is received + + + If true, currently visible avatars will be stored + in dictionaries inside Simulator.ObjectPrimitives. + If false, a new Avatar or Primitive object will be created + each time an object update packet is received + + + If true, position and velocity will periodically be + interpolated (extrapolated, technically) for objects and + avatars that are being tracked by the library. This is + necessary to increase the accuracy of speed and position + estimates for simulated objects + + + + If true, utilization statistics will be tracked. There is a minor penalty + in CPU time for enabling this option. + + + + If true, parcel details will be stored in the + Simulator.Parcels dictionary as they are received + + + + If true, an incoming parcel properties reply will automatically send + a request for the parcel access list + + + + + if true, an incoming parcel properties reply will automatically send + a request for the traffic count. + + + + + If true, images, and other assets downloaded from the server + will be cached in a local directory + + + + Path to store cached texture data + + + Maximum size cached files are allowed to take on disk (bytes) + + + Default color used for viewer particle effects + + + Maximum number of times to resend a failed packet + + + Throttle outgoing packet rate + + + UUID of a texture used by some viewers to indentify type of client used + + + + Download textures using GetTexture capability when available + + + + The maximum number of concurrent texture downloads allowed + Increasing this number will not necessarily increase texture retrieval times due to + simulator throttles + + + + The Refresh timer inteval is used to set the delay between checks for stalled texture downloads + + This is a static variable which applies to all instances + + + + Textures taking longer than this value will be flagged as timed out and removed from the pipeline + + + + + Get or set the minimum log level to output to the console by default + + If the library is not compiled with DEBUG defined and this level is set to DEBUG + You will get no output on the console. This behavior can be overriden by creating + a logger configuration file for log4net + + + + Attach avatar names to log messages + + + Log packet retransmission info + + + Log disk cache misses and other info + + + Constructor + Reference to a GridClient object + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Cost of uploading an asset + Read-only since this value is dynamically fetched at login + + + + Simulator (region) properties + + + + No flags set + + + Agents can take damage and be killed + + + Landmarks can be created here + + + Home position can be set in this sim + + + Home position is reset when an agent teleports away + + + Sun does not move + + + No object, land, etc. taxes + + + Disable heightmap alterations (agents can still plant + foliage) + + + Land cannot be released, sold, or purchased + + + All content is wiped nightly + + + Unknown: Related to the availability of an overview world map tile.(Think mainland images when zoomed out.) + + + Unknown: Related to region debug flags. Possibly to skip processing of agent interaction with world. + + + Region does not update agent prim interest lists. Internal debugging option. + + + No collision detection for non-agent objects + + + No scripts are ran + + + All physics processing is turned off + + + Region can be seen from other regions on world map. (Legacy world map option?) + + + Region can be seen from mainland on world map. (Legacy world map option?) + + + Agents not explicitly on the access list can visit the region. + + + Traffic calculations are not run across entire region, overrides parcel settings. + + + Flight is disabled (not currently enforced by the sim) + + + Allow direct (p2p) teleporting + + + Estate owner has temporarily disabled scripting + + + Restricts the usage of the LSL llPushObject function, applies to whole region. + + + Deny agents with no payment info on file + + + Deny agents with payment info on file + + + Deny agents who have made a monetary transaction + + + Parcels within the region may be joined or divided by anyone, not just estate owners/managers. + + + Abuse reports sent from within this region are sent to the estate owner defined email. + + + Region is Voice Enabled + + + Removes the ability from parcel owners to set their parcels to show in search. + + + Deny agents who have not been age verified from entering the region. + + + + Region protocol flags + + + + Nothing special + + + Region supports Server side Appearance + + + Viewer supports Server side Appearance + + + + Access level for a simulator + + + + Unknown or invalid access level + + + Trial accounts allowed + + + PG rating + + + Mature rating + + + Adult rating + + + Simulator is offline + + + Simulator does not exist + + + + + + + + + + + + + + Initialize the UDP packet handler in server mode + + Port to listening for incoming UDP packets on + + + + Initialize the UDP packet handler in client mode + + Remote UDP server to connect to + + + + + + + + + + + + + + + + + + A public reference to the client that this Simulator object + is attached to + + + A Unique Cache identifier for this simulator + + + The capabilities for this simulator + + + + + + The current version of software this simulator is running + + + + + + A 64x64 grid of parcel coloring values. The values stored + in this array are of the type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if your agent has Estate Manager rights on this region + + + + + + + + + + + + Statistics information for this simulator and the + connection to the simulator, calculated by the simulator itself + and the library + + + The regions Unique ID + + + The physical data center the simulator is located + Known values are: + + Dallas + Chandler + SF + + + + + The CPU Class of the simulator + Most full mainland/estate sims appear to be 5, + Homesteads and Openspace appear to be 501 + + + The number of regions sharing the same CPU as this one + "Full Sims" appear to be 1, Homesteads appear to be 4 + + + The billing product name + Known values are: + + Mainland / Full Region (Sku: 023) + Estate / Full Region (Sku: 024) + Estate / Openspace (Sku: 027) + Estate / Homestead (Sku: 029) + Mainland / Homestead (Sku: 129) (Linden Owned) + Mainland / Linden Homes (Sku: 131) + + + + + The billing product SKU + Known values are: + + 023 Mainland / Full Region + 024 Estate / Full Region + 027 Estate / Openspace + 029 Estate / Homestead + 129 Mainland / Homestead (Linden Owned) + 131 Linden Homes / Full Region + + + + + + Flags indicating which protocols this region supports + + + + The current sequence number for packets sent to this + simulator. Must be Interlocked before modifying. Only + useful for applications manipulating sequence numbers + + + + A thread-safe dictionary containing avatars in a simulator + + + + + A thread-safe dictionary containing primitives in a simulator + + + + + Checks simulator parcel map to make sure it has downloaded all data successfully + + true if map is full (contains no 0's) + + + + Is it safe to send agent updates to this sim + AgentMovementComplete message received + + + + Used internally to track sim disconnections + + + Event that is triggered when the simulator successfully + establishes a connection + + + Whether this sim is currently connected or not. Hooked up + to the property Connected + + + Coarse locations of avatars in this simulator + + + AvatarPositions key representing TrackAgent target + + + Sequence numbers of packets we've received + (for duplicate checking) + + + Packets we sent out that need ACKs from the simulator + + + Sequence number for pause/resume + + + Indicates if UDP connection to the sim is fully established + + + + + + Reference to the GridClient object + IPEndPoint of the simulator + handle of the simulator + + + + Called when this Simulator object is being destroyed + + + + + Attempt to connect to this simulator + + Whether to move our agent in to this sim or not + True if the connection succeeded or connection status is + unknown, false if there was a failure + + + + Initiates connection to the simulator + + Should we block until ack for this packet is recieved + + + + Disconnect from this simulator + + + + + Instructs the simulator to stop sending update (and possibly other) packets + + + + + Instructs the simulator to resume sending update packets (unpause) + + + + + Retrieve the terrain height at a given coordinate + + Sim X coordinate, valid range is from 0 to 255 + Sim Y coordinate, valid range is from 0 to 255 + The terrain height at the given point if the + lookup was successful, otherwise 0.0f + True if the lookup was successful, otherwise false + + + + Sends a packet + + Packet to be sent + + + + + + + + + Returns Simulator Name as a String + + + + + + + + + + + + + + + + + + + Sends out pending acknowledgements + + Number of ACKs sent + + + + Resend unacknowledged packets + + + + + Provides access to an internal thread-safe dictionary containing parcel + information found in this simulator + + + + + Provides access to an internal thread-safe multidimensional array containing a x,y grid mapped + to each 64x64 parcel's LocalID. + + + + The IP address and port of the server + + + Whether there is a working connection to the simulator or + not + + + Coarse locations of avatars in this simulator + + + AvatarPositions key representing TrackAgent target + + + Indicates if UDP connection to the sim is fully established + + + + Simulator Statistics + + + + Total number of packets sent by this simulator to this agent + + + Total number of packets received by this simulator to this agent + + + Total number of bytes sent by this simulator to this agent + + + Total number of bytes received by this simulator to this agent + + + Time in seconds agent has been connected to simulator + + + Total number of packets that have been resent + + + Total number of resent packets recieved + + + Total number of pings sent to this simulator by this agent + + + Total number of ping replies sent to this agent by this simulator + + + + Incoming bytes per second + + It would be nice to have this claculated on the fly, but + this is far, far easier + + + + Outgoing bytes per second + + It would be nice to have this claculated on the fly, but + this is far, far easier + + + Time last ping was sent + + + ID of last Ping sent + + + + + + + + + Current time dilation of this simulator + + + Current Frames per second of simulator + + + Current Physics frames per second of simulator + + + + + + + + + + + + + + + + + + + + + + + + + + + Total number of objects Simulator is simulating + + + Total number of Active (Scripted) objects running + + + Number of agents currently in this simulator + + + Number of agents in neighbor simulators + + + Number of Active scripts running in this simulator + + + + + + + + + + + + Number of downloads pending + + + Number of uploads pending + + + + + + + + + Number of local uploads pending + + + Unacknowledged bytes in queue + + + + Simulator handle + + + + + Number of GridClients using this datapool + + + + + Time that the last client disconnected from the simulator + + + + + The cache of prims used and unused in this simulator + + + + + Shared parcel info only when POOL_PARCEL_DATA == true + + + + + + + + + The event subscribers, null of no subscribers + + + Raises the AttachedSound Event + A AttachedSoundEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the AttachedSoundGainChange Event + A AttachedSoundGainChangeEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the SoundTrigger Event + A SoundTriggerEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + The event subscribers, null of no subscribers + + + Raises the PreloadSound Event + A PreloadSoundEventArgs object containing + the data sent from the simulator + + + Thread sync lock object + + + + Construct a new instance of the SoundManager class, used for playing and receiving + sound assets + + A reference to the current GridClient instance + + + + Plays a sound in the current region at full volume from avatar position + + UUID of the sound to be played + + + + Plays a sound in the current region at full volume + + UUID of the sound to be played. + position for the sound to be played at. Normally the avatar. + + + + Plays a sound in the current region + + UUID of the sound to be played. + position for the sound to be played at. Normally the avatar. + volume of the sound, from 0.0 to 1.0 + + + + Plays a sound in the specified sim + + UUID of the sound to be played. + UUID of the sound to be played. + position for the sound to be played at. Normally the avatar. + volume of the sound, from 0.0 to 1.0 + + + + Play a sound asset + + UUID of the sound to be played. + handle id for the sim to be played in. + position for the sound to be played at. Normally the avatar. + volume of the sound, from 0.0 to 1.0 + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Process an incoming packet and raise the appropriate events + The sender + The EventArgs object containing the packet data + + + Raised when the simulator sends us data containing + sound + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Raised when the simulator sends us data containing + ... + + + Provides data for the event + The event occurs when the simulator sends + the sound data which emits from an agents attachment + + The following code example shows the process to subscribe to the event + and a stub to handle the data passed from the simulator + + // Subscribe to the AttachedSound event + Client.Sound.AttachedSound += Sound_AttachedSound; + + // process the data raised in the event here + private void Sound_AttachedSound(object sender, AttachedSoundEventArgs e) + { + // ... Process AttachedSoundEventArgs here ... + } + + + + + + Construct a new instance of the SoundTriggerEventArgs class + + Simulator where the event originated + The sound asset id + The ID of the owner + The ID of the object + The volume level + The + + + Simulator where the event originated + + + Get the sound asset id + + + Get the ID of the owner + + + Get the ID of the Object + + + Get the volume level + + + Get the + + + Provides data for the event + The event occurs when an attached sound + changes its volume level + + + + Construct a new instance of the AttachedSoundGainChangedEventArgs class + + Simulator where the event originated + The ID of the Object + The new volume level + + + Simulator where the event originated + + + Get the ID of the Object + + + Get the volume level + + + Provides data for the event + The event occurs when the simulator forwards + a request made by yourself or another agent to play either an asset sound or a built in sound + + Requests to play sounds where the is not one of the built-in + will require sending a request to download the sound asset before it can be played + + + The following code example uses the , + and + properties to display some information on a sound request on the window. + + // subscribe to the event + Client.Sound.SoundTrigger += Sound_SoundTrigger; + + // play the pre-defined BELL_TING sound + Client.Sound.SendSoundTrigger(Sounds.BELL_TING); + + // handle the response data + private void Sound_SoundTrigger(object sender, SoundTriggerEventArgs e) + { + Console.WriteLine("{0} played the sound {1} at volume {2}", + e.OwnerID, e.SoundID, e.Gain); + } + + + + + + Construct a new instance of the SoundTriggerEventArgs class + + Simulator where the event originated + The sound asset id + The ID of the owner + The ID of the object + The ID of the objects parent + The volume level + The regionhandle + The source position + + + Simulator where the event originated + + + Get the sound asset id + + + Get the ID of the owner + + + Get the ID of the Object + + + Get the ID of the objects parent + + + Get the volume level + + + Get the regionhandle + + + Get the source position + + + Provides data for the event + The event occurs when the simulator sends + the appearance data for an avatar + + The following code example uses the and + properties to display the selected shape of an avatar on the window. + + // subscribe to the event + Client.Avatars.AvatarAppearance += Avatars_AvatarAppearance; + + // handle the data when the event is raised + void Avatars_AvatarAppearance(object sender, AvatarAppearanceEventArgs e) + { + Console.WriteLine("The Agent {0} is using a {1} shape.", e.AvatarID, (e.VisualParams[31] > 0) : "male" ? "female") + } + + + + + + Construct a new instance of the PreloadSoundEventArgs class + + Simulator where the event originated + The sound asset id + The ID of the owner + The ID of the object + + + Simulator where the event originated + + + Get the sound asset id + + + Get the ID of the owner + + + Get the ID of the Object + + + + pre-defined built in sounds + + + + + + + + + + + + + + + + + + + + + + + + + + + + coins + + + cash register bell + + + + + + + + + rubber + + + plastic + + + flesh + + + wood splintering? + + + glass break + + + metal clunk + + + whoosh + + + shake + + + + + + ding + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A dictionary containing all pre-defined sounds + + A dictionary containing the pre-defined sounds, + where the key is the sounds ID, and the value is a string + containing a name to identify the purpose of the sound + + + X position of this patch + + + Y position of this patch + + + A 16x16 array of floats holding decompressed layer data + + + + Creates a LayerData packet for compressed land data given a full + simulator heightmap and an array of indices of patches to compress + + A 256 * 256 array of floating point values + specifying the height at each meter in the simulator + Array of indexes in the 16x16 grid of patches + for this simulator. For example if 1 and 17 are specified, patches + x=1,y=0 and x=1,y=1 are sent + + + + + Add a patch of terrain to a BitPacker + + BitPacker to write the patch to + Heightmap of the simulator, must be a 256 * + 256 float array + X offset of the patch to create, valid values are + from 0 to 15 + Y offset of the patch to create, valid values are + from 0 to 15 + + + The event subscribers. null if no subcribers + + + Raises the LandPatchReceived event + A LandPatchReceivedEventArgs object containing the + data returned from the simulator + + + Thread sync lock object + + + + Default constructor + + + + + Raised when the simulator responds sends + + + Simulator from that sent tha data + + + Sim coordinate of the patch + + + Sim coordinate of the patch + + + Size of tha patch + + + Heightmap for the patch + + + + The current status of a texture request as it moves through the pipeline or final result of a texture request. + + + + The initial state given to a request. Requests in this state + are waiting for an available slot in the pipeline + + + A request that has been added to the pipeline and the request packet + has been sent to the simulator + + + A request that has received one or more packets back from the simulator + + + A request that has received all packets back from the simulator + + + A request that has taken longer than + to download OR the initial packet containing the packet information was never received + + + The texture request was aborted by request of the agent + + + The simulator replied to the request that it was not able to find the requested texture + + + + A callback fired to indicate the status or final state of the requested texture. For progressive + downloads this will fire each time new asset data is returned from the simulator. + + The indicating either Progress for textures not fully downloaded, + or the final result of the request after it has been processed through the TexturePipeline + The object containing the Assets ID, raw data + and other information. For progressive rendering the will contain + the data from the beginning of the file. For failed, aborted and timed out requests it will contain + an empty byte array. + + + + Texture request download handler, allows a configurable number of download slots which manage multiple + concurrent texture downloads from the + + This class makes full use of the internal + system for full texture downloads. + + + A dictionary containing all pending and in-process transfer requests where the Key is both the RequestID + and also the Asset Texture ID, and the value is an object containing the current state of the request and also + the asset data as it is being re-assembled + + + Holds the reference to the client object + + + Maximum concurrent texture requests allowed at a time + + + An array of objects used to manage worker request threads + + + An array of worker slots which shows the availablity status of the slot + + + The primary thread which manages the requests. + + + true if the TexturePipeline is currently running + + + A synchronization object used by the primary thread + + + A refresh timer used to increase the priority of stalled requests + + + + Default constructor, Instantiates a new copy of the TexturePipeline class + + Reference to the instantiated object + + + + Initialize callbacks required for the TexturePipeline to operate + + + + + Shutdown the TexturePipeline and cleanup any callbacks or transfers + + + + + Request a texture asset from the simulator using the system to + manage the requests and re-assemble the image from the packets received from the simulator + + The of the texture asset to download + The of the texture asset. + Use for most textures, or for baked layer texture assets + A float indicating the requested priority for the transfer. Higher priority values tell the simulator + to prioritize the request before lower valued requests. An image already being transferred using the can have + its priority changed by resending the request with the new priority value + Number of quality layers to discard. + This controls the end marker of the data sent + The packet number to begin the request at. A value of 0 begins the request + from the start of the asset texture + The callback to fire when the image is retrieved. The callback + will contain the result of the request and the texture asset data + If true, the callback will be fired for each chunk of the downloaded image. + The callback asset parameter will contain all previously received chunks of the texture asset starting + from the beginning of the request + + + + Sends the actual request packet to the simulator + + The image to download + Type of the image to download, either a baked + avatar texture or a normal texture + Priority level of the download. Default is + 1,013,000.0f + Number of quality layers to discard. + This controls the end marker of the data sent + Packet number to start the download at. + This controls the start marker of the data sent + Sending a priority of 0 and a discardlevel of -1 aborts + download + + + + Cancel a pending or in process texture request + + The texture assets unique ID + + + + Master Download Thread, Queues up downloads in the threadpool + + + + + The worker thread that sends the request and handles timeouts + + A object containing the request details + + + + Handle responses from the simulator that tell us a texture we have requested is unable to be located + or no longer exists. This will remove the request from the pipeline and free up a slot if one is in use + + The sender + The EventArgs object containing the packet data + + + + Handles the remaining Image data that did not fit in the initial ImageData packet + + The sender + The EventArgs object containing the packet data + + + + Handle the initial ImageDataPacket sent from the simulator + + The sender + The EventArgs object containing the packet data + + + Current number of pending and in-process transfers + + + + A request task containing information and status of a request as it is processed through the + + + + The current which identifies the current status of the request + + + The Unique Request ID, This is also the Asset ID of the texture being requested + + + The slot this request is occupying in the threadpoolSlots array + + + The ImageType of the request. + + + The callback to fire when the request is complete, will include + the and the + object containing the result data + + + If true, indicates the callback will be fired whenever new data is returned from the simulator. + This is used to progressively render textures as portions of the texture are received. + + + An object that maintains the data of an request thats in-process. + + + + + + + + + An instance of DelegateWrapper which calls InvokeWrappedDelegate, + which in turn calls the DynamicInvoke method of the wrapped + delegate + + + + + Callback used to call EndInvoke on the asynchronously + invoked DelegateWrapper + + + + + Executes the specified delegate with the specified arguments + asynchronously on a thread pool thread + + + + + + + Invokes the wrapped delegate synchronously + + + + + + + Calls EndInvoke on the wrapper and Close on the resulting WaitHandle + to prevent resource leaks + + + + + + Delegate to wrap another delegate and its arguments + + + + + + + + + + + + + + + + + + + + + Thrown when a packet could not be successfully deserialized + + + + + Default constructor + + + + + Constructor that takes an additional error message + + An error message to attach to this exception + + + + The header of a message template packet. Holds packet flags, sequence + number, packet ID, and any ACKs that will be appended at the end of + the packet + + + + + Convert the AckList to a byte array, used for packet serializing + + Reference to the target byte array + Beginning position to start writing to in the byte + array, will be updated with the ending position of the ACK list + + + + + + + + + + + + + + + + + + + + + A block of data in a packet. Packets are composed of one or more blocks, + each block containing one or more fields + + + + + Create a block from a byte array + + Byte array containing the serialized block + Starting position of the block in the byte array. + This will point to the data after the end of the block when the + call returns + + + + Serialize this block into a byte array + + Byte array to serialize this block into + Starting position in the byte array to serialize to. + This will point to the position directly after the end of the + serialized block when the call returns + + + Current length of the data in this packet + + + A generic value, not an actual packet type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Operation to apply when applying color to texture + + + + + Information needed to translate visual param value to RGBA color + + + + + Construct VisualColorParam + + Operation to apply when applying color to texture + Colors + + + + Represents alpha blending and bump infor for a visual parameter + such as sleive length + + + + Stregth of the alpha to apply + + + File containing the alpha channel + + + Skip blending if parameter value is 0 + + + Use miltiply insted of alpha blending + + + + Create new alhpa information for a visual param + + Stregth of the alpha to apply + File containing the alpha channel + Skip blending if parameter value is 0 + Use miltiply insted of alpha blending + + + + A single visual characteristic of an avatar mesh, such as eyebrow height + + + + Index of this visual param + + + Internal name + + + Group ID this parameter belongs to + + + Name of the wearable this parameter belongs to + + + Displayable label of this characteristic + + + Displayable label for the minimum value of this characteristic + + + Displayable label for the maximum value of this characteristic + + + Default value + + + Minimum value + + + Maximum value + + + Is this param used for creation of bump layer? + + + Alpha blending/bump info + + + Color information + + + Array of param IDs that are drivers for this parameter + + + + Set all the values through the constructor + + Index of this visual param + Internal name + + + Displayable label of this characteristic + Displayable label for the minimum value of this characteristic + Displayable label for the maximum value of this characteristic + Default value + Minimum value + Maximum value + Is this param used for creation of bump layer? + Array of param IDs that are drivers for this parameter + Alpha blending/bump info + Color information + + + + Holds the Params array of all the avatar appearance parameters + + + + + Base class for all Asset types + + + + A byte array containing the raw asset data + + + True if the asset it only stored on the server temporarily + + + A unique ID + + + + Construct a new Asset object + + + + + Construct a new Asset object + + A unique specific to this asset + A byte array containing the raw asset data + + + + Regenerates the AssetData byte array from the properties + of the derived class. + + + + + Decodes the AssetData, placing it in appropriate properties of the derived + class. + + True if the asset decoding succeeded, otherwise false + + + The assets unique ID + + + + The "type" of asset, Notecard, Animation, etc + + + + + Constants for the archiving module + + + + + Path for region settings. + + + + + The location of the archive control file + + + + + Path for the assets held in an archive + + + + + Path for the prims file + + + + + Path for terrains. Technically these may be assets, but I think it's quite nice to split them out. + + + + + Path for region settings. + + + + + The character the separates the uuid from extension information in an archived asset filename + + + + + Extensions used for asset types in the archive + + + + + Archives assets + + + + + Archive assets + + + + + Archive the assets given to this archiver to the given archive. + + + + + + Write an assets metadata file to the given archive + + + + + + Write asset data files to the given archive + + + + + + Temporary code to do the bare minimum required to read a tar archive for our purposes + + + + + Binary reader for the underlying stream + + + + + Used to trim off null chars + + + + + Used to trim off space chars + + + + + Generate a tar reader which reads from the given stream. + + + + + + Read the next entry in the tar file. + + + + the data for the entry. Returns null if there are no more entries + + + + Read the next 512 byte chunk of data as a tar header. + + A tar header struct. null if we have reached the end of the archive. + + + + Read data following a header + + + + + + + Convert octal bytes to a decimal representation + + + + + + + + + Temporary code to produce a tar archive in tar v7 format + + + + + Binary writer for the underlying stream + + + + + Write a directory entry to the tar archive. We can only handle one path level right now! + + + + + + Write a file to the tar archive + + + + + + + Write a file to the tar archive + + + + + + + Finish writing the raw tar archive data to a stream. The stream will be closed on completion. + + + + + Write a particular entry + + + + + + + + Represents an Animation + + + + Default Constructor + + + + Construct an Asset object of type Animation + + A unique specific to this asset + A byte array containing the raw asset data + + + Override the base classes AssetType + + + + Represents an that represents an avatars body ie: Hair, Etc. + + + + + Represents a Wearable Asset, Clothing, Hair, Skin, Etc + + + + A string containing the name of the asset + + + A string containing a short description of the asset + + + The Assets WearableType + + + The For-Sale status of the object + + + An Integer representing the purchase price of the asset + + + The of the assets creator + + + The of the assets current owner + + + The of the assets prior owner + + + The of the Group this asset is set to + + + True if the asset is owned by a + + + The Permissions mask of the asset + + + A Dictionary containing Key/Value pairs of the objects parameters + + + A Dictionary containing Key/Value pairs where the Key is the textures Index and the Value is the Textures + + + Initializes a new instance of an AssetWearable object + + + Initializes a new instance of an AssetWearable object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + + Decode an assets byte encoded data to a string + + true if the asset data was decoded successfully + + + + Encode the assets string represantion into a format consumable by the asset server + + + + Initializes a new instance of an AssetBodyPart object + + + Initializes a new instance of an AssetBodyPart object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + Override the base classes AssetType + + + + Represents a Callingcard with AvatarID and Position vector + + + + UUID of the Callingcard target avatar + + + Construct an Asset of type Callingcard + + + + Construct an Asset object of type Callingcard + + A unique specific to this asset + A byte array containing the raw asset data + + + + Constuct an asset of type Callingcard + + UUID of the target avatar + + + + Encode the raw contents of a string with the specific Callingcard format + + + + + Decode the raw asset data, populating the AvatarID and Position + + true if the AssetData was successfully decoded to a UUID and Vector + + + Override the base classes AssetType + + + + Represents an that can be worn on an avatar + such as a Shirt, Pants, etc. + + + + Initializes a new instance of an AssetScriptBinary object + + + Initializes a new instance of an AssetScriptBinary object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + Override the base classes AssetType + + + + Type of gesture step + + + + + Base class for gesture steps + + + + + Retururns what kind of gesture step this is + + + + + Describes animation step of a gesture + + + + + If true, this step represents start of animation, otherwise animation stop + + + + + Animation asset + + + + + Animation inventory name + + + + + Returns what kind of gesture step this is + + + + + Describes sound step of a gesture + + + + + Sound asset + + + + + Sound inventory name + + + + + Returns what kind of gesture step this is + + + + + Describes sound step of a gesture + + + + + Text to output in chat + + + + + Returns what kind of gesture step this is + + + + + Describes sound step of a gesture + + + + + If true in this step we wait for all animations to finish + + + + + If true gesture player should wait for the specified amount of time + + + + + Time in seconds to wait if WaitForAnimation is false + + + + + Returns what kind of gesture step this is + + + + + Describes the final step of a gesture + + + + + Returns what kind of gesture step this is + + + + + Represents a sequence of animations, sounds, and chat actions + + + + + Keyboard key that triggers the gestyre + + + + + Modifier to the trigger key + + + + + String that triggers playing of the gesture sequence + + + + + Text that replaces trigger in chat once gesture is triggered + + + + + Sequence of gesture steps + + + + + Constructs guesture asset + + + + + Constructs guesture asset + + A unique specific to this asset + A byte array containing the raw asset data + + + + Encodes gesture asset suitable for uplaod + + + + + Decodes gesture assset into play sequence + + true if the asset data was decoded successfully + + + + Returns asset type + + + + + Represents a Landmark with RegionID and Position vector + + + + UUID of the Landmark target region + + + Local position of the target + + + Construct an Asset of type Landmark + + + + Construct an Asset object of type Landmark + + A unique specific to this asset + A byte array containing the raw asset data + + + + Encode the raw contents of a string with the specific Landmark format + + + + + Decode the raw asset data, populating the RegionID and Position + + true if the AssetData was successfully decoded to a UUID and Vector + + + Override the base classes AssetType + + + + Represents Mesh asset + + + + + Decoded mesh data + + + + Initializes a new instance of an AssetMesh object + + + Initializes a new instance of an AssetMesh object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + + TODO: Encodes Collada file into LLMesh format + + + + + Decodes mesh asset. See + to furter decode it for rendering + true + + + Override the base classes AssetType + + + + Represents an Animation + + + + Default Constructor + + + + Construct an Asset object of type Animation + + Asset type + A unique specific to this asset + A byte array containing the raw asset data + + + Override the base classes AssetType + + + + Represents a string of characters encoded with specific formatting properties + + + + A text string containing main text of the notecard + + + List of s embedded on the notecard + + + Construct an Asset of type Notecard + + + + Construct an Asset object of type Notecard + + A unique specific to this asset + A byte array containing the raw asset data + + + + Encode the raw contents of a string with the specific Linden Text properties + + + + + Decode the raw asset data including the Linden Text properties + + true if the AssetData was successfully decoded + + + Override the base classes AssetType + + + + A linkset asset, containing a parent primitive and zero or more children + + + + Initializes a new instance of an AssetPrim object + + + + Initializes a new instance of an AssetPrim object + + A unique specific to this asset + A byte array containing the raw asset data + + + + + + + + + + + + + + Override the base classes AssetType + + + + Only used internally for XML serialization/deserialization + + + + + The deserialized form of a single primitive in a linkset asset + + + + + Represents an AssetScriptBinary object containing the + LSO compiled bytecode of an LSL script + + + + Initializes a new instance of an AssetScriptBinary object + + + Initializes a new instance of an AssetScriptBinary object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + + TODO: Encodes a scripts contents into a LSO Bytecode file + + + + + TODO: Decode LSO Bytecode into a string + + true + + + Override the base classes AssetType + + + + Represents an LSL Text object containing a string of UTF encoded characters + + + + A string of characters represting the script contents + + + Initializes a new AssetScriptText object + + + + Initializes a new AssetScriptText object with parameters + + A unique specific to this asset + A byte array containing the raw asset data + + + + Encode a string containing the scripts contents into byte encoded AssetData + + + + + Decode a byte array containing the scripts contents into a string + + true if decoding is successful + + + Override the base classes AssetType + + + + Represents a Sound Asset + + + + Initializes a new instance of an AssetSound object + + + Initializes a new instance of an AssetSound object with parameters + A unique specific to this asset + A byte array containing the raw asset data + + + + TODO: Encodes a sound file + + + + + TODO: Decode a sound file + + true + + + Override the base classes AssetType + + + + Represents a texture + + + + A object containing image data + + + + + + + + + Initializes a new instance of an AssetTexture object + + + + Initializes a new instance of an AssetTexture object + + A unique specific to this asset + A byte array containing the raw asset data + + + + Initializes a new instance of an AssetTexture object + + A object containing texture data + + + + Populates the byte array with a JPEG2000 + encoded image created from the data in + + + + + Decodes the JPEG2000 data in AssetData to the + object + + True if the decoding was successful, otherwise false + + + + Decodes the begin and end byte positions for each quality layer in + the image + + + + + Override the base classes AssetType + + + = + + + Number of times we've received an unknown CAPS exception in series. + + + For exponential backoff on error. + + + + A set of textures that are layered on texture of each other and "baked" + in to a single texture, for avatar appearances + + + + Final baked texture + + + Component layers + + + Width of the final baked image and scratchpad + + + Height of the final baked image and scratchpad + + + Bake type + + + + Default constructor + + Bake type + + + + Adds layer for baking + + TexturaData struct that contains texture and its params + + + + Converts avatar texture index (face) to Bake type + + Face number (AvatarTextureIndex) + BakeType, layer to which this texture belongs to + + + + Make sure images exist, resize source if needed to match the destination + + Destination image + Source image + Sanitization was succefull + + + + Fills a baked layer as a solid *appearing* color. The colors are + subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from + compressing it too far since it seems to cause upload failures if + the image is a pure solid color + + Color of the base of this layer + + + + Fills a baked layer as a solid *appearing* color. The colors are + subtly dithered on a 16x16 grid to prevent the JPEG2000 stage from + compressing it too far since it seems to cause upload failures if + the image is a pure solid color + + Red value + Green value + Blue value + + + Final baked texture + + + Component layers + + + Width of the final baked image and scratchpad + + + Height of the final baked image and scratchpad + + + Bake type + + + Is this one of the 3 skin bakes + + + + Image width + + + + + Image height + + + + + Image channel flags + + + + + Red channel data + + + + + Green channel data + + + + + Blue channel data + + + + + Alpha channel data + + + + + Bump channel data + + + + + Create a new blank image + + width + height + channel flags + + + + + + + + + + Convert the channels in the image. Channels are created or destroyed as required. + + new channel flags + + + + Resize or stretch the image using nearest neighbor (ugly) resampling + + new width + new height + + + + Create a byte array containing 32-bit RGBA data with a bottom-left + origin, suitable for feeding directly into OpenGL + + A byte array containing raw texture data + + + + Create a byte array containing 32-bit RGBA data with a bottom-left + origin, suitable for feeding directly into OpenGL + + A byte array containing raw texture data + + + + A Wrapper around openjpeg to encode and decode images to and from byte arrays + + + + TGA Header size + + + OpenJPEG is not threadsafe, so this object is used to lock + during calls into unmanaged code + + + + Encode a object into a byte array + + The object to encode + true to enable lossless conversion, only useful for small images ie: sculptmaps + A byte array containing the encoded Image object + + + + Encode a object into a byte array + + The object to encode + a byte array of the encoded image + + + + Decode JPEG2000 data to an and + + + JPEG2000 encoded data + ManagedImage object to decode to + Image object to decode to + True if the decode succeeds, otherwise false + + + + + + + + + + + + + + + + + + + + + Encode a object into a byte array + + The source object to encode + true to enable lossless decoding + A byte array containing the source Bitmap object + + + + Defines the beginning and ending file positions of a layer in an + LRCP-progression JPEG2000 file + + + + + This structure is used to marshal both encoded and decoded images. + MUST MATCH THE STRUCT IN dotnet.h! + + + + + Information about a single packet in a JPEG2000 stream + + + + Packet start position + + + Packet header end position + + + Packet end position + + + + Capability to load TGAs to Bitmap + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Parsing Collada model files into data structures + + + + + Parses Collada document + + Load .dae model from this file + Load and decode images for uploading with model + A list of mesh prims that were parsed from the collada file + + + + Implements mesh upload communications with the simulator + + + + + Inlcude stub convex hull physics, required for uploading to Second Life + + + + + Use the same mesh used for geometry as the physical mesh upload + + + + + Creates instance of the mesh uploader + + GridClient instance to communicate with the simulator + List of ModelPrimitive objects to upload as a linkset + Inventory name for newly uploaded object + Inventory description for newly upload object + + + + Performs model upload in one go, without first checking for the price + + + + + Performs model upload in one go, without first checking for the price + + Callback that will be invoke upon completion of the upload. Null is sent on request failure + + + + Ask server for details of cost and impact of the mesh upload + + Callback that will be invoke upon completion of the upload. Null is sent on request failure + + + + Performas actual mesh and image upload + + Uri recieved in the upload prepare stage + Callback that will be invoke upon completion of the upload. Null is sent on request failure + + + + Callback for mesh upload operations + + null on failure, result from server on success + + + + Interface requirements for Messaging system + + + + + Abstract base for rendering plugins + + + + + Generates a basic mesh structure from a primitive + + Primitive to generate the mesh from + Level of detail to generate the mesh at + The generated mesh + + + + Generates a basic mesh structure from a sculpted primitive and + texture + + Sculpted primitive to generate the mesh from + Sculpt texture + Level of detail to generate the mesh at + The generated mesh + + + + Generates a series of faces, each face containing a mesh and + metadata + + Primitive to generate the mesh from + Level of detail to generate the mesh at + The generated mesh + + + + Generates a series of faces for a sculpted prim, each face + containing a mesh and metadata + + Sculpted primitive to generate the mesh from + Sculpt texture + Level of detail to generate the mesh at + The generated mesh + + + + Apply texture coordinate modifications from a + to a list of vertices + + Vertex list to modify texture coordinates for + Center-point of the face + Face texture parameters + Scale of the prim + + + + Sent to the client to indicate a teleport request has completed + + + + The of the agent + + + + + + The simulators handle the agent teleported to + + + A Uri which contains a list of Capabilities the simulator supports + + + Indicates the level of access required + to access the simulator, or the content rating, or the simulators + map status + + + The IP Address of the simulator + + + The UDP Port the simulator will listen for UDP traffic on + + + Status flags indicating the state of the Agent upon arrival, Flying, etc. + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Sent to the viewer when a neighboring simulator is requesting the agent make a connection to it. + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent to the client which indicates a teleport request has failed + and contains some information on why it failed + + + + + + + A string key of the reason the teleport failed e.g. CouldntTPCloser + Which could be used to look up a value in a dictionary or enum + + + The of the Agent + + + A string human readable message containing the reason + An example: Could not teleport closer to destination + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Contains a list of prim owner information for a specific parcel in a simulator + + + A Simulator will always return at least 1 entry + If agent does not have proper permission the OwnerID will be UUID.Zero + If agent does not have proper permission OR there are no primitives on parcel + the DataBlocksExtended map will not be sent from the simulator + + + + An Array of objects + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Prim ownership information for a specified owner on a single parcel + + + + The of the prim owner, + UUID.Zero if agent has no permission to view prim owner information + + + The total number of prims + + + True if the OwnerID is a + + + True if the owner is online + This is no longer used by the LL Simulators + + + The date the most recent prim was rezzed + + + + The details of a single parcel in a region, also contains some regionwide globals + + + + Simulator-local ID of this parcel + + + Maximum corner of the axis-aligned bounding box for this + parcel + + + Minimum corner of the axis-aligned bounding box for this + parcel + + + Total parcel land area + + + + + + Key of authorized buyer + + + Bitmap describing land layout in 4x4m squares across the + entire region + + + + + + Date land was claimed + + + Appears to always be zero + + + Parcel Description + + + + + + + + + Total number of primitives owned by the parcel group on + this parcel + + + Whether the land is deeded to a group or not + + + + + + Maximum number of primitives this parcel supports + + + The Asset UUID of the Texture which when applied to a + primitive will display the media + + + A URL which points to any Quicktime supported media type + + + A byte, if 0x1 viewer should auto scale media to fit object + + + URL For Music Stream + + + Parcel Name + + + Autoreturn value in minutes for others' objects + + + + + + Total number of other primitives on this parcel + + + UUID of the owner of this parcel + + + Total number of primitives owned by the parcel owner on + this parcel + + + + + + How long is pass valid for + + + Price for a temporary pass + + + + + + Disallows people outside the parcel from being able to see in + + + + + + + + + + + + True if the region denies access to age unverified users + + + + + + This field is no longer used + + + The result of a request for parcel properties + + + Sale price of the parcel, only useful if ForSale is set + The SalePrice will remain the same after an ownership + transfer (sale), so it can be used to see the purchase price after + a sale if the new owner has not changed it + + + + Number of primitives your avatar is currently + selecting and sitting on in this parcel + + + + + + + + A number which increments by 1, starting at 0 for each ParcelProperties request. + Can be overriden by specifying the sequenceID with the ParcelPropertiesRequest being sent. + a Negative number indicates the action in has occurred. + + + + Maximum primitives across the entire simulator + + + Total primitives across the entire simulator + + + + + + Key of parcel snapshot + + + Parcel ownership status + + + Total number of primitives on this parcel + + + + + + + + + A description of the media + + + An Integer which represents the height of the media + + + An integer which represents the width of the media + + + A boolean, if true the viewer should loop the media + + + A string which contains the mime type of the media + + + true to obscure (hide) media url + + + true to obscure (hide) music url + + + true if avatars in this parcel should be invisible to people outside + + + true if avatars outside can hear any sounds avatars inside play + + + true if group members outside can hear any sounds avatars inside play + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + A message sent from the viewer to the simulator to updated a specific parcels settings + + + The of the agent authorized to purchase this + parcel of land or a NULL if the sale is authorized to anyone + + + true to enable auto scaling of the parcel media + + + The category of this parcel used when search is enabled to restrict + search results + + + A string containing the description to set + + + The of the which allows for additional + powers and restrictions. + + + The which specifies how avatars which teleport + to this parcel are handled + + + The LocalID of the parcel to update settings on + + + A string containing the description of the media which can be played + to visitors + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true if avatars in this parcel should be invisible to people outside + + + true if avatars outside can hear any sounds avatars inside play + + + true if group members outside can hear any sounds avatars inside play + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + Base class used for the RemoteParcelRequest message + + + + A message sent from the viewer to the simulator to request information + on a remote parcel + + + + Local sim position of the parcel we are looking up + + + Region handle of the parcel we are looking up + + + Region of the parcel we are looking up + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator to the viewer in response to a + which will contain parcel information + + + + The grid-wide unique parcel ID + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message containing a request for a remote parcel from a viewer, or a response + from the simulator to that request + + + + The request or response details block + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator to an agent which contains + the groups the agent is in + + + + The Agent receiving the message + + + An array containing information + for each the agent is a member of + + + An array containing information + for each the agent is a member of + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + Group Details specific to the agent + + + true of the agent accepts group notices + + + The agents tier contribution to the group + + + The Groups + + + The of the groups insignia + + + The name of the group + + + The aggregate permissions the agent has in the group for all roles the agent + is assigned + + + An optional block containing additional agent specific information + + + true of the agent allows this group to be + listed in their profile + + + + A message sent from the viewer to the simulator which + specifies the language and permissions for others to detect + the language specified + + + + A string containng the default language + to use for the agent + + + true of others are allowed to + know the language setting + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + An EventQueue message sent from the simulator to an agent when the agent + leaves a group + + + + + An Array containing the AgentID and GroupID + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + An object containing the Agents UUID, and the Groups UUID + + + The ID of the Agent leaving the group + + + The GroupID the Agent is leaving + + + Base class for Asset uploads/results via Capabilities + + + + The request state + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the viewer to the simulator to request a temporary upload capability + which allows an asset to be uploaded + + + + The Capability URL sent by the simulator to upload the baked texture to + + + + A message sent from the simulator that will inform the agent the upload is complete, + and the UUID of the uploaded asset + + + + The uploaded texture asset ID + + + + A message sent from the viewer to the simulator to request a temporary + capability URI which is used to upload an agents baked appearance textures + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator which indicates the minimum version required for + using voice chat + + + + Major Version Required + + + Minor version required + + + The name of the region sending the version requrements + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator to the viewer containing the + voice server URI + + + + The Parcel ID which the voice server URI applies + + + The name of the region + + + A uri containing the server/channel information + which the viewer can utilize to participate in voice conversations + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + + + + + + + + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent by the viewer to the simulator to request a temporary + capability for a script contained with in a Tasks inventory to be updated + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator to the viewer to indicate + a Tasks scripts status. + + + + The Asset ID of the script + + + True of the script is compiled/ran using the mono interpreter, false indicates it + uses the older less efficient lsl2 interprter + + + The Task containing the scripts + + + true of the script is in a running state + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message containing the request/response used for updating a gesture + contained with an agents inventory + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message request/response which is used to update a notecard contained within + a tasks inventory + + + + The of the Task containing the notecard asset to update + + + The notecard assets contained in the tasks inventory + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A reusable class containing a message sent from the viewer to the simulator to request a temporary uploader capability + which is used to update an asset in an agents inventory + + + + + The Notecard AssetID to replace + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message containing the request/response used for updating a notecard + contained with an agents inventory + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the simulator to the viewer which indicates + an error occurred while attempting to update a script in an agents or tasks + inventory + + + + true of the script was successfully compiled by the simulator + + + A string containing the error which occured while trying + to update the script + + + A new AssetID assigned to the script + + + + A message sent from the viewer to the simulator + requesting the update of an existing script contained + within a tasks inventory + + + + if true, set the script mode to running + + + The scripts InventoryItem ItemID to update + + + A lowercase string containing either "mono" or "lsl2" which + specifies the script is compiled and ran on the mono runtime, or the older + lsl runtime + + + The tasks which contains the script to update + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message containing either the request or response used in updating a script inside + a tasks inventory + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Response from the simulator to notify the viewer the upload is completed, and + the UUID of the script asset and its compiled status + + + + The uploaded texture asset ID + + + true of the script was compiled successfully + + + + A message sent from a viewer to the simulator requesting a temporary uploader capability + used to update a script contained in an agents inventory + + + + The existing asset if of the script in the agents inventory to replace + + + The language of the script + Defaults to lsl version 2, "mono" might be another possible option + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message containing either the request or response used in updating a script inside + an agents inventory + + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + Base class for Map Layers via Capabilities + + + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Sent by an agent to the capabilities server to request map layers + + + + + A message sent from the simulator to the viewer which contains an array of map images and their grid coordinates + + + + An array containing LayerData items + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + An object containing map location details + + + + The Asset ID of the regions tile overlay + + + The grid location of the southern border of the map tile + + + The grid location of the western border of the map tile + + + The grid location of the eastern border of the map tile + + + The grid location of the northern border of the map tile + + + Object containing request or response + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + New as of 1.23 RC1, no details yet. + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + A string containing the method used + + + + A request sent from an agent to the Simulator to begin a new conference. + Contains a list of Agents which will be included in the conference + + + + An array containing the of the agents invited to this conference + + + The conferences Session ID + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A moderation request sent from a conference moderator + Contains an agent and an optional action to take + + + + The Session ID + + + + + + A list containing Key/Value pairs, known valid values: + key: text value: true/false - allow/disallow specified agents ability to use text in session + key: voice value: true/false - allow/disallow specified agents ability to use voice in session + + "text" or "voice" + + + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + A message sent from the agent to the simulator which tells the + simulator we've accepted a conference invitation + + + + The conference SessionID + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + Key of sender + + + Name of sender + + + Key of destination avatar + + + ID of originating estate + + + Key of originating region + + + Coordinates in originating region + + + Instant message type + + + Group IM session toggle + + + Key of IM session, for Group Messages, the groups UUID + + + Timestamp of the instant message + + + Instant message text + + + Whether this message is held for offline avatars + + + Context specific packed data + + + Is this invitation for voice group/conference chat + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Sent from the simulator to the viewer. + + When an agent initially joins a session the AgentUpdatesBlock object will contain a list of session members including + a boolean indicating they can use voice chat in this session, a boolean indicating they are allowed to moderate + this session, and lastly a string which indicates another agent is entering the session with the Transition set to "ENTER" + + During the session lifetime updates on individuals are sent. During the update the booleans sent during the initial join are + excluded with the exception of the Transition field. This indicates a new user entering or exiting the session with + the string "ENTER" or "LEAVE" respectively. + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + An EventQueue message sent when the agent is forcibly removed from a chatterbox session + + + + + A string containing the reason the agent was removed + + + + + The ChatterBoxSession's SessionID + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + + Event Queue message describing physics engine attributes of a list of objects + Sim sends these when object is selected + + + + Array with the list of physics properties + + + + Serializes the message + + Serialized OSD + + + + Deseializes the message + + Incoming data to deserialize + + + + A message sent from the viewer to the simulator which + specifies that the user has changed current URL + of the specific media on a prim face + + + + + New URL + + + + + Prim UUID where navigation occured + + + + + Face index + + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + Base class used for the ObjectMedia message + + + + Message used to retrive prim media data + + + + + Prim UUID + + + + + Requested operation, either GET or UPDATE + + + + + Serialize object + + Serialized object as OSDMap + + + + Deserialize the message + + An containing the data + + + + Message used to update prim media data + + + + + Prim UUID + + + + + Array of media entries indexed by face number + + + + + Media version string + + + + + Serialize object + + Serialized object as OSDMap + + + + Deserialize the message + + An containing the data + + + + Message used to update prim media data + + + + + Prim UUID + + + + + Array of media entries indexed by face number + + + + + Requested operation, either GET or UPDATE + + + + + Serialize object + + Serialized object as OSDMap + + + + Deserialize the message + + An containing the data + + + + Message for setting or getting per face MediaEntry + + + + The request or response details block + + + + Serialize the object + + An containing the objects data + + + + Deserialize the message + + An containing the data + + + Details about object resource usage + + + Object UUID + + + Object name + + + Indicates if object is group owned + + + Locatio of the object + + + Object owner + + + Resource usage, keys are resource names, values are resource usage for that specific resource + + + + Deserializes object from OSD + + An containing the data + + + + Makes an instance based on deserialized data + + serialized data + Instance containg deserialized data + + + Details about parcel resource usage + + + Parcel UUID + + + Parcel local ID + + + Parcel name + + + Indicates if parcel is group owned + + + Parcel owner + + + Array of containing per object resource usage + + + + Deserializes object from OSD + + An containing the data + + + + Makes an instance based on deserialized data + + serialized data + Instance containg deserialized data + + + Resource usage base class, both agent and parcel resource + usage contains summary information + + + Summary of available resources, keys are resource names, + values are resource usage for that specific resource + + + Summary resource usage, keys are resource names, + values are resource usage for that specific resource + + + + Serializes object + + serialized data + + + + Deserializes object from OSD + + An containing the data + + + Agent resource usage + + + Per attachment point object resource usage + + + + Deserializes object from OSD + + An containing the data + + + + Makes an instance based on deserialized data + + serialized data + Instance containg deserialized data + + + + Detects which class handles deserialization of this message + + An containing the data + Object capable of decoding this message + + + Request message for parcel resource usage + + + UUID of the parel to request resource usage info + + + + Serializes object + + serialized data + + + + Deserializes object from OSD + + An containing the data + + + Response message for parcel resource usage + + + URL where parcel resource usage details can be retrieved + + + URL where parcel resource usage summary can be retrieved + + + + Serializes object + + serialized data + + + + Deserializes object from OSD + + An containing the data + + + + Detects which class handles deserialization of this message + + An containing the data + Object capable of decoding this message + + + Parcel resource usage + + + Array of containing per percal resource usage + + + + Deserializes object from OSD + + An containing the data + + + + Reply to request for bunch if display names + + + + Current display name + + + Following UUIDs failed to return a valid display name + + + + Serializes the message + + OSD containting the messaage + + + + Message sent when requesting change of the display name + + + + Current display name + + + Desired new display name + + + + Serializes the message + + OSD containting the messaage + + + + Message recieved in response to request to change display name + + + + New display name + + + String message indicating the result of the operation + + + Numerical code of the result, 200 indicates success + + + + Serializes the message + + OSD containting the messaage + + + + Message recieved when someone nearby changes their display name + + + + Previous display name, empty string if default + + + New display name + + + + Serializes the message + + OSD containting the messaage + + + + Return a decoded capabilities message as a strongly typed object + + A string containing the name of the capabilities message key + An to decode + A strongly typed object containing the decoded information from the capabilities message, or null + if no existing Message object exists for the specified event + + + + Permissions for control of object media + + + + + Style of cotrols that shold be displayed to the user + + + + + Class representing media data for a single face + + + + Is display of the alternative image enabled + + + Should media auto loop + + + Shoule media be auto played + + + Auto scale media to prim face + + + Should viewer automatically zoom in on the face when clicked + + + Should viewer interpret first click as interaction with the media + or when false should the first click be treated as zoom in commadn + + + Style of controls viewer should display when + viewer media on this face + + + Starting URL for the media + + + Currently navigated URL + + + Media height in pixes + + + Media width in pixels + + + Who can controls the media + + + Who can interact with the media + + + Is URL whitelist enabled + + + Array of URLs that are whitelisted + + + + Serialize to OSD + + OSDMap with the serialized data + + + + Deserialize from OSD data + + Serialized OSD data + Deserialized object + + + + The type of bump-mapping applied to a face + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The level of shininess applied to a face + + + + + + + + + + + + + + + + + The texture mapping style used for a face + + + + + + + + + + + + + + + + + Flags in the TextureEntry block that describe which properties are + set + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level of Detail mesh + + + + + Contains all mesh faces that belong to a prim + + + + List of primitive faces + + + + Decodes mesh asset into FacetedMesh + + Mesh primitive + Asset retrieved from the asset server + Level of detail + Resulting decoded FacetedMesh + True if mesh asset decoding was successful + + + + This is used to login a specific user account(s). It may only be called after + Connector initialization has completed successfully + + Handle returned from successful Connector ‘create’ request + User's account name + User's account password + Values may be “AutoAnswer” or “VerifyAnswer” + "" + This is an integer that specifies how often + the daemon will send participant property events while in a channel. If this is not set + the default will be “on state change”, which means that the events will be sent when + the participant starts talking, stops talking, is muted, is unmuted. + The valid values are: + 0 – Never + 5 – 10 times per second + 10 – 5 times per second + 50 – 1 time per second + 100 – on participant state change (this is the default) + false + + + + + This is used to logout a user session. It should only be called with a valid AccountHandle. + + Handle returned from successful Connector ‘login’ request + + + + + This is used to get a list of audio devices that can be used for capture (input) of voice. + + + + + + This is used to get a list of audio devices that can be used for render (playback) of voice. + + + + + This command is used to select the render device. + + The name of the device as returned by the Aux.GetRenderDevices command. + + + + This command is used to select the capture device. + + The name of the device as returned by the Aux.GetCaptureDevices command. + + + + This command is used to start the audio capture process which will cause + AuxAudioProperty Events to be raised. These events can be used to display a + microphone VU meter for the currently selected capture device. This command + should not be issued if the user is on a call. + + (unused but required) + + + + + This command is used to stop the audio capture process. + + + + + + This command is used to set the mic volume while in the audio tuning process. + Once an acceptable mic level is attained, the application must issue a + connector set mic volume command to have that level be used while on voice + calls. + + the microphone volume (-100 to 100 inclusive) + + + + + This command is used to set the speaker volume while in the audio tuning + process. Once an acceptable speaker level is attained, the application must + issue a connector set speaker volume command to have that level be used while + on voice calls. + + the speaker volume (-100 to 100 inclusive) + + + + + This is used to initialize and stop the Connector as a whole. The Connector + Create call must be completed successfully before any other requests are made + (typically during application initialization). The shutdown should be called + when the application is shutting down to gracefully release resources + + A string value indicting the Application name + URL for the management server + LoggingSettings + + + + + + Shutdown Connector -- Should be called when the application is shutting down + to gracefully release resources + + Handle returned from successful Connector ‘create’ request + + + + Mute or unmute the microphone + + Handle returned from successful Connector ‘create’ request + true (mute) or false (unmute) + + + + Mute or unmute the speaker + + Handle returned from successful Connector ‘create’ request + true (mute) or false (unmute) + + + + Set microphone volume + + Handle returned from successful Connector ‘create’ request + The level of the audio, a number between -100 and 100 where + 0 represents ‘normal’ speaking volume + + + + Set local speaker volume + + Handle returned from successful Connector ‘create’ request + The level of the audio, a number between -100 and 100 where + 0 represents ‘normal’ speaking volume + + + + Start up the Voice service. + + + + + Handle miscellaneous request status + + + + ///If something goes wrong, we log it. + + + + Cleanup oject resources + + + + + Request voice cap when changing regions + + + + + Handle a change in session state + + + + + Close a voice session + + + + + + Locate a Session context from its handle + + Creates the session context if it does not exist. + + + + Handle completion of main voice cap request. + + + + + + + + Daemon has started so connect to it. + + + + + The daemon TCP connection is open. + + + + + Handle creation of the Connector. + + + + + Handle response to audio output device query + + + + + Handle response to audio input device query + + + + + Set voice channel for new parcel + + + + + + Request info from a parcel capability Uri. + + + + + + Receive parcel voice cap + + + + + + + + Tell Vivox where we are standing + + This has to be called when we move or turn. + + + + Start and stop updating out position. + + + + + + Starts a thread that keeps the daemon running + + + + + + + Stops the daemon and the thread keeping it running + + + + + + + + + + + + + Create a Session + Sessions typically represent a connection to a media session with one or more + participants. This is used to generate an ‘outbound’ call to another user or + channel. The specifics depend on the media types involved. A session handle is + required to control the local user functions within the session (or remote + users if the current account has rights to do so). Currently creating a + session automatically connects to the audio media, there is no need to call + Session.Connect at this time, this is reserved for future use. + + Handle returned from successful Connector ‘create’ request + This is the URI of the terminating point of the session (ie who/what is being called) + This is the display name of the entity being called (user or channel) + Only needs to be supplied when the target URI is password protected + This indicates the format of the password as passed in. This can either be + “ClearText” or “SHA1UserName”. If this element does not exist, it is assumed to be “ClearText”. If it is + “SHA1UserName”, the password as passed in is the SHA1 hash of the password and username concatenated together, + then base64 encoded, with the final “=” character stripped off. + + + + + + + Used to accept a call + + SessionHandle such as received from SessionNewEvent + "default" + + + + + This command is used to start the audio render process, which will then play + the passed in file through the selected audio render device. This command + should not be issued if the user is on a call. + + The fully qualified path to the sound file. + True if the file is to be played continuously and false if it is should be played once. + + + + + This command is used to stop the audio render process. + + The fully qualified path to the sound file issued in the start render command. + + + + + This is used to ‘end’ an established session (i.e. hang-up or disconnect). + + Handle returned from successful Session ‘create’ request or a SessionNewEvent + + + + + Set the combined speaking and listening position in 3D space. + + Handle returned from successful Session ‘create’ request or a SessionNewEvent + Speaking position + Listening position + + + + + Set User Volume for a particular user. Does not affect how other users hear that user. + + Handle returned from successful Session ‘create’ request or a SessionNewEvent + + The level of the audio, a number between -100 and 100 where 0 represents ‘normal’ speaking volume + + + + + List of audio input devices + + + + + List of audio output devices + + + + + Set audio test mode + + + + + Event for most mundane request reposnses. + + + + Response to Connector.Create request + + + Response to Aux.GetCaptureDevices request + + + Response to Aux.GetRenderDevices request + + + Audio Properties Events are sent after audio capture is started. + These events are used to display a microphone VU meter + + + Response to Account.Login request + + + This event message is sent whenever the login state of the + particular Account has transitioned from one value to another + + + Enable logging + + + The folder where any logs will be created + + + This will be prepended to beginning of each log file + + + The suffix or extension to be appended to each log file + + + + 0: NONE - No logging + 1: ERROR - Log errors only + 2: WARNING - Log errors and warnings + 3: INFO - Log errors, warnings and info + 4: DEBUG - Log errors, warnings, info and debug + + + + + Constructor for default logging settings + + + + Audio Properties Events are sent after audio capture is started. These events are used to display a microphone VU meter + + + Positional vector of the users position + + + Velocity vector of the position + + + At Orientation (X axis) of the position + + + Up Orientation (Y axis) of the position + + + Left Orientation (Z axis) of the position + + + + Extract the avatar UUID encoded in a SIP URI + + + + + + + Represents a single Voice Session to the Vivox service. + + + + + Close this session. + + + + + Look up an existing Participants in this session + + + + + + diff --git a/bin/OpenMetaverse.dll b/bin/OpenMetaverse.dll new file mode 100755 index 0000000000..9854fe087c Binary files /dev/null and b/bin/OpenMetaverse.dll differ diff --git a/bin/OpenMetaverse.dll.config b/bin/OpenMetaverse.dll.config new file mode 100644 index 0000000000..6b7b99930c --- /dev/null +++ b/bin/OpenMetaverse.dll.config @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bin/OpenMetaverseTypes.XML b/bin/OpenMetaverseTypes.XML new file mode 100644 index 0000000000..a0c9135601 --- /dev/null +++ b/bin/OpenMetaverseTypes.XML @@ -0,0 +1,2602 @@ + + + + OpenMetaverseTypes + + + + + Same as Queue except Dequeue function blocks until there is an object to return. + Note: This class does not need to be synchronized + + + + + Create new BlockingQueue. + + The System.Collections.ICollection to copy elements from + + + + Create new BlockingQueue. + + The initial number of elements that the queue can contain + + + + Create new BlockingQueue. + + + + + BlockingQueue Destructor (Close queue, resume any waiting thread). + + + + + Remove all objects from the Queue. + + + + + Remove all objects from the Queue, resume all dequeue threads. + + + + + Removes and returns the object at the beginning of the Queue. + + Object in queue. + + + + Removes and returns the object at the beginning of the Queue. + + time to wait before returning + Object in queue. + + + + Removes and returns the object at the beginning of the Queue. + + time to wait before returning (in milliseconds) + Object in queue. + + + + Adds an object to the end of the Queue + + Object to put in queue + + + + Open Queue. + + + + + Gets flag indicating if queue has been closed. + + + + + Copy constructor + + Circular queue to copy + + + + An 8-bit color structure including an alpha channel + + + + Red + + + Green + + + Blue + + + Alpha + + + + + + + + + + + + + Builds a color from a byte array + + Byte array containing a 16 byte color + Beginning position in the byte array + True if the byte array stores inverted values, + otherwise false. For example the color black (fully opaque) inverted + would be 0xFF 0xFF 0xFF 0x00 + + + + Returns the raw bytes for this vector + + Byte array containing a 16 byte color + Beginning position in the byte array + True if the byte array stores inverted values, + otherwise false. For example the color black (fully opaque) inverted + would be 0xFF 0xFF 0xFF 0x00 + True if the alpha value is inverted in + addition to whatever the inverted parameter is. Setting inverted true + and alphaInverted true will flip the alpha value back to non-inverted, + but keep the other color bytes inverted + A 16 byte array containing R, G, B, and A + + + + Copy constructor + + Color to copy + + + + IComparable.CompareTo implementation + + Sorting ends up like this: |--Grayscale--||--Color--|. + Alpha is only used when the colors are otherwise equivalent + + + + Builds a color from a byte array + + Byte array containing a 16 byte color + Beginning position in the byte array + True if the byte array stores inverted values, + otherwise false. For example the color black (fully opaque) inverted + would be 0xFF 0xFF 0xFF 0x00 + True if the alpha value is inverted in + addition to whatever the inverted parameter is. Setting inverted true + and alphaInverted true will flip the alpha value back to non-inverted, + but keep the other color bytes inverted + + + + Writes the raw bytes for this color to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 16 bytes before the end of the array + + + + Serializes this color into four bytes in a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 4 bytes before the end of the array + True to invert the output (1.0 becomes 0 + instead of 255) + + + + Writes the raw bytes for this color to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 16 bytes before the end of the array + + + + Ensures that values are in range 0-1 + + + + + Create an RGB color from a hue, saturation, value combination + + Hue + Saturation + Value + An fully opaque RGB color (alpha is 1.0) + + + + Performs linear interpolation between two colors + + Color to start at + Color to end at + Amount to interpolate + The interpolated color + + + A Color4 with zero RGB values and fully opaque (alpha 1.0) + + + A Color4 with full RGB values (1.0) and fully opaque (alpha 1.0) + + + + Attribute class that allows extra attributes to be attached to ENUMs + + + + Text used when presenting ENUM to user + + + Default initializer + + + Text used when presenting ENUM to user + + + + The different types of grid assets + + + + Unknown asset type + + + Texture asset, stores in JPEG2000 J2C stream format + + + Sound asset + + + Calling card for another avatar + + + Link to a location in world + + + Collection of textures and parameters that can be + worn by an avatar + + + Primitive that can contain textures, sounds, + scripts and more + + + Notecard asset + + + Holds a collection of inventory items + + + Root inventory folder + + + Linden scripting language script + + + LSO bytecode for a script + + + Uncompressed TGA texture + + + Collection of textures and shape parameters that can + be worn + + + Trash folder + + + Snapshot folder + + + Lost and found folder + + + Uncompressed sound + + + Uncompressed TGA non-square image, not to be used as a + texture + + + Compressed JPEG non-square image, not to be used as a + texture + + + Animation + + + Sequence of animations, sounds, chat, and pauses + + + Simstate file + + + Contains landmarks for favorites + + + Asset is a link to another inventory item + + + Asset is a link to another inventory folder + + + Beginning of the range reserved for ensembles + + + End of the range reserved for ensembles + + + Folder containing inventory links to wearables and attachments + that are part of the current outfit + + + Folder containing inventory items or links to + inventory items of wearables and attachments + together make a full outfit + + + Root folder for the folders of type OutfitFolder + + + Linden mesh format + + + Marketplace direct delivery inbox ("Received Items") + + + Marketplace direct delivery outbox + + + + + + + Inventory Item Types, eg Script, Notecard, Folder, etc + + + + Unknown + + + Texture + + + Sound + + + Calling Card + + + Landmark + + + Notecard + + + + + + Folder + + + + + + an LSL Script + + + + + + + + + + + + + + + + + + + + + + Item Sale Status + + + + Not for sale + + + The original is for sale + + + Copies are for sale + + + The contents of the object are for sale + + + + Types of wearable assets + + + + Body shape + + + Skin textures and attributes + + + Hair + + + Eyes + + + Shirt + + + Pants + + + Shoes + + + Socks + + + Jacket + + + Gloves + + + Undershirt + + + Underpants + + + Skirt + + + Alpha mask to hide parts of the avatar + + + Tattoo + + + Physics + + + Invalid wearable asset + + + + Identifier code for primitive types + + + + None + + + A Primitive + + + A Avatar + + + Linden grass + + + Linden tree + + + A primitive that acts as the source for a particle stream + + + A Linden tree + + + + Primary parameters for primitives such as Physics Enabled or Phantom + + + + Deprecated + + + Whether physics are enabled for this object + + + + + + + + + + + + + + + + + + + + + Whether this object contains an active touch script + + + + + + Whether this object can receive payments + + + Whether this object is phantom (no collisions) + + + + + + + + + + + + + + + Deprecated + + + + + + + + + + + + Deprecated + + + + + + + + + + + + + + + Server flag, will not be sent to clients. Specifies that + the object is destroyed when it touches a simulator edge + + + Server flag, will not be sent to clients. Specifies that + the object will be returned to the owner's inventory when it + touches a simulator edge + + + Server flag, will not be sent to clients. + + + Server flag, will not be sent to client. Specifies that + the object is hovering/flying + + + + + + + + + + + + + + + + Sound flags for sounds attached to primitives + + + + + + + + + + + + + + + + + + + + + + + + + + Material type for a primitive + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Used in a helper function to roughly determine prim shape + + + + + Extra parameters for primitives, these flags are for features that have + been added after the original ObjectFlags that has all eight bits + reserved already + + + + Whether this object has flexible parameters + + + Whether this object has light parameters + + + Whether this object is a sculpted prim + + + Whether this object is a light image map + + + Whether this object is a mesh + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Attachment points for objects on avatar bodies + + + Both InventoryObject and InventoryAttachment types can be attached + + + + Right hand if object was not previously attached + + + Chest + + + Skull + + + Left shoulder + + + Right shoulder + + + Left hand + + + Right hand + + + Left foot + + + Right foot + + + Spine + + + Pelvis + + + Mouth + + + Chin + + + Left ear + + + Right ear + + + Left eyeball + + + Right eyeball + + + Nose + + + Right upper arm + + + Right forearm + + + Left upper arm + + + Left forearm + + + Right hip + + + Right upper leg + + + Right lower leg + + + Left hip + + + Left upper leg + + + Left lower leg + + + Stomach + + + Left pectoral + + + Right pectoral + + + HUD Center position 2 + + + HUD Top-right + + + HUD Top + + + HUD Top-left + + + HUD Center + + + HUD Bottom-left + + + HUD Bottom + + + HUD Bottom-right + + + Neck + + + Avatar Center + + + + Tree foliage types + + + + Pine1 tree + + + Oak tree + + + Tropical Bush1 + + + Palm1 tree + + + Dogwood tree + + + Tropical Bush2 + + + Palm2 tree + + + Cypress1 tree + + + Cypress2 tree + + + Pine2 tree + + + Plumeria + + + Winter pinetree1 + + + Winter Aspen tree + + + Winter pinetree2 + + + Eucalyptus tree + + + Fern + + + Eelgrass + + + Sea Sword + + + Kelp1 plant + + + Beach grass + + + Kelp2 plant + + + + Grass foliage types + + + + + + + + + + + + + + + + + + + + + + + Action associated with clicking on an object + + + + Touch object + + + Sit on object + + + Purchase object or contents + + + Pay the object + + + Open task inventory + + + Play parcel media + + + Open parcel media + + + + Type of physics representation used for this prim in the simulator + + + + Use prim physics form this object + + + No physics, prim doesn't collide + + + Use convex hull represantion of this prim + + + For thread safety + + + For thread safety + + + + Purges expired objects from the cache. Called automatically by the purge timer. + + + + + A thread-safe lockless queue that supports multiple readers and + multiple writers + + + + Queue head + + + Queue tail + + + Queue item count + + + + Constructor + + + + + Enqueue an item + + Item to enqeue + + + + Try to dequeue an item + + Dequeued item if the dequeue was successful + True if an item was successfully deqeued, otherwise false + + + Gets the current number of items in the queue. Since this + is a lockless collection this value should be treated as a close + estimate + + + + Provides a node container for data in a singly linked list + + + + Pointer to the next node in list + + + The data contained by the node + + + + Constructor + + + + + Constructor + + + + + Convert this matrix to euler rotations + + X euler angle + Y euler angle + Z euler angle + + + + Convert this matrix to a quaternion rotation + + A quaternion representation of this rotation matrix + + + + Construct a matrix from euler rotation values in radians + + X euler angle in radians + Y euler angle in radians + Z euler angle in radians + + + + Get a formatted string representation of the vector + + A string representation of the vector + + + A 4x4 matrix containing all zeroes + + + A 4x4 identity matrix + + + X value + + + Y value + + + Z value + + + W value + + + + Build a quaternion from normalized float values + + X value from -1.0 to 1.0 + Y value from -1.0 to 1.0 + Z value from -1.0 to 1.0 + + + + Constructor, builds a quaternion object from a byte array + + Byte array containing four four-byte floats + Offset in the byte array to start reading at + Whether the source data is normalized or + not. If this is true 12 bytes will be read, otherwise 16 bytes will + be read. + + + + Normalizes the quaternion + + + + + Builds a quaternion object from a byte array + + The source byte array + Offset in the byte array to start reading at + Whether the source data is normalized or + not. If this is true 12 bytes will be read, otherwise 16 bytes will + be read. + + + + Normalize this quaternion and serialize it to a byte array + + A 12 byte array containing normalized X, Y, and Z floating + point values in order using little endian byte ordering + + + + Writes the raw bytes for this quaternion to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 12 bytes before the end of the array + + + + Convert this quaternion to euler angles + + X euler angle + Y euler angle + Z euler angle + + + + Convert this quaternion to an angle around an axis + + Unit vector describing the axis + Angle around the axis, in radians + + + + Returns the conjugate (spatial inverse) of a quaternion + + + + + Build a quaternion from an axis and an angle of rotation around + that axis + + + + + Build a quaternion from an axis and an angle of rotation around + that axis + + Axis of rotation + Angle of rotation + + + + Creates a quaternion from a vector containing roll, pitch, and yaw + in radians + + Vector representation of the euler angles in + radians + Quaternion representation of the euler angles + + + + Creates a quaternion from roll, pitch, and yaw euler angles in + radians + + X angle in radians + Y angle in radians + Z angle in radians + Quaternion representation of the euler angles + + + + Conjugates and renormalizes a vector + + + + + Spherical linear interpolation between two quaternions + + + + + Get a string representation of the quaternion elements with up to three + decimal digits and separated by spaces only + + Raw string representation of the quaternion + + + A quaternion with a value of 0,0,0,1 + + + + Determines the appropriate events to set, leaves the locks, and sets the events. + + + + + A routine for lazily creating a event outside the lock (so if errors + happen they are outside the lock and that we don't do much work + while holding a spin lock). If all goes well, reenter the lock and + set 'waitEvent' + + + + + Waits on 'waitEvent' with a timeout of 'millisceondsTimeout. + Before the wait 'numWaiters' is incremented and is restored before leaving this routine. + + + + + A hierarchical token bucket for bandwidth throttling. See + http://en.wikipedia.org/wiki/Token_bucket for more information + + + + Parent bucket to this bucket, or null if this is a root + bucket + + + Size of the bucket in bytes. If zero, the bucket has + infinite capacity + + + Rate that the bucket fills, in bytes per millisecond. If + zero, the bucket always remains full + + + Number of tokens currently in the bucket + + + Time of the last drip, in system ticks + + + + Default constructor + + Parent bucket if this is a child bucket, or + null if this is a root bucket + Maximum size of the bucket in bytes, or + zero if this bucket has no maximum capacity + Rate that the bucket fills, in bytes per + second. If zero, the bucket always remains full + + + + Remove a given number of tokens from the bucket + + Number of tokens to remove from the bucket + True if the requested number of tokens were removed from + the bucket, otherwise false + + + + Remove a given number of tokens from the bucket + + Number of tokens to remove from the bucket + True if tokens were added to the bucket + during this call, otherwise false + True if the requested number of tokens were removed from + the bucket, otherwise false + + + + Add tokens to the bucket over time. The number of tokens added each + call depends on the length of time that has passed since the last + call to Drip + + True if tokens were added to the bucket, otherwise false + + + + The parent bucket of this bucket, or null if this bucket has no + parent. The parent bucket will limit the aggregate bandwidth of all + of its children buckets + + + + + Maximum burst rate in bytes per second. This is the maximum number + of tokens that can accumulate in the bucket at any one time + + + + + The speed limit of this bucket in bytes per second. This is the + number of tokens that are added to the bucket per second + + Tokens are added to the bucket any time + is called, at the granularity of + the system tick interval (typically around 15-22ms) + + + + The number of bytes that can be sent at this moment. This is the + current number of tokens in the bucket + If this bucket has a parent bucket that does not have + enough tokens for a request, will + return false regardless of the content of this bucket + + + + Used for converting degrees to radians + + + Used for converting radians to degrees + + + Provide a single instance of the CultureInfo class to + help parsing in situations where the grid assumes an en-us + culture + + + UNIX epoch in DateTime format + + + Provide a single instance of the MD5 class to avoid making + duplicate copies and handle thread safety + + + Provide a single instance of the SHA-1 class to avoid + making duplicate copies and handle thread safety + + + Provide a single instance of a random number generator + to avoid making duplicate copies and handle thread safety + + + + Clamp a given value between a range + + Value to clamp + Minimum allowable value + Maximum allowable value + A value inclusively between lower and upper + + + + Clamp a given value between a range + + Value to clamp + Minimum allowable value + Maximum allowable value + A value inclusively between lower and upper + + + + Clamp a given value between a range + + Value to clamp + Minimum allowable value + Maximum allowable value + A value inclusively between lower and upper + + + + Round a floating-point value to the nearest integer + + Floating point number to round + Integer + + + + Test if a single precision float is a finite number + + + + + Test if a double precision float is a finite number + + + + + Get the distance between two floating-point values + + First value + Second value + The distance between the two values + + + + Compute the MD5 hash for a byte array + + Byte array to compute the hash for + MD5 hash of the input data + + + + Compute the SHA1 hash for a byte array + + Byte array to compute the hash for + SHA1 hash of the input data + + + + Calculate the SHA1 hash of a given string + + The string to hash + The SHA1 hash as a string + + + + Compute the SHA256 hash for a byte array + + Byte array to compute the hash for + SHA256 hash of the input data + + + + Calculate the SHA256 hash of a given string + + The string to hash + The SHA256 hash as a string + + + + Calculate the MD5 hash of a given string + + The password to hash + An MD5 hash in string format, with $1$ prepended + + + + Calculate the MD5 hash of a given string + + The string to hash + The MD5 hash as a string + + + + Generate a random double precision floating point value + + Random value of type double + + + + Get the current running platform + + Enumeration of the current platform we are running on + + + + Get the current running runtime + + Enumeration of the current runtime we are running on + + + + Convert the first two bytes starting in the byte array in + little endian ordering to a signed short integer + + An array two bytes or longer + A signed short integer, will be zero if a short can't be + read at the given position + + + + Convert the first two bytes starting at the given position in + little endian ordering to a signed short integer + + An array two bytes or longer + Position in the array to start reading + A signed short integer, will be zero if a short can't be + read at the given position + + + + Convert the first four bytes starting at the given position in + little endian ordering to a signed integer + + An array four bytes or longer + Position to start reading the int from + A signed integer, will be zero if an int can't be read + at the given position + + + + Convert the first four bytes of the given array in little endian + ordering to a signed integer + + An array four bytes or longer + A signed integer, will be zero if the array contains + less than four bytes + + + + Convert the first eight bytes of the given array in little endian + ordering to a signed long integer + + An array eight bytes or longer + A signed long integer, will be zero if the array contains + less than eight bytes + + + + Convert the first eight bytes starting at the given position in + little endian ordering to a signed long integer + + An array eight bytes or longer + Position to start reading the long from + A signed long integer, will be zero if a long can't be read + at the given position + + + + Convert the first two bytes starting at the given position in + little endian ordering to an unsigned short + + Byte array containing the ushort + Position to start reading the ushort from + An unsigned short, will be zero if a ushort can't be read + at the given position + + + + Convert two bytes in little endian ordering to an unsigned short + + Byte array containing the ushort + An unsigned short, will be zero if a ushort can't be + read + + + + Convert the first four bytes starting at the given position in + little endian ordering to an unsigned integer + + Byte array containing the uint + Position to start reading the uint from + An unsigned integer, will be zero if a uint can't be read + at the given position + + + + Convert the first four bytes of the given array in little endian + ordering to an unsigned integer + + An array four bytes or longer + An unsigned integer, will be zero if the array contains + less than four bytes + + + + Convert the first eight bytes of the given array in little endian + ordering to an unsigned 64-bit integer + + An array eight bytes or longer + An unsigned 64-bit integer, will be zero if the array + contains less than eight bytes + + + + Convert four bytes in little endian ordering to a floating point + value + + Byte array containing a little ending floating + point value + Starting position of the floating point value in + the byte array + Single precision value + + + + Convert an integer to a byte array in little endian format + + The integer to convert + A four byte little endian array + + + + Convert an integer to a byte array in big endian format + + The integer to convert + A four byte big endian array + + + + Convert a 64-bit integer to a byte array in little endian format + + The value to convert + An 8 byte little endian array + + + + Convert a 64-bit unsigned integer to a byte array in little endian + format + + The value to convert + An 8 byte little endian array + + + + Convert a floating point value to four bytes in little endian + ordering + + A floating point value + A four byte array containing the value in little endian + ordering + + + + Converts an unsigned integer to a hexadecimal string + + An unsigned integer to convert to a string + A hexadecimal string 10 characters long + 0x7fffffff + + + + Convert a variable length UTF8 byte array to a string + + The UTF8 encoded byte array to convert + The decoded string + + + + Converts a byte array to a string containing hexadecimal characters + + The byte array to convert to a string + The name of the field to prepend to each + line of the string + A string containing hexadecimal characters on multiple + lines. Each line is prepended with the field name + + + + Converts a byte array to a string containing hexadecimal characters + + The byte array to convert to a string + Number of bytes in the array to parse + A string to prepend to each line of the hex + dump + A string containing hexadecimal characters on multiple + lines. Each line is prepended with the field name + + + + Convert a string to a UTF8 encoded byte array + + The string to convert + A null-terminated UTF8 byte array + + + + Converts a string containing hexadecimal characters to a byte array + + String containing hexadecimal characters + If true, gracefully handles null, empty and + uneven strings as well as stripping unconvertable characters + The converted byte array + + + + Returns true is c is a hexadecimal digit (A-F, a-f, 0-9) + + Character to test + true if hex digit, false if not + + + + Converts 1 or 2 character string into equivalant byte value + + 1 or 2 character string + byte + + + + Convert a float value to a byte given a minimum and maximum range + + Value to convert to a byte + Minimum value range + Maximum value range + A single byte representing the original float value + + + + Convert a byte to a float value given a minimum and maximum range + + Byte array to get the byte from + Position in the byte array the desired byte is at + Minimum value range + Maximum value range + A float value inclusively between lower and upper + + + + Convert a byte to a float value given a minimum and maximum range + + Byte to convert to a float value + Minimum value range + Maximum value range + A float value inclusively between lower and upper + + + + Attempts to parse a floating point value from a string, using an + EN-US number format + + String to parse + Resulting floating point number + True if the parse was successful, otherwise false + + + + Attempts to parse a floating point value from a string, using an + EN-US number format + + String to parse + Resulting floating point number + True if the parse was successful, otherwise false + + + + Tries to parse an unsigned 32-bit integer from a hexadecimal string + + String to parse + Resulting integer + True if the parse was successful, otherwise false + + + + Returns text specified in EnumInfo attribute of the enumerator + To add the text use [EnumInfo(Text = "Some nice text here")] before declaration + of enum values + + Enum value + Text representation of the enum + + + + Takes an AssetType and returns the string representation + + The source + The string version of the AssetType + + + + Translate a string name of an AssetType into the proper Type + + A string containing the AssetType name + The AssetType which matches the string name, or AssetType.Unknown if no match was found + + + + Convert an InventoryType to a string + + The to convert + A string representation of the source + + + + Convert a string into a valid InventoryType + + A string representation of the InventoryType to convert + A InventoryType object which matched the type + + + + Convert a SaleType to a string + + The to convert + A string representation of the source + + + + Convert a string into a valid SaleType + + A string representation of the SaleType to convert + A SaleType object which matched the type + + + + Converts a string used in LLSD to AttachmentPoint type + + String representation of AttachmentPoint to convert + AttachmentPoint enum + + + + Copy a byte array + + Byte array to copy + A copy of the given byte array + + + + Packs to 32-bit unsigned integers in to a 64-bit unsigned integer + + The left-hand (or X) value + The right-hand (or Y) value + A 64-bit integer containing the two 32-bit input values + + + + Unpacks two 32-bit unsigned integers from a 64-bit unsigned integer + + The 64-bit input integer + The left-hand (or X) output value + The right-hand (or Y) output value + + + + Convert an IP address object to an unsigned 32-bit integer + + IP address to convert + 32-bit unsigned integer holding the IP address bits + + + + Gets a unix timestamp for the current time + + An unsigned integer representing a unix timestamp for now + + + + Convert a UNIX timestamp to a native DateTime object + + An unsigned integer representing a UNIX + timestamp + A DateTime object containing the same time specified in + the given timestamp + + + + Convert a UNIX timestamp to a native DateTime object + + A signed integer representing a UNIX + timestamp + A DateTime object containing the same time specified in + the given timestamp + + + + Convert a native DateTime object to a UNIX timestamp + + A DateTime object you want to convert to a + timestamp + An unsigned integer representing a UNIX timestamp + + + + Swap two values + + Type of the values to swap + First value + Second value + + + + Try to parse an enumeration value from a string + + Enumeration type + String value to parse + Enumeration value on success + True if the parsing succeeded, otherwise false + + + + Swaps the high and low words in a byte. Converts aaaabbbb to bbbbaaaa + + Byte to swap the words in + Byte value with the words swapped + + + + Attempts to convert a string representation of a hostname or IP + address to a + + Hostname to convert to an IPAddress + Converted IP address object, or null if the conversion + failed + + + + Operating system + + + + Unknown + + + Microsoft Windows + + + Microsoft Windows CE + + + Linux + + + Apple OSX + + + + Runtime platform + + + + .NET runtime + + + Mono runtime: http://www.mono-project.com/ + + + + A 128-bit Universally Unique Identifier, used throughout the Second + Life networking protocol + + + + The System.Guid object this struct wraps around + + + + Constructor that takes a string UUID representation + + A string representation of a UUID, case + insensitive and can either be hyphenated or non-hyphenated + UUID("11f8aa9c-b071-4242-836b-13b7abe0d489") + + + + Constructor that takes a System.Guid object + + A Guid object that contains the unique identifier + to be represented by this UUID + + + + Constructor that takes a byte array containing a UUID + + Byte array containing a 16 byte UUID + Beginning offset in the array + + + + Constructor that takes an unsigned 64-bit unsigned integer to + convert to a UUID + + 64-bit unsigned integer to convert to a UUID + + + + Copy constructor + + UUID to copy + + + + IComparable.CompareTo implementation + + + + + Assigns this UUID from 16 bytes out of a byte array + + Byte array containing the UUID to assign this UUID to + Starting position of the UUID in the byte array + + + + Returns a copy of the raw bytes for this UUID + + A 16 byte array containing this UUID + + + + Writes the raw bytes for this UUID to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 16 bytes before the end of the array + + + + Calculate an LLCRC (cyclic redundancy check) for this UUID + + The CRC checksum for this UUID + + + + Create a 64-bit integer representation from the second half of this UUID + + An integer created from the last eight bytes of this UUID + + + + Generate a UUID from a string + + A string representation of a UUID, case + insensitive and can either be hyphenated or non-hyphenated + UUID.Parse("11f8aa9c-b071-4242-836b-13b7abe0d489") + + + + Generate a UUID from a string + + A string representation of a UUID, case + insensitive and can either be hyphenated or non-hyphenated + Will contain the parsed UUID if successful, + otherwise null + True if the string was successfully parse, otherwise false + UUID.TryParse("11f8aa9c-b071-4242-836b-13b7abe0d489", result) + + + + Combine two UUIDs together by taking the MD5 hash of a byte array + containing both UUIDs + + First UUID to combine + Second UUID to combine + The UUID product of the combination + + + + + + + + + + Return a hash code for this UUID, used by .NET for hash tables + + An integer composed of all the UUID bytes XORed together + + + + Comparison function + + An object to compare to this UUID + True if the object is a UUID and both UUIDs are equal + + + + Comparison function + + UUID to compare to + True if the UUIDs are equal, otherwise false + + + + Get a hyphenated string representation of this UUID + + A string representation of this UUID, lowercase and + with hyphens + 11f8aa9c-b071-4242-836b-13b7abe0d489 + + + + Equals operator + + First UUID for comparison + Second UUID for comparison + True if the UUIDs are byte for byte equal, otherwise false + + + + Not equals operator + + First UUID for comparison + Second UUID for comparison + True if the UUIDs are not equal, otherwise true + + + + XOR operator + + First UUID + Second UUID + A UUID that is a XOR combination of the two input UUIDs + + + + String typecasting operator + + A UUID in string form. Case insensitive, + hyphenated or non-hyphenated + A UUID built from the string representation + + + An UUID with a value of all zeroes + + + A cache of UUID.Zero as a string to optimize a common path + + + + A two-dimensional vector with floating-point values + + + + X value + + + Y value + + + + Test if this vector is equal to another vector, within a given + tolerance range + + Vector to test against + The acceptable magnitude of difference + between the two vectors + True if the magnitude of difference between the two vectors + is less than the given tolerance, otherwise false + + + + Test if this vector is composed of all finite numbers + + + + + IComparable.CompareTo implementation + + + + + Builds a vector from a byte array + + Byte array containing two four-byte floats + Beginning position in the byte array + + + + Returns the raw bytes for this vector + + An eight-byte array containing X and Y + + + + Writes the raw bytes for this vector to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 8 bytes before the end of the array + + + + Parse a vector from a string + + A string representation of a 2D vector, enclosed + in arrow brackets and separated by commas + + + + Interpolates between two vectors using a cubic equation + + + + + Get a formatted string representation of the vector + + A string representation of the vector + + + + Get a string representation of the vector elements with up to three + decimal digits and separated by spaces only + + Raw string representation of the vector + + + A vector with a value of 0,0 + + + A vector with a value of 1,1 + + + A vector with a value of 1,0 + + + A vector with a value of 0,1 + + + + A three-dimensional vector with floating-point values + + + + X value + + + Y value + + + Z value + + + + Constructor, builds a vector from a byte array + + Byte array containing three four-byte floats + Beginning position in the byte array + + + + Test if this vector is equal to another vector, within a given + tolerance range + + Vector to test against + The acceptable magnitude of difference + between the two vectors + True if the magnitude of difference between the two vectors + is less than the given tolerance, otherwise false + + + + IComparable.CompareTo implementation + + + + + Test if this vector is composed of all finite numbers + + + + + Builds a vector from a byte array + + Byte array containing a 12 byte vector + Beginning position in the byte array + + + + Returns the raw bytes for this vector + + A 12 byte array containing X, Y, and Z + + + + Writes the raw bytes for this vector to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 12 bytes before the end of the array + + + + Parse a vector from a string + + A string representation of a 3D vector, enclosed + in arrow brackets and separated by commas + + + + Calculate the rotation between two vectors + + Normalized directional vector (such as 1,0,0 for forward facing) + Normalized target vector + + + + Interpolates between two vectors using a cubic equation + + + + + Get a formatted string representation of the vector + + A string representation of the vector + + + + Get a string representation of the vector elements with up to three + decimal digits and separated by spaces only + + Raw string representation of the vector + + + + Cross product between two vectors + + + + + Explicit casting for Vector3d > Vector3 + + + + + + A vector with a value of 0,0,0 + + + A vector with a value of 1,1,1 + + + A unit vector facing forward (X axis), value 1,0,0 + + + A unit vector facing left (Y axis), value 0,1,0 + + + A unit vector facing up (Z axis), value 0,0,1 + + + + A three-dimensional vector with doubleing-point values + + + + X value + + + Y value + + + Z value + + + + Constructor, builds a vector from a byte array + + Byte array containing three eight-byte doubles + Beginning position in the byte array + + + + Test if this vector is equal to another vector, within a given + tolerance range + + Vector to test against + The acceptable magnitude of difference + between the two vectors + True if the magnitude of difference between the two vectors + is less than the given tolerance, otherwise false + + + + IComparable.CompareTo implementation + + + + + Test if this vector is composed of all finite numbers + + + + + Builds a vector from a byte array + + Byte array containing a 24 byte vector + Beginning position in the byte array + + + + Returns the raw bytes for this vector + + A 24 byte array containing X, Y, and Z + + + + Writes the raw bytes for this vector to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 24 bytes before the end of the array + + + + Parse a vector from a string + + A string representation of a 3D vector, enclosed + in arrow brackets and separated by commas + + + + Interpolates between two vectors using a cubic equation + + + + + Get a formatted string representation of the vector + + A string representation of the vector + + + + Get a string representation of the vector elements with up to three + decimal digits and separated by spaces only + + Raw string representation of the vector + + + + Cross product between two vectors + + + + + Implicit casting for Vector3 > Vector3d + + + + + + A vector with a value of 0,0,0 + + + A vector with a value of 1,1,1 + + + A unit vector facing forward (X axis), value of 1,0,0 + + + A unit vector facing left (Y axis), value of 0,1,0 + + + A unit vector facing up (Z axis), value of 0,0,1 + + + X value + + + Y value + + + Z value + + + W value + + + + Constructor, builds a vector from a byte array + + Byte array containing four four-byte floats + Beginning position in the byte array + + + + Test if this vector is equal to another vector, within a given + tolerance range + + Vector to test against + The acceptable magnitude of difference + between the two vectors + True if the magnitude of difference between the two vectors + is less than the given tolerance, otherwise false + + + + IComparable.CompareTo implementation + + + + + Test if this vector is composed of all finite numbers + + + + + Builds a vector from a byte array + + Byte array containing a 16 byte vector + Beginning position in the byte array + + + + Returns the raw bytes for this vector + + A 16 byte array containing X, Y, Z, and W + + + + Writes the raw bytes for this vector to a byte array + + Destination byte array + Position in the destination array to start + writing. Must be at least 16 bytes before the end of the array + + + + Get a string representation of the vector elements with up to three + decimal digits and separated by spaces only + + Raw string representation of the vector + + + A vector with a value of 0,0,0,0 + + + A vector with a value of 1,1,1,1 + + + A vector with a value of 1,0,0,0 + + + A vector with a value of 0,1,0,0 + + + A vector with a value of 0,0,1,0 + + + A vector with a value of 0,0,0,1 + + + diff --git a/bin/OpenMetaverseTypes.dll b/bin/OpenMetaverseTypes.dll new file mode 100755 index 0000000000..39b8f75c70 Binary files /dev/null and b/bin/OpenMetaverseTypes.dll differ diff --git a/bin/OpenSim.32BitLaunch.exe.config b/bin/OpenSim.32BitLaunch.exe.config new file mode 100644 index 0000000000..5b7807a8e3 --- /dev/null +++ b/bin/OpenSim.32BitLaunch.exe.config @@ -0,0 +1,75 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/OpenSim.32BitLaunch.pdb b/bin/OpenSim.32BitLaunch.pdb new file mode 100644 index 0000000000..5083dd5df5 Binary files /dev/null and b/bin/OpenSim.32BitLaunch.pdb differ diff --git a/bin/OpenSim.ConsoleClient.exe.config b/bin/OpenSim.ConsoleClient.exe.config new file mode 100644 index 0000000000..3a50408186 --- /dev/null +++ b/bin/OpenSim.ConsoleClient.exe.config @@ -0,0 +1,34 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/OpenSim.ConsoleClient.ini.example b/bin/OpenSim.ConsoleClient.ini.example new file mode 100644 index 0000000000..5223d1d262 --- /dev/null +++ b/bin/OpenSim.ConsoleClient.ini.example @@ -0,0 +1,14 @@ +[Startup] + ; Set here or use the -user command-line switch + ;user = Test + + ; Set here or use the -host command-line switch + ;host = localhost + + ; Set here or use the -port command-line switch + ;port = 8003 + + ; Set here or use the -pass command-line switch + ; Please be aware that this is not secure since the password is in the clear + ; we recommend the use of -pass wherever possible + ;pass = secret diff --git a/bin/OpenSim.exe.config b/bin/OpenSim.exe.config new file mode 100755 index 0000000000..b01191e140 --- /dev/null +++ b/bin/OpenSim.exe.config @@ -0,0 +1,71 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example new file mode 100644 index 0000000000..71e76dbe8e --- /dev/null +++ b/bin/OpenSim.ini.example @@ -0,0 +1,1162 @@ +;; This is the main configuration file for OpenSimulator. +;; If it's named OpenSim.ini then it will be loaded by OpenSimulator. +;; If it's named OpenSim.ini.example then you will need to copy it to +;; OpenSim.ini first (if that file does not already exist) +;; +;; Once you have copied OpenSim.ini.example to OpenSim.ini you will at least +;; need to set the constants in the [Const] section and pick an architecture +;; in the [Architecture] section at the end of this file. The architecture +;; will require a suitable .ini file in the config-include directory, either +;; StandaloneCommon.ini or GridCommon.ini which you can copy and modify from the +;; available .example files. +;; +;; The settings in this file are in the form " = ". For example, +;; save_crashes = false in the [Startup] section below. +;; +;; All settings are initially commented out and the default value used, as +;; found in OpenSimDefaults.ini. To change a setting, first uncomment it by +;; deleting the initial semicolon (;) and then change the value. This will +;; override the value in OpenSimDefaults.ini +;; +;; If you want to find out what configuration OpenSimulator has finished with +;; once all the configuration files are loaded then type "config show" on the +;; region console command line. +;; +;; +;; NOTES FOR DEVELOPERS REGARDING THE FORMAT OF THIS FILE +;; +;; All leading white space is ignored, but preserved. +;; +;; Double semicolons denote a text comment +;; +;; ;# denotes a configuration directive description +;; formatted as: +;; {option} {depends on} {question to ask} {choices} default value +;; Any text comments following the declaration, up to the next blank line. +;; will be copied to the generated file (NOTE: generation is not yet +;; implemented) +;; +;; A * in the choices list will allow an empty entry. +;; An empty question will set the default if the dependencies are +;; satisfied. +;; +;; ; denotes a commented out option. +;; Any options added to OpenSim.ini.example should be initially commented +;; out. + + +[Const] + ; For a grid these will usually be the externally accessible IP/DNS + ; name and use default public port 8002 and default private port 8003 + ; For a standalone this will usually be the externally accessible IP/DNS + ; name and use default public port 9000. The private port is not used + ; in the configuration for a standalone. + + ;# {BaseURL} {} {BaseURL} {"http://example.com" "http://127.0.0.1"} "http://127.0.0.1" + BaseURL = http://127.0.0.1 + + ;# {PublicPort} {} {PublicPort} {8002 9000} "8002" + PublicPort = "8002" + + ;# {PrivatePort} {} {PrivatePort} {8003} "8003" + PrivatePort = "8003" + + +[Startup] + ;# {ConsolePrompt} {} {ConsolePrompt} {} "Region (\R) " + ;; Console prompt + ;; Certain special characters can be used to customize the prompt + ;; Currently, these are + ;; \R - substitute region name + ;; \\ - substitute \ + ; ConsolePrompt = "Region (\R) " + + ;# {ConsoleHistoryFileEnabled} {} {Save console commands to a history file?} {true false} true + ;; Console commands can be saved to a file, so the command history persists after a restart. (default is false) + ; ConsoleHistoryFileEnabled = true + + ;# {ConsoleHistoryFile} {} {Filename in which to save history} {} OpenSimConsoleHistory.txt + ;; The history file can be just a filename (relative to OpenSim's bin/ directory + ;; or it can be a full path to somewhere else. (default is OpenSimConsoleHistory.txt in bin/) + ; ConsoleHistoryFile = "OpenSimConsoleHistory.txt" + + ;# {ConsoleHistoryFileLines} {} {How many lines of history to save?} {} 100 + ;; How many lines of command history should we keep? (default is 100) + ; ConsoleHistoryFileLines = 100 + + ;# {save_crashes} {} {Save crashes to disk?} {true false} false + ;; Set this to true if you want to log crashes to disk + ;; this can be useful when submitting bug reports. + ;; However, this will only log crashes within OpenSimulator that cause the + ;; entire program to exit + ;; It will not log crashes caused by virtual machine failures, which + ;; includes mono and ODE failures. + ;; You will need to capture these native stack traces by recording the + ;; session log itself. + ; save_crashes = false + + ;# {crash_dir} {save_crashes:true} {Directory to save crashes to?} {} crashes + ;; Directory to save crashes to if above is enabled + ;; (default is /opensimdir/crashes/*.txt or C:\opensim\crashes\*.txt) + ; crash_dir = "crashes" + + ;# {PIDFile} {} {Path to PID file?} {} + ;; Place to create a PID file + ; PIDFile = "/tmp/OpenSim.exe.pid" + + ;# {RegistryLocation} {} {Addins Registry Location} {} + ; Set path to directory for addin registry if you want addins outside of bin. + ; Information about the registered repositories and installed plugins will + ; be stored here. The OpenSim.exe process must have R/W access to the location. + ; RegistryLocation = "." + + ;# {ConfigDirectory} {} {Set path to directory for modular ini files} {} + ; Used by region module addins. You can set this to outside bin, so that addin + ; configurations will survive updates. The OpenSim.exe process must have R/W access + ; to the location. + ; ConfigDirectory = "." + + ;# {region_info_source} {} {Where to load region from?} {filesystem web} filesystem + ;; Determine where OpenSimulator looks for the files which tell it + ;; which regions to server + ;; Default is "filesystem" + ; region_info_source = "filesystem" + ; region_info_source = "web" + + ;# {regionload_regionsdir} {region_info_source} {Location of file?} {} Regions + ;; Determines where the region XML files are stored if you are loading + ;; these from the filesystem. + ;; Defaults to bin/Regions in your OpenSimulator installation directory + ; regionload_regionsdir="C:\somewhere\xmlfiles\" + + ;# {regionload_webserver_url} {region_info_source} {URL to load region from?} {} + ;; Determines the page from which regions xml is retrieved if you are + ;; loading these from the web. + ;; The XML here has the same format as it does on the filesystem + ;; (including the tag), except that everything is also enclosed + ;; in a tag. + ; regionload_webserver_url = "http://example.com/regions.xml"; + + ;# {allow_regionless} {} {Allow simulator to start up with no regions configured.} {true false} false + ;; Allow the simulator to start up if there are no region configuration available + ;; from the selected region_info_source. + ; allow_regionless = false + + ;# {MaxPrimUndos} {} {Maximum number of undos avialable for position, rotation and scale changes of each prim} {} 20 + ;; Increasing the number of undos available number will increase memory usage. + MaxPrimUndos = 20 + + ;# {NonPhysicalPrimMin} {} {Minimum size of nonphysical prims?} {} 0.001 + ;; Minimum size for non-physical prims. Affects resizing of existing + ;; prims. This can be overridden in the region config file (as + ;; NonPhysicalPrimMin!). + ; NonPhysicalPrimMin = 0.001 + + ;# {NonPhysicalPrimMax} {} {Maximum size of nonphysical prims?} {} 256 + ;; Maximum size for non-physical prims. Affects resizing of existing + ;; prims. This can be overridden in the region config file (as + ;; NonPhysicalPrimMax!). + ; NonPhysicalPrimMax = 256 + + ;# {PhysicalPrimMin} {} {Minimum size of physical prims?} {} 0.01 + ;; Minimum size where a prim can be physical. Affects resizing of + ;; existing prims. This can be overridden in the region config file. + ; PhysicalPrimMin = 0.01 + + ;# {PhysicalPrimMax} {} {Maximum size of physical prims?} {} 64 + ;; Maximum size where a prim can be physical. Affects resizing of + ;; existing prims. This can be overridden in the region config file. + ; PhysicalPrimMax = 64 + + ;# {ClampPrimSize} {} {Clamp viewer rezzed prims to max sizes?} {true false} false + ;; If a viewer attempts to rez a prim larger than the non-physical or + ;; physical prim max, clamp the dimensions to the appropriate maximum + ;; This can be overridden in the region config file. + ; ClampPrimSize = false + + ;# {LinksetPrims} {} {Max prims an object will hold?} {} 0 + ;; Maximum number of prims allowable in a linkset. Affects creating new + ;; linksets. Ignored if less than or equal to zero. + ;; This can be overridden in the region config file. + ; LinksetPrims = 0 + + ;# {AllowScriptCrossing} {} {Allow scripts to cross into this region} {true false} true + ;; Allow scripts to keep running when they cross region boundaries, rather + ;; than being restarted. State is reloaded on the destination region. + ;; This only applies when crossing to a region running in a different + ;; simulator. + ;; For crossings where the regions are on the same simulator the script is + ;; always kept running. + ; AllowScriptCrossing = true + + ;# {TrustBinaries} {AllowScriptCrossing:true} {Accept compiled binary script code? (DANGEROUS!)} {true false} false + ;; Allow compiled script binary code to cross region boundaries. + ;; If you set this to "true", any region that can teleport to you can + ;; inject ARBITRARY BINARY CODE into your system. Use at your own risk. + ;; YOU HAVE BEEN WARNED!!! + ; TrustBinaries = false + + ;# {CombineContiguousRegions} {} {Create megaregions where possible? (Do not use with existing content or varregions!)} {true false} false + ;; Combine all contiguous regions into one large megaregion + ;; Order your regions from South to North, West to East in your regions.ini + ;; and then set this to true + ;; Warning! Don't use this with regions that have existing content!, + ;; This will likely break them + ;; Also, this setting should be set to false for varregions as they are proper larger single regions rather than combined smaller regions. + ; CombineContiguousRegions = false + + ;# {InworldRestartShutsDown} {} {Shutdown instance on region restart?} {true false} false + ;; If you have only one region in an instance, or to avoid the many bugs + ;; that you can trigger in modules by restarting a region, set this to + ;; true to make the entire instance exit instead of restarting the region. + ;; This is meant to be used on systems where some external system like + ;; Monit will restart any instance that exits, thereby making the shutdown + ;; into a restart. + ; InworldRestartShutsDown = false + + ;; Persistence of changed objects happens during regular sweeps. The + ;; following control that behaviour to prevent frequently changing objects + ;; from heavily loading the region data store. + ;; If both of these values are set to zero then persistence of all changed + ;; objects will happen on every sweep. + + ;# {MinimumTimeBeforePersistenceConsidered} {} {Time before un-changed object may be persisted} {} 60 + ;; Objects will be considered for persistance in the next sweep when they + ;; have not changed for this number of seconds. + ; MinimumTimeBeforePersistenceConsidered = 60 + + ;# {MaximumTimeBeforePersistenceConsidered} {} {Time before changed objects may be persisted?} {} 600 + ;; Objects will always be considered for persistance in the next sweep + ;; if the first change occurred this number of seconds ago. + ; MaximumTimeBeforePersistenceConsidered = 600 + + ;# {physical_prim} {} {Allow prims to be physical?} {true false} true + ;; if you would like to allow prims to be physical and move by physics + ;; with the physical checkbox in the client set this to true. + ; physical_prim = true + + ;; Select a mesher here. + ;; + ;; Meshmerizer properly handles complex prims by using triangle meshes. + ;; Note that only the ODE physics engine currently deals with meshed + ;; prims in a satisfactory way. + + ;# {meshing} {} {Select mesher} {Meshmerizer ZeroMesher} Meshmerizer + ;; ZeroMesher is faster but leaves the physics engine to model the mesh + ;; using the basic shapes that it supports. + ;; Usually this is only a box. + ;; Default is Meshmerizer + ; meshing = Meshmerizer + ; meshing = ZeroMesher + + ;; Choose one of the physics engines below + ;# {physics} {} {Select physics engine} {OpenDynamicsEngine BulletSim basicphysics POS} BulletSim + ;; BulletSim is the default physics engine. It provides the best performance and most functionality. + ;; BulletSim supports varregions. + ;; OpenDynamicsEngine was the previous default physics engine in OpenSimulator 0.7.6.1 and before. + ;; It continues to provide a workable physics implementation. It does not currently support varregions. + ;; basicphysics effectively does not model physics at all, making all objects phantom. + ;; Default is BulletSim + ; physics = OpenDynamicsEngine + ; physics = BulletSim + ; physics = basicphysics + ; physics = POS + + ;# {DefaultScriptEngine} {} {Default script engine} {XEngine} XEngine + ;; Default script engine to use. Currently, we only have XEngine + ; DefaultScriptEngine = "XEngine" + + ;# {HttpProxy} {} {Proxy URL for llHTTPRequest and dynamic texture loading} {} http://proxy.com:8080 + ;; Http proxy setting for llHTTPRequest and dynamic texture loading, if + ;; required + ; HttpProxy = "http://proxy.com:8080" + + ;# {HttpProxyExceptions} {HttpProxy} {Set of regular expressions defining URL that should not be proxied} {} + ;; If you're using HttpProxy, then you can set HttpProxyExceptions to a + ;; list of regular expressions for URLs that you don't want to go through + ;; the proxy. + ;; For example, servers inside your firewall. + ;; Separate patterns with a ';' + ; HttpProxyExceptions = ".mydomain.com;localhost" + + ;# {emailmodule} {} {Provide llEmail and llGetNextEmail functionality? (requires SMTP server)} {true false} false + ;; The email module requires some configuration. It needs an SMTP + ;; server to send mail through. + ; emailmodule = DefaultEmailModule + + ;# {SpawnPointRouting} {} {Set routing method for Telehub Spawnpoints} {closest random sequence} closest + ;; SpawnPointRouting adjusts the landing for incoming avatars. + ;; "closest" will place the avatar at the SpawnPoint located in the closest + ;; available spot to the destination (typically map click/landmark). + ;; "random" will place the avatar on a randomly selected spawnpoint; + ;; "sequence" will place the avatar on the next sequential SpawnPoint + ; SpawnPointRouting = closest + + ;# {TelehubAllowLandmark} {} {Allow users with landmarks to override telehub routing} {true false} false + ;; TelehubAllowLandmark allows users with landmarks to override telehub + ;; routing and land at the landmark coordinates when set to true + ;; default is false + ; TelehubAllowLandmark = false + + +[AccessControl] + ;# {AllowedClients} {} {Bar (|) separated list of allowed clients} {} + ;; Bar (|) separated list of viewers which may gain access to the regions. + ;; One can use a substring of the viewer name to enable only certain + ;; versions + ;; Example: Agent uses the viewer "Imprudence 1.3.2.0" + ;; - "Imprudence" has access + ;; - "Imprudence 1.3" has access + ;; - "Imprudence 1.3.1" has no access + ; AllowedClients = + + ;# {BannedClients} {} {Bar (|) separated list of banned clients} {} + ;; Bar (|) separated list of viewers which may not gain access to the regions. + ;; One can use a Substring of the viewer name to disable only certain + ;; versions + ;; Example: Agent uses the viewer "Imprudence 1.3.2.0" + ;; - "Imprudence" has no access + ;; - "Imprudence 1.3" has no access + ;; - "Imprudence 1.3.1" has access + ; BannedClients = + + +[Map] + ;# {GenerateMaptiles} {} {Generate map tiles?} {true false} true + ;; Map tile options. + ;; If true, then maptiles are generated using the MapImageModule below. + ;; If false then the texture referenced by MaptileStaticUUID is used instead, which can also be overridden + ;; in individual region config file(s). If you do not want to upload map tiles at all, then you will need + ;; both to set this to false and comment out the [Modules] MapImageServiceModule setting in config-include/ + ; GenerateMaptiles = true + + ;# {MapImageModule} {} {The map image module to use} {MapImageModule Warp3DImageModule} MapImageModule + ;; The module to use in order to generate map images. + ;; MapImageModule is the default. Warp3DImageModule is an alternative experimental module that can + ;; generate better images. + ;MapImageModule = "MapImageModule" + + ;# {MaptileRefresh} {GenerateMaptiles} {Maptile refresh period?} {} 0 + ;; If desired, a running region can update the map tiles periodically + ;; to reflect building activity. This names no sense of you don't have + ;; prims on maptiles. Value is in seconds. + ; MaptileRefresh = 0 + + ;# {MaptileStaticUUID} {} {Asset ID for static map texture} {} 00000000-0000-0000-0000-000000000000 + ;; If not generating maptiles, use this static texture asset ID + ;; This may be overridden on a per region basis in Regions.ini + ; MaptileStaticUUID = "00000000-0000-0000-0000-000000000000" + + ;# {TextureOnMapTile} {} {Use terrain textures for map tiles?} {true false} true + ;; Use terrain texture for maptiles if true, use shaded green if false + ; TextureOnMapTile = true + + ;# {DrawPrimOnMapTile} {} {Draw prim shapes on map tiles?} {true false} false + ;; Draw objects on maptile. This step might take a long time if you've + ;; got a large number of objects, so you can turn it off here if you'd like. + ; DrawPrimOnMapTile = true + + ;# {TexturePrims} {} {Texture prims on map tiles?} {true false} true + ;; Texture the faces of the prims that are rendered on the map tiles. + ; TexturePrims = true + + ;# {TexturePrimSize} {} {Size of prims to texture faces?} {} 48 + ;; Only texture prims that have a diagonal size greater than this number + ; TexturePrimSize = 48 + + ;# {RenderMeshes} {} {Render meshes and sculpties on map tiles?} {true false} false + ;; Attempt to render meshes and sculpties on the map + ; RenderMeshes = false; + + +[Permissions] + ;# {permissionmodules} {} {Permission modules to use (may specify multiple modules, separated by comma} {} DefaultPermissionsModule + ;; Permission modules to use, separated by comma. + ;; Possible modules are DefaultPermissionsModule, PrimLimitsModule + ; permissionmodules = DefaultPermissionsModule + + ;# {serverside_object_permissions} {permissionmodules:DefaultPermissionsModule} {Activate permission handling by the sim?} {true false} true + ;; These are the parameters for the default permissions module + ;; + ;; If set to false, then, in theory, the server never carries out + ;; permission checks (allowing anybody to copy + ;; any item, etc. This may not yet be implemented uniformally. + ;; If set to true, then all permissions checks are carried out + ; serverside_object_permissions = true + + ;# {allow_grid_gods} {} {Allow grid gods?} {true false} false + ;; This allows users with a UserLevel of 200 or more to assume god + ;; powers in the regions in this simulator. + ; allow_grid_gods = false + + ;; This allows some control over permissions + ;; please note that this still doesn't duplicate SL, and is not intended to + ;# {region_owner_is_god} {} {Allow region owner gods} {true false} true + ;; Allow region owners to assume god powers in their regions + ; region_owner_is_god = true + + ;# {region_manager_is_god} {} {Allow region manager gods} {true false} false + ;; Allow region managers to assume god powers in regions they manage + ; region_manager_is_god = false + + ;# {parcel_owner_is_god} {} {Allow parcel owner gods} {true false} true + ;; Allow parcel owners to assume god powers in their parcels + ; parcel_owner_is_god = true + + ;# {simple_build_permissions} {} {Allow building in parcel by access list (no groups)} {true false} false + ;; More control over permissions + ;; This is definitely not SL! + ;; Provides a simple control for land owners to give build rights to + ;; specific avatars in publicly accessible parcels that disallow object + ;; creation in general. + ;; Owners specific avatars by adding them to the Access List of the parcel + ;; without having to use the Groups feature + ; simple_build_permissions = false + + +[Estates] + ; If these values are commented out then the user will be asked for estate details when required (this is the normal case). + ; If these values are uncommented then they will be used to create a default estate as necessary. + ; New regions will be automatically assigned to that default estate. + + ;# {DefaultEstateName} {} {Default name for estate?} {} My Estate + ;; Name for the default estate + ; DefaultEstateName = My Estate + + ;# {DefaultEstateOwnerName} {} {Default estate owner name?} {} FirstName LastName + ;; Name for default estate owner + ; DefaultEstateOwnerName = FirstName LastName + + + ; ** Standalone Estate Settings ** + ; The following parameters will only be used on a standalone system to + ; create an estate owner that does not already exist + + ;# {DefaultEstateOwnerUUID} {} {Default estate owner UUID?} {} 00000000-0000-0000-0000-000000000000 + ;; If DefaultEstateOwnerUUID is left at UUID.Zero (as below) then a random + ;; UUID will be assigned. This is normally what you want + ; DefaultEstateOwnerUUID = 00000000-0000-0000-0000-000000000000 + + ;# {DefaultEstateOwnerEMail} {} {Default estate owner email?} {} + ;; Email address for the default estate owner + ; DefaultEstateOwnerEMail = owner@domain.com + + ;# {DefaultEstateOwnerPassword} {} {Default estate owner password} {} + ;; Password for the default estate owner + ; DefaultEstateOwnerPassword = password + + +[SMTP] + ;; The SMTP server enabled the email module to send email to external + ;; destinations. + + ;# {enabled} {[Startup]emailmodule:DefaultEmailModule} {Enable SMTP service?} {true false} false + ;; Enable sending email via SMTP + ; enabled = false + + ;# {internal_object_host} {[Startup]emailmodule:DefaultEmailModule enabled:true} {Host name to treat as internal (object to object) email?} {} lsl.opensim.local + ; internal_object_host = lsl.opensim.local + + ;# {host_domain_header_from} {[Startup]emailmodule:DefaultEmailModule enabled:true} {From address to use in the sent email header?} {} 127.0.0.1 + ; host_domain_header_from = "127.0.0.1" + + ;# {email_pause_time} {[Startup]emailmodule:DefaultEmailModule enabled:true} {Period in seconds to delay after an email is sent.} {} 20 + ; email_pause_time = 20 + + ;# {email_max_size} {[Startup]emailmodule:DefaultEmailModule enabled:true} {Maximum total size of email in bytes.} {} 4096 + ; email_max_size = 4096 + + ;# {SMTP_SERVER_HOSTNAME} {[Startup]emailmodule:DefaultEmailModule enabled:true} {SMTP server name?} {} 127.0.0.1 + ; SMTP_SERVER_HOSTNAME = "127.0.0.1" + + ;# {SMTP_SERVER_PORT} {[Startup]emailmodule:DefaultEmailModule enabled:true} {SMTP server name?} {SMTP server port?} {} 25 + ; SMTP_SERVER_PORT = 25 + + ;# {SMTP_SERVER_LOGIN} {[Startup]emailmodule:DefaultEmailModule enabled:true} {SMTP server user name?} {} + ; SMTP_SERVER_LOGIN = "" + + ;# {SMTP_SERVER_PASSWORD} {[Startup]emailmodule:DefaultEmailModule enabled:true} {SMTP server password} {} + ; SMTP_SERVER_PASSWORD = "" + + +[Network] + ;# {ConsoleUser} {} {User name for console account} {} + ;; Configure the remote console user here. This will not actually be used + ;; unless you use -console=rest at startup. + ; ConsoleUser = "Test" + ;# {ConsolePass} {} {Password for console account} {} + ; ConsolePass = "secret" + ;# {console_port} {} {Port for console connections} {} 0 + ; console_port = 0 + + ;# {http_listener_port} {} {TCP Port for this simulator to listen on? (This must be unique to the simulator!)} {} 9000 + ;; Simulator HTTP port. This is not the region port, but the port the + ;; entire simulator listens on. This port uses the TCP protocol, while + ;; the region ports use UDP. + ; http_listener_port = 9000 + + ; By default, OpenSimulator does not allow scripts to make HTTP calls to addresses on the simulator's LAN. + ; See the OutboundDisallowForUserScripts parameter in OpenSimDefaults.ini for more information on this filter. + ; If you need to allow scripts to make some LAN calls use the OutboundDisallowForUserScriptsExcept parameter below. + ; We recommend that you do not override OutboundDisallowForUserScripts directly unless you are very sure about what you're doing. + ; + ; You can whitelist individual endpoints by IP or FQDN, e.g. + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003 + ; + ; You can specify multiple addresses by separating them with a bar. For example, + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003|myinternalserver:8000 + ; + ; If an address if given without a port number then port 80 is assumed + ; + ; You can also specify a network range in CIDR notation to whitelist, e.g. + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.0/24 + ; + ; to whitelist all ports on addresses 192.168.1.0 to 192.168.1.255 + ; To specify an individual IP address use the /32 netmask + ; + ; OutboundDisallowForUserScriptsExcept = 192.168.1.2/32 + ; + ; See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation for more information on CIDR notation + + ;# {ExternalHostNameForLSL} {} {Hostname to use for HTTP-IN URLs. This should be reachable from the internet.} {} + ;; Hostname to use in llRequestURL/llRequestSecureURL + ;; if not defined - default machine name is being used + ;; (on Windows this mean NETBIOS name - useably only inside local network) + ; ExternalHostNameForLSL = "127.0.0.1" + + ;# {shard} {} {Name to use for X-Secondlife-Shard header? (press enter if unsure)} {} OpenSim + ;; What is reported as the "X-Secondlife-Shard" + ;; Defaults to the user server url if not set + ;; The old default is "OpenSim", set here for compatibility + ;; The below is not commented for compatibility. + shard = "OpenSim" + + ;# {user_agent} {} {User agent to report to web servers?} {} OpenSim LSL (Mozilla Compatible) + ;; What is reported as the "User-Agent" when using llHTTPRequest + ;; Defaults to not sent if not set here. See the notes section in the wiki + ;; at http://wiki.secondlife.com/wiki/LlHTTPRequest for comments on adding + ;; " (Mozilla Compatible)" to the text where there are problems with a + ;; web server + ; user_agent = "OpenSim LSL (Mozilla Compatible)" + + ;; The follow 3 variables are for HTTP Basic Authentication for the Robust services. + ;; Use this if your central services in port 8003 need to be accessible on the Internet + ;; but you want to protect them from unauthorized access. The username and password + ;; here need to match the ones in the Robust service configuration. + ; AuthType = "BasicHttpAuthentication" + ; HttpAuthUsername = "some_username" + ; HttpAuthPassword = "some_password" + ;; + ;; Any of these 3 variables above can be overriden in any of the service sections. + + +[XMLRPC] + ;# {XmlRpcRouterModule} {} {Module used to route incoming llRemoteData calls} {XmlRpcRouterModule XmlRpcGridRouterModule} XmlRpcRouterModule + ;; If enabled and set to XmlRpcRouterModule, this will post an event, + ;; "xmlrpc_uri(string)" to the script concurrently with the first + ;; remote_data event. This will contain the fully qualified URI an + ;; external site needs to use to send XMLRPC requests to that script + ;; + ;; If enabled and set to XmlRpcGridRouterModule, newly created channels + ;; will be registered with an external service via a configured uri + ;XmlRpcRouterModule = "XmlRpcRouterModule" + + ;# {XmlRpcPort} {} {Port for incoming llRemoteData xmlrpc calls} {} 20800 + ;XmlRpcPort = 20800 + + ;# {XmlRpcHubURI} {XmlRpcRouterModule} {URI for external service used to register xmlrpc channels created in the simulator. This depends on XmlRpcRouterModule being set to XmlRpcGridRouterModule} {} http://example.com + ;; If XmlRpcRouterModule is set to XmlRpcGridRouterModule, the simulator + ;; will use this address to register xmlrpc channels on the external + ;; service + ; XmlRpcHubURI = http://example.com + + +[ClientStack.LindenUDP] + ;; See OpensSimDefaults.ini for the throttle options. You can copy the + ;; relevant sections and override them here. + ;; DO NOT MODIFY OpenSimDefaults.ini, as your changes would be lost + ;; with the next update! + + ;# {DisableFacelights} {} {Stop facelights from working?} {true false} false + ;; Quash and remove any light properties from attachments not on the + ;; hands. This allows flashlights and lanterns to function, but kills + ;; silly vanity "Facelights" dead. Sorry, head mounted miner's lamps + ;; will also be affected. + ;; This is especially important in artistic builds that depend on lights + ;; on the build for their appearance, since facelights will cause the + ;; building's lights to possibly not be rendered. + ; DisableFacelights = "false" + + +[ClientStack.LindenCaps] + ;; For the long list of capabilities, see OpenSimDefaults.ini + ;; Here are the few ones you may want to change. Possible values + ;; are: + ;; "" -- empty, capability disabled + ;; "localhost" -- capability enabled and served by the simulator + ;; "" -- capability enabled and served by some other server + ;; + ; These are enabled by default to localhost. Change if you see fit. + Cap_GetTexture = "localhost" + Cap_GetMesh = "localhost" + Cap_AvatarPickerSearch = "localhost" + Cap_GetDisplayNames = "localhost" + + +[SimulatorFeatures] + + ;# {SearchServerURI} {} {URL of the search server} {} + ;; This is identical to the Robust LoginService SearchURL setting + ;; and will override that value if set here. The Robust setting + ;; provides a working default for the grid and setting here is + ;; optional. + ;SearchServerURI = "http://127.0.0.1:9000/" + ;# {DestinationGuideURI} {} {URL of the destination guide} {} + ;; + ;; This serves the same purpose as the DestinationGuideURI in the + ;; LoginService setting in the Robust server. This will override + ;; the Robust setting if desired as an option. + ;DestinationGuideURI = "http://127.0.0.1:9000/" + + +[Chat] + ;# {whisper_distance} {} {Distance at which a whisper is heard, in meters?} {} 10 + ;; Distance in meters that whispers should travel. + ; whisper_distance = 10 + + ;# {say_distance} {} {Distance at which normal chat is heard, in meters?} {} 20 + ;; Distance in meters that ordinary chat should travel. + ; say_distance = 20 + + ;# {shout_distance} {} {Distance at which a shout is heard, in meters?} {} 100 + ;; Distance in meters that shouts should travel. + ; shout_distance = 100 + + +[EntityTransfer] + ;# {DisableInterRegionTeleportCancellation} {} {Determine whether the cancel button is shown at all during teleports.} {false true} false + ;; This option exists because cancelling at certain points can result in an unuseable session (frozen avatar, etc.) + ;; Disabling cancellation can be okay in small closed grids where all teleports are highly likely to suceed. + ;DisableInterRegionTeleportCancellation = false + + +[Messaging] + ;# {OfflineMessageModule} {} {Module to use for offline message storage} {OfflineMessageModule "Offline Message Module V2" *} + ;; Module to handle offline messaging. The core module requires an external + ;; web service to do this. See OpenSim wiki. + ; OfflineMessageModule = OfflineMessageModule + ;; Or, alternatively, use this one, which works for both standalones and grids + ; OfflineMessageModule = "Offline Message Module V2" + + ;# {OfflineMessageURL} {OfflineMessageModule:OfflineMessageModule Offline Message Module V2:Offline Message Module V2} {URL of offline messaging service} {} + ;; URL of web service for offline message storage. Leave it commented if your service is local to the sim. + ; OfflineMessageURL = ${Const|BaseURL}/Offline.php + ; OfflineMessageURL = ${Const|BaseURL}:${Const|PrivatePort} + + ;# {StorageProvider} {Offline Message Module V2:Offline Message Module V2} {DLL that provides the storage interface} {OpenSim.Data.MySQL.dll} + ;; For standalones, this is the storage dll. + ; StorageProvider = OpenSim.Data.MySQL.dll + + ;# {MuteListModule} {OfflineMessageModule:OfflineMessageModule} {} {} MuteListModule + ;; Mute list handler (not yet implemented). MUST BE SET to allow offline + ;; messages to work + ; MuteListModule = MuteListModule + + ;# {MuteListURL} {OfflineMessageModule:OfflineMessageModule} {} {} http://yourserver/Mute.php + ;; URL of the web service that serves mute lists. Not currently used, but + ;; must be set to allow offline messaging to work. + ; MuteListURL = http://yourserver/Mute.php + + ;; Control whether group invites and notices are stored for offline users. + ;; Default is true. + ;; This applies to both core groups module. + ; ForwardOfflineGroupMessages = true + + +[BulletSim] + ;# {AvatarToAvatarCollisionsByDefault} {[Startup]physics:BulletSim} {Should avatars collide with each other?} {true false} true + AvatarToAvatarCollisionsByDefault = true + + +[ODEPhysicsSettings] + ;# {mesh_sculpted_prim} {[Startup]physics:OpenDynamicsEngine} {Mesh sculpties so they collide as they look?} {true false} true + ;; Do we want to mesh sculpted prim to collide like they look? + ;; If you are seeing sculpt texture decode problems + ;; (messages such as "Decoded image with unhandled number of components: 0 shortly followed by a physcs exception") + ;; then you might want to try setting this to false. + ; mesh_sculpted_prim = true + + ;# {use_NINJA_physics_joints} {[Startup]physics:OpenDynamicsEngine} {Use jointed (NINJA) physics?} {true false} false + ;; If you would like physics joints to be enabled through a special naming + ;; convention in the client, set this to true. + ;; (see NINJA Physics, http://opensimulator.org/wiki/NINJA_Physics) + ; use_NINJA_physics_joints = false + + +[RemoteAdmin] + ;; This is the remote admin module, which uses XMLRPC requests to + ;; manage regions from a web interface. + + ;# {enabled} {} {Enable the remote admin interface?} {true false} false + ; enabled = false + + ;# {port} {enabled:true} {Port to use for the remote admin interface? (0 = default)} {} 0 + ;; Set this to a nonzero value to have remote admin use a different port + ; port = 0 + + ;# {access_password} {enabled:true} {Password for the remote admin interface} {} + ;; This password is required to make any XMLRPC call (should be set as + ;; the "password" parameter) + ; access_password = "" + + ;# {access_ip_addresses} {enabled:true} {List the IP addresses allowed to call RemoteAdmin?} {} + ;; List the IP addresses allowed to call RemoteAdmin + ;; If access_ip_addresses isn't set, then all IP addresses can access RemoteAdmin. + ;; access_ip_addresses = 0.0.0.0, 0.0.0.0 ... + ; access_ip_addresses = + + ;# {create_region_enable_voice} {enabled:true} {Enable voice for newly created regions?} {true false} false + ;; set this variable to true if you want the create_region XmlRpc + ;; call to unconditionally enable voice on all parcels for a newly + ;; created region + ; create_region_enable_voice = false + + ;# {create_region_public} {enabled:true} {Make newly created regions public?} {true false} false + ;; set this variable to false if you want the create_region XmlRpc + ;; call to create all regions as private per default (can be + ;; overridden in the XmlRpc call) + ; create_region_public = false + + ;# {enabled_methods} {enabled:true} {List of methods to allow, separated by |} {} all + ;; enable only those methods you deem to be appropriate using a | delimited + ;; whitelist. + ;; For example: + ;; enabled_methods = admin_broadcast|admin_save_oar|admin_save_xml + ;; if this parameter is not specified but enabled = true, all methods + ;; will be available + ; enabled_methods = all + + ;; specify the default appearance for an avatar created through the remote + ;; admin interface + ;; This will only take effect is the file specified by the + ;; default_appearance setting below exists + ; default_male = Default Male + ; default_female = Default Female + + ;; Update appearance copies inventory items and wearables of default + ;; avatars. if this value is false, just worn assets are copied to the + ;; Clothes folder; if true, all Clothes and Bodyparts subfolders are copied. + ;; The receiver will wear the same items the default avatar did wear. + ; copy_folders = false + + ;; Path to default appearance XML file that specifies the look of the + ;; default avatars + ; default_appearance = default_appearance.xml + + +[Wind] + ;# {enabled} {} {Enable wind module?} {true false} true + ;; Enables the wind module. + ; enabled = true + + ;# {wind_update_rate} {enabled:true} {Wind update rate in frames?} {} 150 + ;; How often should wind be updated, as a function of world frames. + ;; Approximately 50 frames a second + ; wind_update_rate = 150 + + ;; The Default Wind Plugin to load + ; wind_plugin = SimpleRandomWind + + ;; These settings are specific to the ConfigurableWind plugin + ;; To use ConfigurableWind as the default, simply change wind_plugin + ;; to ConfigurableWind and uncomment the following. + ; avg_strength = 5.0 + ; avg_direction = 0.0 + ; var_strength = 5.0 + ; var_direction = 30.0 + ; rate_change = 1.0 + + ;# {strength} {enabled:true wind_plugin:SimpleRandomWind} {Wind strength?} {} 1.0 + ;; This setting is specific to the SimpleRandomWind plugin + ;; Adjusts wind strength. 0.0 = no wind, 1.0 = normal wind. + ; strength = 1.0 + + +[LightShare] + ;# {enable_windlight} {} {Enable LightShare technology?} {true false} false + ;; This enables the transmission of Windlight scenes to supporting clients, + ;; such as the Meta7 viewer. + ;; It has no ill effect on viewers which do not support server-side + ;; windlight settings. + ; enable_windlight = false + + +[Materials] + ;# {enable_materials} {} {Enable Materials support?} {true false} true + ;; This enables the use of Materials. + ; enable_materials = true + ; MaxMaterialsPerTransaction = 50 + +[DataSnapshot] + ;# {index_sims} {} {Enable data snapshotting (search)?} {true false} false + ;; The following set of configs pertains to search. + ;; Set index_sims to true to enable search engines to index your + ;; searchable data. + ;; If false, no data will be exposed, DataSnapshot module will be off, + ;; and you can ignore the rest of these search-related configs. + ; index_sims = false + + ;# {data_exposure} {index_sims:true} {How much data should be exposed?} {minimum all} minimum + ;; The variable data_exposure controls what the regions expose: + ;; minimum: exposes only things explicitly marked for search + ;; all: exposes everything + ; data_exposure = minimum + + ;# {gridname} {index_sims:true} {Enter the name fo your grid} {} OSGrid + ;; If search is on, change this to your grid name; will be ignored for + ;; standalones + ; gridname = "OSGrid" + + ;# {default_snapshot_period} {index_sims:true} {Period between data snapshots?} {} 1200 + ;; Period between data snapshots, in seconds. 20 minutes, for starters, + ;; so that you see the initial changes fast. + ;; Later, you may want to increase this to 3600 (1 hour) or more + ; default_snapshot_period = 1200 + + ;; This will be created in bin, if it doesn't exist already. It will hold + ;; the data snapshots. + ; snapshot_cache_directory = "DataSnapshot" + + ;; [Supported, but obsolete] + ;# {data_services} {index_sims:true} {Data service URLs to register with?} {} http://metaverseink.com/cgi-bin/register.py + ; This semicolon-separated string serves to notify specific data services + ; about the existence of this sim. Uncomment if you want to index your + ; data with this and/or other search providers. + ; data_services="http://metaverseink.com/cgi-bin/register.py" + + ;; New way of specifying data services, one per service + ;DATA_SRV_MISearch = "http://metaverseink.com/cgi-bin/register.py" + +[Economy] + ;# {SellEnabled} {} {Enable selling for 0?} {true false} true + ; The default economy module only implements just enough to allow free actions (transfer of objects, etc). + ; There is no intention to implement anything further in core OpenSimulator. + ; This functionality has to be provided by third party modules. + + ;; Enables selling things for $0. Default is true. + ; SellEnabled = true + + ;# {PriceUpload} {} {Price for uploading?} {} 0 + ;; Money Unit fee to upload textures, animations etc. Default is 0. + ; PriceUpload = 0 + + ;# {PriceGroupCreate} {} {Fee for group creation} {} 0 + ;; Money Unit fee to create groups. Default is 0. + ; PriceGroupCreate = 0 + + +[XEngine] + ;# {Enabled} {} {Enable the XEngine scripting engine?} {true false} true + ;; Enable this engine in this OpenSim instance + ; Enabled = true + + ;; How many threads to keep alive even if nothing is happening + ; MinThreads = 2 + + ;; How many threads to start at maximum load + ; MaxThreads = 100 + + ;; Time a thread must be idle (in seconds) before it dies + ; IdleTimeout = 60 + + ;# {Priority} {Enabled:true} {Priority for script engine threads?} {Lowest BelowNormal Normal AboveNormal Highest} BelowNormal + ;; Thread priority ("Lowest", "BelowNormal", "Normal", "AboveNormal", + ;; "Highest") + ; Priority = "BelowNormal" + + ;; Maximum number of events to queue for a script (excluding timers) + ; MaxScriptEventQueue = 300 + + ;; Stack size per script engine thread in bytes. + ;; If you are experiencing StackOverflowExceptions you may want to increase this (e.g. double it). + ;; The trade-off may be increased memory usage by the script engine. + ; ThreadStackSize = 262144 + + ;; Set this to true (the default) to load each script into a separate + ;; AppDomain. + ;; + ;; Setting this to false will load all script assemblies into the + ;; current AppDomain, which will significantly improve script loading times. + ;; It will also reduce initial per-script memory overhead. + ;; + ;; However, setting this to false will also prevent script DLLs from being unloaded from memory if the script is deleted. + ;; This may cause an OutOfMemory problem over time when avatars with scripted attachments move in and out of the region. + ;; Some Windows users have also reported script loading problems when AppDomainLoading = false + ; AppDomainLoading = true + + ;; Controls whether scripts are stopped by aborting their threads externally (abort) or by co-operative checks from the compiled script (co-op) + ;; co-op will be more stable but this option is currently experimental. + ;; If moving from co-op to abort, existing script DLLs will need to be recompiled. + ;; This currently can only be done manually, either by setting DeleteScriptsOnStartup = true for one run + ;; or by deleting the script DLL* files in bin/ScriptEngines// + ;; One can move from co-op back to abort without recompilation, but reverting back to co-op again will need script recompile + ;; Current valid values are "abort" and "co-op" + ; ScriptStopStrategy = abort + + + ;# {DeleteScriptsOnStartup} {} {Delete previously compiled script DLLs on startup?} {true false} true + ;; Controls whether previously compiled scripts DLLs are deleted on sim restart. If you set this to false + ;; then startup will be considerably faster since scripts won't need to be recompiled. However, then it becomes your responsibility to delete the + ;; compiled scripts if you're recompiling OpenSim from source code and internal interfaces used + ;; by scripts have changed. + ; DeleteScriptsOnStartup = true + + ;# {DefaultCompileLanguage} {Enabled:true} {Default script language?} {lsl vb cs} lsl + ;; Default language for scripts + ; DefaultCompileLanguage = "lsl" + + ;# {AllowedCompilers} {Enabled:true} {Languages to allow (comma separated)?} {} lsl + ;; List of allowed languages (lsl,vb,cs) + ;; AllowedCompilers=lsl,cs,vb + ;; *warning*, non lsl languages have access to static methods such as + ;; System.IO.File. Enable at your own risk. + ; AllowedCompilers = "lsl" + + ;; Compile debug info (line numbers) into the script assemblies + ; CompileWithDebugInformation = true + + ; ==== Settings for MOD and OSSL functions have been moved to the [OSSL] section + + ;# {EventLimit} {} {Amount of time a script can spend in an event handler} {} 30 + ;; Time a script can spend in an event handler before it is interrupted + ; EventLimit = 30 + + ;# {KillTimedOutScripts} {} {Kill script in case of event time overruns?} {true false} false + ;; If a script overruns it's event limit, kill the script? + ; KillTimedOutScripts = false + + ;# {ScriptDelayFactor} {} {Multiplier for scripting delays} {} 1.0 + ;; Sets the multiplier for the scripting delays + ; ScriptDelayFactor = 1.0 + + ;# {ScriptDistanceLimitFactor} {} {Multiplier for 10.0m distance limits?} {} + ;; The factor the 10 m distances limits are multiplied by + ; ScriptDistanceLimitFactor = 1.0 + + ;# {NotecardLineReadCharsMax} {} {Maximum length of notecard line?} {} 255 + ;; Maximum length of notecard line read + ;; Increasing this to large values potentially opens + ;; up the system to malicious scripters + ; NotecardLineReadCharsMax = 255 + + ;# {SensorMaxRange} {} {Sensor range} {} 96.0 + ;; Sensor settings + ; SensorMaxRange = 96.0 + ;# {SensorMaxResults} {} {Max sensor results returned?} {} + ; SensorMaxResults = 16 + + ;# {DisableUndergroundMovement} {} {Disable underground movement of prims} {true false} true + ;; Disable underground movement of prims (default true); set to + ;; false to allow script controlled underground positioning of + ;; prims + ; DisableUndergroundMovement = true + + ;# {ScriptEnginesPath} {} {Path to script assemblies} {} ScriptEngines + ;; Path to script engine assemblies + ;; Default is ./bin/ScriptEngines + ; ScriptEnginesPath = "ScriptEngines" + +[OSSL] + ;# {Include-osslEnable} {} {Include file for enabling and permissions for OSSL functions} {} + ;; Optionally include file to enable OSSL functions and set permissions on who can use which. + ;; If this INI file is not included, the OSSL functions are disabled. + Include-osslEnable = "config-include/osslEnable.ini" + + +[MRM] + ;; Enables the Mini Region Modules Script Engine. + ; Enabled = false + + ;; Runs MRM in a Security Sandbox + ;; WARNING: DISABLING IS A SECURITY RISK. + ; Sandboxed = true + + ;; The level sandbox to use, adjust at your OWN RISK. + ;; Valid values are: + ;; * FullTrust + ;; * SkipVerification + ;; * Execution + ;; * Nothing + ;; * LocalIntranet + ;; * Internet + ;; * Everything + ; SandboxLevel = "Internet" + + ;; Only allow Region Owners to run MRMs + ;; May represent a security risk if you disable this. + ; OwnerOnly = true + + +[FreeSwitchVoice] + ;; In order for this to work you need a functioning FreeSWITCH PBX set up. + ;; Configuration details at http://opensimulator.org/wiki/Freeswitch_Module + ; Enabled = false + + ;; You need to load a local service for a standalone, and a remote service + ;; for a grid region. Use one of the lines below, as appropriate + ;; If you're using Freeswitch on a standalone then you will also need to configure the [FreeswitchService] section in config-include/StandaloneCommon.ini + ; LocalServiceModule = OpenSim.Services.FreeswitchService.dll:FreeswitchService + ; LocalServiceModule = OpenSim.Services.Connectors.dll:RemoteFreeswitchConnector + + ;; If using a remote connector, specify the server URL + ; FreeswitchServiceURL = http://my.grid.server:8004/fsapi + + +[Groups] + ;# {Enabled} {} {Enable groups?} {true false} false + ;; Enables the groups module + ; Enabled = false + + ;# {LevelGroupCreate} {Enabled:true} {User level for creating groups} {} 0 + ;; Minimum user level required to create groups + ; LevelGroupCreate = 0 + + ;# {Module} {Enabled:true} {Groups module to use? (Use GroupsModule to use Flotsam/Simian)} {Default "Groups Module V2"} Default + ;; The default module can use a PHP XmlRpc server from the Flotsam project at + ;; http://code.google.com/p/flotsam/ + ;; or from the SimianGrid project at http://code.google.com/p/openmetaverse + ; Module = Default + ;; or... use Groups Module V2, which works for standalones and robust grids + ; Module = "Groups Module V2" + + ;# {StorageProvider} {Module:Groups Module V2} {The DLL that provides the storage for V2} {OpenSim.Data.MySQL.dll} + ; StorageProvider = OpenSim.Data.MySQL.dll + + ;# {ServicesConnectorModule} {Module:GroupsModule Module:Groups Module V2} {Service connector to use for groups} {XmlRpcGroupsServicesConnector SimianGroupsServicesConnector "Groups Local Service Connector" "Groups Remote Service Connector" "Groups HG Service Connector"} XmlRpcGroupsServicesConnector + ;; Service connectors to the Groups Service as used in the GroupsModule. Select one as follows: + ;; -- for Flotsam Groups use XmlRpcGroupsServicesConnector + ;; -- for Simian Groups use SimianGroupsServicesConnector + ;; -- for V2 Groups, standalone, non-HG use "Groups Local Service Connector" + ;; -- for V2 Groups, grided sim, non-HG use "Groups Remote Service Connector" + ;; -- for V2 Groups, HG, both standalone and grided sim, use "Groups HG Service Connector" + ;; Note that the quotes "" around the words are important! + ; ServicesConnectorModule = XmlRpcGroupsServicesConnector + + ;# {LocalService} {ServicesConnectorModule:Groups HG Service Connector} {Is the group service in this process or elsewhere?} {local remote} local + ;; Used for V2 in HG only. If standalone, set this to local; if grided sim, set this to remote + ; LocalService = local + + ;# {SecretKey} {ServicesConnectorModule:Groups Remote Service Connector} {Secret key between sim and remote group service} {} "" + ;; Used for V2 in Remote only. + ; SecretKey = "" + + ;# {GroupsServerURI} {Module:GroupsModule (ServicesConnectorModule:Groups Remote Service Connector or (ServicesConnectorModule:Groups HG Service Connector and LocalService:remote))} {Groups Server URI} {} + ;; URI for the groups services of this grid + ;; e.g. http://yourxmlrpcserver.com/xmlrpc.php for Flotsam XmlRpc + ;; or http://mygridserver.com:82/Grid/ for SimianGrid + ;; or ${Const|BaseURL}:${Const|PrivatePort} for robust, V2 + ;; Leave it commented for standalones, V2 + ; GroupsServerURI = "" + + ;# {HomeURI} {ServicesConnectorModule:Groups HG Service Connector} {What's the home address of this world?} {} + ;; Used for V2 in HG only. For example + ;; http://mygridserver.com:9000 or http://mygridserver.com:8002 + ;; If you have this set under [Startup], no need to set it here, leave it commented + ; HomeURI = "" + + ;# {MessagingEnabled} {Module:GroupsModule Module:Groups Module V2} {Is groups messaging enabled?} {true false} true + ; MessagingEnabled = true + + ;# {MessagingModule} {MessagingEnabled:true} {Module to use for groups messaging} {GroupsMessagingModule "Groups Messaging Module V2"} GroupsMessagingModule + ; MessagingModule = GroupsMessagingModule + ; or use "Groups Messaging Module V2" for Groups V2 + ; MessagingModule = "Groups Messaging Module V2" + + ;# {NoticesEnabled} {Module:GroupsModule Module:Groups Module V2} {Enable group notices?} {true false} true + ;; Enable Group Notices + ; NoticesEnabled = true + + ;# {MessageOnlineUsersOnly} {Module:GroupsModule Module} {Message online users only?} {true false} false + ; Experimental option to only message online users rather than all users + ; Should make large groups with few online members messaging faster, as the expense of more calls to presence service + ; Applies Flotsam Group only. V2 has this always on, no other option + ; MessageOnlineUsersOnly = false + + ;; This makes the Group module very chatty on the console. + ; DebugEnabled = false + + ; This makes the Group Messaging module very chatty on the console. + ; DebugMessagingEnabled = false + + ;; XmlRpc Security settings. These must match those set on your backend + ;; groups service if the service is using these keys + ; XmlRpcServiceReadKey = 1234 + ; XmlRpcServiceWriteKey = 1234 + + +[InterestManagement] + ;# {UpdatePrioritizationScheme} {} {Update prioritization scheme?} {BestAvatarResponsiveness Time Distance SimpleAngularDistance FrontBack} BestAvatarResponsiveness + ;; This section controls how state updates are prioritized for each client + ;; Valid values are BestAvatarResponsiveness, Time, Distance, + ;; SimpleAngularDistance, FrontBack + ; UpdatePrioritizationScheme = BestAvatarResponsiveness + + +[MediaOnAPrim] + ;# {Enabled} {} {Enable Media-on-a-Prim (MOAP)} {true false} true + ;; Enable media on a prim facilities + ; Enabled = true; + + +[NPC] + ;# {Enabled} {} {Enable Non Player Character (NPC) facilities} {true false} false + ; Enabled = false + + +[Terrain] + ;# {InitialTerrain} {} {Initial terrain type} {pinhead-island flat} pinhead-island + ; InitialTerrain = "pinhead-island" + + +[UserProfiles] + ;# {ProfileServiceURL} {} {Set url to UserProfilesService} {} + ;; Set the value of the url to your UserProfilesService + ;; If un-set / "" the module is disabled + ;; ProfileServiceURL = ${Const|BaseURL}:${Const|PublicPort} + +[XBakes] + ;# {URL} {} {Set URL for Baked texture service} {} + ;; Sets the URL for the baked texture ROBUST service. + ;; Disabled when unset. + ;; URL = ${Const|BaseURL}:${Const|PrivatePort} + +;; +;; Optional module to highlight God names in the viewer. +;; Uncomment and customize appropriately if you want this behavior. +;; +;[GodNames] +; Enabled = false +; FullNames = "Test User, Foo Bar" +; Surnames = "Kryztlsk" + +[Architecture] + ;# {Include-Architecture} {} {Choose one of the following architectures} {config-include/Standalone.ini config-include/StandaloneHypergrid.ini config-include/Grid.ini config-include/GridHypergrid.ini config-include/SimianGrid.ini config-include/HyperSimianGrid.ini} config-include/Standalone.ini + ;; Uncomment one of the following includes as required. For instance, to create a standalone OpenSim, + ;; uncomment Include-Architecture = "config-include/Standalone.ini" + ;; + ;; Then you will need to copy and edit the corresponding *Common.example file in config-include/ + ;; that the referenced .ini file goes on to include. + ;; + ;; For instance, if you chose "config-include/Standalone.ini" then you will need to copy + ;; "config-include/StandaloneCommon.ini.example" to "config-include/StandaloneCommon.ini" before + ;; editing it to set the database and backend services that OpenSim will use. + ;; + ; Include-Architecture = "config-include/Standalone.ini" + ; Include-Architecture = "config-include/StandaloneHypergrid.ini" + ; Include-Architecture = "config-include/Grid.ini" + ; Include-Architecture = "config-include/GridHypergrid.ini" + ; Include-Architecture = "config-include/SimianGrid.ini" + ; Include-Architecture = "config-include/HyperSimianGrid.ini" diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini new file mode 100644 index 0000000000..c0c81346ff --- /dev/null +++ b/bin/OpenSimDefaults.ini @@ -0,0 +1,2112 @@ +; This file contains defaults for various settings in OpenSimulator. These can be overriden +; by changing the same setting in OpenSim.ini (once OpenSim.ini.example has been copied to OpenSim.ini). + +[Startup] + ; Console prompt + ; Certain special characters can be used to customize the prompt + ; Currently, these are + ; \R - substitute region name + ; \\ - substtitue \ + ConsolePrompt = "Region (\R) " + + ; Console commands can be saved to a file, so the command history persists after a restart. (default is true) + ConsoleHistoryFileEnabled = true + + ; Log file location. This can be set to a simple file path + ; LogFile = OpenSim.log + + ; The history file can be just a filename (relative to OpenSim's bin/ directory + ; or it can be a full path to somewhere else. (default is OpenSimConsoleHistory.txt in bin/) + ConsoleHistoryFile = "OpenSimConsoleHistory.txt" + + ; How many lines of command history should we keep? (default is 100) + ConsoleHistoryFileLines = 100 + + ; Set this to true if you want to log crashes to disk + ; this can be useful when submitting bug reports. + ; However, this will only log crashes within OpenSimulator that cause the entire program to exit + ; It will not log crashes caused by virtual machine failures, which includes mono and ODE failures. + ; You will need to capture these native stack traces by recording the session log itself. + save_crashes = false + + ; Directory to save crashes to if above is enabled + ; (default is /opensimdir/crashes/*.txt or C:\opensim\crashes\*.txt) + crash_dir = "crashes" + + ; Place to create a PID file + ; PIDFile = "/tmp/OpenSim.exe.pid" + + ; Console commands run at startup + startup_console_commands_file = "startup_commands.txt" + + ; Console commands run on shutdown + shutdown_console_commands_file = "shutdown_commands.txt" + + ; Console commands run every 20 minutes + ; timer_Script = "filename" + + ; timer_Script time interval (default 20 min) + ; The time is 60 per minute + ; timer_Interval = 1200 + + ; ## + ; ## SYSTEM + ; ## + + ; Sets the method that OpenSim will use to fire asynchronous + ; events. Valid values are UnsafeQueueUserWorkItem, + ; QueueUserWorkItem, BeginInvoke, SmartThreadPool, and Thread. + ; + ; SmartThreadPool is reported to work well on Mono/Linux, but + ; UnsafeQueueUserWorkItem has been benchmarked with better + ; performance on .NET/Windows + ; + ; UnsafeQueueUserWorkItem refers to the fact that the code creating the event could elevate its security + ; privileges. However, as calling code is trusted anyway this is safe (if you set + ; TrustedBinaries = true in the [XEngine] section then you already have to trust that incoming code for other reasons). + async_call_method = SmartThreadPool + + ; Max threads to allocate on the FireAndForget thread pool + ; when running with the SmartThreadPool option above + MaxPoolThreads = 300 + + ; Allow certain jobs to be run consecutively in a job engine rather than always concurrently. + ; This improves performance in regions with large numbers of connections (in the hundreds). + JobEngineEnabled = true + + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information about the + ; registered repositories and installed plugins will be stored here. + ; The OpenSim.exe process must have R/W access to the location. + RegistryLocation = "." + + ; Used by region module addins. You can set this to outside bin, so that addin + ; configurations will survive updates. The OpenSim.exe process must have R/W access + ; to the location. + ConfigDirectory = "." + + ; ## + ; ## CLIENTS + ; ## + + ; Set this to the DLL containing the client stack to use. + clientstack_plugin="OpenSim.Region.ClientStack.LindenUDP.dll" + + ; ## + ; ## REGIONS + ; ## + + ; Determine where OpenSimulator looks for the files which tell it which regions to server + ; Defaults to "filesystem" if this setting isn't present + region_info_source = "filesystem" + ; region_info_source = "web" + + ; Determines where the region XML files are stored if you are loading these from the filesystem. + ; Defaults to bin/Regions in your OpenSimulator installation directory + ; regionload_regionsdir="C:\somewhere\xmlfiles\" + + ; Determines the page from which regions xml is retrieved if you are loading these from the web + ; The XML here has the same format as it does on the filesystem (including the tag), + ; except that everything is also enclosed in a tag. + ; regionload_webserver_url = "http://example.com/regions.xml"; + + ;; Allow the simulator to start up if there are no region configuration available + ;; from the selected region_info_source. + allow_regionless = false + + ;; Allow child agents to see into the region even if their root counterpart isn't allowed in here + see_into_region = true + + ; Maximum number of position, rotation and scale changes for each prim that the simulator will store for later undos + ; Increasing this number will increase memory usage. + MaxPrimUndos = 20 + + ; Maximum size of non physical prims. Affects resizing of existing prims. This can be overridden in the region config file (as NonPhysicalPrimMax!). + NonPhysicalPrimMax = 256 + + ; Maximum size of physical prims. Affects resizing of existing prims. This can be overridden in the region config file. + PhysicalPrimMax = 64 + + ; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum + ; This can be overridden in the region config file. + ClampPrimSize = false + + ; Maximum number of prims allowable in a linkset. Affects creating new linksets. Ignored if less than or equal to zero. + ; This can be overridden in the region config file. + LinksetPrims = 0 + + ; Allow scripts to keep running when they cross region boundaries, rather than being restarted. State is reloaded on the destination region. + ; This only applies when crossing to a region running in a different simulator. + ; For crossings where the regions are on the same simulator the script is always kept running. + AllowScriptCrossing = true + + ; Allow compiled script binary code to cross region boundaries. + ; If you set this to "true", any region that can teleport to you can + ; inject ARBITRARY BINARY CODE into your system. Use at your own risk. + ; YOU HAVE BEEN WARNED!!! + TrustBinaries = false + + ; Combine all contiguous regions into one large megaregion + ; Order your regions from South to North, West to East in your regions.ini and then set this to true + ; Warning! Don't use this with regions that have existing content!, This will likely break them + CombineContiguousRegions = false + + ; Extend the region's draw distance; 255m is the default which includes + ; one neighbor on each side of the current region, 767m would go three + ; neighbors on each side for a total of 49 regions in view. Warning, unless + ; all the regions have the same drawdistance, you will end up with strange + ; effects because the agents that get closed may be inconsistent. + DefaultDrawDistance = 255.0 + + ; If you have only one region in an instance, or to avoid the many bugs + ; that you can trigger in modules by restarting a region, set this to + ; true to make the entire instance exit instead of restarting the region. + ; This is meant to be used on systems where some external system like + ; Monit will restart any instance that exits, thereby making the shutdown + ; into a restart. + InworldRestartShutsDown = false + + ; The minimum proportion of a second that any particular frame can take to execute. + ; Only change this if you really know what you're doing, and be prepared to change UpdatePhysicsEveryNFrames + ; (and other Frames params) to match! For instance, halving MinFrameTime to 0.0445 require + ; UpdatePhysicsEveryNFrames = 2 unless you don't mind your avatar walking like Benny Hill. + MinFrameTime = 0.089 + + ; Send scheduled updates to objects in the scene + ; This must be a whole number + UpdateObjectsEveryNFrames = 1; + + ; Send position/velocity, etc. updates to agents in the scene + ; This must be a whole number + UpdateAgentsEveryNFrames = 1; + + ; Apply pending forces from physics calculations to an entity. + ; This must be a whole number + UpdateEntityMovementEveryNFrames = 1; + + ; Send coarse location updates to viewers. In a classic viewer, this updates the minimap. + ; This must be a whole number + UpdateCoarseLocationsEveryNFrames = 50; + + ; Update physics. Within each update physics also updates in a series of contigous mini-steps + ; This must be a whole number + UpdatePhysicsEveryNFrames = 1; + + ; Send out the on frame event to modules and other listeners. This should probably never deviate from 1. + ; This must be a whole number + UpdateEventsEveryNFrames = 1; + + ; Send terrain updates to viewers + ; This must be a whole number + UpdateTerrainEveryNFrames = 50; + + ; Persitently store any objects which meet the PRIM STORAGE criteria + ; This must be a whole number + UpdateStorageEveryNFrames = 200; + + ; Clean up temp on rez objects. + ; This must be a whole number + UpdateTempCleaningEveryNSeconds = 180; + + ; ## + ; ## PRIM STORAGE + ; ## + + ; Persistence of changed objects happens during regular sweeps. The following control that behaviour to + ; prevent frequently changing objects from heavily loading the region data store. + ; If both of these values are set to zero then persistence of all changed objects will happen on every sweep. + ; + ; Objects will be considered for persistance in the next sweep when they have not changed for this number of seconds + MinimumTimeBeforePersistenceConsidered = 60 + ; Objects will always be considered for persistance in the next sweep if the first change occurred this number of seconds ago + MaximumTimeBeforePersistenceConsidered = 600 + + ; ## + ; ## PHYSICS + ; ## + + ; If true then prims can be collided with by avatars, other prims, etc. + ; If false then all prims are phantom, no matter whether their phantom flag is checked or unchecked. + ; Also, no prims are subject to physics. + collidable_prim = true + + ; If true then prims can be made subject to physics (gravity, pushing, etc.). + ; If false then physics flag can be set but it is not honoured. However, prims are still solid for the purposes of collision direction + physical_prim = true + + ; Select a mesher here. + ; + ; Meshmerizer properly handles complex prims by using triangle meshes. + ; Note that only the ODE physics engine currently deals with meshed prims in a satisfactory way + ; + ; ZeroMesher is faster but leaves the physics engine to model the mesh using the basic shapes that it supports + ; Usually this is only a box + + meshing = Meshmerizer + ;meshing = ZeroMesher + + ; Path to decoded sculpty maps + ; Defaults to "j2kDecodeCache + ;DecodedSculptMapPath = "j2kDecodeCache" + + ; if you use Meshmerizer and want sculpt map collisions, setting this to + ; to true will store decoded sculpt maps in a special folder in your bin + ; folder, which can reduce startup times by reducing asset requests. Some + ; versions of mono dont work well when reading the cache files, so set this + ; to false if you have compatibility problems. + ;CacheSculptMaps = true + + ;; BulletSim is the default physics engine. It provides the best performance and most functionality. + ;; BulletSim supports varregions. + ;; OpenDynamicsEngine was the previous default physics engine in OpenSimulator 0.7.6.1 and before. + ;; It continues to provide a workable physics implementation. It does not currently support varregions. + ;; basicphysics effectively does not model physics at all, making all objects phantom. + ;; Default is OpenDynamicsEngine + physics = BulletSim + ;physics = modified_BulletX + ;physics = OpenDynamicsEngine + ;physics = basicphysics + ;physics = POS + + ; ## + ; ## SCRIPT ENGINE + ; ## + + DefaultScriptEngine = "XEngine" + + ; ## + ; ## EMAIL MODULE + ; ## + + ;emailmodule = DefaultEmailModule + + ; ## + ; ## ANIMATIONS + ; ## + + ; If enabled, enableFlySlow will change the primary fly state to + ; FLYSLOW, and the "always run" state will be the regular fly. + enableflyslow = false + + ; PreJump is an additional animation state, but it probably + ; won't look right until the physics engine supports it + ; (i.e delays takeoff for a moment) + + ; Simulator statistics are output to the console periodically at debug level INFO. + ; Setting this to zero disables this output. + ; LogShowStatsSeconds = 3600 + + ; Simulator Stats URI + ; Enable JSON simulator data by setting a URI name (case sensitive) + ; Returns regular sim stats (SimFPS, ...) + Stats_URI = "jsonSimStats" + + ; Simulator StatsManager URI + ; Enable fetch of StatsManager registered stats. Fetch is query which can optionally + ; specify category, container and stat to fetch. If not selected, returns all of that type. + ; http://simulatorHTTPport/ManagedStats/?cat=Category&cont=Container&stat=Statistic + ; ManagedStatsRemoteFetchURI = "ManagedStats" + + ; Make OpenSim start all regions woth logins disabled. They will need + ; to be enabled from the console if this is set + ; StartDisabled = false + + ; Image decoding. Use CSJ2K for layer boundary decoding if true, + ; OpenJPEG if false + ; UseCSJ2K = true + + ; Use "Trash" folder for items deleted from the scene + ; When set to True (the default) items deleted from the scene will be + ; stored in the user's trash or lost and found folder. When set to + ; False items will be removed from the scene permanently + UseTrashOnDelete = True + + ; # + ; # Logging + ; # + + ; Force logging when the thread pool approaches an overload condition + ; Provides useful data for post-mortem analysis even in a production + ; system with reduced logging + LogOverloads = True + +[Map] + ;WorldMapModule = "WorldMap" + ;MapImageModule = "MapImageModule" + + ; World map blacklist timeout in seconds + ;BlacklistTimeout = 600 + + ; Set to false to not generate any maptiles + ;GenerateMaptiles = true + + ; Refresh (in seconds) the map tile periodically + ;MaptileRefresh = 0 + + ; If not generating maptiles, use this static texture asset ID + ;MaptileStaticUUID = "00000000-0000-0000-0000-000000000000" + + ; Draw objects on maptile. This step might take a long time if you've got a large number of + ; objects, so you can turn it off here if you'd like. + DrawPrimOnMapTile = true + + ; Use terrain texture for maptiles if true, use shaded green if false + TextureOnMapTile = true + + ; Texture prims + TexturePrims = true + + ; Only texture prims that have a diagonal size greater than this number + TexturePrimSize = 48 + + ; Attempt to render meshes and sculpties on the map + RenderMeshes = false; + +[Permissions] + ; ## + ; ## PERMISSIONS + ; ## + + ;permissionmodules = "DefaultPermissionsModule" + + ; If set to false, then, in theory, the server never carries out permission checks (allowing anybody to copy + ; any item, etc. This may not yet be implemented uniformally. + ; If set to true, then all permissions checks are carried out + ; Default is true + serverside_object_permissions = true + + allow_grid_gods = false + + ; This allows somne control over permissions + ; please note that this still doesn't duplicate SL, and is not intended to + ;region_owner_is_god = true + ;region_manager_is_god = false + ;parcel_owner_is_god = true + + ; Control user types that are allowed to create new scripts + ; Only enforced if serviceside_object_permissions is true + ; + ; Current possible values are + ; all - anyone can create scripts (subject to normal permissions) + ; gods - only administrators can create scripts (as long as allow_grid_gods is true) + ; Default value is all + ; allowed_script_creators = all + + ; Control user types that are allowed to edit (save) scripts + ; Only enforced if serviceside_object_permissions is true + ; + ; Current possible values are + ; all - anyone can edit scripts (subject to normal permissions) + ; gods - only administrators can edit scripts (as long as allow_grid_gods is true) + ; Default value is all + ; allowed_script_editors = all + + ; Provides a simple control for land owners to give build rights to specific avatars + ; in publicly accessible parcels that disallow object creation in general. + ; Owners specific avatars by adding them to the Access List of the parcel + ; without having to use the Groups feature + ; Disabled by default + ; simple_build_permissions = False + + ; Minimum user level required to upload assets + ;LevelUpload = 0 + + +[RegionReady] + ; Enable this module to get notified once all items and scripts in the region have been completely loaded and compiled + enabled = true + + ; Channel on which to signal region readiness through a message + ; formatted as follows: "{server_startup|oar_file_load},{0|1},n,[oar error]" + ; - the first field indicating whether this is an initial server startup + ; - the second field is a number indicating whether the OAR file loaded ok (1 == ok, 0 == error) + ; - the third field is a number indicating how many scripts failed to compile + ; - "oar error" if supplied, provides the error message from the OAR load + channel_notify = -800 + + ; - disallow logins while scripts are loading + ; Instability can occur on regions with 100+ scripts if users enter before they have finished loading + login_disable = true + + ; - send an alert as json to a service + ; alert_uri = "http://myappserver.net/my_handler/" + + +[EstateManagement] + ; If false, then block any region restart requests from the client even if they are otherwise valid. + ; Default is true + AllowRegionRestartFromClient = true + + +[UserProfiles] + ;# {ProfileURL} {} {Set url to UserProfilesService} {} + ;; Set the value of the url to your UserProfilesService + ;; If un-set / "" the module is disabled + ;; If the ProfileURL is not set, then very BASIC + ;; profile support will be configured. If the ProfileURL is set to a + ;; valid URL, then full profile support will be configured. The URL + ;; points to your grid's Robust user profiles service + ;; + ; ProfileURL = http://127.0.0.1:9000 + + +[SMTP] + enabled = false + + ;enabled = true + ;internal_object_host = lsl.opensim.local + ;host_domain_header_from = 127.0.0.1 + ;SMTP_SERVER_HOSTNAME = 127.0.0.1 + ;SMTP_SERVER_PORT = 25 + ;SMTP_SERVER_LOGIN = foo + ;SMTP_SERVER_PASSWORD = bar + + +[Network] + ConsoleUser = "Test" + ConsolePass = "secret" + http_listener_port = 9000 + console_port = 0 + + ; ssl config: Experimental! The auto https config only really works definately on windows XP now + ; you need a Cert Request/Signed pair installed in the MY store with the CN specified below + ; you can use https on other platforms, but you'll need to configure the httpapi yourself for now + http_listener_ssl = false ; Also create a SSL server + http_listener_cn = "localhost" ; Use the cert with the common name + http_listener_sslport = 9001 ; Use this port for SSL connections + http_listener_ssl_cert = "" ; Currently unused, but will be used for OSHttpServer + + ; HTTPS for "Out of band" management applications such as the remote + ; admin module + ; + ; Create https_listener = "True" will create a listener on the port + ; specified. Provide the path to your server certificate along with it's + ; password + ; https_listener = False + ; Set our listener to this port + ; https_port = 0 + ; Path to X509 certificate + ; cert_path = "path/to/cert.p12" + ; Password for cert + ; cert_pass = "password" + + ; Hostname to use in llRequestURL/llRequestSecureURL + ; if not defined - default machine name is being used + ; (on Windows this mean NETBIOS name - useably only inside local network) + ; ExternalHostNameForLSL=127.0.0.1 + + ; Disallow the following address ranges for user scripting calls (e.g. llHttpRequest()) + ; This is based on http://en.wikipedia.org/wiki/Reserved_IP_addresses + ; This stops users making HTTP calls to machines in the simulator's local network. + ; If you need to allow some LAN calls we recommend you use OutboundDisallowForUserScriptsExcept documented in OpenSim.ini.example + ; If you override OutboundDisallowForUserScripts directly you need to be very careful. + ; + ; Network ranges are specified in CIDR notation (http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) with multiple entries separated by | + ; To specify an individual IP address use the /32 netmask (e.g. 192.168.1.3/32) + ; You can also specify individual : endpoints (e.g. 192.168.1.3:8003) + ; If an address if given without a port number then port 80 is assumed. + OutboundDisallowForUserScripts = 0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32 + ; + ; You can also prevent all user script outgoing calls with the following override in OpenSim.ini + ; + ; OutboundDisallowForUserScripts = 0.0.0.0/0 + ; + ; You can also disable the blacklist entirely with an empty entry + ; + ; OutboundDisallowForUserScripts = "" + + ; What is reported as the "X-Secondlife-Shard" + ; Defaults to the user server url if not set + ; The old default is "OpenSim", set here for compatibility + shard = "OpenSim" + + ; What is reported as the "User-Agent" when using llHTTPRequest + ; Defaults to not sent if not set here. See the notes section in the wiki at + ; http://wiki.secondlife.com/wiki/LlHTTPRequest for comments on adding + ; " (Mozilla Compatible)" to the text where there are problems with a web server + ;user_agent = "OpenSim LSL (Mozilla Compatible)" + + ; OpenSim can send multiple simultaneous requests for services such as asset + ; retrieval. However, some versions of mono appear to hang when there are too + ; many simultaneous requests, default is 30 and is currently applied only to assets + ;MaxRequestConcurrency = 30 + + +[ClientStack.LindenUDP] + ; Set this to true to process incoming packets asynchronously. Networking is + ; already separated from packet handling with a queue, so this will only + ; affect whether networking internals such as packet decoding and + ; acknowledgement accounting are done synchronously or asynchronously + ; Default is true. + ; + ;async_packet_handling = true + + ; The client socket receive buffer size determines how many + ; incoming requests we can process; the default on .NET is 8192 + ; which is about 2 4k-sized UDP datagrams. On mono this is + ; whatever the underlying operating system has as default; for + ; example, ubuntu 8.04 or SLES11 have about 111k, which is about + ; 27 4k-sized UDP datagrams (on linux platforms you can [as root] + ; do "sysctl net.core.rmem_default" to find out what your system + ; uses a default socket receive buffer size. + ; + ; client_socket_rcvbuf_size allows you to specify the receive + ; buffer size LLUDPServer should use. NOTE: this will be limited + ; by the system's settings for the maximum client receive buffer + ; size (on linux systems you can set that with "sysctl -w + ; net.core.rmem_max=X") + ; + ;client_socket_rcvbuf_size = 8388608 + + ; Maximum outbound bytes per second for a single scene. This can be used to + ; throttle total outbound UDP traffic for a simulator. The default value is + ; 0, meaning no throttling at the scene level. The example given here is + ; 20 megabits + ; + ;scene_throttle_max_bps = 2500000 + + ; Maximum bytes per second to send to any single client. This will override + ; the user's viewer preference settings. The default value is 0, meaning no + ; aggregate throttling on clients (only per-category throttling). The + ; example given here is 1.5 megabits + ; + ;client_throttle_max_bps = 187500 + + ; Minimum bytes per second to send to any single client as a result of + ; adaptive throttling. Viewer preferences set to a lower number will + ; override the settin. The example given here ensures that adaptive + ; throttling will never decrease per client bandwidth below 256 kbps. + ; + ;adaptive_throttle_min_bps = 32000 + + ; Adaptive throttling attempts to limit network overload when multiple + ; clients login by starting each connection more slowly. Disabled by + ; default + ; + enable_adaptive_throttles = true + + ; Per-client bytes per second rates for the various throttle categories. + ; These are default values that will be overridden by clients. These + ; defaults are approximately equivalent to the throttles set by the Imprudence + ; viewer when maximum bandwidth is set to 350kbps + + ;resend_default = 6625 + ;land_default = 9125 + ;wind_default = 1750 + ;cloud_default = 1750 + ;task_default = 18500 + ;texture_default = 18500 + ;asset_default = 10500 + + ; Configures how ObjectUpdates are aggregated. These numbers + ; do not literally mean how many updates will be put in each + ; packet that goes over the wire, as packets are + ; automatically split on a 1400 byte boundary. These control + ; the balance between responsiveness of interest list updates + ; and total throughput. Higher numbers will ensure more full- + ; sized packets and faster sending of data, but more delay in + ; updating interest lists + ; + ;PrimUpdatesPerCallback = 100 + + ; TextureSendLimit determines how many packets will be put on + ; the outgoing queue each cycle. Like the settings above, this + ; is a balance between responsiveness to priority updates and + ; total throughput. Higher numbers will give a better + ; throughput at the cost of reduced responsiveness to client + ; priority changes or transfer aborts + ; + ;TextureSendLimit = 20 + + ; CannibalizeTextureRate allows bandwidth to be moved from the + ; UDP texture throttle to the task throttle. Since most viewers + ; use HTTP textures, this provides a means of using what is largely + ; unused bandwidth in the total throttle. The value is the proportion + ; of the texture rate to move to the task queue. It must be between + ; 0.0 (none of the bandwidth is cannibalized) and 0.9 (90% of the + ; bandwidth is grabbed) + ; + ; CannibalizeTextureRate = 0.5 + + ; Quash and remove any light properties from attachments not on the + ; hands. This allows flashlights and lanterns to function, but kills + ; silly vanity "Facelights" dead. Sorry, head mounted miner's lamps + ; will also be affected. + ; + ;DisableFacelights = false + + ; The time to wait before disconecting an unresponsive client. + ; The time is in seconds. The default is one minute + ; + ;AckTimeout = 60 + + ; The time to wait before disconecting an unresponsive paused client. + ; A client can be paused when the file selection dialog is open during file upload. + ; This gives extra time to find files via the dialog but will still disconnect if + ; the client crashes or loses its network connection + ; The time is in seconds. The default is five minutes. + ; + ;PausedAckTimeout = 300 + + +[ClientStack.LindenCaps] + ;; Long list of capabilities taken from + ;; http://wiki.secondlife.com/wiki/Current_Sim_Capabilities + ;; Not all are supported by OpenSim. The ones supported are + ;; set to localhost. These defaults can be overwritten + ;; in OpenSim.ini + ;; + Cap_AttachmentResources = "" + Cap_ChatSessionRequest = "" + Cap_CopyInventoryFromNotecard = "localhost" + Cap_DispatchRegionInfo = "" + Cap_EstateChangeInfo = "" + Cap_EnvironmentSettings = "localhost" + Cap_EventQueueGet = "localhost" + Cap_FetchInventory = "" + Cap_ObjectMedia = "localhost" + Cap_ObjectMediaNavigate = "localhost" + Cap_FetchLib = "" + Cap_FetchLibDescendents = "" + Cap_GetDisplayNames = "localhost" + Cap_GetTexture = "localhost" + Cap_GetMesh = "localhost" + Cap_GetObjectCost = "" + Cap_GetObjectPhysicsData = "" + Cap_GroupProposalBallot = "" + Cap_HomeLocation = "" + Cap_LandResources = "" + Cap_MapLayer = "localhost" + Cap_MapLayerGod = "localhost" + Cap_NewFileAgentInventory = "localhost" + Cap_NewFileAgentInventoryVariablePrice = "localhost" + Cap_ObjectAdd = "localhost" + Cap_ParcelPropertiesUpdate = "localhost" + Cap_ParcelMediaURLFilterList = "" + Cap_ParcelNavigateMedia = "" + Cap_ParcelVoiceInfoRequest = "" + Cap_ProductInfoRequest = "" + Cap_ProvisionVoiceAccountRequest = "" + Cap_RemoteParcelRequest = "localhost" + Cap_RequestTextureDownload = "" + Cap_SearchStatRequest = "" + Cap_SearchStatTracking = "" + Cap_SendPostcard = "" + Cap_SendUserReport = "" + Cap_SendUserReportWithScreenshot = "" + Cap_ServerReleaseNotes = "" + Cap_SimConsole = "" + Cap_SimulatorFeatures = "" + Cap_SetDisplayName = "" + Cap_StartGroupProposal = "" + Cap_TextureStats = "" + Cap_UntrustedSimulatorMessage = "" + Cap_UpdateAgentInformation = "" + Cap_UpdateAgentLanguage = "" + Cap_UpdateGestureAgentInventory = "" + Cap_UpdateNotecardAgentInventory = "localhost" + Cap_UpdateScriptAgent = "localhost" + Cap_UpdateGestureTaskInventory = "" + Cap_UpdateNotecardTaskInventory = "localhost" + Cap_UpdateScriptTask = "localhost" + Cap_UploadBakedTexture = "localhost" + Cap_UploadObjectAsset = "localhost" + Cap_ViewerStartAuction = "" + Cap_ViewerStats = "" + + ; Capabilities for fetching inventory over HTTP rather than UDP + ; FetchInventoryDescendents2 and FetchInventory2 are the ones used in the latest Linden Lab viewers (from some point in the v2 series and above) + ; It appears that Linden Lab viewer 3.3.1 onwards will not work properly if FetchInventoryDescendents2 and FetchInventory2 are not enabled + Cap_WebFetchInventoryDescendents = "" + Cap_FetchInventoryDescendents2 = "localhost" + Cap_FetchInventory2 = "localhost" + + ; Capability for searching for people + Cap_AvatarPickerSearch = "localhost" + + +[Chat] + ; Controls whether the chat module is enabled. Default is true. + enabled = true; + + ; Distance in meters that whispers should travel. Default is 10m + whisper_distance = 10 + + ; Distance in meters that ordinary chat should travel. Default is 20m + say_distance = 20 + + ; Distance in meters that shouts should travel. Default is 100m + shout_distance = 100 + + +[EntityTransfer] + ; The maximum protocol version that we will use for outgoing transfers + ; Valid values are + ; "SIMULATION/0.3" + ; - This is the default, and it supports teleports to variable-sized regions + ; - Older versions can teleport to this one, but only if the destination region + ; is 256x256 + ; "SIMULATION/0.2" + ; - A source simulator which only implements "SIMULATION/0.1" can still teleport with that protocol + ; - this protocol is more efficient than "SIMULATION/0.1" + ; "SIMULATION/0.1" + ; - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + MaxOutgoingTransferVersion = "SIMULATION/0.3" + + ; The maximum distance in regions that an agent is allowed to teleport + ; along the x or y axis. This is set to 65535 because current viewers + ; can't handle teleports that are greater than this distance + ; Setting to 0 will allow teleports of any distance + ; + max_distance = 65535 + + ; Allow avatars to cross into and out of the region. + AllowAvatarCrossing = true + + ; Minimum user level required for HyperGrid teleports + LevelHGTeleport = 0 + + ; Determine whether the cancel button is shown at all during teleports. + ; This option exists because cancelling at certain points can result in an unuseable session (frozen avatar, etc.) + ; Disabling cancellation can be okay in small closed grids where all teleports are highly likely to suceed. + DisableInterRegionTeleportCancellation = false + + +[Messaging] + ; Control which region module is used for instant messaging. + ; Default is InstantMessageModule (this is the name of the core IM module as well as the setting) + InstantMessageModule = InstantMessageModule + ; MessageTransferModule = MessageTransferModule + ; OfflineMessageModule = OfflineMessageModule + ; OfflineMessageURL = http://yourserver/Offline.php + ; MuteListModule = MuteListModule + ; MuteListURL = http://yourserver/Mute.php + + ; Control whether group messages are forwarded to offline users. Default is true. + ; ForwardOfflineGroupMessages = true + + +[Inventory] + ; Control whether multiple objects sent to inventory should be coaleseced into a single item + ; There are still some issues with coalescence, including the fact that rotation is not restored + ; and some assets may be missing from archive files. + CoalesceMultipleObjectsToInventory = true + + +[Appearance] + ; Persist avatar baked textures + ; Persisting baked textures can speed up login and region border + ; crossings especially with large numbers of users, though it + ; will store potentially large numbers of textures in your asset + ; database + PersistBakedTextures = false + + ; Control the delay before appearance is sent to other avatars and + ; saved in the avatar service. Attempts to limit the impact caused + ; by the very chatty dialog that sets appearance when an avatar + ; logs in or teleports into a region; values are in seconds + DelayBeforeAppearanceSave = 5 + DelayBeforeAppearanceSend = 2 + + ; If true, avatar appearance information is resent to other avatars in the simulator every 60 seconds. + ; This may help with some situations where avatars are persistently grey, though it will not help + ; in other situations (e.g. appearance baking failures where the avatar only appears as a cloud to others). + ResendAppearanceUpdates = false + + ; Turning this on responds to CachedTexture packets to possibly avoid rebaking the avatar + ; on every login + ReuseTextures = false + + +[Attachments] + ; Controls whether avatar attachments are enabled. + ; Defaults to true - only set to false for debugging purposes + Enabled = true + + ; Controls the number of milliseconds that are slept per 100 prims rezzed in attachments + ; Experimental setting to control CPU spiking when avatars with many attachments login/change outfit + ; or when multiple avatars with medium level attachments login/change outfit simultaneously. + ; If 0 then no throttling is performed. + ThrottlePer100PrimsRezzed = 0; + + +[Mesh] + ; enable / disable Collada mesh support + ; default is true + AllowMeshUpload = true + + ; if you use Meshmerizer and want collisions for meshies, setting this to true + ; will cause OpenSim to attempt to decode meshies assets, extract the physics + ; mesh, and use it for collisions. + UseMeshiesPhysicsMesh = true + + ; Minimum user level required to upload meshes + ;LevelUpload = 0 + + +[Textures] + ; If true, textures generated dynamically (i.e. through osSetDynamicTextureData() and similar OSSL functions) are reused where possible + ; Chiefly, reuse occurs if a texture has already been generated with identical data and settings, and that texture contains no dynamic components + ; (e.g. images pulled from an external HTTP address). + ; Reusing previously generated textures results in a much faster update on the viewer but may cause issues if the viewer didn't receive all resolutions of the texture. + ; Currently, it will also increase asset cache use since temporary dynamic textures are no longer deleted. + ; Hence, currently considered experimental. + ; Default is false. + ReuseDynamicTextures = false + + ; If true, then textures generated dynamically that have a low data size relative to their pixel size are not reused + ; This is to workaround an apparent LL 3.3.4 and earlier viewer bug where such textures are not redisplayed properly when pulled from the viewer cache. + ; Only set this to true if you are sure that all the viewers using your simulator will not suffer from this problem. + ; This setting only has an affect is ReuseDynamicTextures = true + ; Default is false + ReuseDynamicLowDataTextures = false + + +[ODEPhysicsSettings] + ; ## + ; ## Physics stats settings + ; + + ; If collect_stats is enabled, then extra stat information is collected which is accessible via the MonitorModule + ; (see http://opensimulator.org/wiki/Monitoring_Module for more details). + collect_stats = false + + ; ## + ; ## Physics logging settings - logfiles are saved to *.DIF files + ; ## + + ; default is false + ;physics_logging = true + ;; every n simulation iterations, the physics snapshot file is updated + ;physics_logging_interval = 50 + ;; append to existing physics logfile, or overwrite existing logfiles? + ;physics_logging_append_existing_logfile = true + + ;## + ;## World Settings + ;## + + ;Gravity. Feel like falling up? change world_gravityz to 9.8 instead of -9.8. m/s + world_gravityx = 0 + world_gravityy = 0 + world_gravityz = -9.8 + + ; Terminal velocity of a falling avatar + ; This is the same http://en.wikipedia.org/wiki/Terminal_velocity#Examples + ; Max value is 255, min value is 0 + avatar_terminal_velocity = 54 + + ; World Step size. (warning these are dangerous. Changing these will probably cause your scene to explode dramatically) + ; reference: fps = (0.089/ODE_STEPSIZE) * 1000; + world_stepsize = 0.0178 + world_internal_steps_without_collisions = 10 + + ;World Space settings. Affects memory consumption vs Collider CPU time for avatar and physical prim + world_hashspace_size_low = -4 + world_hashSpace_size_high = 128 + + ;Dynamic space settings Affects memory consumption vs Collider CPU time for static prim + meters_in_small_space = 29.9 + small_hashspace_size_low = -4 + small_hashspace_size_high = 66 + + ; ## + ; ## Contact properties. (the stuff that happens when things come in contact with each other) + ; ## + + ; surface layer around geometries other geometries can sink into before generating a contact + world_contact_surface_layer = 0.001 + + ; Filtering collisions helps keep things stable physics wise, but sometimes + ; it can be overzealous. If you notice bouncing, chances are it's that. + filter_collisions = false + + ; Non Moving Terrain Contact (avatar isn't moving) + nm_terraincontact_friction = 255.0 + nm_terraincontact_bounce = 0.1 + nm_terraincontact_erp = 0.1025 + + ; Moving Terrain Contact (avatar is moving) + m_terraincontact_friction = 75.0 + m_terraincontact_bounce = 0.05 + m_terrainContact_erp = 0.05025 + + ; Moving Avatar to object Contact + m_avatarobjectcontact_friction = 75.0 + m_avatarobjectcontact_bounce = 0.1 + + ; Object to Object Contact and Non-Moving Avatar to object + objectcontact_friction = 250.0 + objectcontact_bounce = 0.2 + + ; ## + ; ## Avatar Control + ; ## + + ; PID Controller Settings. These affect the math that causes the avatar to reach the + ; desired velocity + ; See http://en.wikipedia.org/wiki/PID_controller + + av_pid_derivative_linux = 2200.0 + av_pid_proportional_linux = 900.0; + + av_pid_derivative_win = 2200.0 + av_pid_proportional_win = 900.0; + + ;girth of the avatar. Adds radius to the height also + av_capsule_radius = 0.37 + + ; Max force permissible to use to keep the avatar standing up straight + av_capsule_standup_tensor_win = 550000 + av_capsule_standup_tensor_linux = 550000 + + ; specifies if the capsule should be tilted (=true; old compatibility mode) + ; or straight up-and-down (=false; better and more consistent physics behavior) + av_capsule_tilted = false + + ; used to calculate mass of avatar. + ; float AVvolume = (float) (Math.PI*Math.Pow(CAPSULE_RADIUS, 2)*CAPSULE_LENGTH); + ; av_density * AVvolume; + av_density = 80 + + ; use this value to cut 52% of the height the sim gives us + ; Currently unused + ; av_height_fudge_factor = 0.52 + + ; Movement. Smaller is faster. + + ; speed of movement with Always Run off + av_movement_divisor_walk = 1.3 + + ; speed of movement with Always Run on + av_movement_divisor_run = 0.8 + + ; When the avatar flies, it will be moved up by this amount off the ground (in meters) + minimum_ground_flight_offset = 3.0 + + ; Plant avatar. This reduces the effect of physical contacts with the avatar. + ; If you have a group of unruly and rude visitors that bump each other, turn this on to make that less attractive. + ; The avatar still allows a small movement based on the PID settings above. Stronger PID settings AND this active + ; will lock the avatar in place + av_planted = false + + ; No Avatar Avatar Collissions. This causes avatar to be able to walk through each other as if they're ghosts but still interact with the environment + av_av_collisions_off = false + + ; ## + ; ## Object options + ; ## + + ; used in the mass calculation. + geometry_default_density = 10.000006836 + + ; amount of ODE steps where object is non moving for ODE to automatically put it to sleep + body_frames_auto_disable = 20 + + ; used to control llMove2Target + body_pid_derivative = 35 + body_pid_gain = 25 + + ; maximum number of contact points to generate per collision + contacts_per_collision = 80 + + ; amount of time a geom/body will try to cross a region border before it gets disabled + geom_crossing_failures_before_outofbounds = 5 + + ; start throttling the object updates if object comes in contact with 3 or more other objects + geom_contactpoints_start_throttling = 3 + + ; send 1 update for every x updates below when throttled + geom_updates_before_throttled_update = 15 + + ; Used for llSetStatus. How rigid the object rotation is held on the axis specified + body_motor_joint_maxforce_tensor_linux = 5 + body_motor_joint_maxforce_tensor_win = 5 + + ; Maximum mass an object can be before it is clamped + maximum_mass_object = 10000.01 + + ; ## + ; ## Sculpted Prim settings + ; ## + + ; Do we want to mesh sculpted prim to collide like they look? + mesh_sculpted_prim = true + + ; number^2 non-physical level of detail of the sculpt texture. 32x32 - 1024 verticies + mesh_lod = 32 + + ; number^2 physical level of detail of the sculpt texture. 16x16 - 256 verticies + mesh_physical_lod = 16 + + ; ## + ; ## Joint support + ; ## + + ; If you would like physics joints to be enabled through a special naming + ; convention in the client, set this to true. + ; (See NINJA Physics documentation, http://opensimulator.org/wiki/NINJA_Physics) + ; Default is false + ;use_NINJA_physics_joints = true + + ; ## + ; ## additional meshing options + ; ## + + ; Physical collision mesh proxies are normally created for complex prim shapes, + ; and collisions for simple boxes and spheres are computed algorithmically. + ; If you would rather have mesh proxies for simple prims, you can set this to + ; true. Note that this will increase memory usage and region startup time. + ; Default is false. + ;force_simple_prim_meshing = true + + +[BulletSim] + ; All the BulletSim parameters can be displayed with the console command + ; "physics get all" and all are defined in the source file + ; OpenSim/Regions/Physics/BulletSPlugin/BSParam.cs. + + ; There are two bullet physics libraries, bulletunmanaged is the default and is a + ; native c++ dll bulletxna is a managed C# dll. They have comparible functionality + ; but the c++ one is much faster. + BulletEngine = "bulletunmanaged" + ; BulletEngine = "bulletxna" + + ; BulletSim can run on its own thread independent of the simulator's heartbeat + ; thread. Enabling this will not let the physics engine slow down avatar movement, etc. + UseSeparatePhysicsThread = false + + ; Terrain implementation can use either Bullet's heightField or BulletSim can build + ; a mesh. 0=heightField, 1=mesh + TerrainImplementation = 0 + ; For mesh terrain, the detail of the created mesh. '1' gives 256x256 (heightfield + ; resolution). '2' gives 512x512. Etc. Cannot be larger than '4'. Higher + ; magnifications use lots of memory. + TerrainMeshMagnification = 2 + + ; Should avatars collide with each other? + AvatarToAvatarCollisionsByDefault = true + + ; Avatar physics height adjustments. + ; http://opensimulator.org/wiki/BulletSim#Adjusting_Avatar_Height + AvatarHeightLowFudge = 0 ; Adjustment at low end of height range + AvatarHeightMidFudge = 0 ; Adjustment at mid point of avatar height range + AvatarHeightHighFudge = 0 ; Adjustment at high end of height range + + ; Avatar walk-up-stairs parameters + ; If an avatar collides with an object 'close to its feet', the avatar will be + ; moved/pushed up do simulate stepping up. + ;AvatarStepHeight = 0.6f ; The height, below which is considered a step collision. + ;AvatarStepAngle = 0.3f ; The angle from vertical (in radians) to consider a surface a step + ;AvatarStepApproachFactor = 2f ; Approach angle factor. O=straight on, .6=~45 degrees. + ;AvatarStepGroundFudge = 0.1f ; Fudge added to bottom of avatar below which step collisions happen + ;AvatarStepForceFactor = 0f ; Avatar is pushed up by its mass times this factor + ;AvatarStepUpCorrectionFactor = 0.8f ; Avatar is displaced up the collision height times this factor + ;AvatarStepSmoothingSteps = 1 ; Number of frames after a step collision that up correction is applied + + ; Terminal velocity of a falling avatar + ; This is the same http://en.wikipedia.org/wiki/Terminal_velocity#Examples + ; negative for a downward speed. + AvatarTerminalVelocity = -54 + + ; Default linkset implmentation + ; 'Constraint' uses physics constraints to hold linkset together. 'Compound' + ; builds a compound shape from the children shapes to create a single physical + ; shape. 'Compound' uses a lot less CPU time. + LinkImplementation = 1 ; 0=constraint, 1=compound + + ; If 'true', offset a linkset's origin based on mass of linkset parts. + LinksetOffsetCenterOfMass = false + + ; If 'true', turn scuplties into meshes + MeshSculptedPrim = true + + ; If 'true', force simple prims (box and sphere) to be meshed + ; If 'false', the Bullet native special case shape is used for square rectangles + ; and even dimensioned spheres. + ForceSimplePrimMeshing = false + + ; If 'true', when creating meshes, remove all triangles that have two equal vertexes. + ; Happens often in sculpties. If turned off, there will be some doorways + ; that cannot be walked through. + ShouldRemoveZeroWidthTriangles = true + + ; If 'true', use convex hull definition in mesh asset if present. + ShouldUseAssetHulls = true + + ; If there are thousands of physical objects, these maximums should be increased. + MaxCollisionsPerFrame = 2048 + MaxUpdatesPerFrame = 8192 + + ; Detailed physics debug logging. Very verbose. + PhysicsLoggingEnabled = False + PhysicsLoggingDir = "." + VehicleLoggingEnabled = False + + +[RemoteAdmin] + enabled = false + + ; Set this to a nonzero value to have remote admin use a different port + port = 0 + + ; Set this to the ip address that you want the admin server to bind to + bind_ip_address = "0.0.0.0" + + ; This password is required to make any XMLRPC call (should be set as the "password" parameter) + access_password = unknown + + ; List the IP addresses allowed to call RemoteAdmin + ; If access_ip_addresses isn't set, then all IP addresses can access RemoteAdmin. + ; access_ip_addresses = 0.0.0.0, 0.0.0.0 ... + ; access_ip_addresses = + + ; set this variable to true if you want the create_region XmlRpc + ; call to unconditionally enable voice on all parcels for a newly + ; created region [default: false] + create_region_enable_voice = false + + ; set this variable to false if you want the create_region XmlRpc + ; call to create all regions as private per default (can be + ; overridden in the XmlRpc call) [default: true] + create_region_public = false + + ; the create_region XmlRpc call uses region_file_template to generate + ; the file name of newly create regions (if they are created + ; persistent). the parameter available are: + ; {0} - X location + ; {1} - Y location + ; {2} - region UUID + ; {3} - region port + ; {4} - region name with " ", ":", "/" mapped to "_" + region_file_template = "{0}x{1}-{2}.ini" + + ; we can limit the number of regions that XmlRpcCreateRegion will + ; allow by setting this to a positive, non-0 number: as long as the + ; number of regions is below region_limits, XmlRpcCreateRegion will + ; succeed. setting region_limit to 0 disables the check. + ; default is 0 + ;region_limit = 0 + + ; enable only those methods you deem to be appropriate using a | delimited whitelist + ; for example, enabled_methods = admin_broadcast|admin_region_query|admin_save_oar|admin_save_xml + ; if this parameter is not specified but enabled = true, all methods will be available + enabled_methods = all + + ; specify the default appearance for an avatar created through the remote admin interface + ; This will only take effect is the file specified by the default_appearance setting below exists + ;default_male = Default Male + ;default_female = Default Female + + ; update appearance copies inventory items and wearables of default avatars. if this value is false + ; (default), just worn assets are copied to the Clothes folder; if true, all Clothes and Bodyparts + ; subfolders are copied. the receiver will wear the same items the default avatar did wear. + ;copy_folders = false + + ; path to default appearance XML file that specifies the look of the default avatars + ;default_appearance = default_appearance.xml + + +; RestPlugins are not currently operational. +;[RestPlugins] +; ; Change this to true to enable REST Plugins. This must be true if you wish to use +; ; REST Region or REST Asset and Inventory Plugins +; enabled = false +; god_key = SECRET +; prefix = /admin + + +;[RestRegionPlugin] +; ; Change this to true to enable the REST Region Plugin +; enabled = false + + +;[RestHandler] +; ; Change this to true to enable the REST Asset and Inventory Plugin +; enabled = false +; authenticate = true +; secured = true +; extended-escape = true +; realm = OpenSim REST +; dump-asset = false +; path-fill = true +; dump-line-size = 32 +; flush-on-error = true + + +; IRC bridge is experimental, so if it breaks... keep both parts... yada yada +; also, not good error detection when it fails +[IRC] + enabled = false; you need to set this to true otherwise it won't connect + ;server = name.of.irc.server.on.the.net + ;; user password - only use this if the server requires one + ;password = mypass + ;nick = OpenSimBotNameProbablyMakeThisShorter + ;channel = #the_irc_channel_you_want_to_connect_to + ;user = "USER OpenSimBot 8 * :I'm an OpenSim to IRC bot" + ;port = 6667 + ;; channel to listen for configuration commands + ;commands_enabled = false + ;command_channel = 2777 + ;report_clients = true + ;; relay private chat connections + ;; relay_private_channels = true: will relay IRC chat from/to private in-world channels + ;; relay_private_channel_out -- channel to send messages out to the IRC bridge + ;; relay_private_channel_in -- channel to receive message from the IRC bridge + ;; relay_chat = false: IRC bridge will not relay normal chat + ;; access_password -- simple security device + ;; + ;; so, to just relay chat from an IRC channel to in-world region and vice versa: + ;; + ;; relay_private_channels = false + ;; relay_chat = true + ;; + ;; to relay chat only to/from private in-world channels: + ;; + ;; relay_chat = false + ;; relay_private_channels = true + ;; relay_private_channel_in = 2226 + ;; relay_private_channel_out = 2225 + ;; + ;; in this example, all chat coming in from IRC will be send out via + ;; in-world channel 2226, and all chat from in-world channel 2225 will + ;; be relayed to the IRC channel. + ;; + ;relay_private_channels = false + ;relay_private_channel_in = 2226 + ;relay_private_channel_out = 2225 + ;relay_chat = true + ;access_password = foobar + + ;;fallback_region = name of "default" region + ;;MSGformat fields : 0=botnick, 1=user, 2=region, 3=message + ;; must start with "PRIVMSG {0} : " or irc server will get upset + ;;for : : + ;;msgformat = "PRIVMSG {0} :<{1} in {2}>: {3}" + ;;for : - : + ;msgformat = "PRIVMSG {0} : {3} - {1} of {2}" + ;;for : - from : + ;;msgformat = "PRIVMSG {0} : {3} - from {1}" + + ;; exclude_list allows you to stop the IRC connector from announcing the + ;;arrival and departure of certain users. For example: admins, bots. + + ;exclude_list=User 1,User 2,User 3 + + ;;Shows modal alertbox for entering agent on IRC enabled regions + ;; + ;; Enable Alert, default = false + ;alert_show = false + ;; + ;; Show IRC serverinfo, default = true + ;alert_show_serverinfo = true + ;; + ;alert_msg_pre = "This region is linked to Irc." + ;alert_msg_post = "Everything you say in public chat can be listened." + + +; The following settings control the progression of daytime +; in the Sim. The defaults are the same as the commented out settings +[Sun] + ; number of wall clock hours for an opensim day. 24.0 would mean realtime + ;day_length = 4 + ; Year length in days + ;year_length = 60 + ; Day to Night Ratio + ;day_night_offset = 0.45 + ; send a Sun update every update_interval # of frames. A lower number will + ; make for smoother sun transition at the cost of network + ;update_interval = 100 + + +[Wind] + ; Enables the wind module. Default is true + enabled = true + + ; How often should wind be updated, as a function of world frames. Approximately 50 frames a second + wind_update_rate = 150 + + ; The Default Wind Plugin to load + wind_plugin = SimpleRandomWind + + ; These settings are specific to the ConfigurableWind plugin + ; To use ConfigurableWind as the default, simply change wind_plugin to ConfigurableWind and uncomment the following. + ; avg_strength = 5.0 + ; avg_direction = 0.0 + ; var_strength = 0.0 + ; var_direction = 0.0 + ; rate_change = 1.0 + + ; This setting is specific to the SimpleRandomWind plugin + ; Adjusts wind strength. 0.0 = no wind, 1.0 = normal wind. Default is 1.0 + strength = 1.0 + + +[Cloud] + ; Enable this to generate classic particle clouds above the sim. + ; default is disabled - turn it on here + enabled = false + + ; Density of cloud cover 0.0 to 1.0 Defult 0.5 + density = 0.5 + + ; update interval for the cloud cover data returned by llCloud(). + ; default is 1000 + cloud_update_rate = 1000 + + +[LightShare] + ; This enables the transmission of Windlight scenes to supporting clients, such as the Meta7 viewer. + ; It has no ill effect on viewers which do not support server-side windlight settings. + enable_windlight = false + + +[Trees] + ; Enable this to allow the tree module to manage your sim trees, including growing, reproducing and dying + ; default is false + active_trees = false + + ; Density of tree population + tree_density = 1000.0 + + +[VectorRender] + ; the font to use for rendering text (default: Arial) + ; font_name = "Arial" + + +[LL-Functions] + ; Set the following to true to allow administrator owned scripts to execute console commands + ; currently unused + ; AllowosConsoleCommand=false + + ; Are god functions such as llSetObjectPermMask() allowed? If true then gods and only gods have access to these functions. + ; If false then gods cannot execute these functions either. + AllowGodFunctions = false + + ; Restrict the email address used by llEmail to the address associated with the avatars user account? + ; If true then llEmail will only send email to the address in the user account of the avatar who owns the object containing the script. + ; If false then email may be sent to any valid email address. + RestrictEmail = false + + ; Maximum number of llListen events we allow over the entire region. + ; Set this to 0 to have no limit imposed + max_listens_per_region = 1000 + + ; Maximum number of llListen events we allow per script + ; Set this to 0 to have no limit imposed. + max_listens_per_script = 64 + + ; Maximum number of external urls that scripts can set up in this simulator (e.g. via llRequestURL()) + max_external_urls_per_simulator = 100 + + ; Use size boxes instead of meshed prims, sculpts and mesh when calculating bounding boxes. + ; Speeds up calculations but can make them inaccurate, in some cases very inaccurate. + UseSimpleBoxesInGetBoundingBox = false + + ; Add a third vector with stats when returning results from llGetBoundingBox. + ; Lists objects (groups), prims/meshes/avatars (parts) and vertices rendered. + AddStatsInGetBoundingBox = false + + ; Avatar bounding box, lower X value, constant part, when standing + LowerAvatarBoundingBoxStandingXconst = -0.275 + + ; Avatar bounding box, upper X value, constant part, when standing + UpperAvatarBoundingBoxStandingXconst = 0.275 + + ; Avatar bounding box, lower Y value, constant part, when standing + LowerAvatarBoundingBoxStandingYconst = -0.35 + + ; Avatar bounding box, upper Y value, constant part, when standing + UpperAvatarBoundingBoxStandingYconst = 0.35 + + ; Avatar bounding box, lower Z value, constant part, when standing + LowerAvatarBoundingBoxStandingZconst = -0.1 + + ; Avatar bounding box, lower Z value, coefficient to multiply with avatar height, when standing + LowerAvatarBoundingBoxStandingZcoeff = -0.5 + + ; Avatar bounding box, upper Z value, constant part, when standing + UpperAvatarBoundingBoxStandingZconst = 0.1 + + ; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when standing + UpperAvatarBoundingBoxStandingZcoeff = 0.5 + + ; Avatar bounding box, lower X value, constant part, when groundsitting + LowerAvatarBoundingBoxGroundsittingXconst = -0.3875 + + ; Avatar bounding box, upper X value, constant part, when groundsitting + UpperAvatarBoundingBoxGroundsittingXconst = 0.3875 + + ; Avatar bounding box, lower Y value, constant part, when groundsitting + LowerAvatarBoundingBoxGroundsittingYconst = -0.5 + + ; Avatar bounding box, upper Y value, constant part, when groundsitting + UpperAvatarBoundingBoxGroundsittingYconst = 0.5 + + ; Avatar bounding box, lower Z value, constant part, when groundsitting + LowerAvatarBoundingBoxGroundsittingZconst = -0.05 + + ; Avatar bounding box, lower Z value, coefficient to multiply with avatar height, when groundsitting + LowerAvatarBoundingBoxGroundsittingZcoeff = -0.375 + + ; Avatar bounding box, upper Z value, constant part, when groundsitting + UpperAvatarBoundingBoxGroundsittingZconst = 0.5 + + ; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when groundsitting + UpperAvatarBoundingBoxGroundsittingZcoeff = 0.0 + + ; Avatar bounding box, lower X value, constant part, when sitting + LowerAvatarBoundingBoxSittingXconst = -0.5875 + + ; Avatar bounding box, upper X value, constant part, when sitting + UpperAvatarBoundingBoxSittingXconst = 0.1875 + + ; Avatar bounding box, lower Y value, constant part, when sitting + LowerAvatarBoundingBoxSittingYconst = -0.35 + + ; Avatar bounding box, upper Y value, constant part, when sitting + UpperAvatarBoundingBoxSittingYconst = 0.35 + + ; Avatar bounding box, lower Z value, constant part, when sitting + LowerAvatarBoundingBoxSittingZconst = -0.35 + + ; Avatar bounding box, lower Z value, coefficient to multiply with avatar height, when sitting + LowerAvatarBoundingBoxSittingZcoeff = -0.375 + + ; Avatar bounding box, upper Z value, constant part, when sitting + UpperAvatarBoundingBoxSittingZconst = -0.25 + + ; Avatar bounding box, upper Z value, coefficient to multiply with avatar height, when sitting + UpperAvatarBoundingBoxSittingZcoeff = 0.25 + + ; Safety coefficient for max bounding box from prim size box X coordinate + ; Worst case is twisted and sheared box, 1+sqrt(2) + PrimBoundingBoxSafetyCoefficientX = 2.414214 + + ; Safety coefficient for max bounding box from prim size box Y coordinate + ; Worst case is twisted and sheared box, 1+sqrt(2) + PrimBoundingBoxSafetyCoefficientY = 2.414214 + + ; Safety coefficient for max bounding box from prim size box Z coordinate + ; Worst case is twisted tube, 0.5+sqrt(1.25) + PrimBoundingBoxSafetyCoefficientZ = 1.618034 + + ; Use llCastRay V3 if true. + ; Implements llCastRay similar but not identical to Second Life. + ; See http://wiki.secondlife.com/wiki/LlCastRay . + ; NEW + ; Meshes prims for good accuracy in ray hit detection, + ; handling basic and tortured prims, sculpts and meshes. + ; Uses ellipsoid, correctly sized avatar capsules. + ; Handles complex terrain, multi-prim objects and seated avatars. + ; Implements throttling and the status codes + ; RCERR_UNKNOWN and RCERR_CAST_TIME_EXCEEDED, + ; so LSL scripts need to handle these responses and RCERR_SIM_PERF_LOW. + ; WARNING + ; Can be faster on some servers and scenes, but slower on others, + ; compared to previous version of llCastRay in OpenSimulator. + ; Is in most cases considerably slower than llCastRay in Second Life. + ; Generates geometry meshes and can therefore use much system resources. + UseLlCastRayV3 = false + + ; Accepted calculation precision error in calculations in llCastRay V3 + FloatToleranceInLlCastRay = 0.00001 + + ; Accepted distance difference between duplicate hits in llCastRay V3 + FloatTolerance2InLlCastRay = 0.001 + + ; Detail level when rendering prims in llCastRay V3 + ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call + PrimDetailLevelInLlCastRay = 1 + + ; Detail level when rendering sculpts in llCastRay V3 + ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call + SculptDetailLevelInLlCastRay = 1 + + ; Detail level when rendering meshes in llCastRay V3 + ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call + MeshDetailLevelInLlCastRay = 3 + + ; Detail level when rendering avatar capsules in llCastRay V3 + ; 0 = Low, 1 = Medium, 2 = High, 3 = Highest, higer level gives better accuracy but slower call + AvatarDetailLevelInLlCastRay = 1 + + ; Maximum number of returned hits from llCastRay V3 + MaxHitsInLlCastRay = 16 + + ; Maximum number of returned hits per prim from llCastRay V3 + MaxHitsPerPrimInLlCastRay = 16 + + ; Maximum number of returned hits per object from llCastRay V3 + MaxHitsPerObjectInLlCastRay = 16 + + ; Report ray intersections with surfaces on exits from a prim as hits in llCastRay V3 if true + DetectExitHitsInLlCastRay = false + + ; Filter on parts instead of groups in llCastRay V3 if true + FilterPartsInLlCastRay = false + + ; Detect attachments in llCastRay V3 if true + DoAttachmentsInLlCastRay = false + + ; Throttle period length in ms before which all old llCastRay use is discarded in llCastRay V3 + ; The sum of AvailableTimeInMsPerRegionInLlCastRay and all AvailableTimeInMsPerAvatarInLlCastRay should not exceed this + ThrottleTimeInMsInLlCastRay = 200 + + ; Available time in ms for llCastRay per throttle period and 65536 m2 land area in llCastRay V3 + AvailableTimeInMsPerRegionInLlCastRay = 40 + + ; Available time in ms for llCastRay per throttle period and avatar when script in attachment or vehicle in llCastRay V3 + AvailableTimeInMsPerAvatarInLlCastRay = 10 + + ; Required available time in ms left to perform a new llCastRay in llCastRay V3 + RequiredAvailableTimeInMsInLlCastRay = 2 + + ; Maximum available time in ms possible in llCastRay V3, not to get too high values with varregions + MaximumAvailableTimeInMsInLlCastRay = 40 + + ; Use cached meshes in llCastRay V3 if true + ; Improves performance but uses more memory + UseMeshCacheInLlCastRay = true + + +[DataSnapshot] + ; The following set of configs pertains to search. + ; Set index_sims to true to enable search engines to index your searchable data + ; If false, no data will be exposed, DataSnapshot module will be off, and you can ignore the rest of these search-related configs + ; default is false + index_sims = false + + ; The variable data_exposure controls what the regions expose: + ; minimum: exposes only things explicitly marked for search + ; all: exposes everything + data_exposure = minimum + + ; If search is on, change this to your grid name; will be ignored for standalones + gridname = "OSGrid" + + ; Period between data snapshots, in seconds. 20 minutes, for starters, so that you see the initial changes fast. + ; Later, you may want to increase this to 3600 (1 hour) or more + default_snapshot_period = 1200 + + ; This will be created in bin, if it doesn't exist already. It will hold the data snapshots. + snapshot_cache_directory = "DataSnapshot" + + ; Uncomment if you want to index your data with this and/or other search providers. One entry per + ; data service + ;DATA_SRV_MISearch = "http://metaverseink.com/cgi-bin/register.py" + + +[Economy] + ; These economy values get used in the BetaGridLikeMoneyModule. - This module is for demonstration only - + ; The default economy module only implements just enough to allow free actions (transfer of objects, etc). + ; There is no intention to implement anything further in core OpenSimulator. + ; This functionality has to be provided by third party modules. + + ;; Enables selling things for $0. Default is true. + SellEnabled = true + + ;; Money Unit fee to upload textures, animations etc. Default is 0. + PriceUpload = 0 + + ;; Money Unit fee to create groups. Default is 0. + PriceGroupCreate = 0 + + ; We don't really know what the rest of these values do. These get sent to the client + ; These taken from Agni at a Public Telehub. Change at your own risk. + ObjectCount = 0 + PriceEnergyUnit = 100 + PriceObjectClaim = 10 + PricePublicObjectDecay = 4 + PricePublicObjectDelete = 4 + PriceParcelClaim = 1 + PriceParcelClaimFactor = 1 + + PriceRentLight = 5 + TeleportMinPrice = 2 + TeleportPriceExponent = 2 + EnergyEfficiency = 1 + PriceObjectRent = 1 + PriceObjectScaleFactor = 10 + PriceParcelRent = 1 + + +[XEngine] + ; Enable this engine in this OpenSim instance + Enabled = true + + ; How many threads to keep alive even if nothing is happening + MinThreads = 2 + + ; How many threads to start at maximum load + MaxThreads = 100 + + ; Time a thread must be idle (in seconds) before it dies + IdleTimeout = 60 + + ; Thread priority ("Lowest", "BelowNormal", "Normal", "AboveNormal", "Highest") + Priority = "BelowNormal" + + ; Maximum number of events to queue for a script (excluding timers) + MaxScriptEventQueue = 300 + + ; Stack size per thread created + ThreadStackSize = 262144 + + ; Set this to true (the default) to load each script into a separate + ; AppDomain. Setting this to false will load all script assemblies into the + ; current AppDomain, which will reduce the per-script overhead at the + ; expense of reduced security and the inability to garbage collect the + ; script assemblies + AppDomainLoading = true + + ; Controls whether previously compiled scripts DLLs are deleted on sim restart. If you set this to false + ; then startup will be considerably faster since scripts won't need to be recompiled. However, then it becomes your responsibility to delete the + ; compiled scripts if you're recompiling OpenSim from source code and internal interfaces used + ; by scripts have changed. + ; DeleteScriptsOnStartup = false + + ; Controls whether scripts are stopped by aborting their threads externally (abort) + ; or by co-operative checks inserted by OpenSimulator into compiled script (co-op). + ; co-op will be more stable as aborting threads can cause instability. + ; abort was the default option in OpenSimulator 0.8 and before. + ; If this setting is changed between co-op and abort, then existing scripts will automatically be recompiled if necessary. + ; However, the setting change will not take affect until the next time you restart the simulator. + ; Setting changes will not affect state information stored for scripts. + ScriptStopStrategy = co-op + + ; Rate to poll for asynchronous command replies (ms) + ; currently unused + ;AsyncLLCommandLoopms = 50 + + ; Save the source of all compiled scripts + WriteScriptSourceToDebugFile = false + + ; Default language for scripts + DefaultCompileLanguage = lsl + + ; List of allowed languages (lsl,vb,cs) + ; AllowedCompilers=lsl,cs,vb + ; *warning*, non lsl languages have access to static methods such as System.IO.File. Enable at your own risk. + AllowedCompilers=lsl + + ; Compile debug info (line numbers) into the script assemblies + CompileWithDebugInformation = true + + ; Allow the user of mod* functions. This allows a script to pass messages + ; to a region module via the modSendCommand() function + ; Default is false + AllowMODFunctions = false + + ; Allow the use of os* functions (some are dangerous) + AllowOSFunctions = false + + ; Allow the user of LightShare functions + AllowLightShareFunctions = false + + ; Threat level to allow, one of None, VeryLow, Low, Moderate, High, VeryHigh, Severe + OSFunctionThreatLevel = VeryLow + + ; OS Functions enable/disable + ; For each function, you can add one line, as shown + ; The default for all functions allows them if below threat level + + ; true allows the use of the function unconditionally + ; Allow_osSetRegionWaterHeight = true + + ; false disables the function completely + ; Allow_osSetRegionWaterHeight = false + + ; Comma separated list of UUIDS allows the function for that list of UUIDS + ; Allow_osSetRegionWaterHeight = 888760cb-a3cf-43ac-8ea4-8732fd3ee2bb + + ; Comma separated list of owner classes that allow the function for a particular class of owners. Choices are + ; - PARCEL_GROUP_MEMBER: allow if objectgroup is the same group as the parcel + ; - PARCEL_OWNER: allow if the objectowner is parcelowner + ; - ESTATE_MANAGER: allow if the object owner is a estate manager + ; - ESTATE_OWNER: allow if objectowner is estateowner + ; Allow_osSetRegionWaterHeight = 888760cb-a3cf-43ac-8ea4-8732fd3ee2bb, PARCEL_OWNER, ESTATE_OWNER>, ... + + ; You can also use script creators as the uuid + ; Creators_osSetRegionWaterHeight = , ... + + ; If both Allow_ and Creators_ are given, effective permissions + ; are the union of the two. + + ; Interval (s) between background save of script states + SaveInterval = 120 + + ; Interval (s) between maintenance runs (0 = disable) + MaintenanceInterval = 10 + + ; Time a script can spend in an event handler before it is interrupted + EventLimit = 30 + + ; If a script overruns it's event limit, kill the script? + KillTimedOutScripts = false + + ; Amount of time in milliseconds we will wait for an event to completely normally when a script stop is requested + ; before aborting the thread (such as when an object containing scripts is taken into inventory). + WaitForEventCompletionOnScriptStop = 1000; + + ; Sets the multiplier for the scripting delays + ScriptDelayFactor = 1.0 + + ; The factor the 10 m distances llimits are multiplied by + ScriptDistanceLimitFactor = 1.0 + + ; Maximum length of notecard line read + ; Increasing this to large values potentially opens + ; up the system to malicious scripters + ; NotecardLineReadCharsMax = 255 + + ; Minimum settable timer interval. Any timer setting less than this is + ; rounded up to this minimum interval. + ; MinTimerInterval = 0.5 + + ; Sensor settings + SensorMaxRange = 96.0 + SensorMaxResults = 16 + + ; Allow for llCreateLink and llBreakLink to work without asking for permission + ; only enable this in a trusted environment otherwise you may be subject to hijacking + ; AutomaticLinkPermission = false + + ; Disable underground movement of prims (default true); set to + ; false to allow script controlled underground positioning of + ; prims + ; DisableUndergroundMovement = true + + ;; Path to script assemblies + ; ScriptEnginesPath = "ScriptEngines" + + +[Concierge] + ; Enable concierge module + ; Default is false + enabled = false + + ; name of the concierge + whoami = "jeeves" + + ; password for updating the welcome message templates via XmlRpc + password = SECRET + + ; regex specifying for which regions concierge service is desired; if + ; empty, then for all + regions = "^MeetingSpace-" + + ; for each region that matches the regions regexp you can provide + ; (optionally) a welcome template using format substitution: + ; {0} is replaced with the name of the avatar entering the region + ; {1} is replaced with the name of the region + ; {2} is replaced with the name of the concierge (whoami variable above) + + welcomes = /path/to/welcome/template/directory + + ; Concierge can send attendee lists to an event broker whenever an + ; avatar enters or leaves a concierged region. the URL is subject + ; to format substitution: + ; {0} is replaced with the region's name + ; {1} is replaced with the region's UUID + broker = "http://broker.place.com/{1}" + + +[MRM] + ; Enables the Mini Region Modules Script Engine. + ; default is false + Enabled = false + + ; Runs MRM in a Security Sandbox + ; WARNING: DISABLING IS A SECURITY RISK. + Sandboxed = true + + ; The level sandbox to use, adjust at your OWN RISK. + ; Valid values are: + ; * FullTrust + ; * SkipVerification + ; * Execution + ; * Nothing + ; * LocalIntranet + ; * Internet + ; * Everything + SandboxLevel = "Internet" + + ; Only allow Region Owners to run MRMs + ; May represent a security risk if you disable this. + OwnerOnly = true + + +[Hypergrid] + ; Keep it false for now. Making it true requires the use of a special client in order to access inventory + safemode = false + + +[VivoxVoice] + ; The VivoxVoice module will allow you to provide voice on your + ; region(s). It uses the same voice technology as the LL grid and + ; works with recent LL clients (we have tested 1.22.9.110075, so + ; anything later ought to be fine as well). + ; + ; For this to work you need to obtain an admin account from Vivox + ; that allows you to create voice accounts and region channels. + + enabled = false + + ; vivox voice server + vivox_server = www.foobar.vivox.com + + ; vivox SIP URI + vivox_sip_uri = foobar.vivox.com + + ; vivox admin user name + vivox_admin_user = DeepThroat + + ; vivox admin password + vivox_admin_password = VoiceG4te + + ; channel type: "channel" or "positional" + ; - positional: spatial sound (default) + ; - channel: normal "conference call", no spatial sound + ;vivox_channel_type = positional + + ; channel characteristics (unless you know what you are doing, i'd + ; leave them as they are --- now you WILL muck around with them, + ; huh? sigh) + + ; channel distance model: + ; 0 - no attenuation + ; 1 - inverse distance attenuation + ; 2 - linear attenuation (default) + ; 3 - exponential attenuation + ;vivox_channel_distance_model = 2 + + ; channel mode: + ; - "open" (default) + ; - "lecture" + ; - "presentation" + ; - "auditorium" + ;vivox_channel_mode = "open" + + ; channel roll off: rate of attenuation + ; - a value between 1.0 and 4.0, default is 2.0 + ;vivox_channel_roll_off = 2.0 + + ; channel max range: distance at which channel is silent + ; - a value between 0 and 160, default is 80 + ;vivox_channel_max_range = 80 + + ; channel clamping distance: distance before attenuation applies + ; - a value between 0 and 160, default is 10 + ;vivox_channel_clamping_distance = 10 + + +[Groups] + Enabled = false + + ; This is the current groups stub in Region.CoreModules.Avatar.Groups. All the other settings below only really + ; apply to the Flotsam/SimianGrid GroupsModule + Module = Default + + ; This module can use a PHP XmlRpc server from the Flotsam project at http://code.google.com/p/flotsam/ + ; or from the SimianGrid project at http://code.google.com/p/openmetaverse + ;Module = GroupsModule + + ; Enable Group Notices + ;NoticesEnabled = true + + ; This makes the Group module very chatty on the console. + DebugEnabled = false + + ; This makes the Groups Messaging module very chatty on the console. + DebugMessagingEnabled = false + + ; Groups data is cached for this number of seconds before another request is made to the groups service + ; Set to 0 to disable the cache. + ; Default is 30 seconds + GroupsCacheTimeout = 30 + + ; Specify which messaging module to use for groups messaging and if it's enabled + MessagingModule = GroupsMessagingModule + ;MessagingEnabled = true + + ; Experimental option to only message cached online users rather than all users + ; Should make large group with few online members messaging faster, as the expense of more calls to ROBUST presence service + ; (Flotsam groups only; in V2 this is always on) + MessageOnlineUsersOnly = false + + ; Service connectors to the Groups Service. Select one depending on whether you're using a Flotsam XmlRpc backend or a SimianGrid backend + + ; SimianGrid Service for Groups + ;ServicesConnectorModule = SimianGroupsServicesConnector + ;GroupsServerURI = http://mygridserver.com:82/Grid/ + + ; Flotsam XmlRpc Service for Groups + ;ServicesConnectorModule = XmlRpcGroupsServicesConnector + ;GroupsServerURI = http://yourxmlrpcserver.com/xmlrpc.php + + ; XmlRpc Security settings. These must match those set on your backend groups service if the service is using these keys + ;XmlRpcServiceReadKey = 1234 + ;XmlRpcServiceWriteKey = 1234 + + ; Disables HTTP Keep-Alive for XmlRpcGroupsServicesConnector HTTP Requests, + ; this is a work around fora problem discovered on some Windows based region servers. + ; Only disable keep alive if you see a large number (dozens) of the following Exceptions: + ; System.Net.WebException: The request was aborted: The request was canceled. + ; XmlRpcDisableKeepAlive = false + + ; Minimum user level required to create groups + ;LevelGroupCreate = 0 + + +[PacketPool] + ;RecyclePackets = true; + ;RecycleDataBlocks = true; + + ; If true, then the basic packet objects used to receive data are also recycled, not just the LLUDP packets. + ; This reduces data churn + RecycleBaseUDPPackets = true + + +[InterestManagement] + ; This section controls how state updates are prioritized for each client + ; Valid values are BestAvatarResponsiveness, Time, Distance, + ; SimpleAngularDistance, and FrontBack + UpdatePrioritizationScheme = BestAvatarResponsiveness + ReprioritizationEnabled = true + ReprioritizationInterval = 2000.0 + RootReprioritizationDistance = 10.0 + ChildReprioritizationDistance = 20.0 + + ; If n > 1, only every n UDP terse updates will be sent to observers of an avatar that are in the same region + ; Updates will always be sent to the avatar that the update addresses and if av velocity is effectively zero (to prevent drift due to missing updates). + ; n > 1 will reduce UDP traffic but will lead to laggier movement observed in other avatars. + RootTerseUpdatePeriod = 0 + + ; If n > 1, only every n UDP terse updates will be sent to observers of an avatar that are in another region + ; n > 1 will reduce UDP traffic but may lead to laggier movement observed in other avatars, though values up to 4 may not generate a noticeable effect. + ChildTerseUpdatePeriod = 0 + + ; Send an update to clients if the difference from the last sent avatar position is greater than this tolerance + RootPositionUpdateTolerance = 0.05 + + ; Send an update to clients if the euclidian difference from the last sent avatar rotation is greater than this tolerance + RootRotationUpdateTolerance = 0.1 + + ; Send an update to clients if the difference from the last sent avatar velocity is greater than this tolerance + RootVelocityUpdateTolerance = 0.001 + +[Monitoring] + ; Enable region monitoring + ; If true, this will print out an error if more than a minute has passed since the last simulator frame + ; Also is another source of region statistics provided via the regionstats URL + Enabled = true + + +[WebStats] + ; View region statistics via a web page + ; See http://opensimulator.org/wiki/FAQ#Region_Statistics_on_a_Web_Page + ; Use a web browser and type in the "Login URI" + "/SStats/" + ; For example- http://127.0.0.1:9000/SStats/ + ; enabled=false + +[Statistics] + ; NumberOfFrames is used in a moving average calculation, where NumberOfFrames is the number of frames + ; to include in the averaging calculations + NumberOfFrames=10 + +[MediaOnAPrim] + ; Enable media on a prim facilities + Enabled = true; + + +[NPC] + ;; Enable Non Player Character (NPC) facilities + Enabled = false + + +[Terrain] + ; Values can be "pinhead-island" or "flat" + InitialTerrain = "pinhead-island" + ; If 'true' each avatar is only sent terrain patches within their view distance + ; This also changes the region terrain loading from 'lawn mower' to ordered around + ; the avatar outward. + SendTerrainUpdatesByViewDistance = True + +[LandManagement] + ; When editing terrain or objects, parcel layer info is updated in the viewer. + ; This can be expensive for large regions. If this variable is 'true', only the + ; parcel layer data around the area of interest is sent. The parcel layer info + ; is sent for 'ParcelLayerViewDistance' around the interest point. + ; If 'ParcelLayerViewDistance' is >= 128, the operation for legacy sized regions + ; will be what it has always been (send the whole region's parcel layer info). + ; Other parcel updates (login, changing parcel ownership, ...) will still send + ; whole region. + LimitParcelLayerUpdateDistance = true + ParcelLayerViewDistance = 128 + +;; +;; If you are using a simian grid frontend you can enable +;; this module to upload tile images for the mapping fn +;; +[SimianGridMaptiles] + Enabled = False + MaptileURL = "http://www.mygrid.com/Grid/" + RefreshTime = 3600 + + +;; +;; JsonStore module provides structured store for scripts +;; +[JsonStore] + Enabled = False + + ;; Enable direct access to the SOP dynamic attributes + EnableObjectStore = False + MaxStringSpace = 0 + + +;; +;; These are defaults that are overwritten below in [Architecture]. +;; These defaults allow OpenSim to work out of the box with +;; zero configuration +;; +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "assets/AssetSets.xml" + + ; Disable this to prevent the default asset set from being inserted into the + ; asset store each time the region starts + AssetLoaderEnabled = true + + +[GridService] + ;; default standalone, overridable in StandaloneCommon.ini + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + + +[AutoBackupModule] + ;; default is module is disabled at the top level + AutoBackupModuleEnabled = false + + +[Sounds] + ;; {Module} {} {Implementation of ISoundModule to use.} {OpenSim.Region.CoreModules.dll:SoundModule} + Module = OpenSim.Region.CoreModules.dll:SoundModule + + ;; {MaxDistance} {} {Cut-off distance at which sounds will not be sent to users} {100.0} + MaxDistance = 100.0 + + +[ServiceThrottle] + ;; Default time interval (in ms) for the throttle service thread to wake up + Interval = 5000 + +[Dwell] + ;; This enables the built in basic dwell module + DwellModule = DefaultDwellModule + +[Modules] + Include-modules = "addon-modules/*/config/*.ini" + diff --git a/bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config b/bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config new file mode 100755 index 0000000000..2763525619 --- /dev/null +++ b/bin/Physics/OpenSim.Region.Physics.BulletSPlugin.dll.config @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/bin/Prebuild.exe b/bin/Prebuild.exe new file mode 100755 index 0000000000..05deb1f551 Binary files /dev/null and b/bin/Prebuild.exe differ diff --git a/bin/PrimMesher.dll b/bin/PrimMesher.dll new file mode 100755 index 0000000000..87022b7cfc Binary files /dev/null and b/bin/PrimMesher.dll differ diff --git a/bin/Regions/.keep b/bin/Regions/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/Regions/Regions.ini.example b/bin/Regions/Regions.ini.example new file mode 100644 index 0000000000..e20fee609b --- /dev/null +++ b/bin/Regions/Regions.ini.example @@ -0,0 +1,98 @@ +; * This is an example region config file. +; * +; * If OpenSimulator is started up without any regions, it will ask you configuration questions to generate a Regions.ini file for you. +; * So there is no need to change this file directly, it is only for reference. +; * However, if you prefer you can also copy this file to Regions.ini and appropriately change the parameters below. +; * Only files ending with .ini and .xml in this directly will be loaded by OpenSimulator. +; * +; * You can multiple regions into one file or make one file per region +; * The section name is the region name +; * + +[Default Region] + +; * +; * You MUST change this! It will NOT be done for you! +; * + +RegionUUID = 11111111-2222-3333-4444-555555555555 + +Location = 1000,1000 +InternalAddress = 0.0.0.0 +InternalPort = 9000 +AllowAlternatePorts = False +ExternalHostName = SYSTEMIP + +; * +; * Variable-sized regions allows the creation of large, borderless spaces. +; * The default is 256 meters. For larger spaces, set these to multiples of 256. +; * For the time being, X and Y need to be the same. +; * +; SizeX = 512 +; SizeY = 512 + +; * +; * Prim data +; * This allows limiting the sizes of prims and the region prim count +; * + +; NonPhysicalPrimMax = 256 +; PhysicalPrimMax = 64 +; ClampPrimSize = False +; MaxPrims = 15000 +; MaxAgents = 100 + +; * Max prims per user (per parcel). +; * Negative values will disable the check. +; MaxPrimsPerUser = -1 + +; * +; * Multi-Tenancy. Only set if needed +; * + +; ScopeID = "00000000-0000-0000-0000-000000000000" + +; * +; * Product name (used in search from viewer 1.23 +; * + +; RegionType = "Mainland" + +; * Region Specific Static Maptiles: +; * Important: To use any kind of texture *assets* as a static maptile, the following +; * things must be set in the [Map] section of OpenSim.ini : +; * +; * MapImageModule = "MapImageModule" +; * GenerateMaptiles = false +; * +; * Now, there is a setting in [Map] in OpenSim.ini called +; * +; * MaptileStaticUUID = 00000000-0000-0000-0000-000000000000 +; * +; * where, given the criteria above, lets you specify the UUID of a texture asset to use +; * as a maptile *Simulator Wide*. Here, you can override that on a per region basis for +; * Simulators that run multiple regions: + +; MaptileStaticUUID = 00000000-0000-0000-0000-000000000000 + + +; * Region Specific Static Maptiles from file: +; * It is also possible to create maptiles using external image files of the right size +; * and supported formats (bmp,png,jpg in RGB 24bpp format) +; * +; * Important: To use any kind of texture *files* as a static maptile, the following +; * things must be set in the [Map] section of OpenSim.ini : +; * +; * MapImageModule = "MapImageModule" +; * GenerateMaptiles = true +; * +; * The image must be the same size in pixels as the region or varregion is in meters. +; * i.e. 256x256 pixels for single region of 256x256m, or 1280x1280 pixels for a varregion +; * of size 1280x1280m. The image can be loaded from anywhere by setting the path +; * ie: MaptileStaticFile = "maptiles/SomeFile.png" +; * +; * If this setting is used, then the base map is generated from this file instead of being +; * built using MapImageModule's terrain and prim renderer. Parcel 'for sale' overlays are +; * still drawn on top of the static map by the World Map module. + +; MaptileStaticFile = "SomeFile.png" diff --git a/bin/Robust.32BitLaunch.exe.config b/bin/Robust.32BitLaunch.exe.config new file mode 100644 index 0000000000..0399a1bd34 --- /dev/null +++ b/bin/Robust.32BitLaunch.exe.config @@ -0,0 +1,63 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example new file mode 100644 index 0000000000..36025d5fba --- /dev/null +++ b/bin/Robust.HG.ini.example @@ -0,0 +1,778 @@ +; * Run +; * $ Robust.exe -inifile Robust.HG.ini +; * + +; * Configurations for enabling HG1.5 +; * +; * HG1.5 handlers are: OpenSim.Server.Handlers.dll:GatekeeperService +; * OpenSim.Server.Handlers.dll:UserAgentService +; * Additional OpenSim.Server.Handlers.dll:AssetServiceConnector and +; * OpenSim.Server.Handlers.dll:XInventoryInConnector +; * are started in port 8002, outside the firewall +; * +; ** +; * +; * The Const section allows us to define some basic information that we +; * will use throughout our configuration. We will provide examples for +; * setting the base url of the Robust server and the public and private ports +; * it uses. Changing the values of the constants will set the operating +; * parameters thoughout the configuration. Other constants that may prove +; * to be useful may be added to the followin section. They may be +; * referenced anywhere in the configuration by using ${Const|Name}. One +; * such use is providing a base path for setting locations that Robust +; * uses to write data. +; * +[Const] + + ; The URL of the Robust server + BaseURL = "http://127.0.0.1" + + ; The public port of the Robust server + PublicPort = "8002" + + ; The private port of the Robust server + PrivatePort = "8003" + +; * The startup section lists all the connectors to start up in this server +; * instance. This may be only one, or it may be the entire server suite. +; * Multiple connectors should be separated by commas. +; * +; * These are the IN connectors the server uses, the in connectors +; * read this config file and load the needed service and database connectors +; * +; * The full syntax of a connector string is: +; * [[@]/][:] +; * +[Startup] + ; Place to create a PID file + ; If no path if specified then a PID file is not created. + ; PIDFile = "/tmp/Robust.exe.pid" + + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must have R/W access to the location + RegistryLocation = "." + + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must have R/W access to the location + ConfigDirectory = "." + + ; Console commands can be saved to a file, so the command history persists after a restart. (default is true) + ConsoleHistoryFileEnabled = true + + ; The history file can be just a filename (relative to OpenSim's bin/ directory + ; or it can be a full path to somewhere else. (default is OpenSimConsoleHistory.txt in bin/) + ConsoleHistoryFile = "RobustConsoleHistory.txt" + + ; How many lines of command history should we keep? (default is 100) + ConsoleHistoryFileLines = 100 + + +[ServiceList] + AssetServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AssetServiceConnector" + InventoryInConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XInventoryInConnector" + ;; Uncomment if you have set up Freeswitch (see [FreeswitchService] below) + ;VoiceConnector = "8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector" + GridServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridServiceConnector" + GridInfoServerInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:GridInfoServerInConnector" + AuthenticationServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" + OpenIdServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:OpenIdServerConnector" + AvatarServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AvatarServiceConnector" + LLLoginServiceInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector" + PresenceServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:PresenceServiceConnector" + UserAccountServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:UserAccountServiceConnector" + GridUserServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridUserServiceConnector" + AgentPreferencesServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AgentPreferencesServiceConnector" + FriendsServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:FriendsServiceConnector" + MapAddServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:MapAddServiceConnector" + MapGetServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:MapGetServiceConnector" + ;; Uncomment this if you want offline IM to work + ; OfflineIMServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.OfflineIM.dll:OfflineIMServiceRobustConnector" + ;; Uncomment this if you want Groups V2 to work + ; GroupsServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" + ;; Uncomment to provide bakes caching + ;BakedTextureService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XBakesConnector" + + ;; Additions for Hypergrid + + GatekeeperServiceInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector" + UserAgentServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:UserAgentServerConnector" + HeloServiceInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:HeloServiceInConnector" + HGFriendsServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:HGFriendsServerConnector" + InstantMessageServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:InstantMessageServerConnector" + HGInventoryServiceConnector = "HGInventoryService@${Const|PublicPort}/OpenSim.Server.Handlers.dll:XInventoryInConnector" + HGAssetServiceConnector = "HGAssetService@${Const|PublicPort}/OpenSim.Server.Handlers.dll:AssetServiceConnector" + ;; Uncomment this if you want Groups V2, HG to work + ; HGGroupsServiceConnector = "${Const|PublicPort}/OpenSim.Addons.Groups.dll:HGGroupsServiceRobustConnector" + + ;; Uncomment for UserProfiles see [UserProfilesService] to configure... + ; UserProfilesServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:UserProfilesConnector" + + ;; Uncomment if you want to have centralized estate data + ; EstateDataService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:EstateDataRobustConnector" + +; * This is common for all services, it's the network setup for the entire +; * server instance, if none is specified above +; * +[Network] + port = ${Const|PrivatePort} + + ; HTTPS for "Out of band" management applications such as the remote admin + ; module. May specify https_main = True to make the main http server + ; use https or "False" to make the main server HTTP + ; https_main = False + ; + ; Create https_listener = "True" will create a listener on the port + ; specified. Provide the path to your server certificate along with it's + ; password + ; https_listener = False + ; + ; Set our listener to this port + ; https_port = 0 + ; + ; Path to X509 certificate + ; cert_path = "path/to/cert.p12" + ; + ; Password for cert + ; cert_pass = "password" + + ;; The follow 3 variables are for HTTP Basic Authentication for the Robust services. + ;; Use this if your central services in port ${Const|PrivatePort} need to be accessible on the Internet + ;; but you want to protect them from unauthorized access. + ; AuthType = "BasicHttpAuthentication" + ; HttpAuthUsername = "some_username" + ; HttpAuthPassword = "some_password" + ;; + ;; AuthType above can be overriden in any of the service sections below by + ; AuthType = "None" + ;; This is useful in cases where you want to protect most of the services, + ;; but unprotect individual services. Username and Password can also be + ;; overriden if you want to use different credentials for the different services. + ;; Hypergrid services are not affected by this; they are publicly available + ;; by design. + + ;; By default, scripts are not allowed to call private services via llHttpRequest() + ;; Such calls are detected by the X-SecondLife-Shared HTTP header + ;; If you allow such calls you must be sure that they are restricted to very trusted scripters + ;; (remember scripts can also be in visiting avatar attachments). + ;; This can be overriden in individual private service sections if necessary + AllowllHTTPRequestIn = false + + ; * The following are for the remote console + ; * They have no effect for the local or basic console types + ; * Leave commented to diable logins to the console + ;ConsoleUser = Test + ;ConsolePass = secret + ;ConsolePort = 0 + + +[Hypergrid] + ;# {HomeURI} {Hypergrid} {The Home URL of this world} {} + ;; This is the address of the external robust server that + ;; runs the UserAgentsService, possibly this server. + ;; For example http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;# {GatekeeperURI} {Hypergrid} {The URL of the gatekeeper of this world} {} + ;; This is the address of the external robust server + ;; that runs the Gatekeeper service, possibly this server. + ;; For example http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + + +[DatabaseService] + ; PGSQL + ; Uncomment these lines if you want to use PGSQL storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.PGSQL.dll" + ;ConnectionString = "Server=localhost;Database=opensim;User Id=opensim; password=***;" + + ; MySQL + ; Uncomment these lines if you want to use MySQL storage + ; Change the connection string to your db details + StorageProvider = "OpenSim.Data.MySQL.dll" + ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + + +; * As an example, the below configuration precisely mimicks the legacy +; * asset server. It is read by the asset IN connector (defined above) +; * and it then loads the OUT connector (a local database module). That, +; * in turn, reads the asset loader and database connection information +; * +[AssetService] + + ;; Choose an asset service (Only one option should be enabled) + LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" + ;LocalServiceModule = "OpenSim.Services.FSAssetService.dll:FSAssetConnector" + + ;; FSAsset Directories. Base directory, where final asset files are stored and Spool directory for temp files + ;; These directories must be on the same physical filesystem + ;BaseDirectory = "./fsassets/data" + ;SpoolDirectory = "./fsassets/tmp" + + ;; Original service can be checked if FSAssets can not find an asset + ;FallbackService = "OpenSim.Services.AssetService.dll:AssetService"; + + ;; How many days since last updating the access time before its updated again by FSAssets when accessing an asset + ;; Reduces DB calls if asset is requested often. Default value 0 will always update access time + ;DaysBetweenAccessTimeUpdates = 30 + + ;; FSAssets Custom Database Config (Leave blank to use grids default database configuration) + ;StorageProvider = "" + ;ConnectionString = "" + ;Realm = "fsassets" + + ;; The following are common to both the default asset service and FSAsset service + + ;; Common asset service options + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "./assets/AssetSets.xml" + + +; * This configuration loads the inventory server modules. It duplicates +; * the function of the legacy inventory server +; * +[InventoryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + + ; Will calls to purge folders (empty trash) and immediately delete/update items or folders (not move to trash first) succeed? + ; If this is set to false then some other arrangement must be made to perform these operations if necessary. + AllowDelete = true + + +; * This is the new style grid service. +; * "Realm" is the table that is used for user lookup. +; * It defaults to "regions", which uses the legacy tables +; * +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + + ; Realm = "regions" + ; AllowDuplicateNames = "True" + + ;; Perform distance check for the creation of a linked region + ; Check4096 = "True" + + ;; Needed to display non-default map tile images for linked regions + AssetService = "OpenSim.Services.AssetService.dll:AssetService" + + ;; Directory for map tile images of linked regions + ; MapTileDirectory = "./maptiles" + + ;; Next, we can specify properties of regions, including default and fallback regions + ;; The syntax is: Region_ = "" + ;; or: Region_ = "" + ;; where can be DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; + ; Region_Welcome_Area = "DefaultRegion, FallbackRegion" + ; (replace spaces with underscore) + + ;; Allow Hyperlinks to be created at the console + HypergridLinker = true + + ;; Allow supporting viewers to export content + ;; Set to false to prevent export + ExportSupported = true + + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + + +; * This is the configuration for the freeswitch server in grid mode +[FreeswitchService] + LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" + + ;; The IP address of your FreeSWITCH server. + ;; This address must be reachable by viewers. + ; ServerAddress = 127.0.0.1 + + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms + ; DefaultTimeout = 5000 + + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false + + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. +; * +[AuthenticationService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + ; Realm = "useraccounts" + + ;; Allow the service to process HTTP getauthinfo calls. + ;; Default is false. + ; AllowGetAuthInfo = false + + ;; Allow the service to process HTTP setauthinfo calls. + ;; Default is false. + ; AllowSetAuthInfo = false + + ;; Allow the service to process HTTP setpassword calls. + ;; Default is false. + ; AllowSetPassword = false + + +[OpenIdService] + ; for the server connector + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + UserAccountServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + +; * This is the new style user service. +; * "Realm" is the table that is used for user lookup. +; * It defaults to "useraccounts", which uses the new style. +; * Realm = "users" will use the legacy tables as an authentication source +; * +[UserAccountService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + ; Realm = "useraccounts" + + ; These are for creating new accounts by the service + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 + ;; to show a default "Ruth" avatar rather than a cloud for a newly created user. + ;; Default is false + CreateDefaultAvatarEntries = true + + ;; Allow the service to process HTTP createuser calls. + ;; Default is false. + ; AllowCreateUser = false + + ;; Allow the service to process HTTP setaccount calls. + ;; Default is false. + ; AllowSetAccount = false + + +[GridUserService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + + +[AgentPreferencesService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:AgentPreferencesService" + + +[PresenceService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" + + +[AvatarService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" + + +[FriendsService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.FriendsService.dll:FriendsService" + +[EstateService] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[LibraryService] + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + + +[LoginService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + ; for the service + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" + LibraryService = "OpenSim.Services.InventoryService.dll:LibraryService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + + ; This inventory service will be used to initialize the user's inventory + HGInventoryServicePlugin = "OpenSim.Services.HypergridService.dll:HGSuitcaseInventoryService" + HGInventoryServiceConstructorArg = "HGInventoryService" + + ;; Ask co-operative viewers to use a different currency name + ;Currency = "" + + ;; Set minimum fee to publish classified + ; ClassifiedFee = 0 + + WelcomeMessage = "Welcome, Avatar!" + AllowRemoteSetLoginLevel = "false" + + ; For V2 map + MapTileURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; Url to search service + ; SearchURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; For V3 destination guide + ; DestinationGuide = "${Const|BaseURL}/guide" + + ; For V3 avatar picker (( work in progress )) + ; AvatarPicker = "${Const|BaseURL}/avatars" + + ; If you run this login server behind a proxy, set this to true + ; HasProxy = false + + ; Defaults for the users, if none is specified in the useraccounts table entry (ServiceURLs) + ;; If you have GatekeeperURI set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + + SRV_HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_InventoryServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_AssetServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_ProfileServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_FriendsServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_IMServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_GroupsServerURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not receive timezone information from the server - almost all (?) default to Pacific Standard Time + ;; However, they do rely on the server to tell them whether it's Daylight Saving Time or not. + ;; Hence, calculating DST based on a different timezone can result in a misleading viewer display and inconsistencies between grids. + ;; By default, this setting uses various timezone names to calculate DST with regards to the viewer's standard PST. + ;; Options are + ;; "none" no DST + ;; "local" use the server's only timezone to calculate DST. This is previous OpenSimulator behaviour. + ;; "America/Los_Angeles;Pacific Standard Time" use these timezone names to look up Daylight savings. + ;; 'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows + DSTZone = "America/Los_Angeles;Pacific Standard Time" + + +[MapImageService] + LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" + + ; Set this if you want to change the default + ; TilesStoragePath = "maptiles" + ; + ; If for some reason you have the AddMapTile service outside the firewall (e.g. ${Const|PublicPort}), + ; you may want to set this. Otherwise, don't set it, because it's already protected. + ; GridService = "OpenSim.Services.GridService.dll:GridService" + ; + ; Additionally, if you run this server behind a proxy, set this to true + ; HasProxy = false + + +[GridInfoService] + ; These settings are used to return information on a get_grid_info call. + ; Client launcher scripts and third-party clients make use of this to + ; autoconfigure the client and to provide a nice user experience. If you + ; want to facilitate that, you should configure the settings here according + ; to your grid or standalone setup. + ; + ; See http://opensimulator.org/wiki/GridInfo + + ; login uri: for grid this is the login server URI + login = ${Const|BaseURL}:${Const|PublicPort}/ + + ; long grid name: the long name of your grid + gridname = "the lost continent of hippo" + + ; short grid name: the short name of your grid + gridnick = "hippogrid" + + ; login page: optional: if it exists it will be used to tell the client to use + ; this as splash page + ;welcome = ${Const|BaseURL}/welcome + + ; helper uri: optional: if it exists if will be used to tell the client to use + ; this for all economy related things + ;economy = ${Const|BaseURL}:${Const|PublicPort}/ + + ; web page of grid: optional: page providing further information about your grid + ;about = ${Const|BaseURL}/about/ + + ; account creation: optional: page providing further information about obtaining + ; a user account on your grid + ;register = ${Const|BaseURL}/register + + ; help: optional: page providing further assistance for users of your grid + ;help = ${Const|BaseURL}/help + + ; password help: optional: page providing password assistance for users of your grid + ;password = ${Const|BaseURL}/password + + ; HG address of the gatekeeper, if you have one + ; this is the entry point for all the regions of the world + ; gatekeeper = ${Const|BaseURL}:${Const|PublicPort}/ + + ; HG user domain, if you have one + ; this is the entry point for all user-related HG services + ; uas = ${Const|BaseURL}:${Const|PublicPort}/ + + +[GatekeeperService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:GatekeeperService" + ;; for the service + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AuthenticationService = "OpenSim.Services.Connectors.dll:AuthenticationServicesConnector" + SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" + ; how does the outside world reach me? This acts as public key too. + ;; If you have GatekeeperURI set under [Hypergrid], no need to set it here, leave it commented + ; ExternalName = "${Const|BaseURL}:${Const|PublicPort}" + + ; Does this grid allow incoming links to any region in it? + ; If false, HG TPs happen only to the Default regions specified in [GridService] section + AllowTeleportsToAnyRegion = true + + ; If you run this gatekeeper server behind a proxy, set this to true + ; HasProxy = false + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ;; Are foreign visitors allowed? + ;ForeignAgentsAllowed = true + ;; + ;; If ForeignAgentsAllowed is true, make exceptions using AllowExcept. + ;; Leave blank or commented for no exceptions. + ; AllowExcept = "http://griefer.com:8002, http://enemy.com:8002" + ;; + ;; If ForeignAgentsAllowed is false, make exceptions using DisallowExcept + ;; Leave blank or commented for no exceptions. + ; DisallowExcept = "http://myfriendgrid.com:8002, http://myboss.com:8002" + + +[UserAgentService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:UserAgentService" + ;; for the service + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + GridService = "OpenSim.Services.GridService.dll:GridService" + GatekeeperService = "OpenSim.Services.HypergridService.dll:GatekeeperService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ; If you run this user agent server behind a proxy, set this to true + ; HasProxy = false + + ;; If you separate the UserAgentService from the LoginService, set this to + ;; the IP address of the machine where your LoginService is + ;LoginServerIP = "127.0.0.1" + + ; User level required to be contacted from other grids + ;LevelOutsideContacts = 0 + + ;; Restrictions on destinations of local users. + ;; Are local users allowed to visit other grids? + ;; What user level? Use variables of this forrm: + ;; ForeignTripsAllowed_Level_ = true | false + ;; (the default is true) + ;; For example: + ; ForeignTripsAllowed_Level_0 = false + ; ForeignTripsAllowed_Level_200 = true ; true is default, no need to say it + ;; + ;; If ForeignTripsAllowed is false, make exceptions using DisallowExcept + ;; Leave blank or commented for no exceptions. + ; DisallowExcept_Level_0 = "http://myothergrid.com:8002, http://boss.com:8002" + ;; + ;; If ForeignTripsAllowed is true, make exceptions using AllowExcept. + ;; Leave blank or commented for no exceptions. + ; AllowExcept_Level_200 = "http://griefer.com:8002, http://enemy.com:8002" + + ;; This variable controls what is exposed to profiles of local users + ;; as seen from outside of this grid. Leave it uncommented for exposing + ;; UserTitle, UserFlags and the creation date. Uncomment and change to False + ;; to block this info from being exposed. + ; ShowUserDetailsInHGProfile = True + + +; * The interface that local users get when they are in other grids. +; * This restricts the inventory operations while in other grids. +; * Still not completely safe, especially if users perform inventory operations +; * while in those grids. The more the user accesses his/her inventory, the more +; * those simulators will know about the user's inventory. +; * +[HGInventoryService] + ; For the InventoryServiceInConnector + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGSuitcaseInventoryService" + ;; alternatives: + ;; HG1.5, more permissive, not recommended, but still supported + ;LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInventoryService" + ;; HG1.0, totally permissive, not recommended, but OK for grids with 100% trust + ;LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + + UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + + ; HGInventoryService is a public-facing inventory service that allows users to + ; interact with their suitcase folder when on a foreign grid. This reuses the general inventory service connector. + ; Hence, if the user has set up authentication in [Network] to protect their private services + ; make sure it is not set here. + AuthType = None + + ;; Can overwrite the default in [Hypergrid], but probably shouldn't + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + +; * The interface that local users get when they are in other grids. +; * This restricts the access that the rest of the world has to +; * the assets of this world. +; * +[HGAssetService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" + UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ; HGAssetService is a public-facing service that allows users to + ; read and create assets when on another grid. This reuses the general asset service connector. + ; Hence, if the user has set up authentication in [Network] to protect their private services + ; make sure it is overriden for this public service. + AuthType = None + + ;; Can overwrite the default in [Hypergrid], but probably shouldn't + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; The asset types that this grid can export to / import from other grids. + ;; Comma separated. + ;; Valid values are all the asset types in OpenMetaverse.AssetType, namely: + ;; Unknown, Texture, Sound, CallingCard, Landmark, Clothing, Object, Notecard, LSLText, + ;; LSLBytecode, TextureTGA, Bodypart, SoundWAV, ImageTGA, ImageJPEG, Animation, Gesture, Mesh + ;; + ;; Leave blank or commented if you don't want to apply any restrictions. + ;; A more strict, but still reasonable, policy may be to disallow the exchange + ;; of scripts, like so: + ; DisallowExport ="LSLText" + ; DisallowImport ="LSLBytecode" + + +[HGFriendsService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGFriendsService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + + +[HGInstantMessageService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + ; This should always be true in the Robust config + InGatekeeper = True + + +[Messaging] + ; OfflineIM + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" + + +[Groups] + ;; for the HG Groups service + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ;; What is the HomeURI of users associated with this grid? + ;; Can overwrite the default in [Hypergrid], but probably shouldn't + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; Sets the maximum number of groups an agent may join + ; MaxAgentGroups = 42 + + +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + ;; Configure this for separate profiles database + ;; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + ;; Realm = UserProfiles + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + +[BakedTextureService] + LocalServiceModule = "OpenSim.Server.Handlers.dll:XBakes" + ;; This directory must be writable by the user ROBUST runs as. It will be created automatically. + BaseDirectory = "./bakes" diff --git a/bin/Robust.Tests.dll.config b/bin/Robust.Tests.dll.config new file mode 100644 index 0000000000..a4c43e75d0 --- /dev/null +++ b/bin/Robust.Tests.dll.config @@ -0,0 +1,43 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/Robust.Tests.ini b/bin/Robust.Tests.ini new file mode 100644 index 0000000000..c25e39b646 --- /dev/null +++ b/bin/Robust.Tests.ini @@ -0,0 +1,468 @@ +; * FOR TESTS ONLY -- DO NOT USE THIS FILE +[Const] + + ; The URL of the Robust server + BaseURL = "http://127.0.0.1" + + ; The public port of the Robust server + PublicPort = "8888" + + ; The private port of the Robust server, same for testing + PrivatePort = "8888" + + +; * The startup section lists all the connectors to start up in this server +; * instance. This may be only one, or it may be the entire server suite. +; * Multiple connectors should be separated by commas. +; * +; * These are the IN connectors the server uses, the in connectors +; * read this config file and load the needed service and database connectors +; * +; * The full syntax of a connector string is: +; * [[@]/][:] +; * +[Startup] + ; Place to create a PID file + ; If no path if specified then a PID file is not created. + ; PIDFile = "/tmp/Robust.exe.pid" + + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must have R/W access to the location + RegistryLocation = "." + + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must have R/W access to the location + ConfigDirectory = "." + + console = "rest" + + ; Console commands can be saved to a file, so the command history persists after a restart. (default is true) + ConsoleHistoryFileEnabled = false + + ; The history file can be just a filename (relative to OpenSim's bin/ directory + ; or it can be a full path to somewhere else. (default is OpenSimConsoleHistory.txt in bin/) + ConsoleHistoryFile = "RobustConsoleHistory.txt" + + ; How many lines of command history should we keep? (default is 100) + ConsoleHistoryFileLines = 100 + +[ServiceList] + GridServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridServiceConnector" + PresenceServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:PresenceServiceConnector" + InstantMessageServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:InstantMessageServerConnector" + UserAccountServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:UserAccountServiceConnector" + InventoryInConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XInventoryInConnector" + + ;; Uncomment as more tests are added + ;AssetServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AssetServiceConnector" + ;GridInfoServerInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:GridInfoServerInConnector" + ;AuthenticationServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" + ;OpenIdServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:OpenIdServerConnector" + ;AvatarServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AvatarServiceConnector" + ;LLLoginServiceInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector" + ;GridUserServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridUserServiceConnector" + ;FriendsServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:FriendsServiceConnector" + ;MapAddServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:MapAddServiceConnector" + ;MapGetServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:MapGetServiceConnector" + ;OfflineIMServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.OfflineIM.dll:OfflineIMServiceRobustConnector" + ;GroupsServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" + ;BakedTextureService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XBakesConnector" + ;UserProfilesServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:UserProfilesConnector" + ;EstateDataService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:EstateDataRobustConnector" + +; * This is common for all services, it's the network setup for the entire +; * server instance, if none is specified above +; * +[Network] + port = ${Const|PrivatePort} + + ;; The follow 3 variables are for HTTP Basic Authentication for the Robust services. + ;; Use this if your central services in port ${Const|PrivatePort} need to be accessible on the Internet + ;; but you want to protect them from unauthorized access. + ; AuthType = "BasicHttpAuthentication" + ; HttpAuthUsername = "some_username" + ; HttpAuthPassword = "some_password" + ;; + ;; AuthType above can be overriden in any of the service sections below by + ; AuthType = "None" + ;; This is useful in cases where you want to protect most of the services, + ;; but unprotect individual services. Username and Password can also be + ;; overriden if you want to use different credentials for the different services. + + ;; By default, scripts are not allowed to call private services via llHttpRequest() + ;; Such calls are detected by the X-SecondLife-Shared HTTP header + ;; If you allow such calls you must be sure that they are restricted to very trusted scripters + ;; (remember scripts can also be in visiting avatar attachments). + ;; This can be overriden in individual private service sections if necessary + AllowllHTTPRequestIn = false + + ; * The following are for the remote console + ; * They have no effect for the local or basic console types + ; * Leave commented to diable logins to the console + ;ConsoleUser = Test + ;ConsolePass = secret + ;ConsolePort = 0 + + +[DatabaseService] + ; PGSQL + ; Uncomment these lines if you want to use PGSQL storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.PGSQL.dll" + ;ConnectionString = "Server=localhost;Database=opensim;User Id=opensim; password=***;" + + ; Null + ; Uncomment these lines if you want to use MySQL storage + ; Change the connection string to your db details + StorageProvider = "OpenSim.Data.Null.dll" + ConnectionString = "" + + +; * As an example, the below configuration precisely mimicks the legacy +; * asset server. It is read by the asset IN connector (defined above) +; * and it then loads the OUT connector (a local database module). That, +; * in turn, reads the asset loader and database connection information +; * +[AssetService] + LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" + DefaultAssetLoader = "" + + ; Allow maptile assets to remotely deleted by remote calls to the asset service. + ; There is no harm in having this as false - it just means that historical maptile assets are not deleted. + ; This only applies to maptiles served via the version 1 viewer mechanisms + ; Default is false + AllowRemoteDelete = false + + ; Allow all assets to be remotely deleted. + ; Only set this to true if you are operating a grid where you control all calls to the asset service + ; (where a necessary condition is that you control all simulators) and you need this for admin purposes. + ; If set to true, AllowRemoteDelete = true is required as well. + ; Default is false. + AllowRemoteDeleteAllTypes = false + + +; * This configuration loads the inventory server modules. It duplicates +; * the function of the legacy inventory server +; * +[InventoryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + + StorageProvider = "OpenSim.Tests.Common.dll:TestXInventoryDataPlugin" + ConnectionString = "" + + ; Will calls to purge folders (empty trash) and immediately delete/update items or folders (not move to trash first) succeed? + ; If this is set to false then some other arrangement must be made to perform these operations if necessary. + AllowDelete = true + + +; * This is the new style grid service. +; * "Realm" is the table that is used for user lookup. +; * It defaults to "regions", which uses the legacy tables +; * +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + ; Realm = "regions" + ; AllowDuplicateNames = "True" + + ;; Next, we can specify properties of regions, including default and fallback regions + ;; The syntax is: Region_ = "" + ;; or: Region_ = "" + ;; where can be DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; + ;; Example specification: + ; Region_Welcome_Area = "DefaultRegion, FallbackRegion" + ; (replace spaces with underscore) + + ;; Allow supporting viewers to export content + ;; Set to false to prevent export + ExportSupported = true + + + + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. +; * +[AuthenticationService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + ;; Allow the service to process HTTP getauthinfo calls. + ;; Default is false. + ; AllowGetAuthInfo = false + + ;; Allow the service to process HTTP setauthinfo calls. + ;; Default is false. + ; AllowSetAuthInfo = false + + ;; Allow the service to process HTTP setpassword calls. + ;; Default is false. + ; AllowSetPassword = false + + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. "Realm" is the table that is used for user lookup. +; * It defaults to "useraccounts", which uses the new style. +; * Realm = "users" will use the legacy tables as an authentication source +; * +[UserAccountService] + StorageProvider = "OpenSim.Data.Null.dll" + ConnectionString = "" + + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + ; Realm = "useraccounts" + + ; These are for creating new accounts by the service + ;AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + ;AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + ;GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 + ;; to show a default "Ruth" avatar rather than a cloud for a newly created user. + ;; Default is false + CreateDefaultAvatarEntries = true + + ;; Allow the service to process HTTP createuser calls. + ;; Default is false. + AllowCreateUser = true + + ;; Allow the service to process HTTP setaccount calls. + ;; Default is false. + AllowSetAccount = true + + +[GridUserService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + + +[PresenceService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" + ; Set this to true to allow the use of advanced web services and multiple + ; bots using one account + AllowDuplicatePresences = false; + + +[AvatarService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" + + +[FriendsService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.FriendsService.dll:FriendsService" + +[EstateService] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[LibraryService] + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + + +[LoginService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + ; for the service + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" + LibraryService = "OpenSim.Services.InventoryService.dll:LibraryService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + + ; The minimum user level required for a user to be able to login. 0 by default + ; If you disable a particular user's account then you can set their login level below this number. + ; You can also change this level from the console though these changes will not be persisted. + ; MinLoginLevel = 0 + + ; Ask co-operative viewers to use a different currency name + ;Currency = "" + + ;; Set minimum fee to publish classified + ; ClassifiedFee = 0 + + WelcomeMessage = "Welcome, Avatar!" + AllowRemoteSetLoginLevel = "false" + + ; For V2 map + MapTileURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; Url to search service + ; SearchURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; For V3 destination guide + ; DestinationGuide = "${Const|BaseURL}/guide" + + ; For V3 avatar picker (( work in progress )) + ; AvatarPicker = "${Const|BaseURL}/avatars" + + ; If you run this login server behind a proxy, set this to true + ; HasProxy = false + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not listen to timezone sent by the server. They use Pacific Standard Time instead, + ;; but rely on the server to calculate Daylight Saving Time. Sending another DST than US Pacific + ;; would result in time inconsistencies between grids (during summer and around DST transition period) + ;; default let OpenSim calculate US Pacific DST + ;; "none" disable DST (equivallent to "local" with system set to GMT) + ;; "local" force legacy behaviour (using local system time to calculate DST) + ; DSTZone = "America/Los_Angeles;Pacific Standard Time" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not receive timezone information from the server - almost all (?) default to Pacific Standard Time + ;; However, they do rely on the server to tell them whether it's Daylight Saving Time or not. + ;; Hence, calculating DST based on a different timezone can result in a misleading viewer display and inconsistencies between grids. + ;; By default, this setting uses various timezone names to calculate DST with regards to the viewer's standard PST. + ;; Options are + ;; "none" no DST + ;; "local" use the server's only timezone to calculate DST. This is previous OpenSimulator behaviour. + ;; "America/Los_Angeles;Pacific Standard Time" use these timezone names to look up Daylight savings. + ;; 'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows + DSTZone = "America/Los_Angeles;Pacific Standard Time" + + ;Basic Login Service Dos Protection Tweaks + ;; + ;; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true + ;; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to + ;; get around this basic DOS protection. + ;DOSAllowXForwardedForHeader = false + ;; + ;; The protector adds up requests during this rolling period of time, default 10 seconds + ;DOSRequestTimeFrameMS = 10000 + ;; + ;; The amount of requests in the above timeframe from the same endpoint that triggers protection + ;DOSMaxRequestsInTimeFrame = 5 + ;; + ;; The amount of time that a specific endpoint is blocked. Default 2 minutes. + ;DOSForgiveClientAfterMS = 120000 + ;; + ;; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0. + + +[MapImageService] + LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" + + ; Set this if you want to change the default + ; TilesStoragePath = "maptiles" + ; + ; If for some reason you have the AddMapTile service outside the firewall (e.g. ${Const|PublicPort}), + ; you may want to set this. Otherwise, don't set it, because it's already protected. + ; GridService = "OpenSim.Services.GridService.dll:GridService" + ; + ; Additionally, if you run this server behind a proxy, set this to true + ; HasProxy = false + + +[Messaging] + ; OfflineIM + OfflineIMService = "" + + +[GridInfoService] + ; These settings are used to return information on a get_grid_info call. + ; Client launcher scripts and third-party clients make use of this to + ; autoconfigure the client and to provide a nice user experience. If you + ; want to facilitate that, you should configure the settings here according + ; to your grid or standalone setup. + ; + ; See http://opensimulator.org/wiki/GridInfo + + ; login uri: for grid this is the login server URI + login = ${Const|BaseURL}:${Const|PublicPort}/ + + ; long grid name: the long name of your grid + gridname = "the lost continent of hippo" + + ; short grid name: the short name of your grid + gridnick = "hippogrid" + + ; login page: optional: if it exists it will be used to tell the client to use + ; this as splash page + ;welcome = ${Const|BaseURL}/welcome + + ; helper uri: optional: if it exists if will be used to tell the client to use + ; this for all economy related things + ;economy = ${Const|BaseURL}:${Const|PublicPort}/ + + ; web page of grid: optional: page providing further information about your grid + ;about = ${Const|BaseURL}/about/ + + ; account creation: optional: page providing further information about obtaining + ; a user account on your grid + ;register = ${Const|BaseURL}/register + + ; help: optional: page providing further assistance for users of your grid + ;help = ${Const|BaseURL}/help + + ; password help: optional: page providing password assistance for users of your grid + ;password = ${Const|BaseURL}/password + + +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + ;; Configure this for separate profiles database + ;; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + ;; Realm = UserProfiles + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + +[BakedTextureService] + LocalServiceModule = "OpenSim.Server.Handlers.dll:XBakes" + ;; This directory must be writable by the user ROBUST runs as. It will be created automatically. + BaseDirectory = "./bakes" + +[HGInstantMessageService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + UserAgentService = "" + ; This should always be true in the Robust config + InGatekeeper = True diff --git a/bin/Robust.exe.config b/bin/Robust.exe.config new file mode 100644 index 0000000000..7db6458d26 --- /dev/null +++ b/bin/Robust.exe.config @@ -0,0 +1,63 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example new file mode 100644 index 0000000000..284e9699e1 --- /dev/null +++ b/bin/Robust.ini.example @@ -0,0 +1,568 @@ +; * Run +; * $ Robust.exe -inifile Robust.ini +; * +; ** +; * +; * The Const section allows us to define some basic information that we +; * will use throughout our configuration. We will provide examples for +; * setting the base url of the Robust server and the public and private ports +; * it uses. Changing the values of the constants will set the operating +; * parameters thoughout the configuration. Other constants that may prove +; * to be useful may be added to the followin section. They may be +; * referenced anywhere in the configuration by using ${Const|Name}. One +; * such use is providing a base path for setting locations that Robust +; * uses to write data. +; * +[Const] + + ; The URL of the Robust server + BaseURL = "http://127.0.0.1" + + ; The public port of the Robust server + PublicPort = "8002" + + ; The private port of the Robust server + PrivatePort = "8003" + + +; * The startup section lists all the connectors to start up in this server +; * instance. This may be only one, or it may be the entire server suite. +; * Multiple connectors should be separated by commas. +; * +; * These are the IN connectors the server uses, the in connectors +; * read this config file and load the needed service and database connectors +; * +; * The full syntax of a connector string is: +; * [[@]/][:] +; * +[Startup] + ; Place to create a PID file + ; If no path if specified then a PID file is not created. + ; PIDFile = "/tmp/Robust.exe.pid" + + ; Plugin Registry Location + ; Set path to directory for plugin registry. Information + ; about the registered repositories and installed plugins + ; will be stored here + ; The Robust.exe process must have R/W access to the location + RegistryLocation = "." + + ; Modular configurations + ; Set path to directory for modular ini files... + ; The Robust.exe process must have R/W access to the location + ConfigDirectory = "." + + ; Console commands can be saved to a file, so the command history persists after a restart. (default is true) + ConsoleHistoryFileEnabled = true + + ; The history file can be just a filename (relative to OpenSim's bin/ directory + ; or it can be a full path to somewhere else. (default is OpenSimConsoleHistory.txt in bin/) + ConsoleHistoryFile = "RobustConsoleHistory.txt" + + ; How many lines of command history should we keep? (default is 100) + ConsoleHistoryFileLines = 100 + +[ServiceList] + AssetServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AssetServiceConnector" + InventoryInConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XInventoryInConnector" + ;; Uncomment if you have set up Freeswitch (see [FreeswitchService] below) + ;VoiceConnector = "8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector" + GridServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridServiceConnector" + GridInfoServerInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:GridInfoServerInConnector" + AuthenticationServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" + OpenIdServerConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:OpenIdServerConnector" + AvatarServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AvatarServiceConnector" + LLLoginServiceInConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector" + PresenceServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:PresenceServiceConnector" + UserAccountServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:UserAccountServiceConnector" + GridUserServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:GridUserServiceConnector" + AgentPreferencesServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:AgentPreferencesServiceConnector" + FriendsServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:FriendsServiceConnector" + MapAddServiceConnector = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:MapAddServiceConnector" + MapGetServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:MapGetServiceConnector" + ;; Uncomment this if you want offline IM to work + ;OfflineIMServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.OfflineIM.dll:OfflineIMServiceRobustConnector" + ;; Uncomment this if you want Groups V2 to work + ;GroupsServiceConnector = "${Const|PrivatePort}/OpenSim.Addons.Groups.dll:GroupsServiceRobustConnector" + ;; Uncomment to provide bakes caching + ;BakedTextureService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:XBakesConnector" + + ;; Uncomment for UserProfiles see [UserProfilesService] to configure... + ; UserProfilesServiceConnector = "${Const|PublicPort}/OpenSim.Server.Handlers.dll:UserProfilesConnector" + + ;; Uncomment if you want to have centralized estate data + ; EstateDataService = "${Const|PrivatePort}/OpenSim.Server.Handlers.dll:EstateDataRobustConnector" + +; * This is common for all services, it's the network setup for the entire +; * server instance, if none is specified above +; * +[Network] + port = ${Const|PrivatePort} + + ; HTTPS for "Out of band" management applications such as the remote admin + ; module. May specify https_main = True to make the main http server + ; use https or "False" to make the main server HTTP + ; https_main = False + ; + ; Create https_listener = "True" will create a listener on the port + ; specified. Provide the path to your server certificate along with it's + ; password + ; https_listener = False + ; + ; Set our listener to this port + ; https_port = 0 + ; + ; Path to X509 certificate + ; cert_path = "path/to/cert.p12" + ; + ; Password for cert + ; cert_pass = "password" + + ;; The follow 3 variables are for HTTP Basic Authentication for the Robust services. + ;; Use this if your central services in port ${Const|PrivatePort} need to be accessible on the Internet + ;; but you want to protect them from unauthorized access. + ; AuthType = "BasicHttpAuthentication" + ; HttpAuthUsername = "some_username" + ; HttpAuthPassword = "some_password" + ;; + ;; AuthType above can be overriden in any of the service sections below by + ; AuthType = "None" + ;; This is useful in cases where you want to protect most of the services, + ;; but unprotect individual services. Username and Password can also be + ;; overriden if you want to use different credentials for the different services. + + ;; By default, scripts are not allowed to call private services via llHttpRequest() + ;; Such calls are detected by the X-SecondLife-Shared HTTP header + ;; If you allow such calls you must be sure that they are restricted to very trusted scripters + ;; (remember scripts can also be in visiting avatar attachments). + ;; This can be overriden in individual private service sections if necessary + AllowllHTTPRequestIn = false + + ; * The following are for the remote console + ; * They have no effect for the local or basic console types + ; * Leave commented to diable logins to the console + ;ConsoleUser = Test + ;ConsolePass = secret + ;ConsolePort = 0 + + +[DatabaseService] + ; PGSQL + ; Uncomment these lines if you want to use PGSQL storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.PGSQL.dll" + ;ConnectionString = "Server=localhost;Database=opensim;User Id=opensim; password=***;" + + ; MySQL + ; Uncomment these lines if you want to use MySQL storage + ; Change the connection string to your db details + StorageProvider = "OpenSim.Data.MySQL.dll" + ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + + +; * As an example, the below configuration precisely mimicks the legacy +; * asset server. It is read by the asset IN connector (defined above) +; * and it then loads the OUT connector (a local database module). That, +; * in turn, reads the asset loader and database connection information +; * +[AssetService] + + ;; Choose an asset service (Only one option should be enabled) + LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" + ;LocalServiceModule = "OpenSim.Services.FSAssetService.dll:FSAssetConnector" + + ;; FSAsset Directories. Base directory, where final asset files are stored and Spool directory for temp files + ;; These directories must be on the same physical filesystem + ;BaseDirectory = "./fsassets/data" + ;SpoolDirectory = "./fsassets/tmp" + + ;; Original service can be checked if FSAssets can not find an asset + ;FallbackService = "OpenSim.Services.AssetService.dll:AssetService"; + + ;; How many days since last updating the access time before its updated again by FSAssets when accessing an asset + ;; Reduces DB calls if asset is requested often. Default value 0 will always update access time + ;DaysBetweenAccessTimeUpdates = 30 + + ;; FSAssets Custom Database Config (Leave blank to use grids default database configuration) + ;StorageProvider = "" + ;ConnectionString = "" + ;Realm = "fsassets" + + ;; The following are common to both the default asset service and FSAsset service + + ;; Default loader for loading default assets from XML on first run + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "./assets/AssetSets.xml" + + ; Allow maptile assets to remotely deleted by remote calls to the asset service. + ; There is no harm in having this as false - it just means that historical maptile assets are not deleted. + ; This only applies to maptiles served via the version 1 viewer mechanisms + ; Default is false + AllowRemoteDelete = false + + ; Allow all assets to be remotely deleted. + ; Only set this to true if you are operating a grid where you control all calls to the asset service + ; (where a necessary condition is that you control all simulators) and you need this for admin purposes. + ; If set to true, AllowRemoteDelete = true is required as well. + ; Default is false. + AllowRemoteDeleteAllTypes = false + + +; * This configuration loads the inventory server modules. It duplicates +; * the function of the legacy inventory server +; * +[InventoryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + + ; Will calls to purge folders (empty trash) and immediately delete/update items or folders (not move to trash first) succeed? + ; If this is set to false then some other arrangement must be made to perform these operations if necessary. + AllowDelete = true + + +; * This is the new style grid service. +; * "Realm" is the table that is used for user lookup. +; * It defaults to "regions", which uses the legacy tables +; * +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + ; Realm = "regions" + ; AllowDuplicateNames = "True" + + ;; Next, we can specify properties of regions, including default and fallback regions + ;; The syntax is: Region_ = "" + ;; or: Region_ = "" + ;; where can be DefaultRegion, DefaultHGRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut, Reservation, NoMove, Authenticate + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; + ;; Example specification: + ; Region_Welcome_Area = "DefaultRegion, FallbackRegion" + ; (replace spaces with underscore) + + ;; Allow supporting viewers to export content + ;; Set to false to prevent export + ExportSupported = true + + +; * This is the configuration for the freeswitch server in grid mode +[FreeswitchService] + LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" + + ;; The IP address of your FreeSWITCH server. + ;; This address must be reachable by viewers. + ; ServerAddress = 127.0.0.1 + + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms + ; DefaultTimeout = 5000 + + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false + + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. +; * +[AuthenticationService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + ;; Allow the service to process HTTP getauthinfo calls. + ;; Default is false. + ; AllowGetAuthInfo = false + + ;; Allow the service to process HTTP setauthinfo calls. + ;; Default is false. + ; AllowSetAuthInfo = false + + ;; Allow the service to process HTTP setpassword calls. + ;; Default is false. + ; AllowSetPassword = false + + +[OpenIdService] + ; for the server connector + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + UserAccountServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. "Realm" is the table that is used for user lookup. +; * It defaults to "useraccounts", which uses the new style. +; * Realm = "users" will use the legacy tables as an authentication source +; * +[UserAccountService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + ; Realm = "useraccounts" + + ; These are for creating new accounts by the service + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 + ;; to show a default "Ruth" avatar rather than a cloud for a newly created user. + ;; Default is false + CreateDefaultAvatarEntries = true + + ;; Allow the service to process HTTP createuser calls. + ;; Default is false. + ; AllowCreateUser = false + + ;; Allow the service to process HTTP setaccount calls. + ;; Default is false. + ; AllowSetAccount = false + + +[GridUserService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + + +[AgentPreferencesService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:AgentPreferencesService" + + +[PresenceService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" + ; Set this to true to allow the use of advanced web services and multiple + ; bots using one account + AllowDuplicatePresences = false; + + +[AvatarService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" + + +[FriendsService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.FriendsService.dll:FriendsService" + +[EstateService] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[LibraryService] + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + + +[LoginService] + ; for the server connector + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + ; for the service + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" + LibraryService = "OpenSim.Services.InventoryService.dll:LibraryService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + + ; The minimum user level required for a user to be able to login. 0 by default + ; If you disable a particular user's account then you can set their login level below this number. + ; You can also change this level from the console though these changes will not be persisted. + ; MinLoginLevel = 0 + + ; Ask co-operative viewers to use a different currency name + ;Currency = "" + + ;; Set minimum fee to publish classified + ; ClassifiedFee = 0 + + WelcomeMessage = "Welcome, Avatar!" + AllowRemoteSetLoginLevel = "false" + + ; For V2 map + MapTileURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; Url to search service + ; SearchURL = "${Const|BaseURL}:${Const|PublicPort}/"; + + ; For V3 destination guide + ; DestinationGuide = "${Const|BaseURL}/guide" + + ; For V3 avatar picker (( work in progress )) + ; AvatarPicker = "${Const|BaseURL}/avatars" + + ; If you run this login server behind a proxy, set this to true + ; HasProxy = false + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not listen to timezone sent by the server. They use Pacific Standard Time instead, + ;; but rely on the server to calculate Daylight Saving Time. Sending another DST than US Pacific + ;; would result in time inconsistencies between grids (during summer and around DST transition period) + ;; default let OpenSim calculate US Pacific DST + ;; "none" disable DST (equivallent to "local" with system set to GMT) + ;; "local" force legacy behaviour (using local system time to calculate DST) + ; DSTZone = "America/Los_Angeles;Pacific Standard Time" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not receive timezone information from the server - almost all (?) default to Pacific Standard Time + ;; However, they do rely on the server to tell them whether it's Daylight Saving Time or not. + ;; Hence, calculating DST based on a different timezone can result in a misleading viewer display and inconsistencies between grids. + ;; By default, this setting uses various timezone names to calculate DST with regards to the viewer's standard PST. + ;; Options are + ;; "none" no DST + ;; "local" use the server's only timezone to calculate DST. This is previous OpenSimulator behaviour. + ;; "America/Los_Angeles;Pacific Standard Time" use these timezone names to look up Daylight savings. + ;; 'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows + DSTZone = "America/Los_Angeles;Pacific Standard Time" + + ;Basic Login Service Dos Protection Tweaks + ;; + ;; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true + ;; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to + ;; get around this basic DOS protection. + ;DOSAllowXForwardedForHeader = false + ;; + ;; The protector adds up requests during this rolling period of time, default 10 seconds + ;DOSRequestTimeFrameMS = 10000 + ;; + ;; The amount of requests in the above timeframe from the same endpoint that triggers protection + ;DOSMaxRequestsInTimeFrame = 5 + ;; + ;; The amount of time that a specific endpoint is blocked. Default 2 minutes. + ;DOSForgiveClientAfterMS = 120000 + ;; + ;; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0. + + +[MapImageService] + LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" + + ; Set this if you want to change the default + ; TilesStoragePath = "maptiles" + ; + ; If for some reason you have the AddMapTile service outside the firewall (e.g. ${Const|PublicPort}), + ; you may want to set this. Otherwise, don't set it, because it's already protected. + ; GridService = "OpenSim.Services.GridService.dll:GridService" + ; + ; Additionally, if you run this server behind a proxy, set this to true + ; HasProxy = false + + +[Messaging] + ; OfflineIM + OfflineIMService = "OpenSim.Addons.OfflineIM.dll:OfflineIMService" + + +[Groups] + ;; Sets the maximum number of groups an agent may join + ; MaxAgentGroups = 42 + + +[GridInfoService] + ; These settings are used to return information on a get_grid_info call. + ; Client launcher scripts and third-party clients make use of this to + ; autoconfigure the client and to provide a nice user experience. If you + ; want to facilitate that, you should configure the settings here according + ; to your grid or standalone setup. + ; + ; See http://opensimulator.org/wiki/GridInfo + + ; login uri: for grid this is the login server URI + login = ${Const|BaseURL}:${Const|PublicPort}/ + + ; long grid name: the long name of your grid + gridname = "the lost continent of hippo" + + ; short grid name: the short name of your grid + gridnick = "hippogrid" + + ; login page: optional: if it exists it will be used to tell the client to use + ; this as splash page + ;welcome = ${Const|BaseURL}/welcome + + ; helper uri: optional: if it exists if will be used to tell the client to use + ; this for all economy related things + ;economy = ${Const|BaseURL}:${Const|PublicPort}/ + + ; web page of grid: optional: page providing further information about your grid + ;about = ${Const|BaseURL}/about/ + + ; account creation: optional: page providing further information about obtaining + ; a user account on your grid + ;register = ${Const|BaseURL}/register + + ; help: optional: page providing further assistance for users of your grid + ;help = ${Const|BaseURL}/help + + ; password help: optional: page providing password assistance for users of your grid + ;password = ${Const|BaseURL}/password + + +[UserProfilesService] + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + Enabled = false + ;; Configure this for separate profiles database + ;; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=*****;Old Guids=true;" + ;; Realm = UserProfiles + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + + +[BakedTextureService] + LocalServiceModule = "OpenSim.Server.Handlers.dll:XBakes" + ;; This directory must be writable by the user ROBUST runs as. It will be created automatically. + BaseDirectory = "./bakes" diff --git a/bin/Tools.dll b/bin/Tools.dll new file mode 100755 index 0000000000..08dcf42fde Binary files /dev/null and b/bin/Tools.dll differ diff --git a/bin/Warp3D.dll b/bin/Warp3D.dll new file mode 100755 index 0000000000..8781a82bab Binary files /dev/null and b/bin/Warp3D.dll differ diff --git a/bin/XMLRPC.dll b/bin/XMLRPC.dll new file mode 100755 index 0000000000..1559a73146 Binary files /dev/null and b/bin/XMLRPC.dll differ diff --git a/bin/addon-modules/README b/bin/addon-modules/README new file mode 100644 index 0000000000..120dbc9c7e --- /dev/null +++ b/bin/addon-modules/README @@ -0,0 +1 @@ +Place .ini files here to have them picked up automatically diff --git a/bin/assets/AnimationsAssetSet/autograph_right.dat b/bin/assets/AnimationsAssetSet/autograph_right.dat new file mode 100644 index 0000000000..8e1c958157 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/autograph_right.dat differ diff --git a/bin/assets/AnimationsAssetSet/bouncy_ball_left.dat b/bin/assets/AnimationsAssetSet/bouncy_ball_left.dat new file mode 100644 index 0000000000..67c7385c2c Binary files /dev/null and b/bin/assets/AnimationsAssetSet/bouncy_ball_left.dat differ diff --git a/bin/assets/AnimationsAssetSet/bouncy_ball_right.dat b/bin/assets/AnimationsAssetSet/bouncy_ball_right.dat new file mode 100644 index 0000000000..257c77f24e Binary files /dev/null and b/bin/assets/AnimationsAssetSet/bouncy_ball_right.dat differ diff --git a/bin/assets/AnimationsAssetSet/bouncy_ball_run.dat b/bin/assets/AnimationsAssetSet/bouncy_ball_run.dat new file mode 100644 index 0000000000..58bf4c9662 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/bouncy_ball_run.dat differ diff --git a/bin/assets/AnimationsAssetSet/bouncy_ball_super.dat b/bin/assets/AnimationsAssetSet/bouncy_ball_super.dat new file mode 100644 index 0000000000..d0c00854d2 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/bouncy_ball_super.dat differ diff --git a/bin/assets/AnimationsAssetSet/bouncy_ball_walk.dat b/bin/assets/AnimationsAssetSet/bouncy_ball_walk.dat new file mode 100644 index 0000000000..82b4745c96 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/bouncy_ball_walk.dat differ diff --git a/bin/assets/AnimationsAssetSet/give_and_handshake.dat b/bin/assets/AnimationsAssetSet/give_and_handshake.dat new file mode 100644 index 0000000000..cebe55c045 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/give_and_handshake.dat differ diff --git a/bin/assets/AnimationsAssetSet/handshake_01.dat b/bin/assets/AnimationsAssetSet/handshake_01.dat new file mode 100644 index 0000000000..63a2b4265d Binary files /dev/null and b/bin/assets/AnimationsAssetSet/handshake_01.dat differ diff --git a/bin/assets/AnimationsAssetSet/index.xml b/bin/assets/AnimationsAssetSet/index.xml new file mode 100644 index 0000000000..a26b95fd3f --- /dev/null +++ b/bin/assets/AnimationsAssetSet/index.xml @@ -0,0 +1,87 @@ + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+ +
diff --git a/bin/assets/AnimationsAssetSet/place_marker.dat b/bin/assets/AnimationsAssetSet/place_marker.dat new file mode 100644 index 0000000000..e290e11852 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/place_marker.dat differ diff --git a/bin/assets/AnimationsAssetSet/tpose.dat b/bin/assets/AnimationsAssetSet/tpose.dat new file mode 100644 index 0000000000..62487363d4 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/tpose.dat differ diff --git a/bin/assets/AnimationsAssetSet/tpose2.dat b/bin/assets/AnimationsAssetSet/tpose2.dat new file mode 100644 index 0000000000..6b2d729344 Binary files /dev/null and b/bin/assets/AnimationsAssetSet/tpose2.dat differ diff --git a/bin/assets/AnimationsAssetSet/windsurf_left.dat b/bin/assets/AnimationsAssetSet/windsurf_left.dat new file mode 100644 index 0000000000..fde7bdf4db Binary files /dev/null and b/bin/assets/AnimationsAssetSet/windsurf_left.dat differ diff --git a/bin/assets/AssetSets.xml b/bin/assets/AssetSets.xml new file mode 100644 index 0000000000..829f845e5d --- /dev/null +++ b/bin/assets/AssetSets.xml @@ -0,0 +1,81 @@ + + + + + +
+ +
+ + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ +
diff --git a/bin/assets/Avatar/Newruth/Settings.txt b/bin/assets/Avatar/Newruth/Settings.txt new file mode 100644 index 0000000000..055142e5b4 --- /dev/null +++ b/bin/assets/Avatar/Newruth/Settings.txt @@ -0,0 +1,120 @@ +Sliders: + +Shape + +Body +Height - 70 +Thickness - 8 +Body Fat - 3 + + +Head +Head size - 67 +Head Stretch - 43 +Head Shape - 55 +Egg Head - 72 +Head Length - 71 +Face Shear - 50 +Forehead Angle - 61 +Brow Size - 4 +Upper Cheeks - 42 +Lower Cheeks - 34 +Cheekbones - 67 + +Eyes +Eye size - 35 +Eye opening - 59 +Eye spacing - 44 +Outer Eye Corner - 58 +Inner Eye Corner - 0 +Eye Depth - 39 +Upper Eyelid Fold - 14 +Eye bags - 0 +Puffy Eyelids - 6 +Eyelash Length - 0 +Eye Pop - 50 + +Ears +Ear Size - 39 +Ear Angle - 38 +Attached Earlobes - 37 +Ear Flaps - 6 + +Nose +Nose size - 24 +Nostril Width - 24 +Nostril Division - 61 +Nose thickness - 26 +Upper Bridge - 18 +lower bridge - 64 +bridge width - 58 +nose tip angle - 51 +nose tip shape - 0 +crooked nose - 50 + +Mouth +Lip width - 16 +Lip fullness - 35 +Lip thickness - 62 +Lip ratio - 43 +Mouth position - 43 +Mouth corner - 59 +Lip cleft depth - 42 +Lip cleft - 41 +Shift mouth - 50 + +Chin +Chin angle - 22 +Jaw shape - 21 +Chin depth - 26 +Jaw angle - 64 +Jaw jut - 54 +Jowls - 39 +Chin cleft - 0 +Upper chin cleft - 15 +Chin Neck - 17 + +Torso +Torso muscles - 46 +Neck thickness - 50 +Neck length - 28 +Shoulders - 57 +Breast size - 53 +Breast buoyancy - 34 +Breast Cleavage - 16 +Arm length - 46 +Hand size - 47 +Torse length - 37 +Love handles - 36 +Belly size - 13 + +Legs +Leg muscles - 69 +Leg length - 54 +Hip width - 53 +Butt size - 40 +Saddle bags - 27 +Knee angle - 44 +Foot size - 0 + +Hair +Color +White hair - 11 +Rainbow color - 0 +Blonde hair - 0 +Red hair - 100 + +Style +Hair volume - 45 +Hair front - 0 +Hair sides - 27 +Hair Back - 100 +Big Hair Front - 61 +Big Hair Top - 90 +Big hair back - 12 +Front fringe - 33 +Side fringe - 24 +Back fringe - 9 +Full hair sides - 75 + + diff --git a/bin/assets/Avatar/Newruth/eyes.j2c b/bin/assets/Avatar/Newruth/eyes.j2c new file mode 100644 index 0000000000..c0974def9e Binary files /dev/null and b/bin/assets/Avatar/Newruth/eyes.j2c differ diff --git a/bin/assets/Avatar/Newruth/female body.j2c b/bin/assets/Avatar/Newruth/female body.j2c new file mode 100644 index 0000000000..84fd975e05 Binary files /dev/null and b/bin/assets/Avatar/Newruth/female body.j2c differ diff --git a/bin/assets/Avatar/Newruth/female bottom.j2c b/bin/assets/Avatar/Newruth/female bottom.j2c new file mode 100644 index 0000000000..4fd72a7e2d Binary files /dev/null and b/bin/assets/Avatar/Newruth/female bottom.j2c differ diff --git a/bin/assets/Avatar/Newruth/female face.j2c b/bin/assets/Avatar/Newruth/female face.j2c new file mode 100644 index 0000000000..e0d36fa21b Binary files /dev/null and b/bin/assets/Avatar/Newruth/female face.j2c differ diff --git a/bin/assets/Avatar/Newruth/open sim hair base.j2c b/bin/assets/Avatar/Newruth/open sim hair base.j2c new file mode 100644 index 0000000000..f8e1c951f0 Binary files /dev/null and b/bin/assets/Avatar/Newruth/open sim hair base.j2c differ diff --git a/bin/assets/BodyPartsAssetSet/BodyPartsAssetSet.xml b/bin/assets/BodyPartsAssetSet/BodyPartsAssetSet.xml new file mode 100644 index 0000000000..a3d7ba3a07 --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/BodyPartsAssetSet.xml @@ -0,0 +1,60 @@ + + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + +
diff --git a/bin/assets/BodyPartsAssetSet/base_eyes.dat b/bin/assets/BodyPartsAssetSet/base_eyes.dat new file mode 100644 index 0000000000..520157c209 --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/base_eyes.dat @@ -0,0 +1,26 @@ +LLWearable version 22 +New Eyes + + permissions 0 + { + base_mask 7fffffff + owner_mask 7fffffff + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00082000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 00000000-0000-0000-0000-000000000000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 3 +parameters 2 +98 0 +99 0 +textures 1 +3 6522e74d-1660-4e7f-b601-6f48c1659a77 diff --git a/bin/assets/BodyPartsAssetSet/base_hair.dat b/bin/assets/BodyPartsAssetSet/base_hair.dat new file mode 100644 index 0000000000..05e21d88ac --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/base_hair.dat @@ -0,0 +1,114 @@ +LLWearable version 22 +New Hair + + permissions 0 + { + base_mask 7fffffff + owner_mask 7fffffff + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00082000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 00000000-0000-0000-0000-000000000000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 2 +parameters 90 +16 0 +31 .5 +112 0 +113 0 +114 .5 +115 0 +119 .5 +130 .45 +131 .5 +132 .39 +133 .25 +134 .5 +135 .55 +136 .5 +137 .5 +140 0 +141 0 +142 0 +143 .12 +144 .1 +145 0 +146 0 +147 0 +148 .22 +149 0 +166 0 +167 0 +168 0 +169 0 +171 0 +172 .5 +173 0 +174 0 +175 .3 +176 0 +177 0 +178 0 +179 0 +180 .13 +181 .14 +182 .7 +183 .05 +184 0 +190 0 +191 0 +192 0 +400 .75 +640 0 +641 0 +642 0 +643 0 +644 0 +645 0 +674 -.3 +750 .7 +751 0 +752 .5 +754 0 +755 .05 +757 -1 +761 0 +762 0 +763 .55 +771 0 +774 0 +782 0 +783 0 +784 0 +785 0 +786 0 +787 0 +788 0 +789 0 +790 0 +870 -.29 +871 0 +872 .25 +1000 .5 +1001 .5 +1002 .7 +1003 .7 +1004 0 +1005 0 +1006 0 +1007 0 +1008 0 +1009 0 +1010 0 +1011 0 +1012 .25 +textures 1 +4 7ca39b4c-bd19-4699-aff7-f93fd03d3e7b diff --git a/bin/assets/BodyPartsAssetSet/base_shape.dat b/bin/assets/BodyPartsAssetSet/base_shape.dat new file mode 100644 index 0000000000..bf719c90bc --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/base_shape.dat @@ -0,0 +1,165 @@ +LLWearable version 22 +New Shape + + permissions 0 + { + base_mask 7fffffff + owner_mask 7fffffff + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00082000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 00000000-0000-0000-0000-000000000000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 0 +parameters 142 +1 0 +2 0 +4 0 +5 0 +6 0 +7 0 +8 0 +10 0 +11 0 +12 0 +13 0 +14 0 +15 0 +17 0 +18 0 +19 0 +20 0 +21 0 +22 0 +23 0 +24 0 +25 0 +26 0 +27 0 +28 0 +29 .12 +30 .12 +32 0 +33 0 +34 0 +35 0 +36 -.5 +37 0 +38 0 +40 0 +80 0 +100 0 +104 0 +105 .5 +106 0 +151 0 +152 0 +153 0 +155 0 +156 0 +157 0 +185 0 +186 0 +187 0 +188 0 +189 0 +193 .5 +194 .67 +195 .33 +196 0 +505 .5 +506 0 +507 0 +515 0 +517 0 +518 0 +626 0 +627 0 +629 .5 +630 0 +631 0 +633 0 +634 0 +635 0 +637 0 +646 0 +647 0 +648 0 +649 .5 +650 0 +651 0 +652 .5 +653 0 +655 -.08 +656 0 +657 0 +658 0 +659 .5 +660 0 +661 0 +662 .5 +663 0 +664 0 +665 0 +675 0 +676 0 +677 0 +678 .5 +679 -.08 +680 -.08 +681 -.08 +682 .5 +683 -.15 +684 0 +685 0 +686 0 +687 0 +688 0 +689 0 +690 .5 +691 0 +692 0 +693 .6 +694 -.08 +695 0 +753 0 +756 0 +758 0 +759 .5 +760 0 +764 0 +765 0 +767 0 +768 0 +769 .5 +770 0 +772 0 +773 .5 +794 .17 +795 .25 +796 0 +797 0 +798 0 +799 .5 +841 0 +842 0 +843 0 +853 0 +854 0 +855 0 +879 0 +880 0 +1103 0 +1104 0 +1105 0 +1200 0 +1201 0 +textures 0 diff --git a/bin/assets/BodyPartsAssetSet/base_skin.dat b/bin/assets/BodyPartsAssetSet/base_skin.dat new file mode 100644 index 0000000000..854dd00fbb --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/base_skin.dat @@ -0,0 +1,52 @@ +LLWearable version 22 +Sexy - Female Skin + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 11111111-1111-0000-0000-000100bba000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 1 +parameters 26 +108 0 +110 0 +111 0 +116 0 +117 1 +150 0 +162 0 +163 0 +165 0 +700 .01 +701 .5 +702 .26 +703 0 +704 0 +705 .5 +706 .6 +707 0 +708 0 +709 0 +710 0 +711 .5 +712 0 +713 .7 +714 0 +715 0 +775 0 +textures 3 +0 00000000-0000-1111-9999-000000000012 +5 00000000-0000-1111-9999-000000000010 +6 00000000-0000-1111-9999-000000000011 diff --git a/bin/assets/BodyPartsAssetSet/goblin_skin.dat b/bin/assets/BodyPartsAssetSet/goblin_skin.dat new file mode 100644 index 0000000000..7a7ad8b866 --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/goblin_skin.dat @@ -0,0 +1,49 @@ +LLWearable version 22 +Goblin Skin + + permissions 0 + { + base_mask 00080000 + owner_mask 00080000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00080000 + creator_id 15787b54-7833-4238-9c18-80ddd7687bfe + owner_id 15787b54-7833-4238-9c18-80ddd7687bfe + last_owner_id 15787b54-7833-4238-9c18-80ddd7687bfe + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 1 +parameters 26 +108 .6 +110 .1 +111 1 +116 .68 +117 .28 +150 0 +162 1 +163 .99 +165 .69 +700 .51 +701 .2 +702 0 +703 0 +704 0 +705 .5 +706 .6 +707 0 +708 0 +709 0 +710 0 +711 .5 +712 0 +713 .7 +714 0 +715 0 +775 0 +textures 0 diff --git a/bin/assets/BodyPartsAssetSet/jim_shape.dat b/bin/assets/BodyPartsAssetSet/jim_shape.dat new file mode 100644 index 0000000000..d7e9d3c712 --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/jim_shape.dat @@ -0,0 +1,105 @@ +LLWearable version 22 +Jim Shape + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 11111111-1111-0000-0000-000100bba000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 0 +parameters 82 +1 0 +2 0 +4 0 +5 0 +6 0 +7 0 +8 0 +10 0 +11 0 +12 0 +13 0 +14 0 +15 0 +17 0 +18 0 +19 0 +20 0 +21 0 +22 0 +23 0 +24 0 +25 0 +27 0 +33 -2.3 +34 0 +35 0 +36 -.5 +37 -1.34 +38 0 +80 1 +105 .5 +155 0 +157 0 +185 0 +193 .5 +196 0 +505 .5 +506 0 +507 0 +515 0 +517 0 +518 0 +629 .5 +637 0 +646 0 +647 0 +649 .5 +650 0 +652 .29 +653 0 +656 0 +659 .5 +662 .5 +663 0 +664 0 +665 0 +675 0 +676 0 +678 .5 +682 .5 +683 -.15 +684 0 +685 0 +690 .5 +692 1 +693 .6 +753 0 +756 0 +758 0 +759 .5 +760 0 +764 0 +765 0 +769 .5 +773 .5 +795 .84 +796 0 +799 .5 +841 0 +842 0 +879 0 +880 0 +textures 0 diff --git a/bin/assets/BodyPartsAssetSet/jim_skin.dat b/bin/assets/BodyPartsAssetSet/jim_skin.dat new file mode 100644 index 0000000000..9d03cb20b9 --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/jim_skin.dat @@ -0,0 +1,50 @@ +LLWearable version 22 +Jim skin + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 11111111-1111-0000-0000-000100bba000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 1 +parameters 26 +108 0 +110 0 +111 .5 +116 0 +117 0 +150 0 +162 0 +163 0 +165 0 +700 .25 +701 0 +702 0 +703 0 +704 0 +705 .5 +706 .6 +707 0 +708 0 +709 0 +710 0 +711 .5 +712 0 +713 .7 +714 0 +715 0 +775 0 +textures 0 + diff --git a/bin/assets/BodyPartsAssetSet/little_goblin_shape.dat b/bin/assets/BodyPartsAssetSet/little_goblin_shape.dat new file mode 100644 index 0000000000..b097d3cbce --- /dev/null +++ b/bin/assets/BodyPartsAssetSet/little_goblin_shape.dat @@ -0,0 +1,105 @@ +LLWearable version 22 +See the little goblin + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 11111111-1111-0000-0000-000100bba000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 0 +parameters 82 +1 0 +2 0 +4 0 +5 0 +6 0 +7 0 +8 0 +10 0 +11 0 +12 0 +13 0 +14 0 +15 1.5 +17 0 +18 0 +19 0 +20 0 +21 0 +22 0 +23 0 +24 2 +25 0 +27 0 +33 -2.3 +34 -.7 +35 2 +36 1.4 +37 .16 +38 -1 +80 1 +105 .5 +155 0 +157 .68 +185 0 +193 0 +196 0 +505 .5 +506 0 +507 0 +515 3 +517 .52 +518 0 +629 .5 +637 0 +646 -.4 +647 -.5 +649 .5 +650 0 +652 1 +653 0 +656 0 +659 .5 +662 .5 +663 0 +664 0 +665 0 +675 .3 +676 .95 +678 .74 +682 1 +683 .12 +684 0 +685 -.5 +690 .75 +692 -1 +693 -1 +753 2.34 +756 -1 +758 0 +759 .5 +760 0 +764 0 +765 0 +769 .5 +773 .5 +795 1 +796 3 +799 .5 +841 .66 +842 0 +879 -.5 +880 0 +textures 0 diff --git a/bin/assets/ClothingAssetSet/ClothingAssetSet.xml b/bin/assets/ClothingAssetSet/ClothingAssetSet.xml new file mode 100644 index 0000000000..1640593a4c --- /dev/null +++ b/bin/assets/ClothingAssetSet/ClothingAssetSet.xml @@ -0,0 +1,14 @@ + +
+ + + + +
+
+ + + + +
+
diff --git a/bin/assets/ClothingAssetSet/newpants.dat b/bin/assets/ClothingAssetSet/newpants.dat new file mode 100644 index 0000000000..6587a72e24 --- /dev/null +++ b/bin/assets/ClothingAssetSet/newpants.dat @@ -0,0 +1,33 @@ +LLWearable version 22 +New Pants + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 00000000-0000-0000-0000-000000000000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 5 +parameters 9 +625 0 +638 0 +806 .8 +807 .2 +808 .2 +814 1 +815 .8 +816 0 +869 0 +textures 1 +2 5748decc-f629-461c-9a36-a35a221fe21f diff --git a/bin/assets/ClothingAssetSet/newshirt.dat b/bin/assets/ClothingAssetSet/newshirt.dat new file mode 100644 index 0000000000..4d6d264e2d --- /dev/null +++ b/bin/assets/ClothingAssetSet/newshirt.dat @@ -0,0 +1,34 @@ +LLWearable version 22 +New Shirt + + permissions 0 + { + base_mask 00000000 + owner_mask 00000000 + group_mask 00000000 + everyone_mask 00000000 + next_owner_mask 00000000 + creator_id 11111111-1111-0000-0000-000100bba000 + owner_id 11111111-1111-0000-0000-000100bba000 + last_owner_id 00000000-0000-0000-0000-000000000000 + group_id 00000000-0000-0000-0000-000000000000 + } + sale_info 0 + { + sale_type not + sale_price 10 + } +type 4 +parameters 10 +781 .78 +800 .65 +801 .82 +802 .78 +803 .5 +804 .5 +805 .6 +828 0 +840 0 +868 0 +textures 1 +1 5748decc-f629-461c-9a36-a35a221fe21f diff --git a/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml b/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml new file mode 100644 index 0000000000..b570c554be --- /dev/null +++ b/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml @@ -0,0 +1,341 @@ + + + +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
diff --git a/bin/assets/CollisionSoundsAssetSet/attribution.txt b/bin/assets/CollisionSoundsAssetSet/attribution.txt new file mode 100644 index 0000000000..876419b0f0 --- /dev/null +++ b/bin/assets/CollisionSoundsAssetSet/attribution.txt @@ -0,0 +1,8 @@ +thanvannispen - http://www.freesound.org/people/thanvannispen/sounds/30012/ +hoobtastic - http://www.freesound.org/people/hoobtastic/sounds/132627/ +kbnevel - http://www.freesound.org/people/kbnevel/sounds/119859/ +adcbicycle - http://www.freesound.org/people/adcbicycle/sounds/13856/ +adcbicycle - http://www.freesound.org/people/adcbicycle/sounds/13855/ +110110010 - http://www.freesound.org/people/110110010/sounds/66397/ +qubodup - http://www.freesound.org/people/qubodup/sounds/50941/ +vibe_crc - http://www.freesound.org/people/vibe_crc/sounds/59317/ \ No newline at end of file diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshFlesh.ogg new file mode 100644 index 0000000000..5f3aeb71ec Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshGlass.ogg new file mode 100644 index 0000000000..3a322c63ff Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshMetal.ogg new file mode 100644 index 0000000000..edcf17a708 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshPlastic.ogg new file mode 100644 index 0000000000..acf53e54de Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshRubber.ogg new file mode 100644 index 0000000000..6373610569 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshStone.ogg new file mode 100644 index 0000000000..eccbbb84f4 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_FleshWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_FleshWood.ogg new file mode 100644 index 0000000000..67133809ae Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_FleshWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassFlesh.ogg new file mode 100644 index 0000000000..6951d444eb Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassGlass.ogg new file mode 100644 index 0000000000..1806a55479 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassMetal.ogg new file mode 100644 index 0000000000..f1470244aa Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassPlastic.ogg new file mode 100644 index 0000000000..204a4c668f Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassRubber.ogg new file mode 100644 index 0000000000..243f185a9e Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassStone.ogg new file mode 100644 index 0000000000..085213548a Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_GlassWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_GlassWood.ogg new file mode 100644 index 0000000000..2c13690ad1 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_GlassWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalFlesh.ogg new file mode 100644 index 0000000000..c11d19f59b Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalGlass.ogg new file mode 100644 index 0000000000..36348e146b Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalMetal.ogg new file mode 100644 index 0000000000..957b3c2da7 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalPlastic.ogg new file mode 100644 index 0000000000..5674907f99 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalRubber.ogg new file mode 100644 index 0000000000..0f9ba2e9bd Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalStone.ogg new file mode 100644 index 0000000000..dc489d8bad Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_MetalWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_MetalWood.ogg new file mode 100644 index 0000000000..de04317eaa Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_MetalWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticFlesh.ogg new file mode 100644 index 0000000000..a9d698310c Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticGlass.ogg new file mode 100644 index 0000000000..c7dcdf1476 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticMetal.ogg new file mode 100644 index 0000000000..4dd270f3a7 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticPlastic.ogg new file mode 100644 index 0000000000..9994745da3 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticRubber.ogg new file mode 100644 index 0000000000..e5c408fb77 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticStone.ogg new file mode 100644 index 0000000000..9865c6e68d Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_PlasticWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_PlasticWood.ogg new file mode 100644 index 0000000000..9f921b98cc Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_PlasticWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberFlesh.ogg new file mode 100644 index 0000000000..b56f7dc773 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberGlass.ogg new file mode 100644 index 0000000000..9f44fcac50 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberMetal.ogg new file mode 100644 index 0000000000..9ff064acea Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberPlastic.ogg new file mode 100644 index 0000000000..8e601b1b46 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberRubber.ogg new file mode 100644 index 0000000000..c84f8e5083 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberStone.ogg new file mode 100644 index 0000000000..d398f6fee8 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_RubberWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_RubberWood.ogg new file mode 100644 index 0000000000..ebb24e34f7 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_RubberWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneFlesh.ogg new file mode 100644 index 0000000000..90275adbf2 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneGlass.ogg new file mode 100644 index 0000000000..b2b33cfc07 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneMetal.ogg new file mode 100644 index 0000000000..accdfdffda Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StonePlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StonePlastic.ogg new file mode 100644 index 0000000000..15f93b6098 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StonePlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneRubber.ogg new file mode 100644 index 0000000000..4b756ffac0 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneStone.ogg new file mode 100644 index 0000000000..88b80336d0 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_StoneWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_StoneWood.ogg new file mode 100644 index 0000000000..4a5b7f36fb Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_StoneWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainFlesh.ogg new file mode 100644 index 0000000000..1d3038a3d4 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainGlass.ogg new file mode 100644 index 0000000000..637fa161d6 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainMetal.ogg new file mode 100644 index 0000000000..919c59be38 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainPlastic.ogg new file mode 100644 index 0000000000..23fa329355 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainRubber.ogg new file mode 100644 index 0000000000..c18d242816 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainStone.ogg new file mode 100644 index 0000000000..6bd9e09448 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_TerrainWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_TerrainWood.ogg new file mode 100644 index 0000000000..f405517e94 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_TerrainWood.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodFlesh.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodFlesh.ogg new file mode 100644 index 0000000000..02621c2520 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodFlesh.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodGlass.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodGlass.ogg new file mode 100644 index 0000000000..03b7fb501f Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodGlass.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodMetal.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodMetal.ogg new file mode 100644 index 0000000000..e26afae646 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodMetal.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodPlastic.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodPlastic.ogg new file mode 100644 index 0000000000..abe419b5fd Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodPlastic.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodRubber.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodRubber.ogg new file mode 100644 index 0000000000..30ccc32435 Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodRubber.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodStone.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodStone.ogg new file mode 100644 index 0000000000..ad9681846b Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodStone.ogg differ diff --git a/bin/assets/CollisionSoundsAssetSet/snd_WoodWood.ogg b/bin/assets/CollisionSoundsAssetSet/snd_WoodWood.ogg new file mode 100644 index 0000000000..76ae52c7cf Binary files /dev/null and b/bin/assets/CollisionSoundsAssetSet/snd_WoodWood.ogg differ diff --git a/bin/assets/GesturesAssetSet/GesturesAssetSet.xml b/bin/assets/GesturesAssetSet/GesturesAssetSet.xml new file mode 100644 index 0000000000..7421d8efa9 --- /dev/null +++ b/bin/assets/GesturesAssetSet/GesturesAssetSet.xml @@ -0,0 +1,114 @@ + +
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + + +
+
diff --git a/bin/assets/GesturesAssetSet/LOL.dat b/bin/assets/GesturesAssetSet/LOL.dat new file mode 100644 index 0000000000..6950d99288 Binary files /dev/null and b/bin/assets/GesturesAssetSet/LOL.dat differ diff --git a/bin/assets/GesturesAssetSet/Wave.dat b/bin/assets/GesturesAssetSet/Wave.dat new file mode 100644 index 0000000000..92d9091b5b Binary files /dev/null and b/bin/assets/GesturesAssetSet/Wave.dat differ diff --git a/bin/assets/GesturesAssetSet/can_we_move_along_.dat b/bin/assets/GesturesAssetSet/can_we_move_along_.dat new file mode 100644 index 0000000000..48584332fe Binary files /dev/null and b/bin/assets/GesturesAssetSet/can_we_move_along_.dat differ diff --git a/bin/assets/GesturesAssetSet/clap.dat b/bin/assets/GesturesAssetSet/clap.dat new file mode 100644 index 0000000000..1542cc0b35 Binary files /dev/null and b/bin/assets/GesturesAssetSet/clap.dat differ diff --git a/bin/assets/GesturesAssetSet/dance1.dat b/bin/assets/GesturesAssetSet/dance1.dat new file mode 100644 index 0000000000..5ee085aa51 Binary files /dev/null and b/bin/assets/GesturesAssetSet/dance1.dat differ diff --git a/bin/assets/GesturesAssetSet/dance2.dat b/bin/assets/GesturesAssetSet/dance2.dat new file mode 100644 index 0000000000..346fcb169c Binary files /dev/null and b/bin/assets/GesturesAssetSet/dance2.dat differ diff --git a/bin/assets/GesturesAssetSet/dance3.dat b/bin/assets/GesturesAssetSet/dance3.dat new file mode 100644 index 0000000000..fab2008ba7 Binary files /dev/null and b/bin/assets/GesturesAssetSet/dance3.dat differ diff --git a/bin/assets/GesturesAssetSet/definitely_YES.dat b/bin/assets/GesturesAssetSet/definitely_YES.dat new file mode 100644 index 0000000000..7a9819cd1b Binary files /dev/null and b/bin/assets/GesturesAssetSet/definitely_YES.dat differ diff --git a/bin/assets/GesturesAssetSet/me_.dat b/bin/assets/GesturesAssetSet/me_.dat new file mode 100644 index 0000000000..36df1a92bc Binary files /dev/null and b/bin/assets/GesturesAssetSet/me_.dat differ diff --git a/bin/assets/GesturesAssetSet/no.dat b/bin/assets/GesturesAssetSet/no.dat new file mode 100644 index 0000000000..5d818cfc05 Binary files /dev/null and b/bin/assets/GesturesAssetSet/no.dat differ diff --git a/bin/assets/GesturesAssetSet/not_sure.dat b/bin/assets/GesturesAssetSet/not_sure.dat new file mode 100644 index 0000000000..2f5a2b4c88 Binary files /dev/null and b/bin/assets/GesturesAssetSet/not_sure.dat differ diff --git a/bin/assets/GesturesAssetSet/raise_hand.dat b/bin/assets/GesturesAssetSet/raise_hand.dat new file mode 100644 index 0000000000..a37325956a Binary files /dev/null and b/bin/assets/GesturesAssetSet/raise_hand.dat differ diff --git a/bin/assets/GesturesAssetSet/suprised.dat b/bin/assets/GesturesAssetSet/suprised.dat new file mode 100644 index 0000000000..7160c4238c Binary files /dev/null and b/bin/assets/GesturesAssetSet/suprised.dat differ diff --git a/bin/assets/GesturesAssetSet/take_it_outside.dat b/bin/assets/GesturesAssetSet/take_it_outside.dat new file mode 100644 index 0000000000..f344e6f88e Binary files /dev/null and b/bin/assets/GesturesAssetSet/take_it_outside.dat differ diff --git a/bin/assets/GesturesAssetSet/whoohoo_.dat b/bin/assets/GesturesAssetSet/whoohoo_.dat new file mode 100644 index 0000000000..22088a6e0e Binary files /dev/null and b/bin/assets/GesturesAssetSet/whoohoo_.dat differ diff --git a/bin/assets/GesturesAssetSet/wink_.dat b/bin/assets/GesturesAssetSet/wink_.dat new file mode 100644 index 0000000000..e9d9be78fe Binary files /dev/null and b/bin/assets/GesturesAssetSet/wink_.dat differ diff --git a/bin/assets/LandmarksAssetSet/LandmarksAssetSet.xml b/bin/assets/LandmarksAssetSet/LandmarksAssetSet.xml new file mode 100644 index 0000000000..3b9154a834 --- /dev/null +++ b/bin/assets/LandmarksAssetSet/LandmarksAssetSet.xml @@ -0,0 +1,3 @@ + + + diff --git a/bin/assets/MyAssetSet/MyAssetSet.xml b/bin/assets/MyAssetSet/MyAssetSet.xml new file mode 100644 index 0000000000..7c21be43be --- /dev/null +++ b/bin/assets/MyAssetSet/MyAssetSet.xml @@ -0,0 +1,2 @@ + + diff --git a/bin/assets/NotecardsAssetSet/NotecardsAssetSet.xml b/bin/assets/NotecardsAssetSet/NotecardsAssetSet.xml new file mode 100644 index 0000000000..1d44a04a4b --- /dev/null +++ b/bin/assets/NotecardsAssetSet/NotecardsAssetSet.xml @@ -0,0 +1,14 @@ + +
+ + + + +
+
+ + + + +
+
diff --git a/bin/assets/NotecardsAssetSet/exampleNote.txt b/bin/assets/NotecardsAssetSet/exampleNote.txt new file mode 100644 index 0000000000..e4b6ced86e --- /dev/null +++ b/bin/assets/NotecardsAssetSet/exampleNote.txt @@ -0,0 +1 @@ +This is a test, this is only a test. Had this been an actual emergency, I would have sent the LoLKat after you :-P...... diff --git a/bin/assets/NotecardsAssetSet/welcomeNote.txt b/bin/assets/NotecardsAssetSet/welcomeNote.txt new file mode 100644 index 0000000000..a012a01cdc --- /dev/null +++ b/bin/assets/NotecardsAssetSet/welcomeNote.txt @@ -0,0 +1,4 @@ + +Hello and thank you for using OpenSim. For more infomation visit http://opensimulator.org/wiki/Main_Page + +In this Library you will find example items to play with and learn on. To use any of these items just double click and/or drag them to the respective folder in your base inventory. Then you can drag them to prims. diff --git a/bin/assets/ObjectsAssetSet/ObjectsAssetSet.xml b/bin/assets/ObjectsAssetSet/ObjectsAssetSet.xml new file mode 100644 index 0000000000..3b9154a834 --- /dev/null +++ b/bin/assets/ObjectsAssetSet/ObjectsAssetSet.xml @@ -0,0 +1,3 @@ + + + diff --git a/bin/assets/PhotosAssetSet/PhotosAssetSet.xml b/bin/assets/PhotosAssetSet/PhotosAssetSet.xml new file mode 100644 index 0000000000..3b9154a834 --- /dev/null +++ b/bin/assets/PhotosAssetSet/PhotosAssetSet.xml @@ -0,0 +1,3 @@ + + + diff --git a/bin/assets/README.txt b/bin/assets/README.txt new file mode 100644 index 0000000000..02cc78f8ad --- /dev/null +++ b/bin/assets/README.txt @@ -0,0 +1,12 @@ +README + +OpenSim comes with a default asset set contained in the OpenSimAssetSet +directory. You can also load up your own asset set to OpenSim on startup by +making a file entry in AssetSets.xml. This file should point towards an XML +file which details the assets in your asset set. The +OpenSimAssetSet/OpenSimAssetSet.xml is a good template for the information +required. + +If you want your assets to show up in the standard inventory library for an +avatar, you will also need to add separate entries to the xml files in the +bin/inventory configuration directory. diff --git a/bin/assets/ScriptsAssetSet/GrafittiBoard.lsl b/bin/assets/ScriptsAssetSet/GrafittiBoard.lsl new file mode 100644 index 0000000000..83adfb1a84 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/GrafittiBoard.lsl @@ -0,0 +1,74 @@ +// Grafitti board 0.0.2 for OpenSim +// By Justin Clark-Casey (justincc) +// http://justincc.wordpress.com + +// This script is available under the BSD License + +string text = ""; + +integer LISTENING_CHANNEL = 43; + +// XXX Only putting this here as well to get around OpenSim's int -> string casting oddness +string LISTENING_CHANNEL_STRING = "43"; + +// FIXME: Should be dynamic! +integer CHARS_WIDTH = 42; + +// Add some additional graffiti +addGraffiti(string message) +{ + while (llStringLength(message) > CHARS_WIDTH) + { + text += "\n\n" + llGetSubString(message, 0, CHARS_WIDTH - 1); + message = llDeleteSubString(message, 0, CHARS_WIDTH - 1); + } + + text += "\n\n" + message; +} + +// Clear the existing graffiti +clearGraffiti() +{ + text = ""; +} + +// Actually fires the graffiti out to the dynamic texture module +draw() +{ + //llSay(0, text); + string drawList = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text " + text + ";"; + + osSetDynamicTextureData("", "vector", drawList, "1024", 0); +} + +default +{ + state_entry() + { + llSetText( + "Say /" + LISTENING_CHANNEL_STRING + " to add text." + + " Say /" + LISTENING_CHANNEL_STRING + + " !clear to clear board", + <0.0, 1.0, 0.0>, 1.0); + + llListen(LISTENING_CHANNEL, "", NULL_KEY, ""); + + addGraffiti("justincc's graffiti board v0.0.2"); + addGraffiti("Now with primitive word wrap!"); + draw(); + } + + listen(integer channel, string name, key id, string message) + { + if (message == "!clear") + { + clearGraffiti(); + } + else + { + addGraffiti(message); + } + + draw(); + } +} diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test01.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test01.lsl new file mode 100644 index 0000000000..5f8a0aabbb --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test01.lsl @@ -0,0 +1,13 @@ +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar!"); + } + + touch_start(integer total_number) + { + llSay( 0, "Touched."); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test02.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test02.lsl new file mode 100644 index 0000000000..2506d95f3c --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test02.lsl @@ -0,0 +1,31 @@ +integer counter; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar! Touch to change color and size."); + counter = 0; + } + + touch_start(integer total_number) + { // do these instructions when the object is touched. + counter = counter + 1; + + // choose three random RGB color components between 0. and 1.0. + float redness = llFrand( 1.0 ); + float greenness = llFrand( 1.0 ); + float blueness = llFrand( 1.0 ); + + // combine color components into a vector and use that vector + // to set object color. + vector prim_color = < redness, greenness, blueness >; + llSetColor( prim_color, ALL_SIDES ); // set object color to new color. + + // choose a random number between 0. and 10. for use as a scale factor. + float new_scale = llFrand(10.0) + 1.0; + llSetScale(< new_scale, new_scale, new_scale > ); // set object scale. + llSay( 0, "Touched by angel number " + (string)counter); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test03.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test03.lsl new file mode 100644 index 0000000000..f371ee9fc7 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test03.lsl @@ -0,0 +1,49 @@ +integer counter; +integer second; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar! Touch to change color and size."); + counter = 0; + } + + touch_start(integer total_number) + { + counter = counter + 1; + + llSay( 0, "Touched by angel number " + (string)counter); + + llSetTimerEvent( 2 ); // create a "timer event" every 2 seconds. + } + + timer() // do these instructions every time the timer event occurs. + { + second++; + + // choose three random RGB color components between 0. and 1.0. + float red = llFrand( 1.0 ); + float green = llFrand( 1.0 ); + float blue = llFrand( 1.0 ); + + // combine color components into a vector and use that vector + // to set object color. + vector prim_color = < red, green, blue >; + llSetColor( prim_color, ALL_SIDES ); // set object color to new color. + + // a choose random number between 0. and 10 for use as a scale factor. + float new_scale = llFrand( 10.0 ); + llSetScale(< new_scale, new_scale, new_scale > ); // set object scale. + + if ( second > 19 ) // then time to wrap this up. + { + // turn object black, print "resting" message, and reset object.... + llSetColor( < 0, 0, 0 >, ALL_SIDES ); + + llSay( 0, "Object now resting and resetting script." ); + llResetScript(); // return object to ready state. + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test04.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test04.lsl new file mode 100644 index 0000000000..5aa25afada --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test04.lsl @@ -0,0 +1,51 @@ +integer counter; +integer second; +vector startPosition; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar! Touch to change position."); + counter = 0; + startPosition = llGetPos(); + } + + touch_start(integer total_number) + { + counter = counter + 1; + + llSay( 0, "Touched by angel number " + (string)counter); + + llSetTimerEvent( 1 ); // arrange for a "timer event" every second. + } + + timer() // do these instructions every time the timer event occurs. + { + second++; + + // choose three random distances between 0. and 10.0. + float X_distance = llFrand( 10.0 ); + float Y_distance = llFrand( 10.0 ); + float Z_distance = llFrand( 10.0 ); + + // combine these distance components into a vector and use it + // to increment the starting position and reposition the object. + vector increment = < X_distance, Y_distance, Z_distance >; + vector newPosition = startPosition + increment; + llSetPos( newPosition ); // reposition object. + + if ( second > 19 ) // then time to wrap this up. + { + // move object back to starting position... + while ( llVecDist( llGetPos(), startPosition ) > 0.001) + { + llSetPos( startPosition ); + } + + llSay( 0, "Object now resting and resetting script." ); + llResetScript(); // return object to ready state. + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test05.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test05.lsl new file mode 100644 index 0000000000..86727cf1f3 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test05.lsl @@ -0,0 +1,30 @@ +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar!"); + vector startPoint = llGetPos(); + } + + touch_start(integer total_number) + { + llSay( 0, "Touched." ); + + // Define a rotation of 10 degrees around the Y-axis. + rotation Y_10 = llEuler2Rot( < 0, 10 * DEG_TO_RAD, 0 > ); + + // now rotate the object 10 degrees in the X-Z plane during + // each loop iteration. note that each call to llSetRot + // causes a .2 second delay. + integer i; + for( i = 1; i < 100; i++ ) + { + // rotate object in the X-Z plane around its own Y-axis. + rotation newRotation = llGetRot() * Y_10; + + llSetRot( newRotation ); + } + llSay( 0, "Rotation stopped" ); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test06.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test06.lsl new file mode 100644 index 0000000000..158d6763f2 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test06.lsl @@ -0,0 +1,8 @@ +default +{ + state_entry() + { + llTargetOmega( < 0, 1, 1 >, .2 * PI, 1.0 ); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test07.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test07.lsl new file mode 100644 index 0000000000..a1258f9ce6 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test07.lsl @@ -0,0 +1,38 @@ +vector rotationCenter; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar!"); + vector startPoint = llGetPos(); + rotationCenter = startPoint + < 3, 3, 3 >; + // distance to the point of rotation should probably be a + // function of the max dimension of the object. + } + + touch_start(integer total_number) + { + llSay( 0, "Touched." ); + + // Define a "rotation" of 10 degrees around the z-axis. + rotation Z_15 = llEuler2Rot( < 0, 0, 15 * DEG_TO_RAD > ); + + integer i; + for( i = 1; i < 100; i++ ) // limit simulation time in case of + { // unexpected behavior. + vector currentPosition = llGetPos(); + + vector currentOffset = currentPosition - rotationCenter; + + // rotate the offset vector in the X-Y plane around the + // distant point of rotation. + vector rotatedOffset = currentOffset * Z_15; + vector newPosition = rotationCenter + rotatedOffset; + + llSetPos( newPosition ); + } + llSay( 0, "Orbiting stopped" ); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test08.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test08.lsl new file mode 100644 index 0000000000..d29428cd57 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test08.lsl @@ -0,0 +1,23 @@ +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar! Touch to launch me straight up."); + llSetStatus( 1, TRUE ); // turn on physics. + } + + touch_start(integer total_number) + { + vector start_color = llGetColor( ALL_SIDES ); // save current color. + llSetColor( < 1.0, 0.0, 0.0 > , ALL_SIDES ); // set color to red. + + float objMass = llGetMass(); + float Z_force = 20.0 * objMass; + + llApplyImpulse( < 0.0, 0.0, Z_force >, FALSE ); + + llSay( 0, "Impulse of " + (string)Z_force + " applied." ); + llSetColor( start_color , ALL_SIDES ); // set color to green. + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test09.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test09.lsl new file mode 100644 index 0000000000..095f942d83 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test09.lsl @@ -0,0 +1,71 @@ +vector startPos; +vector curPos; +vector curForce; +integer second; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar! Touch to launch me straight up."); + llSetStatus( 1, TRUE ); + startPos = < 0, 0, 0 >; + } + + touch_start(integer total_number) + { + startPos = llGetPos(); + curPos = startPos; + curForce = < 0, 0, 0 >; + second = 0; + + llSetColor( < 1.0, 0.0, 0.0 > , ALL_SIDES ); // set color to red. + + float objMass = llGetMass(); + float Z_force = 10.2 * objMass; + + llSetForce( < 0.0, 0.0, Z_force >, FALSE ); + + llSay( 0, "Force of " + (string)Z_force + " being applied." ); + llSetTimerEvent(1); + } + + timer() + { + second++; + curPos = llGetPos(); + float curDisplacement = llVecMag( curPos - startPos ); + + if( ( curDisplacement > 30. ) && // then object is too far away, and + ( llGetForce() != < 0.0, 0.0, 0.0 > ) ) // force not already zero, + { // then let gravity take over, and change color to green. + llSetForce( < 0.0, 0.0, 0.0 >, FALSE ); + llSetColor( < 0, 1.0, 0 >, ALL_SIDES ); + llSay( 0, "Force removed; object in free flight." ); + } + + if ( second > 19 ) // then time to wrap this up. + { + // turn object blue and zero force to be safe.... + llSetColor( < 0, 0, 1.0 >, ALL_SIDES ); // change color to blue. + llSetForce( < 0, 0, 0 >, FALSE ); + + // ...move object back to starting position... + // ...after saving current status of Physics attribute. + integer savedStatus = llGetStatus( 1 ); + llSetStatus( 1, FALSE ); // turn physics off. + while ( llVecDist( llGetPos(), startPos ) > 0.001) + { + llSetPos( startPos ); + } + llSetStatus( 1, savedStatus ); // restore Physics status. + + //...and then turn color to black and Reset the script. + llSetColor( < 1, 1, 1 >, ALL_SIDES ); + llSetTimerEvent( 0 ); // turn off timer events. + llSay( 0, "Done and resetting script." ); + llResetScript(); // return object to ready state. + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test10.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test10.lsl new file mode 100644 index 0000000000..de16df7649 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test10.lsl @@ -0,0 +1,57 @@ +vector startPosition; +float groundLevel; + +default +{ + state_entry() + { + llListen( 0, "", llGetOwner(), ""); + + startPosition = llGetPos(); + groundLevel = llGround( startPosition ); + + llSay( 0, "Control this object with chat commands like:" ); + llSay( 0, "'up' or 'down' followed by a distance." ); + } + + listen( integer channel, string name, key id, string message ) + { + // separate the input into blank-delmited tokens. + list parsed = llParseString2List( message, [ " " ], [] ); + + // get the first part--the "command". + string command = llList2String( parsed, 0 ); + + // get the second part--the "distance". + string distance_string = llList2String( parsed, 1 ); + float distance = ( float )distance_string; + + vector position = llGetPos(); + + if( command == "up" ) + { + if( ( position.z + distance ) < (startPosition.z + 10.0 ) ) + { + llSetPos( llGetPos() + < 0, 0, distance > ); // move up + llSetText( "Went up " + (string)distance, < 1, 0, 0 >, 1 ); + } + else + { + llSetText( "Can't go so high.", < 1, 0, 0 >, 1 ); + } + } + else if( command == "down" ) + { + if( ( position.z - distance ) > groundLevel ) + { + llSetPos( llGetPos() + < 0, 0, -distance > ); // move down + llSetText( "Went down " + (string)distance, < 1, 0, 0 >, 1 ); + } + else + { + llSetText( "Can't go so low.", < 1, 0, 0 >, 1 ); + } + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test11.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test11.lsl new file mode 100644 index 0000000000..e4b404855e --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test11.lsl @@ -0,0 +1,52 @@ +integer dialog_channel= 427; // set a dialog channel +list menu = [ "Go up", "Go down" ]; +vector startPosition; +float groundLevel; + +default +{ + state_entry() + { + // arrange to listen for dialog answers (from multiple users) + llListen( dialog_channel, "", NULL_KEY, ""); + + startPosition = llGetPos(); + groundLevel = llGround( startPosition ); + } + + touch_start(integer total_number) + { + llDialog( llDetectedKey( 0 ), "What do you want to do?", menu, + dialog_channel ); + } + + listen(integer channel, string name, key id, string choice ) + { + vector position = llGetPos(); + + // if a valid choice was made, implement that choice if possible. + // (llListFindList returns -1 if choice is not in the menu list.) + if ( llListFindList( menu, [ choice ]) != -1 ) + { + if ( choice == "Go up" ) + { + if( position.z < ( startPosition.z + 10.0 ) ) + { + llSetPos( llGetPos() + < 0, 0, 1.0 > ); // move up + } + } + else if( choice == "Go down" ) + { + if( position.z > ( groundLevel + 1.0 ) ) + { + llSetPos( llGetPos() + < 0, 0, -1.0 > ); // move down + } + } + } + else + { + llSay( 0, "Invalid choice: " + choice ); + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test12.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test12.lsl new file mode 100644 index 0000000000..eaa885b7f3 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test12.lsl @@ -0,0 +1,46 @@ +vector startPosition; +float groundLevel; + +default +{ + state_entry() + { + // get permission to take over the avatar's control inputs. + llRequestPermissions( llGetOwner(), PERMISSION_TAKE_CONTROLS ); + + startPosition = llGetPos(); + groundLevel = llGround( startPosition ); + } + + run_time_permissions( integer perm ) // event for processing + // permission dialog. + { + if ( perm & PERMISSION_TAKE_CONTROLS ) // permission has been given. + { + // go ahead and take over the forward and backward controls. + llTakeControls( CONTROL_FWD | CONTROL_BACK, TRUE, FALSE ); + } + } + + control( key id, integer held, integer change ) // event for processing + // key press. + { + vector position = llGetPos(); + + if ( change & held & CONTROL_FWD ) + { // the "move forward" control has been activated. + if( position.z < (startPosition.z + 10.0) ) + { + llSetPos( llGetPos() + < 0, 0, 1.0 >); // move up + } + } + else if ( change & held & CONTROL_BACK ) + { // the "move backward" key has been activated. + if( position.z > groundLevel + 1.0 ) + { + llSetPos( llGetPos() + < 0, 0, -1.0 >); // move down + } + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test13.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test13.lsl new file mode 100644 index 0000000000..7238a9b9de --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test13.lsl @@ -0,0 +1,16 @@ +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar!"); + } + + touch_start(integer total_number) + { + llSay( 0, "Touched."); + + llRezObject("Object1", llGetPos() + < 0, 0, 2 >, ZERO_VECTOR, + ZERO_ROTATION, 42); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test14.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test14.lsl new file mode 100644 index 0000000000..2c60035810 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test14.lsl @@ -0,0 +1,70 @@ +integer createdObjectCounter; +integer linkedObjectCounter; + +default +{ + state_entry() + { + llSay( 0, "Hello, Avatar!"); + linkedObjectCounter = 0; // zero the linked object counter. + } + + touch_start(integer total_number) + { + if( createdObjectCounter <= 0 ) // nothing has yet been linked, + { // begin object creation sequence... + // ask for permissions now, since it will be too late later. + llRequestPermissions( llGetOwner(), PERMISSION_CHANGE_LINKS ); + } + else // just do whatever should be done upon touch without + { // creating new objects to link. + // insert commands here to respond to a touch. + } + } + + run_time_permissions( integer permissions_granted ) + { + if( permissions_granted == PERMISSION_CHANGE_LINKS ) + { // create 2 objects. + llRezObject("Object1", llGetPos() + < 1, 0, 2 >, + ZERO_VECTOR, ZERO_ROTATION, 42); + createdObjectCounter = createdObjectCounter + 1; + + llRezObject("Object1", llGetPos() + < -1, 0, 2 >, + ZERO_VECTOR, ZERO_ROTATION, 42); + createdObjectCounter = createdObjectCounter + 1; + + } + else + { + llOwnerSay( "Didn't get permission to change links." ); + return; + } + } + + object_rez( key child_id ) + { + llOwnerSay( "rez happened and produced object with key " + + (string)child_id ); + + // link as parent to the just created child. + llCreateLink( child_id, TRUE ); + + // if all child objects have been created then the script can + // continue to work as a linked set of objects. + linkedObjectCounter++; + if( linkedObjectCounter >= 2 ) + { + // Change all child objects in the set to red (including parent). + llSetLinkColor( LINK_ALL_CHILDREN, < 1, 0, 0 >, ALL_SIDES ); + + // Make child object "2" half-tranparent. + llSetLinkAlpha( 2, .5, ALL_SIDES ); + + // Insert commands here to manage subsequent activity of the + // linkset, like this command to rotate the result: + // llTargetOmega( < 0, 1, 1 >, .2 * PI, 1.0 ); + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test15.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test15.lsl new file mode 100644 index 0000000000..425c9ee597 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test15.lsl @@ -0,0 +1,10 @@ +default +{ + state_entry() + { + llSetStatus(STATUS_PHANTOM,TRUE); + llSetTexture("lit_texture", ALL_SIDES); + llSetTextureAnim (ANIM_ON | LOOP, ALL_SIDES, 4, 4, 0, 0, 15.0); + } +} + diff --git a/bin/assets/ScriptsAssetSet/KanEd-Test16.lsl b/bin/assets/ScriptsAssetSet/KanEd-Test16.lsl new file mode 100644 index 0000000000..536ff19d2d --- /dev/null +++ b/bin/assets/ScriptsAssetSet/KanEd-Test16.lsl @@ -0,0 +1,66 @@ +// This is a script designed to orbit its owner. +vector startPos; +vector curPos; + +vector offset; // offset from Agent +integer iteration; +float rotationRate; // degrees of rotation per iteration +float sensorInterval; // seconds between sensor scan. + +default +{ + state_entry() + { + llOwnerSay( "Hello, Avatar! Touch to start orbiting." ); + llSetStatus( 1, FALSE ); // turn Physics off. + offset = < 2, 2, 1 >; + iteration = 0; + rotationRate = .5; + sensorInterval = .3; + } + + touch_start(integer total_number) + { + startPos = llGetPos(); + curPos = startPos; + + llSleep( .1 ); + + key id = llGetOwner(); + llSensorRepeat( "", id, AGENT, 96, PI, sensorInterval ); + } + + sensor(integer total_number) + { + iteration++; + + if( iteration > 300 ) + { + llResetScript(); + } + + if( llDetectedOwner( 0 ) == llGetOwner() ) + { // the detected Agent is my owner. + vector position = llDetectedPos(0); // find Owner position. + + // calculate next object position relative both to the Owner's + // position and the current time interval counter. That is, + // use the iteration counter to define a rotation, multiply + // the rotation by the constant offset to get a rotated offset + // vector, and add that rotated offset to the current position + // to defne the new position. + + float degreeRotation = llRound( rotationRate * iteration ) % 360; + rotation Rotation = + llEuler2Rot( < 0, 0, degreeRotation * DEG_TO_RAD > ); + vector rotatedOffset = offset * Rotation; + position += rotatedOffset; + + // change the location of the object and save the current (rotated) + // offset for use during the next iteration. + llSetPos( position ); + offset = rotatedOffset; + } + } +} + diff --git a/bin/assets/ScriptsAssetSet/ScriptsAssetSet.xml b/bin/assets/ScriptsAssetSet/ScriptsAssetSet.xml new file mode 100644 index 0000000000..eae9642275 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/ScriptsAssetSet.xml @@ -0,0 +1,243 @@ + +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ + + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
diff --git a/bin/assets/ScriptsAssetSet/llAbs.lsl b/bin/assets/ScriptsAssetSet/llAbs.lsl new file mode 100644 index 0000000000..2b3758487c --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAbs.lsl @@ -0,0 +1,7 @@ +default +{ + state_entry() + { + llOwnerSay("The absolute value of -4 is: "+(string)llAbs(-4) ); + } +} diff --git a/bin/assets/ScriptsAssetSet/llAcos.lsl b/bin/assets/ScriptsAssetSet/llAcos.lsl new file mode 100644 index 0000000000..5450bc0f86 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAcos.lsl @@ -0,0 +1,8 @@ +default +{ + state_entry() + { + float r = llFrand(2) - 1.0; + llOwnerSay("The arccosine of " + (string)r + " is " + llAcos(r)); + } +} diff --git a/bin/assets/ScriptsAssetSet/llAddToLandBanList.lsl b/bin/assets/ScriptsAssetSet/llAddToLandBanList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAddToLandBanList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llAddToLandPassList.lsl b/bin/assets/ScriptsAssetSet/llAddToLandPassList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAddToLandPassList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llAdjustSoundVolume.lsl b/bin/assets/ScriptsAssetSet/llAdjustSoundVolume.lsl new file mode 100644 index 0000000000..4c2d3979f6 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAdjustSoundVolume.lsl @@ -0,0 +1,13 @@ +default +{ + state_entry() + { + llListen(42, "", llGetOwner(), ""); + } + listen(integer chan, string name, key id, string msg) + { + float value = (float)msg; + llAdjustSoundVolume(value); + llOwnerSay("Volume set to: " + (string)value + " of 1.0"); + } +} diff --git a/bin/assets/ScriptsAssetSet/llAllowInventoryDrop.lsl b/bin/assets/ScriptsAssetSet/llAllowInventoryDrop.lsl new file mode 100644 index 0000000000..ca6087c28a --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAllowInventoryDrop.lsl @@ -0,0 +1,17 @@ +integer allow; + +default +{ + touch_start(integer num) + { + llAllowInventoryDrop(allow = !allow); + llOwnerSay("llAllowInventoryDrop == "+llList2String(["FALSE","TRUE"],allow)); + } + changed(integer change) + { + if (change & CHANGED_ALLOWED_DROP) //note that it's & and not &&... it's bitwise! + { + llOwnerSay("The inventory has changed as a result of a user without mod permissions dropping an item on the prim and it being allowed by the script."); + } + } +} diff --git a/bin/assets/ScriptsAssetSet/llAngleBetween.lsl b/bin/assets/ScriptsAssetSet/llAngleBetween.lsl new file mode 100644 index 0000000000..21cd8519c2 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAngleBetween.lsl @@ -0,0 +1,11 @@ +default +{ + state_entry() + { + rotation aRot = ZERO_ROTATION; + rotation bRot = llGetRot(); + float aBetween = llAngleBetween( aRot, bRot ); + llOwnerSay((string)aBetween); + //llGetRot() being < 0, 0, 90 > this should report 1.570796 + } +} diff --git a/bin/assets/ScriptsAssetSet/llApplyImpulse.lsl b/bin/assets/ScriptsAssetSet/llApplyImpulse.lsl new file mode 100644 index 0000000000..add7a08c35 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llApplyImpulse.lsl @@ -0,0 +1,16 @@ +//Rez an object, and drop this script in it. +//This will launch it at the owner. +default +{ + state_entry() + { + list p = llGetObjectDetails(llGetOwner(), [OBJECT_POS]); + if(p != []) + { + llSetStatus(STATUS_PHYSICS, TRUE); + vector pos = llList2Vector(p, 0); + vector direction = llVecNorm(pos - llGetPos()); + llApplyImpulse(direction * 100, 0); + } + } +} diff --git a/bin/assets/ScriptsAssetSet/llAsin.lsl b/bin/assets/ScriptsAssetSet/llAsin.lsl new file mode 100644 index 0000000000..ad37ccd9cf --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAsin.lsl @@ -0,0 +1,9 @@ +// Touch the object with this script in it to see the arcsine of random numbers! +default +{ + touch_start(integer num) + { + float r = llFrand(2) - 1.0; + llOwnerSay("The arcsine of " + (string)r + " is " + llAsin(r)); + } +} diff --git a/bin/assets/ScriptsAssetSet/llAtan2.lsl b/bin/assets/ScriptsAssetSet/llAtan2.lsl new file mode 100644 index 0000000000..9fc1c63eef --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAtan2.lsl @@ -0,0 +1,11 @@ +default +{ + state_entry() + { + float num1 = llFrand(100.0); + float num2 = llFrand(100.0); + llOwnerSay("y = " + (string)num1); + llOwnerSay("x = " + (string)num2); + llOwnerSay("The tangent of y divided by x is " + (string)llAtan2(num1, num2)); + } +} diff --git a/bin/assets/ScriptsAssetSet/llAvatarOnSitTarget.lsl b/bin/assets/ScriptsAssetSet/llAvatarOnSitTarget.lsl new file mode 100644 index 0000000000..47e9588055 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llAvatarOnSitTarget.lsl @@ -0,0 +1,20 @@ +default +{ + state_entry() + { + // set sit target, otherwise this will not work + llSitTarget(<0.0, 0.0, 0.1>, ZERO_ROTATION); + } + changed(integer change) + { + if (change & CHANGED_LINK) + { + key av = llAvatarOnSitTarget(); + //evaluated as true if not NULL_KEY or invalid + if (av) + { + llSay(0, "Hello " + llKey2Name(av) + ", thank you for sitting down"); + } + } + } +} diff --git a/bin/assets/ScriptsAssetSet/llBase64ToString.lsl b/bin/assets/ScriptsAssetSet/llBase64ToString.lsl new file mode 100644 index 0000000000..f0987cbcf5 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llBase64ToString.lsl @@ -0,0 +1,8 @@ +default +{ + state_entry() + { + string test = llBase64ToString("U2VjcmV0Ok9wZW4="); + llOwnerSay(test); + } +} diff --git a/bin/assets/ScriptsAssetSet/llRemoveFromLandBanList.lsl b/bin/assets/ScriptsAssetSet/llRemoveFromLandBanList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llRemoveFromLandBanList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llRemoveFromLandPassList.lsl b/bin/assets/ScriptsAssetSet/llRemoveFromLandPassList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llRemoveFromLandPassList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llResetLandBanList.lsl b/bin/assets/ScriptsAssetSet/llResetLandBanList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llResetLandBanList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llResetLandPassList.lsl b/bin/assets/ScriptsAssetSet/llResetLandPassList.lsl new file mode 100644 index 0000000000..f2df357969 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llResetLandPassList.lsl @@ -0,0 +1,84 @@ +//Commands are: +///5 ban:full_avatar_name +///5 tempban:full_avatar_name +///5 unban:full_avatar_name +///5 pass:full_avatar_name +///5 unpass:full_avatar_name +///5 clearban +///5 clearpass + +string command; + +default +{ + state_entry() + { + llListen(5, "", llGetOwner(), ""); + } + + on_rez(integer param) + { + llResetScript(); + } + + listen(integer chan, string name, key id, string message) + { + if (command != "") + { + llOwnerSay("Sorry, still processing last command, try again in a second."); + } + + list args = llParseString2List(message,[":"],[]); + command = llToLower(llList2String(args,0)); + + if (command == "clearbans") + { + llResetLandBanList(); + } + if (command == "clearpass") + { + llResetLandPassList(); + } + else + { + llSensor(llList2String(args,1),NULL_KEY,AGENT,96,PI); + } + } + + no_sensor() + { + command = ""; + } + + sensor(integer num) + { + integer i; + for (i=0; i< num; ++i) + { + if (command == "ban") + { + // Ban indefinetely + llAddToLandBanList(llDetectedKey(i),0.0); + } + if (command == "tempban") + { + // Ban for 1 hour. + llAddToLandBanList(llDetectedKey(i),1.0); + } + if (command == "unban") + { + llRemoveFromLandBanList(llDetectedKey(i)); + } + if (command == "pass") + { + // Add to land pass list for 1 hour + llAddToLandPassList(llDetectedKey(i),1.0); + } + if (command == "unpass") + { + llRemoveFromLandPassList(llDetectedKey(i)); + } + } + command = ""; + } +} diff --git a/bin/assets/ScriptsAssetSet/llSay.lsl b/bin/assets/ScriptsAssetSet/llSay.lsl new file mode 100644 index 0000000000..dea6fc02fe --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llSay.lsl @@ -0,0 +1,7 @@ +default +{ + state_entry() + { + llSay(0,"This is an incredibly useless program." ); + } +} diff --git a/bin/assets/ScriptsAssetSet/llSetParcelMusicURL.lsl b/bin/assets/ScriptsAssetSet/llSetParcelMusicURL.lsl new file mode 100644 index 0000000000..ec8bf4d927 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llSetParcelMusicURL.lsl @@ -0,0 +1,7 @@ +default +{ + state_entry() + { + llSetParcelMusicURL("http://www.archive.org/download/Torley_Wong_-_The_Final_Selection/Torley_Wong-Lovers__Dance.mp3"); + } +} diff --git a/bin/assets/ScriptsAssetSet/llSetRot.lsl b/bin/assets/ScriptsAssetSet/llSetRot.lsl new file mode 100644 index 0000000000..ebdad2f1bb --- /dev/null +++ b/bin/assets/ScriptsAssetSet/llSetRot.lsl @@ -0,0 +1,13 @@ +default +{ + state_entry() + { + llOwnerSay("Touch me"); + } + touch_start(integer total_number) + { + rotation Y_10 = llEuler2Rot( < 0, 0, 30 * DEG_TO_RAD > ); + rotation newRotation = llGetRot() * Y_10; + llSetRot( newRotation ); + } +} diff --git a/bin/assets/ScriptsAssetSet/osTextBoard.lsl b/bin/assets/ScriptsAssetSet/osTextBoard.lsl new file mode 100644 index 0000000000..7aacab4131 --- /dev/null +++ b/bin/assets/ScriptsAssetSet/osTextBoard.lsl @@ -0,0 +1,48 @@ +string title = ""; +string subtitle = ""; +string text = ""; +string add = ""; +integer channel = 0; // if this is >= 0, llSay on that channel on updates + +push_text() +{ + compile_text(); + draw_text(); +} + +compile_text() +{ + title = "Some Title"; + subtitle = "Some subtitle"; + + text = "Plenty of text for the main body.\n"; + text += "You need to manual do line breaks\n"; + text += "here. No word wrap yet."; + + add = "Additional text at the bottom"; +} + +draw_text() +{ + string drawList = "MoveTo 40,80; PenColour RED; FontSize 48; Text " + title + ";"; + drawList += "MoveTo 160,160; FontSize 32; Text " + subtitle + ";"; + drawList += "PenColour BLACK; MoveTo 40,220; FontSize 24; Text " + text + ";"; + drawList += "PenColour RED; FontName Times New Roman; MoveTo 40,900; Text " + add + ";"; + osSetDynamicTextureData("", "vector", drawList, "1024", 0); +} + +default { + state_entry() + { + push_text(); + } + + touch_start(integer count) + { + push_text(); + if (channel >= 0) { + llSay(channel, text); + } + } + +} diff --git a/bin/assets/ScriptsAssetSet/osWeatherMap.lsl b/bin/assets/ScriptsAssetSet/osWeatherMap.lsl new file mode 100644 index 0000000000..6a2232b9cb --- /dev/null +++ b/bin/assets/ScriptsAssetSet/osWeatherMap.lsl @@ -0,0 +1,43 @@ +integer count = 0; +integer refreshRate = 300; +string URL1 = "http://icons.wunderground.com/data/640x480/2xus_rd.gif"; +string URL2 = "http://icons.wunderground.com/data/640x480/2xus_sf.gif"; +string URL3 = "http://icons.wunderground.com/data/640x480/2xus_st.gif"; +string dynamicID=""; +string contentType="image"; + +refresh_texture() +{ + count++; + string url = ""; + integer c = count % 3; + + if (c == 0) { + url = URL1; + } else if (c == 1) { + url = URL2; + } else { + url = URL3; + } + // refresh rate is not yet respected here, which is why we need the timer + osSetDynamicTextureURL(dynamicID, contentType ,url , "", refreshRate ); +} + +default +{ + state_entry() + { + refresh_texture(); + llSetTimerEvent(refreshRate); // create a "timer event" every 300 seconds. + } + + timer() + { + refresh_texture(); + } + + touch_start(integer times) + { + refresh_texture(); + } +} diff --git a/bin/assets/SoundsAssetSet/OSSndAlert.ogg b/bin/assets/SoundsAssetSet/OSSndAlert.ogg new file mode 100644 index 0000000000..c9b1d7b29b Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndAlert.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndBadKeystroke.ogg b/bin/assets/SoundsAssetSet/OSSndBadKeystroke.ogg new file mode 100644 index 0000000000..789ec92c61 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndBadKeystroke.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndClick.ogg b/bin/assets/SoundsAssetSet/OSSndClick.ogg new file mode 100644 index 0000000000..3e2336cb58 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndClick.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndHealthReductionF.ogg b/bin/assets/SoundsAssetSet/OSSndHealthReductionF.ogg new file mode 100644 index 0000000000..f999364d99 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndHealthReductionF.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndHealthReductionM.ogg b/bin/assets/SoundsAssetSet/OSSndHealthReductionM.ogg new file mode 100644 index 0000000000..2f8e21b83b Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndHealthReductionM.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndInvalidOp.ogg b/bin/assets/SoundsAssetSet/OSSndInvalidOp.ogg new file mode 100644 index 0000000000..6b0eaac484 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndInvalidOp.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndMoneyChangeDown.ogg b/bin/assets/SoundsAssetSet/OSSndMoneyChangeDown.ogg new file mode 100644 index 0000000000..035d5eb1ff Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndMoneyChangeDown.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndMoneyChangeUp.ogg b/bin/assets/SoundsAssetSet/OSSndMoneyChangeUp.ogg new file mode 100644 index 0000000000..1b508dbb1a Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndMoneyChangeUp.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndNewIncomingIMSession.ogg b/bin/assets/SoundsAssetSet/OSSndNewIncomingIMSession.ogg new file mode 100644 index 0000000000..82c56ef5f4 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndNewIncomingIMSession.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndObjectCreate.ogg b/bin/assets/SoundsAssetSet/OSSndObjectCreate.ogg new file mode 100644 index 0000000000..a363ce355a Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndObjectCreate.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndObjectDelete.ogg b/bin/assets/SoundsAssetSet/OSSndObjectDelete.ogg new file mode 100644 index 0000000000..7f154e5cbf Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndObjectDelete.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndObjectRezIn.ogg b/bin/assets/SoundsAssetSet/OSSndObjectRezIn.ogg new file mode 100644 index 0000000000..225ed277fe Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndObjectRezIn.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndPieMenuAppear.ogg b/bin/assets/SoundsAssetSet/OSSndPieMenuAppear.ogg new file mode 100644 index 0000000000..5ea159ba87 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndPieMenuAppear.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndPieMenuSliceHighlight.ogg b/bin/assets/SoundsAssetSet/OSSndPieMenuSliceHighlight.ogg new file mode 100644 index 0000000000..a1b410b5bb Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndPieMenuSliceHighlight.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndSnapshot.ogg b/bin/assets/SoundsAssetSet/OSSndSnapshot.ogg new file mode 100644 index 0000000000..5f5fd2d497 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndSnapshot.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndStartIM.ogg b/bin/assets/SoundsAssetSet/OSSndStartIM.ogg new file mode 100644 index 0000000000..82f521d576 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndStartIM.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndTeleportOut.ogg b/bin/assets/SoundsAssetSet/OSSndTeleportOut.ogg new file mode 100644 index 0000000000..6d712ea271 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndTeleportOut.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndTyping.ogg b/bin/assets/SoundsAssetSet/OSSndTyping.ogg new file mode 100644 index 0000000000..af3da540cf Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndTyping.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndWindowClose.ogg b/bin/assets/SoundsAssetSet/OSSndWindowClose.ogg new file mode 100644 index 0000000000..1742cbd8ad Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndWindowClose.ogg differ diff --git a/bin/assets/SoundsAssetSet/OSSndWindowOpen.ogg b/bin/assets/SoundsAssetSet/OSSndWindowOpen.ogg new file mode 100644 index 0000000000..862823e264 Binary files /dev/null and b/bin/assets/SoundsAssetSet/OSSndWindowOpen.ogg differ diff --git a/bin/assets/SoundsAssetSet/SoundsAssetSet.xml b/bin/assets/SoundsAssetSet/SoundsAssetSet.xml new file mode 100644 index 0000000000..b93a76660e --- /dev/null +++ b/bin/assets/SoundsAssetSet/SoundsAssetSet.xml @@ -0,0 +1,164 @@ + +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
diff --git a/bin/assets/TexturesAssetSet/0187babf-6c0d-5891-ebed-4ecab1426683.j2c b/bin/assets/TexturesAssetSet/0187babf-6c0d-5891-ebed-4ecab1426683.j2c new file mode 100644 index 0000000000..0e63168bd7 Binary files /dev/null and b/bin/assets/TexturesAssetSet/0187babf-6c0d-5891-ebed-4ecab1426683.j2c differ diff --git a/bin/assets/TexturesAssetSet/058c75c0-a0d5-f2f8-43f3-e9699a89c2fc.j2c b/bin/assets/TexturesAssetSet/058c75c0-a0d5-f2f8-43f3-e9699a89c2fc.j2c new file mode 100644 index 0000000000..e351995de6 Binary files /dev/null and b/bin/assets/TexturesAssetSet/058c75c0-a0d5-f2f8-43f3-e9699a89c2fc.j2c differ diff --git a/bin/assets/TexturesAssetSet/073c9723-540c-5449-cdd4-0e87fdc159e3.j2c b/bin/assets/TexturesAssetSet/073c9723-540c-5449-cdd4-0e87fdc159e3.j2c new file mode 100644 index 0000000000..7cae5cbf14 Binary files /dev/null and b/bin/assets/TexturesAssetSet/073c9723-540c-5449-cdd4-0e87fdc159e3.j2c differ diff --git a/bin/assets/TexturesAssetSet/10d2a01a-0818-84b9-4b96-c2eb63256519.j2c b/bin/assets/TexturesAssetSet/10d2a01a-0818-84b9-4b96-c2eb63256519.j2c new file mode 100644 index 0000000000..a6e213a266 Binary files /dev/null and b/bin/assets/TexturesAssetSet/10d2a01a-0818-84b9-4b96-c2eb63256519.j2c differ diff --git a/bin/assets/TexturesAssetSet/18fb888b-e8f1-dce7-7da7-321d651ea6b0.j2c b/bin/assets/TexturesAssetSet/18fb888b-e8f1-dce7-7da7-321d651ea6b0.j2c new file mode 100644 index 0000000000..a10153010b Binary files /dev/null and b/bin/assets/TexturesAssetSet/18fb888b-e8f1-dce7-7da7-321d651ea6b0.j2c differ diff --git a/bin/assets/TexturesAssetSet/2a4880b6-b7a3-690a-2049-bfbe38eafb9f.j2c b/bin/assets/TexturesAssetSet/2a4880b6-b7a3-690a-2049-bfbe38eafb9f.j2c new file mode 100644 index 0000000000..5361a56f35 Binary files /dev/null and b/bin/assets/TexturesAssetSet/2a4880b6-b7a3-690a-2049-bfbe38eafb9f.j2c differ diff --git a/bin/assets/TexturesAssetSet/2caf1179-7861-6ff3-4b7d-46e17780bdfa.j2c b/bin/assets/TexturesAssetSet/2caf1179-7861-6ff3-4b7d-46e17780bdfa.j2c new file mode 100644 index 0000000000..675bdb4399 Binary files /dev/null and b/bin/assets/TexturesAssetSet/2caf1179-7861-6ff3-4b7d-46e17780bdfa.j2c differ diff --git a/bin/assets/TexturesAssetSet/2d784476-d0db-9979-0cff-9408745a7cf3.j2c b/bin/assets/TexturesAssetSet/2d784476-d0db-9979-0cff-9408745a7cf3.j2c new file mode 100644 index 0000000000..0bc1a4fb5d Binary files /dev/null and b/bin/assets/TexturesAssetSet/2d784476-d0db-9979-0cff-9408745a7cf3.j2c differ diff --git a/bin/assets/TexturesAssetSet/30047cec-269d-408e-0c30-b2603b887268.j2c b/bin/assets/TexturesAssetSet/30047cec-269d-408e-0c30-b2603b887268.j2c new file mode 100644 index 0000000000..3a32fcbb9c Binary files /dev/null and b/bin/assets/TexturesAssetSet/30047cec-269d-408e-0c30-b2603b887268.j2c differ diff --git a/bin/assets/TexturesAssetSet/4-tile2.jp2 b/bin/assets/TexturesAssetSet/4-tile2.jp2 new file mode 100644 index 0000000000..8c631046e8 Binary files /dev/null and b/bin/assets/TexturesAssetSet/4-tile2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/4-tile3.jp2 b/bin/assets/TexturesAssetSet/4-tile3.jp2 new file mode 100644 index 0000000000..7890364bd0 Binary files /dev/null and b/bin/assets/TexturesAssetSet/4-tile3.jp2 differ diff --git a/bin/assets/TexturesAssetSet/4726f13e-bd07-f2fb-feb0-bfa2ac58ab61.j2c b/bin/assets/TexturesAssetSet/4726f13e-bd07-f2fb-feb0-bfa2ac58ab61.j2c new file mode 100644 index 0000000000..46eb2da8cf Binary files /dev/null and b/bin/assets/TexturesAssetSet/4726f13e-bd07-f2fb-feb0-bfa2ac58ab61.j2c differ diff --git a/bin/assets/TexturesAssetSet/5894e2e7-ab8d-edfa-e61c-18cf16854ba3.j2c b/bin/assets/TexturesAssetSet/5894e2e7-ab8d-edfa-e61c-18cf16854ba3.j2c new file mode 100644 index 0000000000..34f69c238f Binary files /dev/null and b/bin/assets/TexturesAssetSet/5894e2e7-ab8d-edfa-e61c-18cf16854ba3.j2c differ diff --git a/bin/assets/TexturesAssetSet/5bc11cd6-2f40-071e-a8da-0903394204f9.j2c b/bin/assets/TexturesAssetSet/5bc11cd6-2f40-071e-a8da-0903394204f9.j2c new file mode 100644 index 0000000000..9ac79088d0 Binary files /dev/null and b/bin/assets/TexturesAssetSet/5bc11cd6-2f40-071e-a8da-0903394204f9.j2c differ diff --git a/bin/assets/TexturesAssetSet/64367bd1-697e-b3e6-0b65-3f862a577366.j2c b/bin/assets/TexturesAssetSet/64367bd1-697e-b3e6-0b65-3f862a577366.j2c new file mode 100644 index 0000000000..1650c7866f Binary files /dev/null and b/bin/assets/TexturesAssetSet/64367bd1-697e-b3e6-0b65-3f862a577366.j2c differ diff --git a/bin/assets/TexturesAssetSet/67931331-0c02-4876-1255-28770896c6a2.j2c b/bin/assets/TexturesAssetSet/67931331-0c02-4876-1255-28770896c6a2.j2c new file mode 100644 index 0000000000..3f63493528 Binary files /dev/null and b/bin/assets/TexturesAssetSet/67931331-0c02-4876-1255-28770896c6a2.j2c differ diff --git a/bin/assets/TexturesAssetSet/6c4727b8-ac79-ba44-3b81-f9aa887b47eb.j2c b/bin/assets/TexturesAssetSet/6c4727b8-ac79-ba44-3b81-f9aa887b47eb.j2c new file mode 100644 index 0000000000..adff7dc062 Binary files /dev/null and b/bin/assets/TexturesAssetSet/6c4727b8-ac79-ba44-3b81-f9aa887b47eb.j2c differ diff --git a/bin/assets/TexturesAssetSet/6c9fa78a-1c69-2168-325b-3e03ffa348ce.j2c b/bin/assets/TexturesAssetSet/6c9fa78a-1c69-2168-325b-3e03ffa348ce.j2c new file mode 100644 index 0000000000..e657b96179 Binary files /dev/null and b/bin/assets/TexturesAssetSet/6c9fa78a-1c69-2168-325b-3e03ffa348ce.j2c differ diff --git a/bin/assets/TexturesAssetSet/6de37e4e-7029-61f5-54b8-f5e63f983f58.j2c b/bin/assets/TexturesAssetSet/6de37e4e-7029-61f5-54b8-f5e63f983f58.j2c new file mode 100644 index 0000000000..434ba49d6a Binary files /dev/null and b/bin/assets/TexturesAssetSet/6de37e4e-7029-61f5-54b8-f5e63f983f58.j2c differ diff --git a/bin/assets/TexturesAssetSet/735198cf-6ea0-2550-e222-21d3c6a341ae.j2c b/bin/assets/TexturesAssetSet/735198cf-6ea0-2550-e222-21d3c6a341ae.j2c new file mode 100644 index 0000000000..baedd892cb Binary files /dev/null and b/bin/assets/TexturesAssetSet/735198cf-6ea0-2550-e222-21d3c6a341ae.j2c differ diff --git a/bin/assets/TexturesAssetSet/79504bf5-c3ec-0763-6563-d843de66d0a1.j2c b/bin/assets/TexturesAssetSet/79504bf5-c3ec-0763-6563-d843de66d0a1.j2c new file mode 100644 index 0000000000..134574e48b Binary files /dev/null and b/bin/assets/TexturesAssetSet/79504bf5-c3ec-0763-6563-d843de66d0a1.j2c differ diff --git a/bin/assets/TexturesAssetSet/7a2b3a4a-53c2-53ac-5716-aac7d743c020.j2c b/bin/assets/TexturesAssetSet/7a2b3a4a-53c2-53ac-5716-aac7d743c020.j2c new file mode 100644 index 0000000000..ca37c78258 Binary files /dev/null and b/bin/assets/TexturesAssetSet/7a2b3a4a-53c2-53ac-5716-aac7d743c020.j2c differ diff --git a/bin/assets/TexturesAssetSet/7c0cf89b-44b1-1ce2-dd74-07102a98ac2a.j2c b/bin/assets/TexturesAssetSet/7c0cf89b-44b1-1ce2-dd74-07102a98ac2a.j2c new file mode 100644 index 0000000000..5d556d961a Binary files /dev/null and b/bin/assets/TexturesAssetSet/7c0cf89b-44b1-1ce2-dd74-07102a98ac2a.j2c differ diff --git a/bin/assets/TexturesAssetSet/822ded49-9a6c-f61c-cb89-6df54f42cdf4.j2c b/bin/assets/TexturesAssetSet/822ded49-9a6c-f61c-cb89-6df54f42cdf4.j2c new file mode 100644 index 0000000000..a650bcd474 Binary files /dev/null and b/bin/assets/TexturesAssetSet/822ded49-9a6c-f61c-cb89-6df54f42cdf4.j2c differ diff --git a/bin/assets/TexturesAssetSet/83b77fc6-10b4-63ec-4de7-f40629f238c5.j2c b/bin/assets/TexturesAssetSet/83b77fc6-10b4-63ec-4de7-f40629f238c5.j2c new file mode 100644 index 0000000000..e7771e4c4e Binary files /dev/null and b/bin/assets/TexturesAssetSet/83b77fc6-10b4-63ec-4de7-f40629f238c5.j2c differ diff --git a/bin/assets/TexturesAssetSet/8872f2b8-31db-42d8-580a-b3e4a91262de.j2c b/bin/assets/TexturesAssetSet/8872f2b8-31db-42d8-580a-b3e4a91262de.j2c new file mode 100644 index 0000000000..350b638bb4 Binary files /dev/null and b/bin/assets/TexturesAssetSet/8872f2b8-31db-42d8-580a-b3e4a91262de.j2c differ diff --git a/bin/assets/TexturesAssetSet/8a515889-eac9-fb55-8eba-d2dc09eb32c8.j2c b/bin/assets/TexturesAssetSet/8a515889-eac9-fb55-8eba-d2dc09eb32c8.j2c new file mode 100644 index 0000000000..70821f2637 Binary files /dev/null and b/bin/assets/TexturesAssetSet/8a515889-eac9-fb55-8eba-d2dc09eb32c8.j2c differ diff --git a/bin/assets/TexturesAssetSet/8f458549-173b-23ff-d4ff-bfaa5ea2371b.j2c b/bin/assets/TexturesAssetSet/8f458549-173b-23ff-d4ff-bfaa5ea2371b.j2c new file mode 100644 index 0000000000..881929410c Binary files /dev/null and b/bin/assets/TexturesAssetSet/8f458549-173b-23ff-d4ff-bfaa5ea2371b.j2c differ diff --git a/bin/assets/TexturesAssetSet/92e66e00-f56f-598a-7997-048aa64cde18.j2c b/bin/assets/TexturesAssetSet/92e66e00-f56f-598a-7997-048aa64cde18.j2c new file mode 100644 index 0000000000..287555fc3e Binary files /dev/null and b/bin/assets/TexturesAssetSet/92e66e00-f56f-598a-7997-048aa64cde18.j2c differ diff --git a/bin/assets/TexturesAssetSet/96b4de31-f4fa-337d-ec78-451e3609769e.j2c b/bin/assets/TexturesAssetSet/96b4de31-f4fa-337d-ec78-451e3609769e.j2c new file mode 100644 index 0000000000..4453ecb760 Binary files /dev/null and b/bin/assets/TexturesAssetSet/96b4de31-f4fa-337d-ec78-451e3609769e.j2c differ diff --git a/bin/assets/TexturesAssetSet/99bd60a2-3250-efc9-2e39-2fbcadefbecc.j2c b/bin/assets/TexturesAssetSet/99bd60a2-3250-efc9-2e39-2fbcadefbecc.j2c new file mode 100644 index 0000000000..c965530c0c Binary files /dev/null and b/bin/assets/TexturesAssetSet/99bd60a2-3250-efc9-2e39-2fbcadefbecc.j2c differ diff --git a/bin/assets/TexturesAssetSet/9deab416-9c63-78d6-d558-9a156f12044c.j2c b/bin/assets/TexturesAssetSet/9deab416-9c63-78d6-d558-9a156f12044c.j2c new file mode 100644 index 0000000000..f4e4cba341 Binary files /dev/null and b/bin/assets/TexturesAssetSet/9deab416-9c63-78d6-d558-9a156f12044c.j2c differ diff --git a/bin/assets/TexturesAssetSet/IMG_BLOOM1.jp2 b/bin/assets/TexturesAssetSet/IMG_BLOOM1.jp2 new file mode 100644 index 0000000000..8186d493b5 Binary files /dev/null and b/bin/assets/TexturesAssetSet/IMG_BLOOM1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/Terrain Dirt-b8d3965a-ad78-bf43-699b-bff8eca6c975.texture b/bin/assets/TexturesAssetSet/Terrain Dirt-b8d3965a-ad78-bf43-699b-bff8eca6c975.texture new file mode 100644 index 0000000000..44f31a0dfd Binary files /dev/null and b/bin/assets/TexturesAssetSet/Terrain Dirt-b8d3965a-ad78-bf43-699b-bff8eca6c975.texture differ diff --git a/bin/assets/TexturesAssetSet/Terrain Grass-abb783e6-3e93-26c0-248a-247666855da3.texture b/bin/assets/TexturesAssetSet/Terrain Grass-abb783e6-3e93-26c0-248a-247666855da3.texture new file mode 100644 index 0000000000..13c43b4a48 Binary files /dev/null and b/bin/assets/TexturesAssetSet/Terrain Grass-abb783e6-3e93-26c0-248a-247666855da3.texture differ diff --git a/bin/assets/TexturesAssetSet/Terrain Mountain-179cdabd-398a-9b6b-1391-4dc333ba321f.texture b/bin/assets/TexturesAssetSet/Terrain Mountain-179cdabd-398a-9b6b-1391-4dc333ba321f.texture new file mode 100644 index 0000000000..8971ac3649 Binary files /dev/null and b/bin/assets/TexturesAssetSet/Terrain Mountain-179cdabd-398a-9b6b-1391-4dc333ba321f.texture differ diff --git a/bin/assets/TexturesAssetSet/Terrain Rock-beb169c7-11ea-fff2-efe5-0f24dc881df2.texture b/bin/assets/TexturesAssetSet/Terrain Rock-beb169c7-11ea-fff2-efe5-0f24dc881df2.texture new file mode 100644 index 0000000000..ccbeb08f46 Binary files /dev/null and b/bin/assets/TexturesAssetSet/Terrain Rock-beb169c7-11ea-fff2-efe5-0f24dc881df2.texture differ diff --git a/bin/assets/TexturesAssetSet/TexturesAssetSet.xml b/bin/assets/TexturesAssetSet/TexturesAssetSet.xml new file mode 100644 index 0000000000..f7e4367a18 --- /dev/null +++ b/bin/assets/TexturesAssetSet/TexturesAssetSet.xml @@ -0,0 +1,767 @@ + +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + + +
+
+ + + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ +
+ + + + +
+ + +
+ + + + +
+ +
+ + + + +
+ + +
+ + + + +
+ +
diff --git a/bin/assets/TexturesAssetSet/a6162133-724b-54df-a12f-51cd070ad6f3.j2c b/bin/assets/TexturesAssetSet/a6162133-724b-54df-a12f-51cd070ad6f3.j2c new file mode 100644 index 0000000000..9d93153bc5 Binary files /dev/null and b/bin/assets/TexturesAssetSet/a6162133-724b-54df-a12f-51cd070ad6f3.j2c differ diff --git a/bin/assets/TexturesAssetSet/a85ac674-cb75-4af6-9499-df7c5aaf7a28.j2c b/bin/assets/TexturesAssetSet/a85ac674-cb75-4af6-9499-df7c5aaf7a28.j2c new file mode 100644 index 0000000000..aa222571d5 Binary files /dev/null and b/bin/assets/TexturesAssetSet/a85ac674-cb75-4af6-9499-df7c5aaf7a28.j2c differ diff --git a/bin/assets/TexturesAssetSet/ae874d1a-93ef-54fb-5fd3-eb0cb156afc0.j2c b/bin/assets/TexturesAssetSet/ae874d1a-93ef-54fb-5fd3-eb0cb156afc0.j2c new file mode 100644 index 0000000000..61711d2bf2 Binary files /dev/null and b/bin/assets/TexturesAssetSet/ae874d1a-93ef-54fb-5fd3-eb0cb156afc0.j2c differ diff --git a/bin/assets/TexturesAssetSet/b8eed5f0-64b7-6e12-b67f-43fa8e773440.j2c b/bin/assets/TexturesAssetSet/b8eed5f0-64b7-6e12-b67f-43fa8e773440.j2c new file mode 100644 index 0000000000..f1e7a96611 Binary files /dev/null and b/bin/assets/TexturesAssetSet/b8eed5f0-64b7-6e12-b67f-43fa8e773440.j2c differ diff --git a/bin/assets/TexturesAssetSet/blank.jpc b/bin/assets/TexturesAssetSet/blank.jpc new file mode 100644 index 0000000000..69ee4f1451 Binary files /dev/null and b/bin/assets/TexturesAssetSet/blank.jpc differ diff --git a/bin/assets/TexturesAssetSet/brick1_256.jp2 b/bin/assets/TexturesAssetSet/brick1_256.jp2 new file mode 100644 index 0000000000..a632282632 Binary files /dev/null and b/bin/assets/TexturesAssetSet/brick1_256.jp2 differ diff --git a/bin/assets/TexturesAssetSet/brick2_256.jp2 b/bin/assets/TexturesAssetSet/brick2_256.jp2 new file mode 100644 index 0000000000..efe0a9e0bf Binary files /dev/null and b/bin/assets/TexturesAssetSet/brick2_256.jp2 differ diff --git a/bin/assets/TexturesAssetSet/brick_mono.jp2 b/bin/assets/TexturesAssetSet/brick_mono.jp2 new file mode 100644 index 0000000000..b250960162 Binary files /dev/null and b/bin/assets/TexturesAssetSet/brick_mono.jp2 differ diff --git a/bin/assets/TexturesAssetSet/bricks.jp2 b/bin/assets/TexturesAssetSet/bricks.jp2 new file mode 100644 index 0000000000..9dfdffd4e4 Binary files /dev/null and b/bin/assets/TexturesAssetSet/bricks.jp2 differ diff --git a/bin/assets/TexturesAssetSet/ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c.j2c b/bin/assets/TexturesAssetSet/ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c.j2c new file mode 100644 index 0000000000..927af80074 Binary files /dev/null and b/bin/assets/TexturesAssetSet/ca4e8c27-473c-eb1c-2f5d-50ee3f07d85c.j2c differ diff --git a/bin/assets/TexturesAssetSet/cdd9a9fc-6d0b-f90d-8416-c72b6019bca8.j2c b/bin/assets/TexturesAssetSet/cdd9a9fc-6d0b-f90d-8416-c72b6019bca8.j2c new file mode 100644 index 0000000000..d6e52c2066 Binary files /dev/null and b/bin/assets/TexturesAssetSet/cdd9a9fc-6d0b-f90d-8416-c72b6019bca8.j2c differ diff --git a/bin/assets/TexturesAssetSet/cedar.jp2 b/bin/assets/TexturesAssetSet/cedar.jp2 new file mode 100644 index 0000000000..2894e3be9f Binary files /dev/null and b/bin/assets/TexturesAssetSet/cedar.jp2 differ diff --git a/bin/assets/TexturesAssetSet/cement_block.jp2 b/bin/assets/TexturesAssetSet/cement_block.jp2 new file mode 100644 index 0000000000..971daace99 Binary files /dev/null and b/bin/assets/TexturesAssetSet/cement_block.jp2 differ diff --git a/bin/assets/TexturesAssetSet/clear.jp2 b/bin/assets/TexturesAssetSet/clear.jp2 new file mode 100644 index 0000000000..e4a29fc764 Binary files /dev/null and b/bin/assets/TexturesAssetSet/clear.jp2 differ diff --git a/bin/assets/TexturesAssetSet/cloud.jp2 b/bin/assets/TexturesAssetSet/cloud.jp2 new file mode 100644 index 0000000000..36bec4c6ae Binary files /dev/null and b/bin/assets/TexturesAssetSet/cloud.jp2 differ diff --git a/bin/assets/TexturesAssetSet/cobbles.jp2 b/bin/assets/TexturesAssetSet/cobbles.jp2 new file mode 100644 index 0000000000..0bcc38f747 Binary files /dev/null and b/bin/assets/TexturesAssetSet/cobbles.jp2 differ diff --git a/bin/assets/TexturesAssetSet/coffee.jp2 b/bin/assets/TexturesAssetSet/coffee.jp2 new file mode 100644 index 0000000000..5761e8ee3d Binary files /dev/null and b/bin/assets/TexturesAssetSet/coffee.jp2 differ diff --git a/bin/assets/TexturesAssetSet/creambrick.jp2 b/bin/assets/TexturesAssetSet/creambrick.jp2 new file mode 100644 index 0000000000..a3c22e1ac1 Binary files /dev/null and b/bin/assets/TexturesAssetSet/creambrick.jp2 differ diff --git a/bin/assets/TexturesAssetSet/d21e44ca-ff1c-a96e-b2ef-c0753426b7d9.j2c b/bin/assets/TexturesAssetSet/d21e44ca-ff1c-a96e-b2ef-c0753426b7d9.j2c new file mode 100644 index 0000000000..909f9f972d Binary files /dev/null and b/bin/assets/TexturesAssetSet/d21e44ca-ff1c-a96e-b2ef-c0753426b7d9.j2c differ diff --git a/bin/assets/TexturesAssetSet/d691a01c-13b7-578d-57c0-5caef0b4e7e1.j2c b/bin/assets/TexturesAssetSet/d691a01c-13b7-578d-57c0-5caef0b4e7e1.j2c new file mode 100644 index 0000000000..493a097123 Binary files /dev/null and b/bin/assets/TexturesAssetSet/d691a01c-13b7-578d-57c0-5caef0b4e7e1.j2c differ diff --git a/bin/assets/TexturesAssetSet/d9258671-868f-7511-c321-7baef9e948a4.j2c b/bin/assets/TexturesAssetSet/d9258671-868f-7511-c321-7baef9e948a4.j2c new file mode 100644 index 0000000000..d343f63d76 Binary files /dev/null and b/bin/assets/TexturesAssetSet/d9258671-868f-7511-c321-7baef9e948a4.j2c differ diff --git a/bin/assets/TexturesAssetSet/db9d39ec-a896-c287-1ced-64566217021e.j2c b/bin/assets/TexturesAssetSet/db9d39ec-a896-c287-1ced-64566217021e.j2c new file mode 100644 index 0000000000..c11984bf63 Binary files /dev/null and b/bin/assets/TexturesAssetSet/db9d39ec-a896-c287-1ced-64566217021e.j2c differ diff --git a/bin/assets/TexturesAssetSet/default_alpha.jp2 b/bin/assets/TexturesAssetSet/default_alpha.jp2 new file mode 100644 index 0000000000..af73c1ed01 Binary files /dev/null and b/bin/assets/TexturesAssetSet/default_alpha.jp2 differ diff --git a/bin/assets/TexturesAssetSet/default_avatar.jp2 b/bin/assets/TexturesAssetSet/default_avatar.jp2 new file mode 100644 index 0000000000..116b860d80 Binary files /dev/null and b/bin/assets/TexturesAssetSet/default_avatar.jp2 differ diff --git a/bin/assets/TexturesAssetSet/default_clear.jp2 b/bin/assets/TexturesAssetSet/default_clear.jp2 new file mode 100644 index 0000000000..078edd8b1d Binary files /dev/null and b/bin/assets/TexturesAssetSet/default_clear.jp2 differ diff --git a/bin/assets/TexturesAssetSet/default_iris.jp2 b/bin/assets/TexturesAssetSet/default_iris.jp2 new file mode 100644 index 0000000000..b932a062f9 Binary files /dev/null and b/bin/assets/TexturesAssetSet/default_iris.jp2 differ diff --git a/bin/assets/TexturesAssetSet/default_media.jp2 b/bin/assets/TexturesAssetSet/default_media.jp2 new file mode 100644 index 0000000000..77e74e33c8 Binary files /dev/null and b/bin/assets/TexturesAssetSet/default_media.jp2 differ diff --git a/bin/assets/TexturesAssetSet/e569711a-27c2-aad4-9246-0c910239a179.j2c b/bin/assets/TexturesAssetSet/e569711a-27c2-aad4-9246-0c910239a179.j2c new file mode 100644 index 0000000000..9be14d4756 Binary files /dev/null and b/bin/assets/TexturesAssetSet/e569711a-27c2-aad4-9246-0c910239a179.j2c differ diff --git a/bin/assets/TexturesAssetSet/f2d7b6f6-4200-1e9a-fd5b-96459e950f94.j2c b/bin/assets/TexturesAssetSet/f2d7b6f6-4200-1e9a-fd5b-96459e950f94.j2c new file mode 100644 index 0000000000..cb8a0cbd9c Binary files /dev/null and b/bin/assets/TexturesAssetSet/f2d7b6f6-4200-1e9a-fd5b-96459e950f94.j2c differ diff --git a/bin/assets/TexturesAssetSet/fb2ae204-3fd1-df33-594f-c9f882830e66.j2c b/bin/assets/TexturesAssetSet/fb2ae204-3fd1-df33-594f-c9f882830e66.j2c new file mode 100644 index 0000000000..2db8517045 Binary files /dev/null and b/bin/assets/TexturesAssetSet/fb2ae204-3fd1-df33-594f-c9f882830e66.j2c differ diff --git a/bin/assets/TexturesAssetSet/fe_face.jp2 b/bin/assets/TexturesAssetSet/fe_face.jp2 new file mode 100644 index 0000000000..c40c5050a2 Binary files /dev/null and b/bin/assets/TexturesAssetSet/fe_face.jp2 differ diff --git a/bin/assets/TexturesAssetSet/fe_lower.jp2 b/bin/assets/TexturesAssetSet/fe_lower.jp2 new file mode 100644 index 0000000000..58f6d1725f Binary files /dev/null and b/bin/assets/TexturesAssetSet/fe_lower.jp2 differ diff --git a/bin/assets/TexturesAssetSet/fe_upper.jp2 b/bin/assets/TexturesAssetSet/fe_upper.jp2 new file mode 100644 index 0000000000..2f38c1757f Binary files /dev/null and b/bin/assets/TexturesAssetSet/fe_upper.jp2 differ diff --git a/bin/assets/TexturesAssetSet/femalebody.jp2 b/bin/assets/TexturesAssetSet/femalebody.jp2 new file mode 100644 index 0000000000..a50498fb4b Binary files /dev/null and b/bin/assets/TexturesAssetSet/femalebody.jp2 differ diff --git a/bin/assets/TexturesAssetSet/femalebottom.jp2 b/bin/assets/TexturesAssetSet/femalebottom.jp2 new file mode 100644 index 0000000000..88908c6b6b Binary files /dev/null and b/bin/assets/TexturesAssetSet/femalebottom.jp2 differ diff --git a/bin/assets/TexturesAssetSet/femaleface.jp2 b/bin/assets/TexturesAssetSet/femaleface.jp2 new file mode 100644 index 0000000000..7bd0cb0651 Binary files /dev/null and b/bin/assets/TexturesAssetSet/femaleface.jp2 differ diff --git a/bin/assets/TexturesAssetSet/femalehair.jp2 b/bin/assets/TexturesAssetSet/femalehair.jp2 new file mode 100644 index 0000000000..15a1f36499 Binary files /dev/null and b/bin/assets/TexturesAssetSet/femalehair.jp2 differ diff --git a/bin/assets/TexturesAssetSet/fgrass.jp2 b/bin/assets/TexturesAssetSet/fgrass.jp2 new file mode 100644 index 0000000000..fb496aa2e0 Binary files /dev/null and b/bin/assets/TexturesAssetSet/fgrass.jp2 differ diff --git a/bin/assets/TexturesAssetSet/glasstile2.jp2 b/bin/assets/TexturesAssetSet/glasstile2.jp2 new file mode 100644 index 0000000000..9f62da7521 Binary files /dev/null and b/bin/assets/TexturesAssetSet/glasstile2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/granite.jp2 b/bin/assets/TexturesAssetSet/granite.jp2 new file mode 100644 index 0000000000..c3ef40a29c Binary files /dev/null and b/bin/assets/TexturesAssetSet/granite.jp2 differ diff --git a/bin/assets/TexturesAssetSet/graniteblock.jp2 b/bin/assets/TexturesAssetSet/graniteblock.jp2 new file mode 100644 index 0000000000..c7c03f92bb Binary files /dev/null and b/bin/assets/TexturesAssetSet/graniteblock.jp2 differ diff --git a/bin/assets/TexturesAssetSet/grass.jp2 b/bin/assets/TexturesAssetSet/grass.jp2 new file mode 100644 index 0000000000..63cdfa3b47 Binary files /dev/null and b/bin/assets/TexturesAssetSet/grass.jp2 differ diff --git a/bin/assets/TexturesAssetSet/grass2.jp2 b/bin/assets/TexturesAssetSet/grass2.jp2 new file mode 100644 index 0000000000..74eb2b4907 Binary files /dev/null and b/bin/assets/TexturesAssetSet/grass2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/gravel.jp2 b/bin/assets/TexturesAssetSet/gravel.jp2 new file mode 100644 index 0000000000..21ed22e562 Binary files /dev/null and b/bin/assets/TexturesAssetSet/gravel.jp2 differ diff --git a/bin/assets/TexturesAssetSet/greybrick.jp2 b/bin/assets/TexturesAssetSet/greybrick.jp2 new file mode 100644 index 0000000000..5f45363c8f Binary files /dev/null and b/bin/assets/TexturesAssetSet/greybrick.jp2 differ diff --git a/bin/assets/TexturesAssetSet/hardwood.jp2 b/bin/assets/TexturesAssetSet/hardwood.jp2 new file mode 100644 index 0000000000..ff0fccef47 Binary files /dev/null and b/bin/assets/TexturesAssetSet/hardwood.jp2 differ diff --git a/bin/assets/TexturesAssetSet/hg2.jp2 b/bin/assets/TexturesAssetSet/hg2.jp2 new file mode 100644 index 0000000000..9a66fd325d Binary files /dev/null and b/bin/assets/TexturesAssetSet/hg2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/ivy.jp2 b/bin/assets/TexturesAssetSet/ivy.jp2 new file mode 100644 index 0000000000..d19d18df5d Binary files /dev/null and b/bin/assets/TexturesAssetSet/ivy.jp2 differ diff --git a/bin/assets/TexturesAssetSet/le_face.jp2 b/bin/assets/TexturesAssetSet/le_face.jp2 new file mode 100644 index 0000000000..9d23acf892 Binary files /dev/null and b/bin/assets/TexturesAssetSet/le_face.jp2 differ diff --git a/bin/assets/TexturesAssetSet/le_lower.jp2 b/bin/assets/TexturesAssetSet/le_lower.jp2 new file mode 100644 index 0000000000..17d13bd769 Binary files /dev/null and b/bin/assets/TexturesAssetSet/le_lower.jp2 differ diff --git a/bin/assets/TexturesAssetSet/le_upper.jp2 b/bin/assets/TexturesAssetSet/le_upper.jp2 new file mode 100644 index 0000000000..53b648a63e Binary files /dev/null and b/bin/assets/TexturesAssetSet/le_upper.jp2 differ diff --git a/bin/assets/TexturesAssetSet/licenses.txt b/bin/assets/TexturesAssetSet/licenses.txt new file mode 100644 index 0000000000..f34d26f89e --- /dev/null +++ b/bin/assets/TexturesAssetSet/licenses.txt @@ -0,0 +1,59 @@ +These textures have been collected from various sources: + +1. Blender Texture Disk + +Notice of rights + +This is the collection of textures and materials of the "Blender Texture Disk", a product created by the company "NaN Technologies" in 2001, and later sold in the blender.org e-shop to support Open Source Blender development. + +The Blender Foundation has now decided to open up this content entirely as public domain. We wish you a lot of fun with the collection! +Ton Roosendaal +Chairman Blender Foundation +ton(at)blender(dot)org + +2. Hawaiian Plant Textures + +http://www.vterrain.org/Hawaii/Flora/textures/index.html + +I am placing these texture maps into the public domain; if you want higher resolutions or have any other questions or feedback, please contact me: info@vterrain.org + +3. Golgotha Textures + +Golgotha was a game under development by Crack Dot Com. When game development was abandoned, the authors generously donated their work to the public domain. + + +4. Some made by me, Babblefrog + +A few of the wood textures were created by me using Wood Workshop. These I donate to the public domain. + +5. From the VTerrain project. + + The source code and data in this distribution are Copyright (c) 2001-2005 Virtual Terrain Project. + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated data and documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +6. From Second Life(TM) Viewer Artwork. Copyright (C) 2008 Linden Research, Inc. + +Linden Research, Inc. ("Linden Lab") licenses the Second Life viewer +artwork and other works in the files distributed with this Notice under +the Creative Commons Attribution-Share Alike 3.0 License, available at +http://creativecommons.org/licenses/by- sa/3.0/legalcode. For the license +summary, see http://creativecommons.org/licenses/by-sa/3.0/. + +Notwithstanding the foregoing, all of Linden Lab's trademarks, including +but not limited to the Second Life brand name and Second Life Eye-in-Hand +logo, are subject to our trademark policy at +http://secondlife.com/corporate/trademark/. + +If you distribute any copies or adaptations of the Second Life viewer +artwork or any other works in these files, you must include this Notice +and clearly identify any changes made to the original works. Include +this Notice and information where copyright notices are usually included, +for example, after your own copyright notice acknowledging your use of +the Second Life viewer artwork, in a text file distributed with your +program, in your application's About window, or on a credits page for +your work. diff --git a/bin/assets/TexturesAssetSet/mahogany.jp2 b/bin/assets/TexturesAssetSet/mahogany.jp2 new file mode 100644 index 0000000000..faca335d28 Binary files /dev/null and b/bin/assets/TexturesAssetSet/mahogany.jp2 differ diff --git a/bin/assets/TexturesAssetSet/map1.jp2 b/bin/assets/TexturesAssetSet/map1.jp2 new file mode 100644 index 0000000000..cd2fd94422 Binary files /dev/null and b/bin/assets/TexturesAssetSet/map1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/map_base.jp2 b/bin/assets/TexturesAssetSet/map_base.jp2 new file mode 100644 index 0000000000..5ad1fd0f43 Binary files /dev/null and b/bin/assets/TexturesAssetSet/map_base.jp2 differ diff --git a/bin/assets/TexturesAssetSet/maple.jp2 b/bin/assets/TexturesAssetSet/maple.jp2 new file mode 100644 index 0000000000..1601336753 Binary files /dev/null and b/bin/assets/TexturesAssetSet/maple.jp2 differ diff --git a/bin/assets/TexturesAssetSet/moon.jp2 b/bin/assets/TexturesAssetSet/moon.jp2 new file mode 100644 index 0000000000..235ea5f333 Binary files /dev/null and b/bin/assets/TexturesAssetSet/moon.jp2 differ diff --git a/bin/assets/TexturesAssetSet/mosaic02.jp2 b/bin/assets/TexturesAssetSet/mosaic02.jp2 new file mode 100644 index 0000000000..1f853e9ab5 Binary files /dev/null and b/bin/assets/TexturesAssetSet/mosaic02.jp2 differ diff --git a/bin/assets/TexturesAssetSet/palm1.jp2 b/bin/assets/TexturesAssetSet/palm1.jp2 new file mode 100644 index 0000000000..ddc3be43db Binary files /dev/null and b/bin/assets/TexturesAssetSet/palm1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/papaya.jp2 b/bin/assets/TexturesAssetSet/papaya.jp2 new file mode 100644 index 0000000000..53d037e0a8 Binary files /dev/null and b/bin/assets/TexturesAssetSet/papaya.jp2 differ diff --git a/bin/assets/TexturesAssetSet/papaya_bark.jp2 b/bin/assets/TexturesAssetSet/papaya_bark.jp2 new file mode 100644 index 0000000000..37642cee6e Binary files /dev/null and b/bin/assets/TexturesAssetSet/papaya_bark.jp2 differ diff --git a/bin/assets/TexturesAssetSet/pastelbrick.jp2 b/bin/assets/TexturesAssetSet/pastelbrick.jp2 new file mode 100644 index 0000000000..5d21456f96 Binary files /dev/null and b/bin/assets/TexturesAssetSet/pastelbrick.jp2 differ diff --git a/bin/assets/TexturesAssetSet/pine1_10m.jp2 b/bin/assets/TexturesAssetSet/pine1_10m.jp2 new file mode 100644 index 0000000000..376e9ee58f Binary files /dev/null and b/bin/assets/TexturesAssetSet/pine1_10m.jp2 differ diff --git a/bin/assets/TexturesAssetSet/plywood.jp2 b/bin/assets/TexturesAssetSet/plywood.jp2 new file mode 100644 index 0000000000..2053675dc4 Binary files /dev/null and b/bin/assets/TexturesAssetSet/plywood.jp2 differ diff --git a/bin/assets/TexturesAssetSet/poplar.jp2 b/bin/assets/TexturesAssetSet/poplar.jp2 new file mode 100644 index 0000000000..f8790783de Binary files /dev/null and b/bin/assets/TexturesAssetSet/poplar.jp2 differ diff --git a/bin/assets/TexturesAssetSet/re_face.jp2 b/bin/assets/TexturesAssetSet/re_face.jp2 new file mode 100644 index 0000000000..66c41a4b11 Binary files /dev/null and b/bin/assets/TexturesAssetSet/re_face.jp2 differ diff --git a/bin/assets/TexturesAssetSet/re_lower.jp2 b/bin/assets/TexturesAssetSet/re_lower.jp2 new file mode 100644 index 0000000000..b4e616e2a7 Binary files /dev/null and b/bin/assets/TexturesAssetSet/re_lower.jp2 differ diff --git a/bin/assets/TexturesAssetSet/re_upper.jp2 b/bin/assets/TexturesAssetSet/re_upper.jp2 new file mode 100644 index 0000000000..33b7f5d195 Binary files /dev/null and b/bin/assets/TexturesAssetSet/re_upper.jp2 differ diff --git a/bin/assets/TexturesAssetSet/redtri_tile.jp2 b/bin/assets/TexturesAssetSet/redtri_tile.jp2 new file mode 100644 index 0000000000..5fca48f2ef Binary files /dev/null and b/bin/assets/TexturesAssetSet/redtri_tile.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rockbuilding.jp2 b/bin/assets/TexturesAssetSet/rockbuilding.jp2 new file mode 100644 index 0000000000..463f7e3852 Binary files /dev/null and b/bin/assets/TexturesAssetSet/rockbuilding.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rocks.jp2 b/bin/assets/TexturesAssetSet/rocks.jp2 new file mode 100644 index 0000000000..20b93a1f49 Binary files /dev/null and b/bin/assets/TexturesAssetSet/rocks.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rockwallbig.jp2 b/bin/assets/TexturesAssetSet/rockwallbig.jp2 new file mode 100644 index 0000000000..b0f1d4255d Binary files /dev/null and b/bin/assets/TexturesAssetSet/rockwallbig.jp2 differ diff --git a/bin/assets/TexturesAssetSet/roof01.jp2 b/bin/assets/TexturesAssetSet/roof01.jp2 new file mode 100644 index 0000000000..c1bbd6270e Binary files /dev/null and b/bin/assets/TexturesAssetSet/roof01.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rooftiles1.jp2 b/bin/assets/TexturesAssetSet/rooftiles1.jp2 new file mode 100644 index 0000000000..5a1750bccc Binary files /dev/null and b/bin/assets/TexturesAssetSet/rooftiles1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rooftiles2_peach.jp2 b/bin/assets/TexturesAssetSet/rooftiles2_peach.jp2 new file mode 100644 index 0000000000..aae5dafb64 Binary files /dev/null and b/bin/assets/TexturesAssetSet/rooftiles2_peach.jp2 differ diff --git a/bin/assets/TexturesAssetSet/rooftiles2_roy.jp2 b/bin/assets/TexturesAssetSet/rooftiles2_roy.jp2 new file mode 100644 index 0000000000..ea2c8405e8 Binary files /dev/null and b/bin/assets/TexturesAssetSet/rooftiles2_roy.jp2 differ diff --git a/bin/assets/TexturesAssetSet/saguaro_8m.jp2 b/bin/assets/TexturesAssetSet/saguaro_8m.jp2 new file mode 100644 index 0000000000..9c577b446b Binary files /dev/null and b/bin/assets/TexturesAssetSet/saguaro_8m.jp2 differ diff --git a/bin/assets/TexturesAssetSet/se_face.jp2 b/bin/assets/TexturesAssetSet/se_face.jp2 new file mode 100644 index 0000000000..e785a51263 Binary files /dev/null and b/bin/assets/TexturesAssetSet/se_face.jp2 differ diff --git a/bin/assets/TexturesAssetSet/se_lower.jp2 b/bin/assets/TexturesAssetSet/se_lower.jp2 new file mode 100644 index 0000000000..3158008a13 Binary files /dev/null and b/bin/assets/TexturesAssetSet/se_lower.jp2 differ diff --git a/bin/assets/TexturesAssetSet/se_upper.jp2 b/bin/assets/TexturesAssetSet/se_upper.jp2 new file mode 100644 index 0000000000..f2636610ea Binary files /dev/null and b/bin/assets/TexturesAssetSet/se_upper.jp2 differ diff --git a/bin/assets/TexturesAssetSet/seawater.jp2 b/bin/assets/TexturesAssetSet/seawater.jp2 new file mode 100644 index 0000000000..850371cf47 Binary files /dev/null and b/bin/assets/TexturesAssetSet/seawater.jp2 differ diff --git a/bin/assets/TexturesAssetSet/shingle.jp2 b/bin/assets/TexturesAssetSet/shingle.jp2 new file mode 100644 index 0000000000..abbb496ae3 Binary files /dev/null and b/bin/assets/TexturesAssetSet/shingle.jp2 differ diff --git a/bin/assets/TexturesAssetSet/skins_license.txt b/bin/assets/TexturesAssetSet/skins_license.txt new file mode 100644 index 0000000000..7226d00d11 --- /dev/null +++ b/bin/assets/TexturesAssetSet/skins_license.txt @@ -0,0 +1,5 @@ +Includes makeup-less textures also! ^_^ + +This skin is released under a "BSD license"--which allows you to pretty much copy, mod, transfer as you wish... so you could legally make and sell your own modifications to them or even just "as-is" without any modifications. Really! No, I don't have a rabid team of lawyers waiting to DCMA-pounce you... + +You can download the skin PSDs from: http://eloheliot.blogspot.com/2007/12/my-psds-let-me-show-u-them.html \ No newline at end of file diff --git a/bin/assets/TexturesAssetSet/snow1.jp2 b/bin/assets/TexturesAssetSet/snow1.jp2 new file mode 100644 index 0000000000..a83b349211 Binary files /dev/null and b/bin/assets/TexturesAssetSet/snow1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/steel.jp2 b/bin/assets/TexturesAssetSet/steel.jp2 new file mode 100644 index 0000000000..4da19a0bb4 Binary files /dev/null and b/bin/assets/TexturesAssetSet/steel.jp2 differ diff --git a/bin/assets/TexturesAssetSet/stone1wall.jp2 b/bin/assets/TexturesAssetSet/stone1wall.jp2 new file mode 100644 index 0000000000..d4db4044d6 Binary files /dev/null and b/bin/assets/TexturesAssetSet/stone1wall.jp2 differ diff --git a/bin/assets/TexturesAssetSet/stonetile.jp2 b/bin/assets/TexturesAssetSet/stonetile.jp2 new file mode 100644 index 0000000000..36d8c71110 Binary files /dev/null and b/bin/assets/TexturesAssetSet/stonetile.jp2 differ diff --git a/bin/assets/TexturesAssetSet/street2.jp2 b/bin/assets/TexturesAssetSet/street2.jp2 new file mode 100644 index 0000000000..fd8ffdfa36 Binary files /dev/null and b/bin/assets/TexturesAssetSet/street2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/testpic2.jp2 b/bin/assets/TexturesAssetSet/testpic2.jp2 new file mode 100644 index 0000000000..e6840e2139 Binary files /dev/null and b/bin/assets/TexturesAssetSet/testpic2.jp2 differ diff --git a/bin/assets/TexturesAssetSet/thatch.jp2 b/bin/assets/TexturesAssetSet/thatch.jp2 new file mode 100644 index 0000000000..c6b7e40d7e Binary files /dev/null and b/bin/assets/TexturesAssetSet/thatch.jp2 differ diff --git a/bin/assets/TexturesAssetSet/water1.jp2 b/bin/assets/TexturesAssetSet/water1.jp2 new file mode 100644 index 0000000000..e714d4c4e2 Binary files /dev/null and b/bin/assets/TexturesAssetSet/water1.jp2 differ diff --git a/bin/assets/TexturesAssetSet/water3.jp2 b/bin/assets/TexturesAssetSet/water3.jp2 new file mode 100644 index 0000000000..be2984b695 Binary files /dev/null and b/bin/assets/TexturesAssetSet/water3.jp2 differ diff --git a/bin/assets/TexturesAssetSet/wood1.jp2 b/bin/assets/TexturesAssetSet/wood1.jp2 new file mode 100644 index 0000000000..3758326f5f Binary files /dev/null and b/bin/assets/TexturesAssetSet/wood1.jp2 differ diff --git a/bin/avatar-texture.dat b/bin/avatar-texture.dat new file mode 100644 index 0000000000..09765be134 Binary files /dev/null and b/bin/avatar-texture.dat differ diff --git a/bin/config-include/CenomeCache.ini.example b/bin/config-include/CenomeCache.ini.example new file mode 100644 index 0000000000..4340493355 --- /dev/null +++ b/bin/config-include/CenomeCache.ini.example @@ -0,0 +1,14 @@ +[AssetCache] + ;; + ;; Options for CenomeAssetCache + ;; + + ; Max size of the cache in bytes + ; 134217728 = 128 MB, 26843556 = 256 MB, etc (default: 134217728) + MaxSize = 134217728 + + ; How many assets it is possible to store in the cache (default: 4096) + MaxCount = 4096 + + ; Expiration time in minutes (default: 30) + ExpirationTime = 30 diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example new file mode 100644 index 0000000000..ad74fc14e9 --- /dev/null +++ b/bin/config-include/FlotsamCache.ini.example @@ -0,0 +1,56 @@ +[AssetCache] + ;; + ;; Options for FlotsamAssetCache + ;; + + ; cache directory can be shared by multiple instances + CacheDirectory = ./assetcache + ; Other examples: + ;CacheDirectory = /directory/writable/by/OpenSim/instance + + ; Log level + ; 0 - (Error) Errors only + ; 1 - (Info) Hit Rate Stats + Level 0 + ; 2 - (Debug) Cache Activity (Reads/Writes) + Level 1 + ; + LogLevel = 0 + + ; How often should hit rates be displayed (given in AssetRequests) + ; 0 to disable + HitRateDisplay = 100 + + ; Set to false for no memory cache + MemoryCacheEnabled = false + + ; Set to false for no file cache + FileCacheEnabled = true + + ; How long {in hours} to keep assets cached in memory, .5 == 30 minutes + ; Optimization: for VPS or limited memory system installs set Timeout to .016 (1 minute) + ; increases performance without large memory impact + MemoryCacheTimeout = 2 + + ; How long {in hours} to keep assets cached on disk, .5 == 30 minutes + ; Specify 0 if you do not want your disk cache to expire + FileCacheTimeout = 48 + + ; How often {in hours} should the disk be checked for expired filed + ; Specify 0 to disable expiration checking + FileCleanupTimer = 1.0 ;every hour + + ; If WAIT_ON_INPROGRESS_REQUESTS has been defined then this specifies how + ; long (in miliseconds) to block a request thread while trying to complete + ; an existing write to disk. + ; NOTE: THIS PARAMETER IS NOT CURRENTLY USED BY THE CACHE + ; WaitOnInprogressTimeout = 3000 + + ; Number of tiers to use for cache directories (current valid + ; range 1 to 3) + ;CacheDirectoryTiers = 1 + + ; Number of letters per path tier, 1 will create 16 directories + ; per tier, 2 - 256, 3 - 4096 and 4 - 65K + ;CacheDirectoryTierLength = 3 + + ; Warning level for cache directory size + ;CacheWarnAt = 30000 diff --git a/bin/config-include/Grid.ini b/bin/config-include/Grid.ini new file mode 100644 index 0000000000..3c61ee01fd --- /dev/null +++ b/bin/config-include/Grid.ini @@ -0,0 +1,74 @@ +;; +;; Please don't change this file. +;; All optional settings are in GridCommon.ini.example, +;; which you can copy and change. +;; + +[Includes] + Include-Common = "config-include/GridCommon.ini" + +[Modules] + AssetServices = "RemoteAssetServicesConnector" + InventoryServices = "RemoteXInventoryServicesConnector" + GridServices = "RemoteGridServicesConnector" + AvatarServices = "RemoteAvatarServicesConnector" + NeighbourServices = "RemoteNeighbourServicesConnector" + AuthenticationServices = "RemoteAuthenticationServicesConnector" + AuthorizationServices = "LocalAuthorizationServicesConnector" + PresenceServices = "RemotePresenceServicesConnector" + UserAccountServices = "RemoteUserAccountServicesConnector" + AgentPreferencesServices= "RemoteAgentPreferencesServicesConnector" + GridUserServices = "RemoteGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" + LandServices = "RemoteLandServicesConnector" + MapImageService = "MapImageServiceModule" + SearchModule = "BasicSearchModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = true + +[SimulationService] + ; This is the protocol version which the simulator advertises to the source destination when acting as a target destination for a teleport + ; It is used to control the teleport handoff process. + ; Valid values are + ; "SIMULATION/0.3" + ; - This is the default, and it supports teleports to variable-sized regions + ; - Older versions can teleport to this one, but only if the destination region + ; is 256x256 + ; "SIMULATION/0.2" + ; - A source simulator which only implements "SIMULATION/0.1" can still teleport with that protocol + ; - this protocol is more efficient than "SIMULATION/0.1" + ; "SIMULATION/0.1" + ; - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + ConnectorProtocolVersion = "SIMULATION/0.3" + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.SimulationService.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + ; for the LocalGridServicesConnector which is used by the Remote one + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + NetworkConnector = "OpenSim.Services.Connectors.dll:GridServicesConnector" + + ; Because LocalGridServicesConnector starts this service, in grid mode we need to suppress + ; the inappropriate console commands that it registers. + SuppressConsoleCommands = true + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[Friends] + Connector = "OpenSim.Services.Connectors.dll:FriendsServicesConnector" + +[MapImageService] + LocalServiceModule = "OpenSim.Services.Connectors.dll:MapImageServicesConnector" diff --git a/bin/config-include/GridCommon.ini.example b/bin/config-include/GridCommon.ini.example new file mode 100644 index 0000000000..0922cf5a4a --- /dev/null +++ b/bin/config-include/GridCommon.ini.example @@ -0,0 +1,245 @@ +; This is the main configuration file for an instance of OpenSim running in grid mode + +[DatabaseService] + ; + ; ### Choose the DB + ; + + ; SQLite + Include-Storage = "config-include/storage/SQLiteStandalone.ini"; + + ; MySql + ; Uncomment these lines if you want to use mysql storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.MySQL.dll" + ;ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + ; Uncomment this line if you are using MySQL and want to use a different database for estates + ; The usual application for this is to allow estates to be spread out across multiple simulators by share the same database. + ; Most people won't need to do this so only uncomment if you know what you're doing. + ;EstateConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + + ; MSSQL + ; Uncomment these lines if you want to use MSSQL storage + ; Change the connection string to your db details + ; The value for server property is shown in your SQL Server Management Studio login dialog. + ; (This sample is the default of express edition) + ;StorageProvider = "OpenSim.Data.MSSQL.dll" + ;ConnectionString = "Server=localhost\SQLEXPRESS;Database=opensim;User Id=opensim; password=***;" + + ; PGSQL + ; Uncomment these lines if you want to use PGSQL storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.PGSQL.dll" + ;ConnectionString = "Server=localhost;Database=opensim;User Id=opensim; password=***;" + +[Hypergrid] + ; Uncomment the variables in this section only if you are in + ; Hypergrid configuration. Otherwise, ignore. + + ;# {HomeURI} {Hypergrid} {The Home URL of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server that + ;; runs the UserAgentsService. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;# {GatekeeperURI} {Hypergrid} {The URL of the gatekeeper of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server + ;; that runs the Gatekeeper service. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + +[Modules] + ;; Choose one cache module and the corresponding config file, if it exists. + ;; Copy the config .example file into your own .ini file and adapt that. + ;; We recommend the use of the FlotsamAssetCache since this is most actively maintained. + + AssetCaching = "FlotsamAssetCache" + Include-FlotsamCache = "config-include/FlotsamCache.ini" + + ;AssetCaching = "CenomeMemoryAssetCache" + ;Include-CenomeCache = "config-include/CenomeCache.ini" + + ;AssetCaching = "GlynnTuckerAssetCache" + + ;; Optionally, the port for the LLProxyLoginModule module can be changed + ;Setup_LLProxyLoginModule = "9090/" + + ;; Authorization is not on by default, as it depends on external php + ;AuthorizationServices = "RemoteAuthorizationServicesConnector" + +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "assets/AssetSets.xml" + + ; + ; Change this to your grid-wide asset server. Do not add a slash to the end of any of these addresses. + ; + AssetServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[InventoryService] + ; + ; Change this to your grid-wide inventory server + ; + InventoryServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[GridInfo] + ; + ; Change this to your grid info service + ; + GridInfoURI = "${Const|BaseURL}:${Const|PublicPort}" + +[GridService] + ; + ; Change this to your grid-wide grid server + ; + GridServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + ;AllowHypergridMapSearch = true + + ;; Directory for map tile images of linked regions + ; MapTileDirectory = "./maptiles" + + ; === HG ONLY === + ;; Change this to the address of your Gatekeeper service + ;; (usually bundled with the rest of the services in one + ;; Robust server in port ${Const|PublicPort}, but not always) + Gatekeeper="${Const|BaseURL}:${Const|PublicPort}" + +[EstateDataStore] + ; + ; Uncomment if you want centralized estate data at robust server, + ; in which case the URL in [EstateService] will be used + ; + ;LocalServiceModule = "OpenSim.Services.Connectors.dll:EstateDataRemoteConnector" + +[EstateService] + EstateServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[Messaging] + ; === HG ONLY === + ;; Change this to the address of your Gatekeeper service + ;; (usually bundled with the rest of the services in one + ;; Robust server in port ${Const|PublicPort}, but not always) + Gatekeeper = "${Const|BaseURL}:${Const|PublicPort}" + +[AvatarService] + ; + ; Change this to your grid-wide grid server + ; + AvatarServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[AgentPreferencesService] + ; + ; Change this to your grid-wide avatar prefs server + ; + AgentPreferencesServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[PresenceService] + ; + ; Change this to your grid-wide presence server + ; + PresenceServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[UserAccountService] + ; + ; Change this to your grid-wide user accounts server + ; + UserAccountServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[GridUserService] + ; + ; Change this to your grid-wide user accounts server + ; + GridUserServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[AuthenticationService] + ; + ; Change this to your grid-wide authentication server + ; + AuthenticationServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[FriendsService] + ; + ; Change this to your grid-wide friends server + ; + FriendsServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[HGInventoryAccessModule] + ; + ; === HG ONLY === + ; Change this to your server + ; accessible from other grids + ; + HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + Gatekeeper = "${Const|BaseURL}:${Const|PublicPort}" + ;; If you want to protect your assets from being copied by foreign visitors + ;; set this to false. You may want to do this on sims that have licensed content. + ;; Default is true. + ; OutboundPermission = True + + ;; Send visual reminder to local users that their inventories are unavailable while they are traveling + ;; and available when they return. True by default. + ;RestrictInventoryAccessAbroad = True + + ;; Warning: advanced and unusual. Default is false. + ;; Enables configurations where grids share user services, including inventory, + ;; while separating regions' assets from users' assets. Asset transfer between + ;; the users' asset server and the regions' asset server is done in HG-like manner. + ; CheckSeparateAssets = false + ; RegionHGAssetServerURI = ${Const|BaseURL}:${Const|PublicPort} + + +[HGAssetService] + ; + ; === HG ONLY === + ; Change this to your server + ; accessible from other grids + ; + HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; The asset types that this grid can export to / import from other grids. + ;; Comma separated. + ;; Valid values are all the asset types in OpenMetaverse.AssetType, namely: + ;; Unknown, Texture, Sound, CallingCard, Landmark, Clothing, Object, Notecard, LSLText, + ;; LSLBytecode, TextureTGA, Bodypart, SoundWAV, ImageTGA, ImageJPEG, Animation, Gesture, Mesh + ;; + ;; Leave blank or commented if you don't want to apply any restrictions. + ;; A more strict, but still reasonable, policy may be to disallow the exchange + ;; of scripts, like so: + ; DisallowExport ="LSLText" + ; DisallowImport ="LSLBytecode" + +[HGFriendsModule] + ; User level required to be able to send friendship invitations to foreign users + ;LevelHGFriends = 0; + +[UserAgentService] + ; + ; === HG ONLY === + ; Change this to your user agent server (HG robust) + ; + UserAgentServerURI = "${Const|BaseURL}:${Const|PublicPort}" + +[MapImageService] + MapImageServerURI = "${Const|BaseURL}:${Const|PrivatePort}" + +[AuthorizationService] + ; If you have regions with access restrictions + ; specify them here using the convention + ; Region_ = + ; Valid flags are: + ; DisallowForeigners -- HG visitors not allowed + ; DisallowResidents -- only Admins and Managers allowed + ; Example: + ; Region_Test_1 = "DisallowForeigners" + +;; Uncomment if you are using SimianGrid for grid services +[SimianGrid] + ;; SimianGrid services URL + ;; SimianServiceURL = "http://grid.sciencesim.com/Grid/" + + ;; Capability assigned by the grid administrator for the simulator + ;; SimulatorCapability = "00000000-0000-0000-0000-000000000000" diff --git a/bin/config-include/GridHypergrid.ini b/bin/config-include/GridHypergrid.ini new file mode 100644 index 0000000000..aa64c2af5a --- /dev/null +++ b/bin/config-include/GridHypergrid.ini @@ -0,0 +1,104 @@ +;; +;; Please don't change this file. +;; All optional settings are in GridCommon.ini.example, +;; which you can copy and change. +;; + +[Includes] + Include-Common = "config-include/GridCommon.ini" + +[Startup] + WorldMapModule = "HGWorldMap" + +[Modules] + AssetServices = "HGAssetBroker" + InventoryServices = "HGInventoryBroker" + GridServices = "RemoteGridServicesConnector" + AvatarServices = "RemoteAvatarServicesConnector" + NeighbourServices = "RemoteNeighbourServicesConnector" + AuthenticationServices = "RemoteAuthenticationServicesConnector" + AuthorizationServices = "LocalAuthorizationServicesConnector" + PresenceServices = "RemotePresenceServicesConnector" + UserAccountServices = "RemoteUserAccountServicesConnector" + AgentPreferencesServices= "RemoteAgentPreferencesServicesConnector" + GridUserServices = "RemoteGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "HGEntityTransferModule" + InventoryAccessModule = "HGInventoryAccessModule" + LandServices = "RemoteLandServicesConnector" + FriendsModule = "HGFriendsModule" + MapImageService = "MapImageServiceModule" + UserManagementModule = "HGUserManagementModule" + SearchModule = "BasicSearchModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = true + +[SimulationService] + ; This is the protocol version which the simulator advertises to the source destination when acting as a target destination for a teleport + ; It is used to control the teleport handoff process. + ; Valid values are + ; "SIMULATION/0.3" + ; - This is the default, and it supports teleports to variable-sized regions + ; - Older versions can teleport to this one, but only if the destination region + ; is 256x256 + ; "SIMULATION/0.2" + ; - A source simulator which only implements "SIMULATION/0.1" can still teleport with that protocol + ; - this protocol is more efficient than "SIMULATION/0.1" + ; "SIMULATION/0.1" + ; - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + ConnectorProtocolVersion = "SIMULATION/0.3" + +[Profile] + Module = "BasicProfileModule" + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.SimulationService.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[AssetService] + LocalGridAssetService = "OpenSim.Services.Connectors.dll:AssetServicesConnector" + HypergridAssetService = "OpenSim.Services.Connectors.dll:HGAssetServiceConnector" + +[InventoryService] + LocalGridInventoryService = "OpenSim.Region.CoreModules.dll:RemoteXInventoryServicesConnector" + +[GridService] + ; RemoteGridServicesConnector instantiates a LocalGridServicesConnector, + ; which in turn uses this + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + + NetworkConnector = "OpenSim.Services.Connectors.dll:GridServicesConnector" + + ; Needed to display non-default map tile images for linked regions + AssetService = "OpenSim.Services.Connectors.dll:AssetServicesConnector" + + HypergridLinker = true + AllowHypergridMapSearch = true + SuppressConsoleCommands = true + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[Friends] + Connector = "OpenSim.Services.Connectors.dll:FriendsServicesConnector" + +[Messaging] + MessageTransferModule = HGMessageTransferModule + LureModule = HGLureModule + +[HGInstantMessageService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService" + GridService = "OpenSim.Services.Connectors.dll:GridServicesConnector" + PresenceService = "OpenSim.Services.Connectors.dll:PresenceServicesConnector" + UserAgentService = "OpenSim.Services.Connectors.dll:UserAgentServiceConnector" + +[MapImageService] + LocalServiceModule = "OpenSim.Services.Connectors.dll:MapImageServicesConnector" diff --git a/bin/config-include/HyperSimianGrid.ini b/bin/config-include/HyperSimianGrid.ini new file mode 100644 index 0000000000..efad5772a3 --- /dev/null +++ b/bin/config-include/HyperSimianGrid.ini @@ -0,0 +1,97 @@ +;; +;; Please don't change this file. +;; All optional settings are in GridCommon.ini.example, +;; which you can copy and change. +;; + +;; +;; In GridCommon.ini, these are the URLs you would use if SimianGrid is +;; installed at http://www.mygrid.com/Grid/ +;; +; AssetServerURI = "http://www.mygrid.com/Grid/?id=" +; InventoryServerURI = "http://www.mygrid.com/Grid/" +; AvatarServerURI = "http://www.mygrid.com/Grid/" +; PresenceServerURI = "http://www.mygrid.com/Grid/" +; UserAccountServerURI = "http://www.mygrid.com/Grid/" +; AuthenticationServerURI = "http://www.mygrid.com/Grid/" +; FriendsServerURI = "http://www.mygrid.com/Grid/" +; GroupsServerURI = "http://www.mygrid.com/Grid/" + +[Includes] + Include-Common = "config-include/GridCommon.ini" + +[Modules] + GridServices = "RemoteGridServicesConnector" + PresenceServices = "SimianPresenceServiceConnector" + UserAccountServices = "SimianUserAccountServiceConnector" + AuthenticationServices = "SimianAuthenticationServiceConnector" + AssetServices = "HGAssetBroker" + InventoryServices = "HGInventoryBroker" + AvatarServices = "SimianAvatarServiceConnector" + + NeighbourServices = "RemoteNeighbourServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "HGEntityTransferModule" + InventoryAccessModule = "HGInventoryAccessModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = false + + AssetCaching = "FlotsamAssetCache" + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.Connectors.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.Connectors.dll:EstateDataService" + +[Friends] + Connector = "OpenSim.Services.Connectors.dll:SimianFriendsServiceConnector" + +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + NetworkConnector = "OpenSim.Services.Connectors.dll:SimianGridServiceConnector" + + HypergridLinker = true + AllowHypergridMapSearch = true + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + LocalGridAssetService = "OpenSim.Services.Connectors.dll:SimianAssetServiceConnector" + HypergridAssetService = "OpenSim.Services.Connectors.dll:HGAssetServiceConnector" + AssetLoaderArgs = "assets/AssetSets.xml" + +[InventoryService] + LocalGridInventoryService = "OpenSim.Services.Connectors.dll:SimianInventoryServiceConnector" + +[Groups] + Enabled = true + Module = GroupsModule + DebugEnabled = false + NoticesEnabled = true + MessagingModule = GroupsMessagingModule + MessagingEnabled = true + ServicesConnectorModule = SimianGroupsServicesConnector + +[Profiles] + Module = "SimianProfiles" + +[HGInventoryAccessModule] + ; + ; === HG ONLY === + ; Change this to your profile server + ; accessible from other grids + ; + ProfileServerURI = "http://mygridserver.com:8002/user" + + ;; If you want to protect your assets from being copied by foreign visitors + ;; uncomment the next line. You may want to do this on sims that have licensed content. + ; OutboundPermission = False diff --git a/bin/config-include/SimianGrid.ini b/bin/config-include/SimianGrid.ini new file mode 100644 index 0000000000..5749656231 --- /dev/null +++ b/bin/config-include/SimianGrid.ini @@ -0,0 +1,77 @@ +;; +;; Please don't change this file. +;; All optional settings are in GridCommon.ini.example, +;; which you can copy and change. +;; + +;; +;; In GridCommon.ini, these are the URLs you would use if SimianGrid is +;; installed at http://www.mygrid.com/Grid/ +;; +; AssetServerURI = "http://www.mygrid.com/Grid/?id=" +; InventoryServerURI = "http://www.mygrid.com/Grid/" +; AvatarServerURI = "http://www.mygrid.com/Grid/" +; PresenceServerURI = "http://www.mygrid.com/Grid/" +; UserAccountServerURI = "http://www.mygrid.com/Grid/" +; AuthenticationServerURI = "http://www.mygrid.com/Grid/" +; FriendsServerURI = "http://www.mygrid.com/Grid/" +; GroupsServerURI = "http://www.mygrid.com/Grid/" + +[Includes] + Include-Common = "config-include/GridCommon.ini" + +[Modules] + GridServices = "RemoteGridServicesConnector" + PresenceServices = "SimianPresenceServiceConnector" + UserAccountServices = "SimianUserAccountServiceConnector" + AuthenticationServices = "SimianAuthenticationServiceConnector" + AssetServices = "SimianAssetServiceConnector" + InventoryServices = "SimianInventoryServiceConnector" + AvatarServices = "SimianAvatarServiceConnector" + + NeighbourServices = "RemoteNeighbourServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = false + + AssetCaching = "FlotsamAssetCache" + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.SimulationService.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[Friends] + Connector = "OpenSim.Services.Connectors.dll:SimianFriendsServiceConnector" + +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + NetworkConnector = "OpenSim.Services.Connectors.dll:SimianGridServiceConnector" + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "assets/AssetSets.xml" + +[Groups] + Enabled = true + Module = GroupsModule + DebugEnabled = false + NoticesEnabled = true + MessagingModule = GroupsMessagingModule + MessagingEnabled = true + ServicesConnectorModule = SimianGroupsServicesConnector + +[Profiles] + Module = "SimianProfiles" diff --git a/bin/config-include/Standalone.ini b/bin/config-include/Standalone.ini new file mode 100644 index 0000000000..1fbd171c2a --- /dev/null +++ b/bin/config-include/Standalone.ini @@ -0,0 +1,133 @@ +;; +;; Please don't change this file. +;; All optional settings are in StandaloneCommon.ini.example, +;; which you can copy and change. +;; + +[Modules] + AssetServices = "LocalAssetServicesConnector" + InventoryServices = "LocalInventoryServicesConnector" + NeighbourServices = "LocalNeighbourServicesConnector" + AuthenticationServices = "LocalAuthenticationServicesConnector" + AuthorizationServices = "LocalAuthorizationServicesConnector" + GridServices = "LocalGridServicesConnector" + PresenceServices = "LocalPresenceServicesConnector" + UserProfilesServices = "LocalUserProfilesServicesConnector" + UserAccountServices = "LocalUserAccountServicesConnector" + AgentPreferencesServices= "LocalAgentPreferencesServicesConnector" + GridUserServices = "LocalGridUserServicesConnector" + SimulationServices = "LocalSimulationConnectorModule" + AvatarServices = "LocalAvatarServicesConnector" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" + MapImageService = "MapImageServiceModule" + SearchModule = "BasicSearchModule" + + LibraryModule = true + LLLoginServiceInConnector = true + GridInfoServiceInConnector = true + MapImageServiceInConnector = true + +[SimulationService] + ; This is the protocol version which the simulator advertises to the source destination when acting as a target destination for a teleport + ; It is used to control the teleport handoff process. + ; Valid values are + ; "SIMULATION/0.3" + ; - This is the default, and it supports teleports to variable-sized regions + ; - Older versions can teleport to this one, but only if the destination region + ; is 256x256 + ; "SIMULATION/0.2" + ; - A source simulator which only implements "SIMULATION/0.1" can still teleport with that protocol + ; - this protocol is more efficient than "SIMULATION/0.1" + ; "SIMULATION/0.1" + ; - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + ConnectorProtocolVersion = "SIMULATION/0.3" + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.SimulationService.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[AssetService] + LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" + +[InventoryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[AvatarService] + LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" + +[AuthenticationService] + LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + Realm = "regions" + StorageProvider = "OpenSim.Data.Null.dll" + +[PresenceService] + LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" + StorageProvider = "OpenSim.Data.Null.dll" + +[UserAccountService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ;; These are for creating new accounts + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + GridService = "OpenSim.Services.GridService.dll:GridService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 to show a default "Ruth" avatar rather than a cloud. + CreateDefaultAvatarEntries = true + +[GridUserService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + +[FriendsService] + LocalServiceModule = "OpenSim.Services.FriendsService.dll" + +[Friends] + Connector = "OpenSim.Services.FriendsService.dll" + +[AgentPreferencesService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:AgentPreferencesService" + +[LoginService] + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + + WelcomeMessage = "Welcome, Avatar!" + + ;# {DSTZone} {} {Override Daylight Saving Time rules} {* none local} "America/Los_Angeles;Pacific Standard Time" + ;; Viewers do not receive timezone information from the server - almost all (?) default to Pacific Standard Time + ;; However, they do rely on the server to tell them whether it's Daylight Saving Time or not. + ;; Hence, calculating DST based on a different timezone can result in a misleading viewer display and inconsistencies between grids. + ;; By default, this setting uses various timezone names to calculate DST with regards to the viewer's standard PST. + ;; Options are + ;; "none" no DST + ;; "local" use the server's only timezone to calculate DST. This is previous OpenSimulator behaviour. + ;; "America/Los_Angeles;Pacific Standard Time" use these timezone names to look up Daylight savings. + ;; 'America/Los_Angeles' is used on Linux/Mac systems whilst 'Pacific Standard Time' is used on Windows + DSTZone = "America/Los_Angeles;Pacific Standard Time" + +[MapImageService] + LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" + +;; This should always be the very last thing on this file +[Includes] + Include-Common = "config-include/StandaloneCommon.ini" diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example new file mode 100644 index 0000000000..179be94775 --- /dev/null +++ b/bin/config-include/StandaloneCommon.ini.example @@ -0,0 +1,417 @@ +; This is the main configuration file for an instance of OpenSim running in standalone mode + +[DatabaseService] + ; + ; ### Choose the DB + ; + + ; SQLite + Include-Storage = "config-include/storage/SQLiteStandalone.ini"; + + ; MySql + ; Uncomment these lines if you want to use mysql storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.MySQL.dll" + ;ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + + ; Uncomment this line if you are using MySQL and want to use a different database for estates. + ; The usual application for this is to allow estates to be spread out across multiple simulators by share the same database. + ; Most people won't need to do this so only uncomment if you know what you're doing. + ;EstateConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + + ; MSSQL + ; Uncomment these lines if you want to use MSSQL storage + ; Change the connection string to your db details + ; The value for server property is shown in your SQL Server Management Studio login dialog. + ; (This sample is the default of express edition) + ;StorageProvider = "OpenSim.Data.MSSQL.dll" + ;ConnectionString = "Server=localhost\SQLEXPRESS;Database=opensim;User Id=opensim; password=***;" + + ; PGSQL + ; Uncomment these lines if you want to use PGSQL storage + ; Change the connection string to your db details + ;StorageProvider = "OpenSim.Data.PGSQL.dll" + ;ConnectionString = "Server=localhost;Database=opensim;User Id=opensim; password=***;" + +[Hypergrid] + ; Uncomment the variables in this section only if you are in + ; Hypergrid configuration. Otherwise, ignore. + + ;# {HomeURI} {Hypergrid} {The Home URL of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server that + ;; runs the UserAgentsService. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;# {GatekeeperURI} {Hypergrid} {The URL of the gatekeeper of this world} {} + ;; If this is a standalone world, this is the address of this instance. + ;; If this is a grided simulator, this is the address of the external robust server + ;; that runs the Gatekeeper service. + ;; For example http://myworld.com:9000 or http://myworld.com:8002 + ;; This is a default that can be overwritten in some sections. + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + +[Modules] + ;; Choose one cache module and the corresponding config file, if it exists. + ;; Copy the config .example file into your own .ini file and alter that + ;; We recommend the use of the FlotsamAssetCache since this is most actively maintained. + + AssetCaching = "FlotsamAssetCache" + Include-FlotsamCache = "config-include/FlotsamCache.ini" + + ;AssetCaching = "CenomeMemoryAssetCache" + ;Include-CenomeCache = "config-include/CenomeCache.ini" + + ;AssetCaching = "GlynnTuckerAssetCache" + + ;; Authorization is not on by default, as it depends on external php + ;AuthorizationServices = "LocalAuthorizationServicesConnector" + +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "assets/AssetSets.xml" + +[GridService] + ;; For in-memory region storage (default) + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + ;;--- For MySql region storage (alternative) + ;StorageProvider = "OpenSim.Data.MySQL.dll:MySqlRegionData" + + ;; Directory for map tile images of remote regions + ; MapTileDirectory = "./maptiles" + + ;; Next, we can specify properties of regions, including default and fallback regions + ;; The syntax is: Region_ = "" + ;; where can be DefaultRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut + ;; + ;; DefaultRegion If a local login cannot be placed in the required region (e.g. home region does not exist, avatar is not allowed entry, etc.) + ;; then this region becomes the destination. Only the first online default region will be used. If no DefaultHGRegion + ;; is specified then this will also be used as the region for hypergrid connections that require it (commonly because they have not specified + ;; an explicit region. + ;; + ;; DefaultHGRegion If an avatar connecting via the hypergrid does not specify a region, then they are placed here. Only the first online + ;; region will be used. + ;; + ;; FallbackRegion If the DefaultRegion is not available for a local login, then any FallbackRegions are tried instead. These are tried in the + ;; order specified. This only applies to local logins at this time, not Hypergrid connections. + ;; + ;; NoDirectLogin A hypergrid user cannot directly connect to this region. This does not apply to local logins. + ;; + ;; Persistent When the simulator is shutdown, the region is signalled as offline but left registered on the grid. + ;; + ;; For example: + Region_Welcome_Area = "DefaultRegion, FallbackRegion" + + ;; Allow supporting viewers to export content + ;; Set to false to prevent export + ExportSupported = true + + ; === HG ONLY === + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI="${Const|BaseURL}:${Const|PublicPort}" + +[LibraryModule] + ; Set this if you want to change the name of the OpenSim Library + ;LibraryName = "My World's Library" + +[LoginService] + WelcomeMessage = "Welcome, Avatar!" + ;; If you have Gatekeeper set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + + SRV_HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_InventoryServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_AssetServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_ProfileServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_FriendsServerURI = "${Const|BaseURL}:${Const|PublicPort}" + SRV_IMServerURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; For Viewer 2 + MapTileURL = "${Const|BaseURL}:${Const|PublicPort}/" + + ; Url to search service + ; SearchURL = "${Const|BaseURL}:${Const|PublicPort}"; + + ; The minimum user level required for a user to be able to login. 0 by default + ; If you disable a particular user's account then you can set their login level below this number. + ; You can also change this level from the console though these changes will not be persisted. + ; MinLoginLevel = 0 + + ;; Ask co-operative viewers to use a different currency name + ;Currency = "" + + ;; Set minimum fee to publish classified + ; ClassifiedFee = 0 + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ; Basic Login Service Dos Protection Tweaks + ; ; + ; ; Some Grids/Users use a transparent proxy that makes use of the X-Forwarded-For HTTP Header, If you do, set this to true + ; ; If you set this to true and you don't have a transparent proxy, it may allow attackers to put random things in the X-Forwarded-For header to + ; ; get around this basic DOS protection. + ; DOSAllowXForwardedForHeader = false + ; ; + ; ; The protector adds up requests during this rolling period of time, default 10 seconds + ; DOSRequestTimeFrameMS = 10000 + ; ; + ; ; The amount of requests in the above timeframe from the same endpoint that triggers protection + ; DOSMaxRequestsInTimeFrame = 5 + ; ; + ; ; The amount of time that a specific endpoint is blocked. Default 2 minutes. + ; DOSForgiveClientAfterMS = 120000 + ; ; + ; ; To turn off basic dos protection, set the DOSMaxRequestsInTimeFrame to 0. + +[FreeswitchService] + ;; If FreeSWITCH is not being used then you don't need to set any of these parameters + ;; + ;; The IP address of your FreeSWITCH server. The common case is for this to be the same as the server running the OpenSim standalone + ;; This has to be set for the FreeSWITCH service to work + ;; This address must be reachable by viewers. + ;ServerAddress = 127.0.0.1 + + ;; The following configuration parameters are optional + + ;; By default, this is the same as the ServerAddress + ; Realm = 127.0.0.1 + + ;; By default, this is the same as the ServerAddress on port 5060 + ; SIPProxy = 127.0.0.1:5060 + + ;; Default is 5000ms + ; DefaultTimeout = 5000 + + ;; The dial plan context. Default is "default" + ; Context = default + + ;; Currently unused + ; UserName = freeswitch + + ;; Currently unused + ; Password = password + + ;; The following parameters are for STUN = Simple Traversal of UDP through NATs + ;; See http://wiki.freeswitch.org/wiki/NAT_Traversal + ;; stun.freeswitch.org is not guaranteed to be running so use it in + ;; production at your own risk + ; EchoServer = 127.0.0.1 + ; EchoPort = 50505 + ; AttemptSTUN = false + +[GridInfoService] + ; These settings are used to return information on a get_grid_info call. + ; Client launcher scripts and third-party clients make use of this to + ; autoconfigure the client and to provide a nice user experience. If you + ; want to facilitate that, you should configure the settings here according + ; to your grid or standalone setup. + ; + ; See http://opensimulator.org/wiki/GridInfo + + ; login uri: for grid this is the login server URI + login = ${Const|BaseURL}:${Const|PublicPort}/ + + ; long grid name: the long name of your grid + gridname = "the lost continent of hippo" + + ; short grid name: the short name of your grid + gridnick = "hippogrid" + + ; login page: optional: if it exists it will be used to tell the client to use + ; this as splash page. May also be served from an external web server, e.g. for + ; information on a standalone + ;welcome = ${Const|BaseURL}/welcome + + ; helper uri: optional: if it exists if will be used to tell the client to use + ; this for all economy related things + ; currently unused + ;economy = ${Const|BaseURL}:${Const|PublicPort}/ + + ; web page of grid: optional: page providing further information about your grid + ; currently unused + ;about = ${Const|BaseURL}/about/ + + ; account creation: optional: page providing further information about obtaining + ; a user account on your grid + ; currently unused + ;register = ${Const|BaseURL}/register + + ; help: optional: page providing further assistance for users of your grid + ; currently unused + ;help = ${Const|BaseURL}/help + + ; password help: optional: page providing password assistance for users of your grid + ; currently unused + ;password = ${Const|BaseURL}/password + + ; HG address of the gatekeeper, if you have one + ; this is the entry point for all the regions of the world + ; gatekeeper = ${Const|BaseURL}:${Const|PublicPort}/ + + ; HG user domain, if you have one + ; this is the entry point for all user-related HG services + ; uas = ${Const|BaseURL}:${Const|PublicPort}/ + +[MapImageService] + ; Set this if you want to change the default + ; TilesStoragePath = "maptiles" + +[AuthorizationService] + ; If you have regions with access restrictions + ; specify them here using the convention + ; Region_ = + ; Valid flags are: + ; DisallowForeigners -- HG visitors not allowed + ; DisallowResidents -- only Admins and Managers allowed + ; Example: + ; Region_Test_1 = "DisallowForeigners" + +;; +;; HG configurations +;; +[GatekeeperService] + ;; If you have GatekeeperURI set under [Hypergrid], no need to set it here, leave it commented + ; ExternalName = "${Const|BaseURL}:${Const|PublicPort}" + + ; Does this grid allow incoming links to any region in it? + ; If false, HG TPs happen only to the Default regions specified in [GridService] section + AllowTeleportsToAnyRegion = true + + ;; Regular expressions for controlling which client versions are accepted/denied. + ;; An empty string means nothing is checked. + ;; + ;; Example 1: allow only these 3 types of clients (any version of them) + ;; AllowedClients = "Imprudence|Hippo|Second Life" + ;; + ;; Example 2: allow all clients except these + ;; DeniedClients = "Twisted|Crawler|Cryolife|FuckLife|StreetLife|GreenLife|AntiLife|KORE-Phaze|Synlyfe|Purple Second Life|SecondLi |Emerald" + ;; + ;; Note that these are regular expressions, so every character counts. + ;; Also note that this is very weak security and should not be trusted as a reliable means + ;; for keeping bad clients out; modified clients can fake their identifiers. + ;; + ;; + ;AllowedClients = "" + ;DeniedClients = "" + + ;; Are foreign visitors allowed? + ;ForeignAgentsAllowed = true + ;; + ;; If ForeignAgentsAllowed is true, make exceptions using AllowExcept. + ;; Leave blank or commented for no exceptions. + ; AllowExcept = "http://griefer.com:8002, http://enemy.com:8002" + ;; + ;; If ForeignAgentsAllowed is false, make exceptions using DisallowExcept + ;; Leave blank or commented for no exceptions. + ; DisallowExcept = "http://myfriendgrid.com:8002, http://myboss.com:8002" + +[UserAgentService] + ;; User level required to be contacted from other grids + ;LevelOutsideContacts = 0 + + ;; Restrictions on destinations of local users. + ;; Are local users allowed to visit other grids? + ;; What user level? Use variables of this forrm: + ;; ForeignTripsAllowed_Level_ = true | false + ;; (the default is true) + ;; For example: + ; ForeignTripsAllowed_Level_0 = false + ; ForeignTripsAllowed_Level_200 = true ; true is default, no need to say it + ;; + ;; If ForeignTripsAllowed is false, make exceptions using DisallowExcept + ;; Leave blank or commented for no exceptions. + ; DisallowExcept_Level_0 = "http://myothergrid.com:8002, http://boss.com:8002" + ;; + ;; If ForeignTripsAllowed is true, make exceptions using AllowExcept. + ;; Leave blank or commented for no exceptions. + ; AllowExcept_Level_200 = "http://griefer.com:8002, http://enemy.com:8002" + + ;; This variable controls what is exposed to profiles of local users + ;; as seen from outside of this grid. Leave it uncommented for exposing + ;; UserTitle, UserFlags and the creation date. Uncomment and change to False + ;; to block this info from being exposed. + ; ShowUserDetailsInHGProfile = True + +[HGInventoryService] + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + +[HGAssetService] + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; The asset types that this grid can export to / import from other grids. + ;; Comma separated. + ;; Valid values are all the asset types in OpenMetaverse.AssetType, namely: + ;; Unknown, Texture, Sound, CallingCard, Landmark, Clothing, Object, Notecard, LSLText, + ;; LSLBytecode, TextureTGA, Bodypart, SoundWAV, ImageTGA, ImageJPEG, Animation, Gesture, Mesh + ;; + ;; Leave blank or commented if you don't want to apply any restrictions. + ;; A more strict, but still reasonable, policy may be to disallow the exchange + ;; of scripts, like so: + ; DisallowExport ="LSLText" + ; DisallowImport ="LSLBytecode" + +[HGInventoryAccessModule] + ;; If you have these set under [Hypergrid], no need to set it here, leave it commented + ; HomeURI = "${Const|BaseURL}:${Const|PublicPort}" + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + + ;; If you want to protect your assets from being copied by foreign visitors + ;; uncomment the next line. You may want to do this on sims that have licensed content. + ;; true = allow exports, false = disallow exports. True by default. + ; OutboundPermission = True + + ;; Send visual reminder to local users that their inventories are unavailable while they are traveling + ;; and available when they return. True by default. + ;RestrictInventoryAccessAbroad = True + +[HGFriendsModule] + ; User level required to be able to send friendship invitations to foreign users + ;LevelHGFriends = 0; + +[Messaging] + ; === HG ONLY === + ;; If you have this set under [Hypergrid], no need to set it here, leave it commented + ; GatekeeperURI = "${Const|BaseURL}:${Const|PublicPort}" + +[EntityTransfer] + ;; User level from which local users are allowed to HG teleport. Default 0 (all users) + ;LevelHGTeleport = 0 + + ;; Are local users restricted from taking their appearance abroad? + ;; Default is no restrictions + ;RestrictAppearanceAbroad = false + + ;; If appearance is restricted, which accounts' appearances are allowed to be exported? + ;; Comma-separated list of account names + AccountForAppearance = "Test User, Astronaut Smith" + +[UserProfilesService] + ;; To use, set Enabled to true then configure for your site... + Enabled = false + LocalServiceModule = "OpenSim.Services.UserProfilesService.dll:UserProfilesService" + + ;; Configure this for separate databse + ; ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;Old Guids=true;" + ; Realm = UserProfiles + + UserAccountService = OpenSim.Services.UserAccountService.dll:UserAccountService + AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini new file mode 100644 index 0000000000..51f7fd0939 --- /dev/null +++ b/bin/config-include/StandaloneHypergrid.ini @@ -0,0 +1,210 @@ +;; +;; Please don't change this file. +;; All optional settings are in StandaloneCommon.ini.example, +;; which you can copy and change. +;; + +[Startup] + WorldMapModule = "HGWorldMap" + +[Modules] + AssetServices = "HGAssetBroker" + InventoryServices = "HGInventoryBroker" + NeighbourServices = "LocalNeighbourServicesConnector" + AuthenticationServices = "LocalAuthenticationServicesConnector" + AuthorizationServices = "LocalAuthorizationServicesConnector" + GridServices = "LocalGridServicesConnector" + PresenceServices = "LocalPresenceServicesConnector" + UserAccountServices = "LocalUserAccountServicesConnector" + AgentPreferencesServices= "LocalAgentPreferencesServicesConnector" + GridUserServices = "LocalGridUserServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + AvatarServices = "LocalAvatarServicesConnector" + UserProfilesServices = "LocalUserProfilesServicesConnector" + MapImageService = "MapImageServiceModule" + EntityTransferModule = "HGEntityTransferModule" + InventoryAccessModule = "HGInventoryAccessModule" + FriendsModule = "HGFriendsModule" + UserManagementModule = "HGUserManagementModule" + SearchModule = "BasicSearchModule" + + InventoryServiceInConnector = true + AssetServiceInConnector = true + HypergridServiceInConnector = true + NeighbourServiceInConnector = true + LibraryModule = true + LLLoginServiceInConnector = true + GridInfoServiceInConnector = true + AuthenticationServiceInConnector = true + SimulationServiceInConnector = true + MapImageServiceInConnector = true + +[SimulationService] + ; This is the protocol version which the simulator advertises to the source destination when acting as a target destination for a teleport + ; It is used to control the teleport handoff process. + ; Valid values are + ; "SIMULATION/0.3" + ; - This is the default, and it supports teleports to variable-sized regions + ; - Older versions can teleport to this one, but only if the destination region + ; is 256x256 + ; "SIMULATION/0.2" + ; - A source simulator which only implements "SIMULATION/0.1" can still teleport with that protocol + ; - this protocol is more efficient than "SIMULATION/0.1" + ; "SIMULATION/0.1" + ; - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + ConnectorProtocolVersion = "SIMULATION/0.3" + +[Messaging] + MessageTransferModule = HGMessageTransferModule + LureModule = HGLureModule + +[SimulationDataStore] + LocalServiceModule = "OpenSim.Services.SimulationService.dll:SimulationDataService" + +[EstateDataStore] + LocalServiceModule = "OpenSim.Services.EstateService.dll:EstateDataService" + +[AssetService] + LocalServiceModule = "OpenSim.Services.AssetService.dll:AssetService" + + ; For HGAssetBroker + LocalGridAssetService = "OpenSim.Services.AssetService.dll:AssetService" + HypergridAssetService = "OpenSim.Services.Connectors.dll:HGAssetServiceConnector" + +[InventoryService] + ; For HGInventoryBroker + LocalGridInventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + +[AvatarService] + LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[AuthenticationService] + LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + +[GridService] + ; LocalGridServicesConnector needs this + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + Realm = "regions" + StorageProvider = "OpenSim.Data.Null.dll" + + ; Needed to display non-default map tile images for remote regions + AssetService = "OpenSim.Services.AssetService.dll:AssetService" + + HypergridLinker = true + AllowHypergridMapSearch = true + +[PresenceService] + LocalServiceModule = "OpenSim.Services.PresenceService.dll:PresenceService" + StorageProvider = "OpenSim.Data.Null.dll" + +[UserAccountService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ;; These are for creating new accounts by the service + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + GridService = "OpenSim.Services.GridService.dll:GridService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 to show a default "Ruth" avatar rather than a cloud. + CreateDefaultAvatarEntries = true + +[GridUserService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + +[FriendsService] + LocalServiceModule = "OpenSim.Services.FriendsService.dll" + +[Friends] + Connector = "OpenSim.Services.FriendsService.dll" + +[AgentPreferencesService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:AgentPreferencesService" + +[LoginService] + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" + InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + + ; This inventory service will be used to initialize the user's inventory + HGInventoryServicePlugin = "OpenSim.Services.HypergridService.dll:HGSuitcaseInventoryService" + HGInventoryServiceConstructorArg = "HGInventoryService" + +[MapImageService] + LocalServiceModule = "OpenSim.Services.MapImageService.dll:MapImageService" + +[GatekeeperService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:GatekeeperService" + ;; for the service + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AuthenticationService = "OpenSim.Services.Connectors.dll:AuthenticationServicesConnector" + SimulationService ="OpenSim.Services.Connectors.dll:SimulationServiceConnector" + +[UserAgentService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:UserAgentService" + ;; for the service + GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService" + GridService = "OpenSim.Services.GridService.dll:GridService" + GatekeeperService = "OpenSim.Services.HypergridService.dll:GatekeeperService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + + ;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 to show a default "Ruth" avatar rather than a cloud. + CreateDefaultAvatarEntries = true + +;; The interface that local users get when they are in other grids +;; This greatly restricts the inventory operations while in other grids +[HGInventoryService] + ; For the InventoryServiceInConnector + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGSuitcaseInventoryService" + ;; alternatives: + ;; HG1.5, more permissive, not recommended, but still supported + ;LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInventoryService" + ;; HG1.0, totally permissive, not recommended, but OK for grids with 100% trust + ;LocalServiceModule = "OpenSim.Services.InventoryService.dll:XInventoryService" + + UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + +;; The interface that local users get when they are in other grids +;; This restricts/filters the asset operations from the outside +[HGAssetService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGAssetService" + UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + +[HGFriendsService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGFriendsService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + +[HGInstantMessageService] + LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInstantMessageService" + GridService = "OpenSim.Services.GridService.dll:GridService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + UserAgentService = "OpenSim.Services.HypergridService.dll:UserAgentService" + InGatekeeper = True + +;; This should always be the very last thing on this file +[Includes] + Include-Common = "config-include/StandaloneCommon.ini" diff --git a/bin/config-include/osslEnable.ini b/bin/config-include/osslEnable.ini new file mode 100644 index 0000000000..0a03d4c238 --- /dev/null +++ b/bin/config-include/osslEnable.ini @@ -0,0 +1,230 @@ +; Enable OSSL functions. +; Including this file in a region's set of INI files, causes the OpenSimulator +; specific functions to be enabled. +; See http://opensimulator.org/wiki/OSSL for a description of OSSL functions and +; refer to http://opensimulator.org/wiki/OSSL_Implemented for a list of functions. + +; The below file lists all the functions and specifies who has permission to +; execute the function. Some of the functions are for maintainance or can be +; mis-used so the permission to execute a function can be limited. Ability to +; execute a function is based on the owner of the prim holding the script. + +[XEngine] + ; Allow the use of os* functions (some are dangerous) + AllowOSFunctions = true + + ; Allow the user of mod* functions. This allows a script to pass messages + ; to a region module via the modSendCommand() function and is used by some + ; modules to extend the scripting language. + AllowMODFunctions = true + + ; Allow the use of LightShare functions. + ; The setting enable_windlight = true must also be enabled in the [LightShare] section. + AllowLightShareFunctions = true + + ; Threat level to allow. One of None, VeryLow, Low, Moderate, High, VeryHigh, Severe. + ; See http://opensimulator.org/wiki/Threat_level for more information on these levels. + ; This is the default level and can be overridden with the Allow_ specifications. + ; Blanket enabling the ossl functions is dangerous and we do not recommend setting higher + ; than 'Low' unless you have a high level of trust in all the users that can run scripts + ; in your simulator. It is safer to explicitly allow certain types of user to run + ; higher threat level OSSL functions, as detailed later on. + OSFunctionThreatLevel = VeryLow + + ; Each of the OSSL functions can be enabled or disabled individually. + ; To disable, set the value to 'false'. + ; To enable for everyone, set the value to 'true'. + ; To enable for individuals or groups, set it to a comma separated list. This checks + ; against the owner of the object containing the script. + ; The comma separated entries in the list may be one of: + ; "ESTATE_MANAGER" -- enable for estate manager + ; "ESTATE_OWNER" -- enable for estate owner + ; "PARCEL_OWNER" -- enable for parcel owner + ; "PARCEL_GROUP_MEMBER" -- enable for any member of the parcel group + ; uuid -- enable for specified ID (may be avatar or group ID) + + ; The OSSL function name is prepended with "Allow_" and it checks against + ; the owners of the containing prim. There can also be entries beginning with + ; 'Creators_". The 'Creators_" parameters can only be a list of UUIDs and it is + ; checked against the creator of the script itself. + + ; Allowing ossl functions for anyone owning a parcel can be dangerous especially if + ; a region is selling or otherwise giving away parcel ownership. By default, parcel + ; ownership or group membership does not enable OSSL functions. Uncomment the + ; appropriate line below to allow parcel ownership and groups to do restricted + ; OSSL functions. It might be better to check the list below and edit the ones + ; to enable individually. + osslParcelO = "" + osslParcelOG = "" + ; osslParcelO = "PARCEL_OWNER," + ; osslParcelOG = "PARCEL_GROUP_MEMBER,PARCEL_OWNER," + + ; There are a block of functions for creating and controlling NPCs. + ; These can be mis-used so limit use to those you can trust. + osslNPC = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel None + Allow_osDrawEllipse = true + Allow_osDrawFilledPolygon = true + Allow_osDrawFilledRectangle = true + Allow_osDrawImage = true + Allow_osDrawLine = true + Allow_osDrawPolygon = true + Allow_osDrawRectangle = true + Allow_osDrawText = true + Allow_osGetAgents = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetAvatarList = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetCurrentSunHour = true + Allow_osGetGender = true + Allow_osGetHealth = true + Allow_osGetInventoryDesc = true + Allow_osGetMapTexture = true + Allow_osGetRegionSize = true + Allow_osGetRezzingObject = true + Allow_osGetSunParam = true + Allow_osGetTerrainHeight = true + Allow_osIsNpc = true + Allow_osIsUUID = true + Allow_osList2Double = true + Allow_osMax = true + Allow_osMin = true + Allow_osMovePen = true + Allow_osNpcGetOwner = ${XEngine|osslNPC} + Allow_osParseJSON = true + Allow_osParseJSONNew = true + Allow_osSetFontName = true + Allow_osSetFontSize = true + Allow_osSetPenCap = true + Allow_osSetPenColor = true + Allow_osSetPenSize = true + Allow_osSetSunParam = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osTeleportOwner = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osWindActiveModelPluginName = true + Allow_osCheckODE = true ; Here for completeness. This function cannot be turned off + + ; ThreatLevel Nuisance + Allow_osSetEstateSunSettings = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetRegionSunSettings = ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel VeryLow + Allow_osEjectFromGroup = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceBreakAllLinks = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceBreakLink = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetDrawStringSize = true + Allow_osGetWindParam = true + Allow_osInviteToGroup = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osReplaceString = true + Allow_osSetDynamicTextureData = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetDynamicTextureDataBlend = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetDynamicTextureDataBlendFace = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetDynamicTextureURL = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetDynamicTextureURLBlend = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetDynamicTextureURLBlendFace = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetParcelMediaURL = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetParcelSIPAddress = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetPrimFloatOnWater = true + Allow_osSetWindParam = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osTerrainFlush = ESTATE_MANAGER,ESTATE_OWNER + Allow_osUnixTimeToTimestamp = true + + ; ThreatLevel Low + Allow_osAvatarName2Key = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osFormatString = true + Allow_osKey2Name = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osListenRegex = true + Allow_osLoadedCreationDate = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osLoadedCreationID = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osLoadedCreationTime = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osMessageObject = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osRegexIsMatch = true + Allow_osGetAvatarHomeURI = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel Moderate + Allow_osDropAttachment = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osDropAttachmentAt = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetGridCustom = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetGridGatekeeperURI = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetGridHomeURI = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetGridLoginURI = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetGridName = true + Allow_osGetGridNick = true + Allow_osGetNumberOfAttachments = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetRegionStats = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetSimulatorMemory = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osMessageAttachments = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetSpeed = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel High + Allow_osCauseDamage = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osCauseHealing = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceAttachToAvatar = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceAttachToAvatarFromInventory = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceCreateLink = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceDropAttachment = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osForceDropAttachmentAt = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetAgentIP = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetLinkPrimitiveParams = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetPhysicsEngineType = true + Allow_osGetPrimitiveParams = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetRegionMapTexture = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetScriptEngineName = true + Allow_osGetSimulatorVersion = true + Allow_osMakeNotecard = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osMatchString = true + Allow_osNpcCreate = ${XEngine|osslNPC} + Allow_osNpcGetPos = ${XEngine|osslNPC} + Allow_osNpcGetRot = ${XEngine|osslNPC} + Allow_osNpcLoadAppearance = ${XEngine|osslNPC} + Allow_osNpcMoveTo = ${XEngine|osslNPC} + Allow_osNpcMoveToTarget = ${XEngine|osslNPC} + Allow_osNpcPlayAnimation = ${XEngine|osslNPC} + Allow_osNpcRemove = ${XEngine|osslNPC} + Allow_osNpcSaveAppearance = ${XEngine|osslNPC} + Allow_osNpcSay = ${XEngine|osslNPC} + Allow_osNpcSetRot = ${XEngine|osslNPC} + Allow_osNpcShout = ${XEngine|osslNPC} + Allow_osNpcSit = ${XEngine|osslNPC} + Allow_osNpcStand = ${XEngine|osslNPC} + Allow_osNpcStopAnimation = ${XEngine|osslNPC} + Allow_osNpcStopMoveToTarget = ${XEngine|osslNPC} + Allow_osNpcTouch = ${XEngine|osslNPC} + Allow_osNpcWhisper = ${XEngine|osslNPC} + Allow_osOwnerSaveAppearance = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osParcelJoin = ESTATE_MANAGER,ESTATE_OWNER + Allow_osParcelSubdivide = ESTATE_MANAGER,ESTATE_OWNER + Allow_osRegionRestart = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetContentType = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetPrimitiveParams = false + Allow_osSetProjectionParams = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetRegionWaterHeight = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetStateEvents = false ; deprecated + Allow_osSetTerrainHeight = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetTerrainTexture = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetTerrainTextureHeight = ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel VeryHigh + Allow_osAgentSaveAppearance = ESTATE_MANAGER,ESTATE_OWNER + ; Warning: The next function allows scripts to force animations on avatars without the user giving permission. + ; Enabling this can allow forced animations which can trigger traumatic episodes in vulnerable populations. + ; Similar things can be said for several of the 'force' functions. Enable with care and control. + ; Some of these were added as early functionality for NPCs. This has been replaced with the NPC functions. + Allow_osAvatarPlayAnimation = false + Allow_osAvatarStopAnimation = false + Allow_osForceDetachFromAvatar = false + Allow_osForceOtherSit = false + ; The notecard functions can cause a lot of load on the region if over used + Allow_osGetNotecard = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetNotecardLine = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osGetNumberOfNotecardLines = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osRegionNotice = ESTATE_MANAGER,ESTATE_OWNER + Allow_osSetRot = false + Allow_osSetParcelDetails = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + + ; ThreatLevel Severe + Allow_osConsoleCommand = false + Allow_osForceAttachToOtherAvatarFromInventory = false + Allow_osGrantScriptPermissions = false + Allow_osKickAvatar = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + Allow_osRevokeScriptPermissions = false + Allow_osTeleportAgent = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER + diff --git a/bin/config-include/storage/SQLiteStandalone.ini b/bin/config-include/storage/SQLiteStandalone.ini new file mode 100644 index 0000000000..67d98fff69 --- /dev/null +++ b/bin/config-include/storage/SQLiteStandalone.ini @@ -0,0 +1,39 @@ +; These are the initialization settings for running OpenSim Standalone with an SQLite database + +[DatabaseService] + StorageProvider = "OpenSim.Data.SQLite.dll" + ConnectionString = "URI=file:OpenSim.db,version=3,UseUTF16Encoding=True" + +[AssetService] + ConnectionString = "URI=file:Asset.db,version=3" + +; The HGAssetService section controls the connection given to the AssetService in a Hypergrid configuration. +; This has to be separate from [AssetService] because the Hypergrid facing connector uses [HGAssetService] for its config data instead. +; However, the internal asset service will still use the [AssetService] section. +; Therefore, you will almost certainly want the ConnectionString in [HGAssetService] to be the same as in [AssetService] +; so that they both access the same database. +; This issue does not apply to normal MySQL/MSSQL configurations, since by default they use the settings in [DatabaseService] and +; do not have separate connection strings for different services. +[HGAssetService] + ConnectionString = "URI=file:Asset.db,version=3" + +[InventoryService] + ;ConnectionString = "URI=file:inventory.db,version=3" + ; if you have a legacy inventory store use the connection string below + ConnectionString = "URI=file:inventory.db,version=3,UseUTF16Encoding=True" + +[AvatarService] + ConnectionString = "URI=file:avatars.db,version=3" + +[AuthenticationService] + ConnectionString = "URI=file:auth.db,version=3" + +[UserAccountService] + ConnectionString = "URI=file:userprofiles.db,version=3" + +[GridUserService] + ConnectionString = "URI=file:griduser.db,version=3" + +[FriendsService] + ConnectionString = "URI=file:friends.db,version=3" + diff --git a/bin/data/LICENSE-README-IMPORTANT.txt b/bin/data/LICENSE-README-IMPORTANT.txt new file mode 100644 index 0000000000..dd8671d61f --- /dev/null +++ b/bin/data/LICENSE-README-IMPORTANT.txt @@ -0,0 +1,5 @@ +Not all of the files in this directory are licensed under the BSD license. Some of these files come with the Second Life viewer and are considered licensed under a Creative Commons License. + +These files are: + +- avataranimations.xml (Derivative work of viewerart.ini, Creative Commons Attribution+Share-Alike v2.5 License) diff --git a/bin/data/avataranimations.xml b/bin/data/avataranimations.xml new file mode 100644 index 0000000000..f90a457159 --- /dev/null +++ b/bin/data/avataranimations.xml @@ -0,0 +1,142 @@ + + + + + + + 46bb4359-de38-4ed8-6a22-f1f52fe8f506 + b5b4a67d-0aee-30d2-72cd-77b333e932ef + 3147d815-6338-b932-f011-16b56d9ac18b + ea633413-8006-180a-c3ba-96dd1d756720 + c1bc7f36-3ba0-d844-f93c-93be945d644f + 11000694-3f41-adc2-606b-eee1d66f3724 + fd037134-85d4-f241-72c6-4f42164fedee + c4ca6188-9127-4f31-0158-23c4e2f93304 + db84829b-462c-ee83-1e27-9bbee66bd624 + 82e99230-c906-1403-4d9c-3889dd98daba + 349a3801-54f9-bf2c-3bd0-1ac89772af01 + efcf670c-2d18-8128-973a-034ebc806b67 + 9b0c1c4e-8ac7-7969-1494-28c874c4f668 + 9ba1c942-08be-e43a-fb29-16ad440efc50 + 201f3fdf-cb1f-dbec-201f-7333e328ae7c + 47f5f6fb-22e5-ae44-f871-73aaaf4a6022 + b68a3d7c-de9e-fc87-eec8-543d787e5b0d + 928cae18-e31d-76fd-9cc9-2f55160ff818 + 30047778-10ea-1af7-6881-4db7a3a5a114 + 951469f4-c7b2-c818-9dee-ad7eea8c30b7 + 4bd69a1d-1114-a0b4-625f-84e0a5237155 + cd28b69b-9c95-bb78-3f94-8d605ff1bb12 + a54d8ee2-28bb-80a9-7f0c-7afbbe24a5d6 + b0dc417c-1f11-af36-2e80-7e7489fa7cdc + 57abaae6-1d17-7b1b-5f98-6d11a6411276 + 0f86e355-dd31-a61c-fdb0-3a96b9aad05f + 6b61c8e8-4747-0d75-12d7-e49ff207a4ca + aa2df84d-cf8f-7218-527b-424a52de766e + 5747a48e-073e-c331-f6f3-7c2149613d3e + 1a03b575-9634-b62a-5767-3a679e81f4de + b906c4ba-703b-1940-32a3-0c7f7d791510 + 214aa6c1-ba6a-4578-f27c-ce7688f61d0d + 92624d3e-1068-f1aa-a5ec-8244585193ed + d535471b-85bf-3b4d-a542-93bea4f59d33 + d4416ff1-09d3-300f-4183-1b68a19b9fc1 + 514af488-9051-044a-b3fc-d4dbf76377c6 + 0b8c8211-d78c-33e8-fa28-c51a9594e424 + fee3df48-fa3d-1015-1e26-a205810e3001 + 1e8d90cc-a84e-e135-884c-7c82c8b03a14 + 18b3a4b5-b463-bd48-e4b6-71eaac76c515 + 62570842-0950-96f8-341c-809e65110823 + d63bc1f9-fc81-9625-a0c6-007176d82eb7 + 36f81a92-f076-5893-dc4b-7c3795e487cf + f76cda94-41d4-a229-2872-e0296e58afe1 + 0eb702e2-cc5a-9a88-56a5-661a55c0676a + eb6ebfb2-a4b3-a19c-d388-4dd5c03823f7 + 70ea714f-3a97-d742-1b01-590a8fcd1db5 + a351b1bc-cc94-aac2-7bea-a7e6ebad15ef + b7c7c833-e3d3-c4e3-9fc0-131237446312 + 313b9881-4302-73c0-c7d0-0e7a36b6c224 + 728646d9-cc79-08b2-32d6-937f0a835c24 + b92ec1a5-e7ce-a76b-2b05-bcdb9311417e + 835965c6-7f2f-bda2-5deb-2478737f91bf + 869ecdad-a44b-671e-3266-56aef2e3ac2e + da020525-4d94-59d6-23d7-81fdebf33148 + 9f496bd2-589a-709f-16cc-69bf7df1d36c + 9c05e5c7-6f07-6ca4-ed5a-b230390c3950 + 666307d9-a860-572d-6fd4-c3ab8865c094 + f5fc7433-043d-e819-8298-f519a119b688 + 7db00ccd-f380-f3ee-439d-61968ec69c8a + aec4610c-757f-bc4e-c092-c6e9caf18daf + 2b5a38b2-5e00-3a97-a495-4c826bc443e6 + 9b29cd61-c45b-5689-ded2-91756b8d76a9 + 8b102617-bcba-037b-86c1-b76219f90c88 + ef62d355-c815-4816-2474-b1acc21094a6 + efdc1727-8b8a-c800-4077-975fc27ee2f2 + 3d94bad0-c55b-7dcc-8763-033c59405d33 + 7570c7b5-1f22-56dd-56ef-a9168241bbb6 + 4ae8016b-31b9-03bb-c401-b1ea941db41d + 20f063ea-8306-2562-0b07-5c853b37b31e + 62c5de58-cb33-5743-3d07-9e4cd4352864 + 5ea3991f-c293-392e-6860-91dfa01278a3 + 2305bd75-1ca9-b03b-1faa-b176b8a8c49e + 709ea28e-1573-c023-8bf8-520c8bc637fa + 49aea43b-5ac3-8a44-b595-96100af0beda + 19999406-3a3a-d58c-a2ac-d72e555dcf51 + 7a17b059-12b2-41b1-570a-186368b6aa6f + ca5b3f14-3194-7a2b-c894-aa699b718d1f + 08464f78-3a8e-2944-cba5-0c94aff3af29 + 315c3a41-a5f3-0ba4-27da-f893f769e69b + 5a977ed9-7f72-44e9-4c4c-6e913df8ae74 + d83fa0e5-97ed-7eb2-e798-7bd006215cb4 + f061723d-0a18-754f-66ee-29a44795a32f + b312b10e-65ab-a0a4-8b3c-1326ea8e3ed9 + 17c024cc-eef2-f6a0-3527-9869876d7752 + ec952cca-61ef-aa3b-2789-4d1344f016de + 7a4e87fe-de39-6fcb-6223-024b00893244 + f3300ad9-3462-1d07-2044-0fef80062da0 + eefc79be-daae-a239-8c04-890f5d23654a + c8e42d32-7310-6906-c903-cab5d4a34656 + 35db4f7e-28c2-6679-cea9-3ee108f7fc7f + 0836b67f-7f7b-f37b-c00a-460dc1521f5a + 42dd95d5-0bc6-6392-f650-777304946c0f + 16803a9f-5140-e042-4d7b-d28ba247c325 + 05ddbff8-aaa9-92a1-2b74-8fe77a29b445 + cd7668a6-7011-d7e2-ead8-fc69eff1a104 + e04d450d-fdb5-0432-fd68-818aaf5935f8 + 6bd01860-4ebd-127a-bb3d-d1427e8e0c42 + 1a5fe8ac-a804-8a5d-7cbd-56bd83184568 + b1709c8d-ecd3-54a1-4f28-d55ac0840782 + 245f3c54-f1c0-bf2e-811f-46d8eeb386e7 + 1c7600d6-661f-b87b-efe2-d7421eb93c86 + 1a2bd58e-87ff-0df8-0b4c-53e047b0bb6e + a8dee56f-2eae-9e7a-05a2-6fb92b97e21e + f2bed5f9-9d44-39af-b0cd-257b2a17fe40 + d2f2ee58-8ad1-06c9-d8d3-3827ba31567a + 6802d553-49da-0778-9f85-1599a2266526 + 0a9fb970-8b44-9114-d3a9-bf69cfe804d6 + eae8905b-271a-99e2-4c0e-31106afd100c + f4f00d6e-b9fe-9292-f4cb-0ae06ea58d57 + 2408fe9e-df1d-1d7d-f4ff-1384fa7b350f + 15468e00-3400-bb66-cecc-646d7c14458e + 370f3a20-6ca6-9971-848c-9a01bc42ae3c + 42b46214-4b44-79ae-deb8-0df61424ff4b + f22fed8b-a5ed-2c93-64d5-bdd8b93c889f + 3da1d753-028a-5446-24f3-9c9b856d9422 + 80700431-74ec-a008-14f8-77575e73693f + 1cb562b0-ba21-2202-efb3-30f82cdf9595 + 41426836-7437-7e89-025d-0aa4d10f1d69 + 85428680-6bf9-3e64-b489-6f81087c24bd + 5c682a95-6da4-a463-0bf6-0f5b7be129d1 + aa134404-7dac-7aca-2cba-435f9db875ca + 83ff59fe-2346-f236-9009-4e3608af64c1 + 038fcec9-5ebd-8a8e-0e2e-6e71a0a1ac53 + 6883a61a-b27b-5914-a61e-dda118a9ee2c + 56e0ba0d-4a9f-7f27-6117-32f2ebbf6135 + 2d6daa51-3192-6794-8e2e-a15f8338ec30 + c541c47f-e0c0-058b-ad1a-d6ae3a4584d9 + 6ed24bd8-91aa-4b12-ccc7-c97c857ab4e0 + 7693f268-06c7-ea71-fa21-2b30d6533f8f + b1ed7982-c68e-a982-7561-52a88a5298c0 + c0c4030f-c02b-49de-24ba-2331f43fe41c + b8c8b2a3-9008-1771-3bfc-90924955ab2d + 15dd911d-be82-2856-26db-27659b142875 + 42ecd00b-9947-a97c-400a-bbc9174c7aeb + diff --git a/bin/data/prototype.js b/bin/data/prototype.js new file mode 100644 index 0000000000..6765129079 --- /dev/null +++ b/bin/data/prototype.js @@ -0,0 +1,4222 @@ +/* Prototype JavaScript framework, version 1.6.0.2 + * (c) 2005-2008 Sam Stephenson + * + * Prototype is freely distributable under the terms of an MIT-style license. + * For details, see the Prototype web site: http://www.prototypejs.org/ + * + *--------------------------------------------------------------------------*/ + +var Prototype = { + Version: '1.6.0.2', + + Browser: { + IE: !!(window.attachEvent && !window.opera), + Opera: !!window.opera, + WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1, + Gecko: navigator.userAgent.indexOf('Gecko') > -1 && navigator.userAgent.indexOf('KHTML') == -1, + MobileSafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/) + }, + + BrowserFeatures: { + XPath: !!document.evaluate, + ElementExtensions: !!window.HTMLElement, + SpecificElementExtensions: + document.createElement('div').__proto__ && + document.createElement('div').__proto__ !== + document.createElement('form').__proto__ + }, + + ScriptFragment: ']*>([\\S\\s]*?)<\/script>', + JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, + + emptyFunction: function() { }, + K: function(x) { return x } +}; + +if (Prototype.Browser.MobileSafari) + Prototype.BrowserFeatures.SpecificElementExtensions = false; + + +/* Based on Alex Arnell's inheritance implementation. */ +var Class = { + create: function() { + var parent = null, properties = $A(arguments); + if (Object.isFunction(properties[0])) + parent = properties.shift(); + + function klass() { + this.initialize.apply(this, arguments); + } + + Object.extend(klass, Class.Methods); + klass.superclass = parent; + klass.subclasses = []; + + if (parent) { + var subclass = function() { }; + subclass.prototype = parent.prototype; + klass.prototype = new subclass; + parent.subclasses.push(klass); + } + + for (var i = 0; i < properties.length; i++) + klass.addMethods(properties[i]); + + if (!klass.prototype.initialize) + klass.prototype.initialize = Prototype.emptyFunction; + + klass.prototype.constructor = klass; + + return klass; + } +}; + +Class.Methods = { + addMethods: function(source) { + var ancestor = this.superclass && this.superclass.prototype; + var properties = Object.keys(source); + + if (!Object.keys({ toString: true }).length) + properties.push("toString", "valueOf"); + + for (var i = 0, length = properties.length; i < length; i++) { + var property = properties[i], value = source[property]; + if (ancestor && Object.isFunction(value) && + value.argumentNames().first() == "$super") { + var method = value, value = Object.extend((function(m) { + return function() { return ancestor[m].apply(this, arguments) }; + })(property).wrap(method), { + valueOf: function() { return method }, + toString: function() { return method.toString() } + }); + } + this.prototype[property] = value; + } + + return this; + } +}; + +var Abstract = { }; + +Object.extend = function(destination, source) { + for (var property in source) + destination[property] = source[property]; + return destination; +}; + +Object.extend(Object, { + inspect: function(object) { + try { + if (Object.isUndefined(object)) return 'undefined'; + if (object === null) return 'null'; + return object.inspect ? object.inspect() : String(object); + } catch (e) { + if (e instanceof RangeError) return '...'; + throw e; + } + }, + + toJSON: function(object) { + var type = typeof object; + switch (type) { + case 'undefined': + case 'function': + case 'unknown': return; + case 'boolean': return object.toString(); + } + + if (object === null) return 'null'; + if (object.toJSON) return object.toJSON(); + if (Object.isElement(object)) return; + + var results = []; + for (var property in object) { + var value = Object.toJSON(object[property]); + if (!Object.isUndefined(value)) + results.push(property.toJSON() + ': ' + value); + } + + return '{' + results.join(', ') + '}'; + }, + + toQueryString: function(object) { + return $H(object).toQueryString(); + }, + + toHTML: function(object) { + return object && object.toHTML ? object.toHTML() : String.interpret(object); + }, + + keys: function(object) { + var keys = []; + for (var property in object) + keys.push(property); + return keys; + }, + + values: function(object) { + var values = []; + for (var property in object) + values.push(object[property]); + return values; + }, + + clone: function(object) { + return Object.extend({ }, object); + }, + + isElement: function(object) { + return object && object.nodeType == 1; + }, + + isArray: function(object) { + return object != null && typeof object == "object" && + 'splice' in object && 'join' in object; + }, + + isHash: function(object) { + return object instanceof Hash; + }, + + isFunction: function(object) { + return typeof object == "function"; + }, + + isString: function(object) { + return typeof object == "string"; + }, + + isNumber: function(object) { + return typeof object == "number"; + }, + + isUndefined: function(object) { + return typeof object == "undefined"; + } +}); + +Object.extend(Function.prototype, { + argumentNames: function() { + var names = this.toString().match(/^[\s\(]*function[^(]*\((.*?)\)/)[1].split(",").invoke("strip"); + return names.length == 1 && !names[0] ? [] : names; + }, + + bind: function() { + if (arguments.length < 2 && Object.isUndefined(arguments[0])) return this; + var __method = this, args = $A(arguments), object = args.shift(); + return function() { + return __method.apply(object, args.concat($A(arguments))); + } + }, + + bindAsEventListener: function() { + var __method = this, args = $A(arguments), object = args.shift(); + return function(event) { + return __method.apply(object, [event || window.event].concat(args)); + } + }, + + curry: function() { + if (!arguments.length) return this; + var __method = this, args = $A(arguments); + return function() { + return __method.apply(this, args.concat($A(arguments))); + } + }, + + delay: function() { + var __method = this, args = $A(arguments), timeout = args.shift() * 1000; + return window.setTimeout(function() { + return __method.apply(__method, args); + }, timeout); + }, + + wrap: function(wrapper) { + var __method = this; + return function() { + return wrapper.apply(this, [__method.bind(this)].concat($A(arguments))); + } + }, + + methodize: function() { + if (this._methodized) return this._methodized; + var __method = this; + return this._methodized = function() { + return __method.apply(null, [this].concat($A(arguments))); + }; + } +}); + +Function.prototype.defer = Function.prototype.delay.curry(0.01); + +Date.prototype.toJSON = function() { + return '"' + this.getUTCFullYear() + '-' + + (this.getUTCMonth() + 1).toPaddedString(2) + '-' + + this.getUTCDate().toPaddedString(2) + 'T' + + this.getUTCHours().toPaddedString(2) + ':' + + this.getUTCMinutes().toPaddedString(2) + ':' + + this.getUTCSeconds().toPaddedString(2) + 'Z"'; +}; + +var Try = { + these: function() { + var returnValue; + + for (var i = 0, length = arguments.length; i < length; i++) { + var lambda = arguments[i]; + try { + returnValue = lambda(); + break; + } catch (e) { } + } + + return returnValue; + } +}; + +RegExp.prototype.match = RegExp.prototype.test; + +RegExp.escape = function(str) { + return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); +}; + +/*--------------------------------------------------------------------------*/ + +var PeriodicalExecuter = Class.create({ + initialize: function(callback, frequency) { + this.callback = callback; + this.frequency = frequency; + this.currentlyExecuting = false; + + this.registerCallback(); + }, + + registerCallback: function() { + this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); + }, + + execute: function() { + this.callback(this); + }, + + stop: function() { + if (!this.timer) return; + clearInterval(this.timer); + this.timer = null; + }, + + onTimerEvent: function() { + if (!this.currentlyExecuting) { + try { + this.currentlyExecuting = true; + this.execute(); + } finally { + this.currentlyExecuting = false; + } + } + } +}); +Object.extend(String, { + interpret: function(value) { + return value == null ? '' : String(value); + }, + specialChar: { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '\\': '\\\\' + } +}); + +Object.extend(String.prototype, { + gsub: function(pattern, replacement) { + var result = '', source = this, match; + replacement = arguments.callee.prepareReplacement(replacement); + + while (source.length > 0) { + if (match = source.match(pattern)) { + result += source.slice(0, match.index); + result += String.interpret(replacement(match)); + source = source.slice(match.index + match[0].length); + } else { + result += source, source = ''; + } + } + return result; + }, + + sub: function(pattern, replacement, count) { + replacement = this.gsub.prepareReplacement(replacement); + count = Object.isUndefined(count) ? 1 : count; + + return this.gsub(pattern, function(match) { + if (--count < 0) return match[0]; + return replacement(match); + }); + }, + + scan: function(pattern, iterator) { + this.gsub(pattern, iterator); + return String(this); + }, + + truncate: function(length, truncation) { + length = length || 30; + truncation = Object.isUndefined(truncation) ? '...' : truncation; + return this.length > length ? + this.slice(0, length - truncation.length) + truncation : String(this); + }, + + strip: function() { + return this.replace(/^\s+/, '').replace(/\s+$/, ''); + }, + + stripTags: function() { + return this.replace(/<\/?[^>]+>/gi, ''); + }, + + stripScripts: function() { + return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); + }, + + extractScripts: function() { + var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); + var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + return (this.match(matchAll) || []).map(function(scriptTag) { + return (scriptTag.match(matchOne) || ['', ''])[1]; + }); + }, + + evalScripts: function() { + return this.extractScripts().map(function(script) { return eval(script) }); + }, + + escapeHTML: function() { + var self = arguments.callee; + self.text.data = this; + return self.div.innerHTML; + }, + + unescapeHTML: function() { + var div = new Element('div'); + div.innerHTML = this.stripTags(); + return div.childNodes[0] ? (div.childNodes.length > 1 ? + $A(div.childNodes).inject('', function(memo, node) { return memo+node.nodeValue }) : + div.childNodes[0].nodeValue) : ''; + }, + + toQueryParams: function(separator) { + var match = this.strip().match(/([^?#]*)(#.*)?$/); + if (!match) return { }; + + return match[1].split(separator || '&').inject({ }, function(hash, pair) { + if ((pair = pair.split('='))[0]) { + var key = decodeURIComponent(pair.shift()); + var value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); + + if (key in hash) { + if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; + hash[key].push(value); + } + else hash[key] = value; + } + return hash; + }); + }, + + toArray: function() { + return this.split(''); + }, + + succ: function() { + return this.slice(0, this.length - 1) + + String.fromCharCode(this.charCodeAt(this.length - 1) + 1); + }, + + times: function(count) { + return count < 1 ? '' : new Array(count + 1).join(this); + }, + + camelize: function() { + var parts = this.split('-'), len = parts.length; + if (len == 1) return parts[0]; + + var camelized = this.charAt(0) == '-' + ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) + : parts[0]; + + for (var i = 1; i < len; i++) + camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); + + return camelized; + }, + + capitalize: function() { + return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); + }, + + underscore: function() { + return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase(); + }, + + dasherize: function() { + return this.gsub(/_/,'-'); + }, + + inspect: function(useDoubleQuotes) { + var escapedString = this.gsub(/[\x00-\x1f\\]/, function(match) { + var character = String.specialChar[match[0]]; + return character ? character : '\\u00' + match[0].charCodeAt().toPaddedString(2, 16); + }); + if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; + return "'" + escapedString.replace(/'/g, '\\\'') + "'"; + }, + + toJSON: function() { + return this.inspect(true); + }, + + unfilterJSON: function(filter) { + return this.sub(filter || Prototype.JSONFilter, '#{1}'); + }, + + isJSON: function() { + var str = this; + if (str.blank()) return false; + str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); + return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + }, + + evalJSON: function(sanitize) { + var json = this.unfilterJSON(); + try { + if (!sanitize || json.isJSON()) return eval('(' + json + ')'); + } catch (e) { } + throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); + }, + + include: function(pattern) { + return this.indexOf(pattern) > -1; + }, + + startsWith: function(pattern) { + return this.indexOf(pattern) === 0; + }, + + endsWith: function(pattern) { + var d = this.length - pattern.length; + return d >= 0 && this.lastIndexOf(pattern) === d; + }, + + empty: function() { + return this == ''; + }, + + blank: function() { + return /^\s*$/.test(this); + }, + + interpolate: function(object, pattern) { + return new Template(this, pattern).evaluate(object); + } +}); + +if (Prototype.Browser.WebKit || Prototype.Browser.IE) Object.extend(String.prototype, { + escapeHTML: function() { + return this.replace(/&/g,'&').replace(//g,'>'); + }, + unescapeHTML: function() { + return this.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); + } +}); + +String.prototype.gsub.prepareReplacement = function(replacement) { + if (Object.isFunction(replacement)) return replacement; + var template = new Template(replacement); + return function(match) { return template.evaluate(match) }; +}; + +String.prototype.parseQuery = String.prototype.toQueryParams; + +Object.extend(String.prototype.escapeHTML, { + div: document.createElement('div'), + text: document.createTextNode('') +}); + +with (String.prototype.escapeHTML) div.appendChild(text); + +var Template = Class.create({ + initialize: function(template, pattern) { + this.template = template.toString(); + this.pattern = pattern || Template.Pattern; + }, + + evaluate: function(object) { + if (Object.isFunction(object.toTemplateReplacements)) + object = object.toTemplateReplacements(); + + return this.template.gsub(this.pattern, function(match) { + if (object == null) return ''; + + var before = match[1] || ''; + if (before == '\\') return match[2]; + + var ctx = object, expr = match[3]; + var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); + if (match == null) return before; + + while (match != null) { + var comp = match[1].startsWith('[') ? match[2].gsub('\\\\]', ']') : match[1]; + ctx = ctx[comp]; + if (null == ctx || '' == match[3]) break; + expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); + match = pattern.exec(expr); + } + + return before + String.interpret(ctx); + }); + } +}); +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; + +var $break = { }; + +var Enumerable = { + each: function(iterator, context) { + var index = 0; + iterator = iterator.bind(context); + try { + this._each(function(value) { + iterator(value, index++); + }); + } catch (e) { + if (e != $break) throw e; + } + return this; + }, + + eachSlice: function(number, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var index = -number, slices = [], array = this.toArray(); + while ((index += number) < array.length) + slices.push(array.slice(index, index+number)); + return slices.collect(iterator, context); + }, + + all: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = true; + this.each(function(value, index) { + result = result && !!iterator(value, index); + if (!result) throw $break; + }); + return result; + }, + + any: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result = false; + this.each(function(value, index) { + if (result = !!iterator(value, index)) + throw $break; + }); + return result; + }, + + collect: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + this.each(function(value, index) { + results.push(iterator(value, index)); + }); + return results; + }, + + detect: function(iterator, context) { + iterator = iterator.bind(context); + var result; + this.each(function(value, index) { + if (iterator(value, index)) { + result = value; + throw $break; + } + }); + return result; + }, + + findAll: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (iterator(value, index)) + results.push(value); + }); + return results; + }, + + grep: function(filter, iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var results = []; + + if (Object.isString(filter)) + filter = new RegExp(filter); + + this.each(function(value, index) { + if (filter.match(value)) + results.push(iterator(value, index)); + }); + return results; + }, + + include: function(object) { + if (Object.isFunction(this.indexOf)) + if (this.indexOf(object) != -1) return true; + + var found = false; + this.each(function(value) { + if (value == object) { + found = true; + throw $break; + } + }); + return found; + }, + + inGroupsOf: function(number, fillWith) { + fillWith = Object.isUndefined(fillWith) ? null : fillWith; + return this.eachSlice(number, function(slice) { + while(slice.length < number) slice.push(fillWith); + return slice; + }); + }, + + inject: function(memo, iterator, context) { + iterator = iterator.bind(context); + this.each(function(value, index) { + memo = iterator(memo, value, index); + }); + return memo; + }, + + invoke: function(method) { + var args = $A(arguments).slice(1); + return this.map(function(value) { + return value[method].apply(value, args); + }); + }, + + max: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value >= result) + result = value; + }); + return result; + }, + + min: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var result; + this.each(function(value, index) { + value = iterator(value, index); + if (result == null || value < result) + result = value; + }); + return result; + }, + + partition: function(iterator, context) { + iterator = iterator ? iterator.bind(context) : Prototype.K; + var trues = [], falses = []; + this.each(function(value, index) { + (iterator(value, index) ? + trues : falses).push(value); + }); + return [trues, falses]; + }, + + pluck: function(property) { + var results = []; + this.each(function(value) { + results.push(value[property]); + }); + return results; + }, + + reject: function(iterator, context) { + iterator = iterator.bind(context); + var results = []; + this.each(function(value, index) { + if (!iterator(value, index)) + results.push(value); + }); + return results; + }, + + sortBy: function(iterator, context) { + iterator = iterator.bind(context); + return this.map(function(value, index) { + return {value: value, criteria: iterator(value, index)}; + }).sort(function(left, right) { + var a = left.criteria, b = right.criteria; + return a < b ? -1 : a > b ? 1 : 0; + }).pluck('value'); + }, + + toArray: function() { + return this.map(); + }, + + zip: function() { + var iterator = Prototype.K, args = $A(arguments); + if (Object.isFunction(args.last())) + iterator = args.pop(); + + var collections = [this].concat(args).map($A); + return this.map(function(value, index) { + return iterator(collections.pluck(index)); + }); + }, + + size: function() { + return this.toArray().length; + }, + + inspect: function() { + return '#'; + } +}; + +Object.extend(Enumerable, { + map: Enumerable.collect, + find: Enumerable.detect, + select: Enumerable.findAll, + filter: Enumerable.findAll, + member: Enumerable.include, + entries: Enumerable.toArray, + every: Enumerable.all, + some: Enumerable.any +}); +function $A(iterable) { + if (!iterable) return []; + if (iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; +} + +if (Prototype.Browser.WebKit) { + $A = function(iterable) { + if (!iterable) return []; + if (!(Object.isFunction(iterable) && iterable == '[object NodeList]') && + iterable.toArray) return iterable.toArray(); + var length = iterable.length || 0, results = new Array(length); + while (length--) results[length] = iterable[length]; + return results; + }; +} + +Array.from = $A; + +Object.extend(Array.prototype, Enumerable); + +if (!Array.prototype._reverse) Array.prototype._reverse = Array.prototype.reverse; + +Object.extend(Array.prototype, { + _each: function(iterator) { + for (var i = 0, length = this.length; i < length; i++) + iterator(this[i]); + }, + + clear: function() { + this.length = 0; + return this; + }, + + first: function() { + return this[0]; + }, + + last: function() { + return this[this.length - 1]; + }, + + compact: function() { + return this.select(function(value) { + return value != null; + }); + }, + + flatten: function() { + return this.inject([], function(array, value) { + return array.concat(Object.isArray(value) ? + value.flatten() : [value]); + }); + }, + + without: function() { + var values = $A(arguments); + return this.select(function(value) { + return !values.include(value); + }); + }, + + reverse: function(inline) { + return (inline !== false ? this : this.toArray())._reverse(); + }, + + reduce: function() { + return this.length > 1 ? this : this[0]; + }, + + uniq: function(sorted) { + return this.inject([], function(array, value, index) { + if (0 == index || (sorted ? array.last() != value : !array.include(value))) + array.push(value); + return array; + }); + }, + + intersect: function(array) { + return this.uniq().findAll(function(item) { + return array.detect(function(value) { return item === value }); + }); + }, + + clone: function() { + return [].concat(this); + }, + + size: function() { + return this.length; + }, + + inspect: function() { + return '[' + this.map(Object.inspect).join(', ') + ']'; + }, + + toJSON: function() { + var results = []; + this.each(function(object) { + var value = Object.toJSON(object); + if (!Object.isUndefined(value)) results.push(value); + }); + return '[' + results.join(', ') + ']'; + } +}); + +// use native browser JS 1.6 implementation if available +if (Object.isFunction(Array.prototype.forEach)) + Array.prototype._each = Array.prototype.forEach; + +if (!Array.prototype.indexOf) Array.prototype.indexOf = function(item, i) { + i || (i = 0); + var length = this.length; + if (i < 0) i = length + i; + for (; i < length; i++) + if (this[i] === item) return i; + return -1; +}; + +if (!Array.prototype.lastIndexOf) Array.prototype.lastIndexOf = function(item, i) { + i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1; + var n = this.slice(0, i).reverse().indexOf(item); + return (n < 0) ? n : i - n - 1; +}; + +Array.prototype.toArray = Array.prototype.clone; + +function $w(string) { + if (!Object.isString(string)) return []; + string = string.strip(); + return string ? string.split(/\s+/) : []; +} + +if (Prototype.Browser.Opera){ + Array.prototype.concat = function() { + var array = []; + for (var i = 0, length = this.length; i < length; i++) array.push(this[i]); + for (var i = 0, length = arguments.length; i < length; i++) { + if (Object.isArray(arguments[i])) { + for (var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++) + array.push(arguments[i][j]); + } else { + array.push(arguments[i]); + } + } + return array; + }; +} +Object.extend(Number.prototype, { + toColorPart: function() { + return this.toPaddedString(2, 16); + }, + + succ: function() { + return this + 1; + }, + + times: function(iterator) { + $R(0, this, true).each(iterator); + return this; + }, + + toPaddedString: function(length, radix) { + var string = this.toString(radix || 10); + return '0'.times(length - string.length) + string; + }, + + toJSON: function() { + return isFinite(this) ? this.toString() : 'null'; + } +}); + +$w('abs round ceil floor').each(function(method){ + Number.prototype[method] = Math[method].methodize(); +}); +function $H(object) { + return new Hash(object); +}; + +var Hash = Class.create(Enumerable, (function() { + + function toQueryPair(key, value) { + if (Object.isUndefined(value)) return key; + return key + '=' + encodeURIComponent(String.interpret(value)); + } + + return { + initialize: function(object) { + this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); + }, + + _each: function(iterator) { + for (var key in this._object) { + var value = this._object[key], pair = [key, value]; + pair.key = key; + pair.value = value; + iterator(pair); + } + }, + + set: function(key, value) { + return this._object[key] = value; + }, + + get: function(key) { + return this._object[key]; + }, + + unset: function(key) { + var value = this._object[key]; + delete this._object[key]; + return value; + }, + + toObject: function() { + return Object.clone(this._object); + }, + + keys: function() { + return this.pluck('key'); + }, + + values: function() { + return this.pluck('value'); + }, + + index: function(value) { + var match = this.detect(function(pair) { + return pair.value === value; + }); + return match && match.key; + }, + + merge: function(object) { + return this.clone().update(object); + }, + + update: function(object) { + return new Hash(object).inject(this, function(result, pair) { + result.set(pair.key, pair.value); + return result; + }); + }, + + toQueryString: function() { + return this.map(function(pair) { + var key = encodeURIComponent(pair.key), values = pair.value; + + if (values && typeof values == 'object') { + if (Object.isArray(values)) + return values.map(toQueryPair.curry(key)).join('&'); + } + return toQueryPair(key, values); + }).join('&'); + }, + + inspect: function() { + return '#'; + }, + + toJSON: function() { + return Object.toJSON(this.toObject()); + }, + + clone: function() { + return new Hash(this); + } + } +})()); + +Hash.prototype.toTemplateReplacements = Hash.prototype.toObject; +Hash.from = $H; +var ObjectRange = Class.create(Enumerable, { + initialize: function(start, end, exclusive) { + this.start = start; + this.end = end; + this.exclusive = exclusive; + }, + + _each: function(iterator) { + var value = this.start; + while (this.include(value)) { + iterator(value); + value = value.succ(); + } + }, + + include: function(value) { + if (value < this.start) + return false; + if (this.exclusive) + return value < this.end; + return value <= this.end; + } +}); + +var $R = function(start, end, exclusive) { + return new ObjectRange(start, end, exclusive); +}; + +var Ajax = { + getTransport: function() { + return Try.these( + function() {return new XMLHttpRequest()}, + function() {return new ActiveXObject('Msxml2.XMLHTTP')}, + function() {return new ActiveXObject('Microsoft.XMLHTTP')} + ) || false; + }, + + activeRequestCount: 0 +}; + +Ajax.Responders = { + responders: [], + + _each: function(iterator) { + this.responders._each(iterator); + }, + + register: function(responder) { + if (!this.include(responder)) + this.responders.push(responder); + }, + + unregister: function(responder) { + this.responders = this.responders.without(responder); + }, + + dispatch: function(callback, request, transport, json) { + this.each(function(responder) { + if (Object.isFunction(responder[callback])) { + try { + responder[callback].apply(responder, [request, transport, json]); + } catch (e) { } + } + }); + } +}; + +Object.extend(Ajax.Responders, Enumerable); + +Ajax.Responders.register({ + onCreate: function() { Ajax.activeRequestCount++ }, + onComplete: function() { Ajax.activeRequestCount-- } +}); + +Ajax.Base = Class.create({ + initialize: function(options) { + this.options = { + method: 'post', + asynchronous: true, + contentType: 'application/x-www-form-urlencoded', + encoding: 'UTF-8', + parameters: '', + evalJSON: true, + evalJS: true + }; + Object.extend(this.options, options || { }); + + this.options.method = this.options.method.toLowerCase(); + + if (Object.isString(this.options.parameters)) + this.options.parameters = this.options.parameters.toQueryParams(); + else if (Object.isHash(this.options.parameters)) + this.options.parameters = this.options.parameters.toObject(); + } +}); + +Ajax.Request = Class.create(Ajax.Base, { + _complete: false, + + initialize: function($super, url, options) { + $super(options); + this.transport = Ajax.getTransport(); + this.request(url); + }, + + request: function(url) { + this.url = url; + this.method = this.options.method; + var params = Object.clone(this.options.parameters); + + if (!['get', 'post'].include(this.method)) { + // simulate other verbs over post + params['_method'] = this.method; + this.method = 'post'; + } + + this.parameters = params; + + if (params = Object.toQueryString(params)) { + // when GET, append parameters to URL + if (this.method == 'get') + this.url += (this.url.include('?') ? '&' : '?') + params; + else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) + params += '&_='; + } + + try { + var response = new Ajax.Response(this); + if (this.options.onCreate) this.options.onCreate(response); + Ajax.Responders.dispatch('onCreate', this, response); + + this.transport.open(this.method.toUpperCase(), this.url, + this.options.asynchronous); + + if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); + + this.transport.onreadystatechange = this.onStateChange.bind(this); + this.setRequestHeaders(); + + this.body = this.method == 'post' ? (this.options.postBody || params) : null; + this.transport.send(this.body); + + /* Force Firefox to handle ready state 4 for synchronous requests */ + if (!this.options.asynchronous && this.transport.overrideMimeType) + this.onStateChange(); + + } + catch (e) { + this.dispatchException(e); + } + }, + + onStateChange: function() { + var readyState = this.transport.readyState; + if (readyState > 1 && !((readyState == 4) && this._complete)) + this.respondToReadyState(this.transport.readyState); + }, + + setRequestHeaders: function() { + var headers = { + 'X-Requested-With': 'XMLHttpRequest', + 'X-Prototype-Version': Prototype.Version, + 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' + }; + + if (this.method == 'post') { + headers['Content-type'] = this.options.contentType + + (this.options.encoding ? '; charset=' + this.options.encoding : ''); + + /* Force "Connection: close" for older Mozilla browsers to work + * around a bug where XMLHttpRequest sends an incorrect + * Content-length header. See Mozilla Bugzilla #246651. + */ + if (this.transport.overrideMimeType && + (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) + headers['Connection'] = 'close'; + } + + // user-defined headers + if (typeof this.options.requestHeaders == 'object') { + var extras = this.options.requestHeaders; + + if (Object.isFunction(extras.push)) + for (var i = 0, length = extras.length; i < length; i += 2) + headers[extras[i]] = extras[i+1]; + else + $H(extras).each(function(pair) { headers[pair.key] = pair.value }); + } + + for (var name in headers) + this.transport.setRequestHeader(name, headers[name]); + }, + + success: function() { + var status = this.getStatus(); + return !status || (status >= 200 && status < 300); + }, + + getStatus: function() { + try { + return this.transport.status || 0; + } catch (e) { return 0 } + }, + + respondToReadyState: function(readyState) { + var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); + + if (state == 'Complete') { + try { + this._complete = true; + (this.options['on' + response.status] + || this.options['on' + (this.success() ? 'Success' : 'Failure')] + || Prototype.emptyFunction)(response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + var contentType = response.getHeader('Content-type'); + if (this.options.evalJS == 'force' + || (this.options.evalJS && this.isSameOrigin() && contentType + && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) + this.evalResponse(); + } + + try { + (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); + Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); + } catch (e) { + this.dispatchException(e); + } + + if (state == 'Complete') { + // avoid memory leak in MSIE: clean up + this.transport.onreadystatechange = Prototype.emptyFunction; + } + }, + + isSameOrigin: function() { + var m = this.url.match(/^\s*https?:\/\/[^\/]*/); + return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ + protocol: location.protocol, + domain: document.domain, + port: location.port ? ':' + location.port : '' + })); + }, + + getHeader: function(name) { + try { + return this.transport.getResponseHeader(name) || null; + } catch (e) { return null } + }, + + evalResponse: function() { + try { + return eval((this.transport.responseText || '').unfilterJSON()); + } catch (e) { + this.dispatchException(e); + } + }, + + dispatchException: function(exception) { + (this.options.onException || Prototype.emptyFunction)(this, exception); + Ajax.Responders.dispatch('onException', this, exception); + } +}); + +Ajax.Request.Events = + ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; + +Ajax.Response = Class.create({ + initialize: function(request){ + this.request = request; + var transport = this.transport = request.transport, + readyState = this.readyState = transport.readyState; + + if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + this.status = this.getStatus(); + this.statusText = this.getStatusText(); + this.responseText = String.interpret(transport.responseText); + this.headerJSON = this._getHeaderJSON(); + } + + if(readyState == 4) { + var xml = transport.responseXML; + this.responseXML = Object.isUndefined(xml) ? null : xml; + this.responseJSON = this._getResponseJSON(); + } + }, + + status: 0, + statusText: '', + + getStatus: Ajax.Request.prototype.getStatus, + + getStatusText: function() { + try { + return this.transport.statusText || ''; + } catch (e) { return '' } + }, + + getHeader: Ajax.Request.prototype.getHeader, + + getAllHeaders: function() { + try { + return this.getAllResponseHeaders(); + } catch (e) { return null } + }, + + getResponseHeader: function(name) { + return this.transport.getResponseHeader(name); + }, + + getAllResponseHeaders: function() { + return this.transport.getAllResponseHeaders(); + }, + + _getHeaderJSON: function() { + var json = this.getHeader('X-JSON'); + if (!json) return null; + json = decodeURIComponent(escape(json)); + try { + return json.evalJSON(this.request.options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + }, + + _getResponseJSON: function() { + var options = this.request.options; + if (!options.evalJSON || (options.evalJSON != 'force' && + !(this.getHeader('Content-type') || '').include('application/json')) || + this.responseText.blank()) + return null; + try { + return this.responseText.evalJSON(options.sanitizeJSON || + !this.request.isSameOrigin()); + } catch (e) { + this.request.dispatchException(e); + } + } +}); + +Ajax.Updater = Class.create(Ajax.Request, { + initialize: function($super, container, url, options) { + this.container = { + success: (container.success || container), + failure: (container.failure || (container.success ? null : container)) + }; + + options = Object.clone(options); + var onComplete = options.onComplete; + options.onComplete = (function(response, json) { + this.updateContent(response.responseText); + if (Object.isFunction(onComplete)) onComplete(response, json); + }).bind(this); + + $super(url, options); + }, + + updateContent: function(responseText) { + var receiver = this.container[this.success() ? 'success' : 'failure'], + options = this.options; + + if (!options.evalScripts) responseText = responseText.stripScripts(); + + if (receiver = $(receiver)) { + if (options.insertion) { + if (Object.isString(options.insertion)) { + var insertion = { }; insertion[options.insertion] = responseText; + receiver.insert(insertion); + } + else options.insertion(receiver, responseText); + } + else receiver.update(responseText); + } + } +}); + +Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { + initialize: function($super, container, url, options) { + $super(options); + this.onComplete = this.options.onComplete; + + this.frequency = (this.options.frequency || 2); + this.decay = (this.options.decay || 1); + + this.updater = { }; + this.container = container; + this.url = url; + + this.start(); + }, + + start: function() { + this.options.onComplete = this.updateComplete.bind(this); + this.onTimerEvent(); + }, + + stop: function() { + this.updater.options.onComplete = undefined; + clearTimeout(this.timer); + (this.onComplete || Prototype.emptyFunction).apply(this, arguments); + }, + + updateComplete: function(response) { + if (this.options.decay) { + this.decay = (response.responseText == this.lastText ? + this.decay * this.options.decay : 1); + + this.lastText = response.responseText; + } + this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); + }, + + onTimerEvent: function() { + this.updater = new Ajax.Updater(this.container, this.url, this.options); + } +}); +function $(element) { + if (arguments.length > 1) { + for (var i = 0, elements = [], length = arguments.length; i < length; i++) + elements.push($(arguments[i])); + return elements; + } + if (Object.isString(element)) + element = document.getElementById(element); + return Element.extend(element); +} + +if (Prototype.BrowserFeatures.XPath) { + document._getElementsByXPath = function(expression, parentElement) { + var results = []; + var query = document.evaluate(expression, $(parentElement) || document, + null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + for (var i = 0, length = query.snapshotLength; i < length; i++) + results.push(Element.extend(query.snapshotItem(i))); + return results; + }; +} + +/*--------------------------------------------------------------------------*/ + +if (!window.Node) var Node = { }; + +if (!Node.ELEMENT_NODE) { + // DOM level 2 ECMAScript Language Binding + Object.extend(Node, { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + ENTITY_REFERENCE_NODE: 5, + ENTITY_NODE: 6, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, + NOTATION_NODE: 12 + }); +} + +(function() { + var element = this.Element; + this.Element = function(tagName, attributes) { + attributes = attributes || { }; + tagName = tagName.toLowerCase(); + var cache = Element.cache; + if (Prototype.Browser.IE && attributes.name) { + tagName = '<' + tagName + ' name="' + attributes.name + '">'; + delete attributes.name; + return Element.writeAttribute(document.createElement(tagName), attributes); + } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); + return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + }; + Object.extend(this.Element, element || { }); +}).call(window); + +Element.cache = { }; + +Element.Methods = { + visible: function(element) { + return $(element).style.display != 'none'; + }, + + toggle: function(element) { + element = $(element); + Element[Element.visible(element) ? 'hide' : 'show'](element); + return element; + }, + + hide: function(element) { + $(element).style.display = 'none'; + return element; + }, + + show: function(element) { + $(element).style.display = ''; + return element; + }, + + remove: function(element) { + element = $(element); + element.parentNode.removeChild(element); + return element; + }, + + update: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + content = Object.toHTML(content); + element.innerHTML = content.stripScripts(); + content.evalScripts.bind(content).defer(); + return element; + }, + + replace: function(element, content) { + element = $(element); + if (content && content.toElement) content = content.toElement(); + else if (!Object.isElement(content)) { + content = Object.toHTML(content); + var range = element.ownerDocument.createRange(); + range.selectNode(element); + content.evalScripts.bind(content).defer(); + content = range.createContextualFragment(content.stripScripts()); + } + element.parentNode.replaceChild(content, element); + return element; + }, + + insert: function(element, insertions) { + element = $(element); + + if (Object.isString(insertions) || Object.isNumber(insertions) || + Object.isElement(insertions) || (insertions && (insertions.toElement || insertions.toHTML))) + insertions = {bottom:insertions}; + + var content, insert, tagName, childNodes; + + for (var position in insertions) { + content = insertions[position]; + position = position.toLowerCase(); + insert = Element._insertionTranslations[position]; + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + insert(element, content); + continue; + } + + content = Object.toHTML(content); + + tagName = ((position == 'before' || position == 'after') + ? element.parentNode : element).tagName.toUpperCase(); + + childNodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + + if (position == 'top' || position == 'after') childNodes.reverse(); + childNodes.each(insert.curry(element)); + + content.evalScripts.bind(content).defer(); + } + + return element; + }, + + wrap: function(element, wrapper, attributes) { + element = $(element); + if (Object.isElement(wrapper)) + $(wrapper).writeAttribute(attributes || { }); + else if (Object.isString(wrapper)) wrapper = new Element(wrapper, attributes); + else wrapper = new Element('div', wrapper); + if (element.parentNode) + element.parentNode.replaceChild(wrapper, element); + wrapper.appendChild(element); + return wrapper; + }, + + inspect: function(element) { + element = $(element); + var result = '<' + element.tagName.toLowerCase(); + $H({'id': 'id', 'className': 'class'}).each(function(pair) { + var property = pair.first(), attribute = pair.last(); + var value = (element[property] || '').toString(); + if (value) result += ' ' + attribute + '=' + value.inspect(true); + }); + return result + '>'; + }, + + recursivelyCollect: function(element, property) { + element = $(element); + var elements = []; + while (element = element[property]) + if (element.nodeType == 1) + elements.push(Element.extend(element)); + return elements; + }, + + ancestors: function(element) { + return $(element).recursivelyCollect('parentNode'); + }, + + descendants: function(element) { + return $(element).select("*"); + }, + + firstDescendant: function(element) { + element = $(element).firstChild; + while (element && element.nodeType != 1) element = element.nextSibling; + return $(element); + }, + + immediateDescendants: function(element) { + if (!(element = $(element).firstChild)) return []; + while (element && element.nodeType != 1) element = element.nextSibling; + if (element) return [element].concat($(element).nextSiblings()); + return []; + }, + + previousSiblings: function(element) { + return $(element).recursivelyCollect('previousSibling'); + }, + + nextSiblings: function(element) { + return $(element).recursivelyCollect('nextSibling'); + }, + + siblings: function(element) { + element = $(element); + return element.previousSiblings().reverse().concat(element.nextSiblings()); + }, + + match: function(element, selector) { + if (Object.isString(selector)) + selector = new Selector(selector); + return selector.match($(element)); + }, + + up: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(element.parentNode); + var ancestors = element.ancestors(); + return Object.isNumber(expression) ? ancestors[expression] : + Selector.findElement(ancestors, expression, index); + }, + + down: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return element.firstDescendant(); + return Object.isNumber(expression) ? element.descendants()[expression] : + element.select(expression)[index || 0]; + }, + + previous: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); + var previousSiblings = element.previousSiblings(); + return Object.isNumber(expression) ? previousSiblings[expression] : + Selector.findElement(previousSiblings, expression, index); + }, + + next: function(element, expression, index) { + element = $(element); + if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); + var nextSiblings = element.nextSiblings(); + return Object.isNumber(expression) ? nextSiblings[expression] : + Selector.findElement(nextSiblings, expression, index); + }, + + select: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element, args); + }, + + adjacent: function() { + var args = $A(arguments), element = $(args.shift()); + return Selector.findChildElements(element.parentNode, args).without(element); + }, + + identify: function(element) { + element = $(element); + var id = element.readAttribute('id'), self = arguments.callee; + if (id) return id; + do { id = 'anonymous_element_' + self.counter++ } while ($(id)); + element.writeAttribute('id', id); + return id; + }, + + readAttribute: function(element, name) { + element = $(element); + if (Prototype.Browser.IE) { + var t = Element._attributeTranslations.read; + if (t.values[name]) return t.values[name](element, name); + if (t.names[name]) name = t.names[name]; + if (name.include(':')) { + return (!element.attributes || !element.attributes[name]) ? null : + element.attributes[name].value; + } + } + return element.getAttribute(name); + }, + + writeAttribute: function(element, name, value) { + element = $(element); + var attributes = { }, t = Element._attributeTranslations.write; + + if (typeof name == 'object') attributes = name; + else attributes[name] = Object.isUndefined(value) ? true : value; + + for (var attr in attributes) { + name = t.names[attr] || attr; + value = attributes[attr]; + if (t.values[attr]) name = t.values[attr](element, value); + if (value === false || value === null) + element.removeAttribute(name); + else if (value === true) + element.setAttribute(name, name); + else element.setAttribute(name, value); + } + return element; + }, + + getHeight: function(element) { + return $(element).getDimensions().height; + }, + + getWidth: function(element) { + return $(element).getDimensions().width; + }, + + classNames: function(element) { + return new Element.ClassNames(element); + }, + + hasClassName: function(element, className) { + if (!(element = $(element))) return; + var elementClassName = element.className; + return (elementClassName.length > 0 && (elementClassName == className || + new RegExp("(^|\\s)" + className + "(\\s|$)").test(elementClassName))); + }, + + addClassName: function(element, className) { + if (!(element = $(element))) return; + if (!element.hasClassName(className)) + element.className += (element.className ? ' ' : '') + className; + return element; + }, + + removeClassName: function(element, className) { + if (!(element = $(element))) return; + element.className = element.className.replace( + new RegExp("(^|\\s+)" + className + "(\\s+|$)"), ' ').strip(); + return element; + }, + + toggleClassName: function(element, className) { + if (!(element = $(element))) return; + return element[element.hasClassName(className) ? + 'removeClassName' : 'addClassName'](className); + }, + + // removes whitespace-only text node children + cleanWhitespace: function(element) { + element = $(element); + var node = element.firstChild; + while (node) { + var nextNode = node.nextSibling; + if (node.nodeType == 3 && !/\S/.test(node.nodeValue)) + element.removeChild(node); + node = nextNode; + } + return element; + }, + + empty: function(element) { + return $(element).innerHTML.blank(); + }, + + descendantOf: function(element, ancestor) { + element = $(element), ancestor = $(ancestor); + var originalAncestor = ancestor; + + if (element.compareDocumentPosition) + return (element.compareDocumentPosition(ancestor) & 8) === 8; + + if (element.sourceIndex && !Prototype.Browser.Opera) { + var e = element.sourceIndex, a = ancestor.sourceIndex, + nextAncestor = ancestor.nextSibling; + if (!nextAncestor) { + do { ancestor = ancestor.parentNode; } + while (!(nextAncestor = ancestor.nextSibling) && ancestor.parentNode); + } + if (nextAncestor && nextAncestor.sourceIndex) + return (e > a && e < nextAncestor.sourceIndex); + } + + while (element = element.parentNode) + if (element == originalAncestor) return true; + return false; + }, + + scrollTo: function(element) { + element = $(element); + var pos = element.cumulativeOffset(); + window.scrollTo(pos[0], pos[1]); + return element; + }, + + getStyle: function(element, style) { + element = $(element); + style = style == 'float' ? 'cssFloat' : style.camelize(); + var value = element.style[style]; + if (!value) { + var css = document.defaultView.getComputedStyle(element, null); + value = css ? css[style] : null; + } + if (style == 'opacity') return value ? parseFloat(value) : 1.0; + return value == 'auto' ? null : value; + }, + + getOpacity: function(element) { + return $(element).getStyle('opacity'); + }, + + setStyle: function(element, styles) { + element = $(element); + var elementStyle = element.style, match; + if (Object.isString(styles)) { + element.style.cssText += ';' + styles; + return styles.include('opacity') ? + element.setOpacity(styles.match(/opacity:\s*(\d?\.?\d*)/)[1]) : element; + } + for (var property in styles) + if (property == 'opacity') element.setOpacity(styles[property]); + else + elementStyle[(property == 'float' || property == 'cssFloat') ? + (Object.isUndefined(elementStyle.styleFloat) ? 'cssFloat' : 'styleFloat') : + property] = styles[property]; + + return element; + }, + + setOpacity: function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + return element; + }, + + getDimensions: function(element) { + element = $(element); + var display = $(element).getStyle('display'); + if (display != 'none' && display != null) // Safari bug + return {width: element.offsetWidth, height: element.offsetHeight}; + + // All *Width and *Height properties give 0 on elements with display none, + // so enable the element temporarily + var els = element.style; + var originalVisibility = els.visibility; + var originalPosition = els.position; + var originalDisplay = els.display; + els.visibility = 'hidden'; + els.position = 'absolute'; + els.display = 'block'; + var originalWidth = element.clientWidth; + var originalHeight = element.clientHeight; + els.display = originalDisplay; + els.position = originalPosition; + els.visibility = originalVisibility; + return {width: originalWidth, height: originalHeight}; + }, + + makePositioned: function(element) { + element = $(element); + var pos = Element.getStyle(element, 'position'); + if (pos == 'static' || !pos) { + element._madePositioned = true; + element.style.position = 'relative'; + // Opera returns the offset relative to the positioning context, when an + // element is position relative but top and left have not been defined + if (window.opera) { + element.style.top = 0; + element.style.left = 0; + } + } + return element; + }, + + undoPositioned: function(element) { + element = $(element); + if (element._madePositioned) { + element._madePositioned = undefined; + element.style.position = + element.style.top = + element.style.left = + element.style.bottom = + element.style.right = ''; + } + return element; + }, + + makeClipping: function(element) { + element = $(element); + if (element._overflow) return element; + element._overflow = Element.getStyle(element, 'overflow') || 'auto'; + if (element._overflow !== 'hidden') + element.style.overflow = 'hidden'; + return element; + }, + + undoClipping: function(element) { + element = $(element); + if (!element._overflow) return element; + element.style.overflow = element._overflow == 'auto' ? '' : element._overflow; + element._overflow = null; + return element; + }, + + cumulativeOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + positionedOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + element = element.offsetParent; + if (element) { + if (element.tagName == 'BODY') break; + var p = Element.getStyle(element, 'position'); + if (p !== 'static') break; + } + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + absolutize: function(element) { + element = $(element); + if (element.getStyle('position') == 'absolute') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + var offsets = element.positionedOffset(); + var top = offsets[1]; + var left = offsets[0]; + var width = element.clientWidth; + var height = element.clientHeight; + + element._originalLeft = left - parseFloat(element.style.left || 0); + element._originalTop = top - parseFloat(element.style.top || 0); + element._originalWidth = element.style.width; + element._originalHeight = element.style.height; + + element.style.position = 'absolute'; + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.width = width + 'px'; + element.style.height = height + 'px'; + return element; + }, + + relativize: function(element) { + element = $(element); + if (element.getStyle('position') == 'relative') return; + // Position.prepare(); // To be done manually by Scripty when it needs it. + + element.style.position = 'relative'; + var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); + var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); + + element.style.top = top + 'px'; + element.style.left = left + 'px'; + element.style.height = element._originalHeight; + element.style.width = element._originalWidth; + return element; + }, + + cumulativeScrollOffset: function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return Element._returnOffset(valueL, valueT); + }, + + getOffsetParent: function(element) { + if (element.offsetParent) return $(element.offsetParent); + if (element == document.body) return $(element); + + while ((element = element.parentNode) && element != document.body) + if (Element.getStyle(element, 'position') != 'static') + return $(element); + + return $(document.body); + }, + + viewportOffset: function(forElement) { + var valueT = 0, valueL = 0; + + var element = forElement; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + + // Safari fix + if (element.offsetParent == document.body && + Element.getStyle(element, 'position') == 'absolute') break; + + } while (element = element.offsetParent); + + element = forElement; + do { + if (!Prototype.Browser.Opera || element.tagName == 'BODY') { + valueT -= element.scrollTop || 0; + valueL -= element.scrollLeft || 0; + } + } while (element = element.parentNode); + + return Element._returnOffset(valueL, valueT); + }, + + clonePosition: function(element, source) { + var options = Object.extend({ + setLeft: true, + setTop: true, + setWidth: true, + setHeight: true, + offsetTop: 0, + offsetLeft: 0 + }, arguments[2] || { }); + + // find page position of source + source = $(source); + var p = source.viewportOffset(); + + // find coordinate system to use + element = $(element); + var delta = [0, 0]; + var parent = null; + // delta [0,0] will do fine with position: fixed elements, + // position:absolute needs offsetParent deltas + if (Element.getStyle(element, 'position') == 'absolute') { + parent = element.getOffsetParent(); + delta = parent.viewportOffset(); + } + + // correct by body offsets (fixes Safari) + if (parent == document.body) { + delta[0] -= document.body.offsetLeft; + delta[1] -= document.body.offsetTop; + } + + // set position + if (options.setLeft) element.style.left = (p[0] - delta[0] + options.offsetLeft) + 'px'; + if (options.setTop) element.style.top = (p[1] - delta[1] + options.offsetTop) + 'px'; + if (options.setWidth) element.style.width = source.offsetWidth + 'px'; + if (options.setHeight) element.style.height = source.offsetHeight + 'px'; + return element; + } +}; + +Element.Methods.identify.counter = 1; + +Object.extend(Element.Methods, { + getElementsBySelector: Element.Methods.select, + childElements: Element.Methods.immediateDescendants +}); + +Element._attributeTranslations = { + write: { + names: { + className: 'class', + htmlFor: 'for' + }, + values: { } + } +}; + +if (Prototype.Browser.Opera) { + Element.Methods.getStyle = Element.Methods.getStyle.wrap( + function(proceed, element, style) { + switch (style) { + case 'left': case 'top': case 'right': case 'bottom': + if (proceed(element, 'position') === 'static') return null; + case 'height': case 'width': + // returns '0px' for hidden elements; we want it to return null + if (!Element.visible(element)) return null; + + // returns the border-box dimensions rather than the content-box + // dimensions, so we subtract padding and borders from the value + var dim = parseInt(proceed(element, style), 10); + + if (dim !== element['offset' + style.capitalize()]) + return dim + 'px'; + + var properties; + if (style === 'height') { + properties = ['border-top-width', 'padding-top', + 'padding-bottom', 'border-bottom-width']; + } + else { + properties = ['border-left-width', 'padding-left', + 'padding-right', 'border-right-width']; + } + return properties.inject(dim, function(memo, property) { + var val = proceed(element, property); + return val === null ? memo : memo - parseInt(val, 10); + }) + 'px'; + default: return proceed(element, style); + } + } + ); + + Element.Methods.readAttribute = Element.Methods.readAttribute.wrap( + function(proceed, element, attribute) { + if (attribute === 'title') return element.title; + return proceed(element, attribute); + } + ); +} + +else if (Prototype.Browser.IE) { + // IE doesn't report offsets correctly for static elements, so we change them + // to "relative" to get the values, then change them back. + Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + + $w('positionedOffset viewportOffset').each(function(method) { + Element.Methods[method] = Element.Methods[method].wrap( + function(proceed, element) { + element = $(element); + var position = element.getStyle('position'); + if (position !== 'static') return proceed(element); + // Trigger hasLayout on the offset parent so that IE6 reports + // accurate offsetTop and offsetLeft values for position: fixed. + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + offsetParent.setStyle({ zoom: 1 }); + element.setStyle({ position: 'relative' }); + var value = proceed(element); + element.setStyle({ position: position }); + return value; + } + ); + }); + + Element.Methods.getStyle = function(element, style) { + element = $(element); + style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); + var value = element.style[style]; + if (!value && element.currentStyle) value = element.currentStyle[style]; + + if (style == 'opacity') { + if (value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/)) + if (value[1]) return parseFloat(value[1]) / 100; + return 1.0; + } + + if (value == 'auto') { + if ((style == 'width' || style == 'height') && (element.getStyle('display') != 'none')) + return element['offset' + style.capitalize()] + 'px'; + return null; + } + return value; + }; + + Element.Methods.setOpacity = function(element, value) { + function stripAlpha(filter){ + return filter.replace(/alpha\([^\)]*\)/gi,''); + } + element = $(element); + var currentStyle = element.currentStyle; + if ((currentStyle && !currentStyle.hasLayout) || + (!currentStyle && element.style.zoom == 'normal')) + element.style.zoom = 1; + + var filter = element.getStyle('filter'), style = element.style; + if (value == 1 || value === '') { + (filter = stripAlpha(filter)) ? + style.filter = filter : style.removeAttribute('filter'); + return element; + } else if (value < 0.00001) value = 0; + style.filter = stripAlpha(filter) + + 'alpha(opacity=' + (value * 100) + ')'; + return element; + }; + + Element._attributeTranslations = { + read: { + names: { + 'class': 'className', + 'for': 'htmlFor' + }, + values: { + _getAttr: function(element, attribute) { + return element.getAttribute(attribute, 2); + }, + _getAttrNode: function(element, attribute) { + var node = element.getAttributeNode(attribute); + return node ? node.value : ""; + }, + _getEv: function(element, attribute) { + attribute = element.getAttribute(attribute); + return attribute ? attribute.toString().slice(23, -2) : null; + }, + _flag: function(element, attribute) { + return $(element).hasAttribute(attribute) ? attribute : null; + }, + style: function(element) { + return element.style.cssText.toLowerCase(); + }, + title: function(element) { + return element.title; + } + } + } + }; + + Element._attributeTranslations.write = { + names: Object.extend({ + cellpadding: 'cellPadding', + cellspacing: 'cellSpacing' + }, Element._attributeTranslations.read.names), + values: { + checked: function(element, value) { + element.checked = !!value; + }, + + style: function(element, value) { + element.style.cssText = value ? value : ''; + } + } + }; + + Element._attributeTranslations.has = {}; + + $w('colSpan rowSpan vAlign dateTime accessKey tabIndex ' + + 'encType maxLength readOnly longDesc').each(function(attr) { + Element._attributeTranslations.write.names[attr.toLowerCase()] = attr; + Element._attributeTranslations.has[attr.toLowerCase()] = attr; + }); + + (function(v) { + Object.extend(v, { + href: v._getAttr, + src: v._getAttr, + type: v._getAttr, + action: v._getAttrNode, + disabled: v._flag, + checked: v._flag, + readonly: v._flag, + multiple: v._flag, + onload: v._getEv, + onunload: v._getEv, + onclick: v._getEv, + ondblclick: v._getEv, + onmousedown: v._getEv, + onmouseup: v._getEv, + onmouseover: v._getEv, + onmousemove: v._getEv, + onmouseout: v._getEv, + onfocus: v._getEv, + onblur: v._getEv, + onkeypress: v._getEv, + onkeydown: v._getEv, + onkeyup: v._getEv, + onsubmit: v._getEv, + onreset: v._getEv, + onselect: v._getEv, + onchange: v._getEv + }); + })(Element._attributeTranslations.read.values); +} + +else if (Prototype.Browser.Gecko && /rv:1\.8\.0/.test(navigator.userAgent)) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1) ? 0.999999 : + (value === '') ? '' : (value < 0.00001) ? 0 : value; + return element; + }; +} + +else if (Prototype.Browser.WebKit) { + Element.Methods.setOpacity = function(element, value) { + element = $(element); + element.style.opacity = (value == 1 || value === '') ? '' : + (value < 0.00001) ? 0 : value; + + if (value == 1) + if(element.tagName == 'IMG' && element.width) { + element.width++; element.width--; + } else try { + var n = document.createTextNode(' '); + element.appendChild(n); + element.removeChild(n); + } catch (e) { } + + return element; + }; + + // Safari returns margins on body which is incorrect if the child is absolutely + // positioned. For performance reasons, redefine Element#cumulativeOffset for + // KHTML/WebKit only. + Element.Methods.cumulativeOffset = function(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.offsetTop || 0; + valueL += element.offsetLeft || 0; + if (element.offsetParent == document.body) + if (Element.getStyle(element, 'position') == 'absolute') break; + + element = element.offsetParent; + } while (element); + + return Element._returnOffset(valueL, valueT); + }; +} + +if (Prototype.Browser.IE || Prototype.Browser.Opera) { + // IE and Opera are missing .innerHTML support for TABLE-related and SELECT elements + Element.Methods.update = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) return element.update().insert(content); + + content = Object.toHTML(content); + var tagName = element.tagName.toUpperCase(); + + if (tagName in Element._insertionTranslations.tags) { + $A(element.childNodes).each(function(node) { element.removeChild(node) }); + Element._getContentFromAnonymousElement(tagName, content.stripScripts()) + .each(function(node) { element.appendChild(node) }); + } + else element.innerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +if ('outerHTML' in document.createElement('div')) { + Element.Methods.replace = function(element, content) { + element = $(element); + + if (content && content.toElement) content = content.toElement(); + if (Object.isElement(content)) { + element.parentNode.replaceChild(content, element); + return element; + } + + content = Object.toHTML(content); + var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); + + if (Element._insertionTranslations.tags[tagName]) { + var nextSibling = element.next(); + var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + parent.removeChild(element); + if (nextSibling) + fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); + else + fragments.each(function(node) { parent.appendChild(node) }); + } + else element.outerHTML = content.stripScripts(); + + content.evalScripts.bind(content).defer(); + return element; + }; +} + +Element._returnOffset = function(l, t) { + var result = [l, t]; + result.left = l; + result.top = t; + return result; +}; + +Element._getContentFromAnonymousElement = function(tagName, html) { + var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; + if (t) { + div.innerHTML = t[0] + html + t[1]; + t[2].times(function() { div = div.firstChild }); + } else div.innerHTML = html; + return $A(div.childNodes); +}; + +Element._insertionTranslations = { + before: function(element, node) { + element.parentNode.insertBefore(node, element); + }, + top: function(element, node) { + element.insertBefore(node, element.firstChild); + }, + bottom: function(element, node) { + element.appendChild(node); + }, + after: function(element, node) { + element.parentNode.insertBefore(node, element.nextSibling); + }, + tags: { + TABLE: ['
', '
', 1], + TBODY: ['', '
', 2], + TR: ['', '
', 3], + TD: ['
', '
', 4], + SELECT: ['', 1] + } +}; + +(function() { + Object.extend(this.tags, { + THEAD: this.tags.TBODY, + TFOOT: this.tags.TBODY, + TH: this.tags.TD + }); +}).call(Element._insertionTranslations); + +Element.Methods.Simulated = { + hasAttribute: function(element, attribute) { + attribute = Element._attributeTranslations.has[attribute] || attribute; + var node = $(element).getAttributeNode(attribute); + return node && node.specified; + } +}; + +Element.Methods.ByTag = { }; + +Object.extend(Element, Element.Methods); + +if (!Prototype.BrowserFeatures.ElementExtensions && + document.createElement('div').__proto__) { + window.HTMLElement = { }; + window.HTMLElement.prototype = document.createElement('div').__proto__; + Prototype.BrowserFeatures.ElementExtensions = true; +} + +Element.extend = (function() { + if (Prototype.BrowserFeatures.SpecificElementExtensions) + return Prototype.K; + + var Methods = { }, ByTag = Element.Methods.ByTag; + + var extend = Object.extend(function(element) { + if (!element || element._extendedByPrototype || + element.nodeType != 1 || element == window) return element; + + var methods = Object.clone(Methods), + tagName = element.tagName, property, value; + + // extend methods for specific tags + if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); + + for (property in methods) { + value = methods[property]; + if (Object.isFunction(value) && !(property in element)) + element[property] = value.methodize(); + } + + element._extendedByPrototype = Prototype.emptyFunction; + return element; + + }, { + refresh: function() { + // extend methods for all tags (Safari doesn't need this) + if (!Prototype.BrowserFeatures.ElementExtensions) { + Object.extend(Methods, Element.Methods); + Object.extend(Methods, Element.Methods.Simulated); + } + } + }); + + extend.refresh(); + return extend; +})(); + +Element.hasAttribute = function(element, attribute) { + if (element.hasAttribute) return element.hasAttribute(attribute); + return Element.Methods.Simulated.hasAttribute(element, attribute); +}; + +Element.addMethods = function(methods) { + var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; + + if (!methods) { + Object.extend(Form, Form.Methods); + Object.extend(Form.Element, Form.Element.Methods); + Object.extend(Element.Methods.ByTag, { + "FORM": Object.clone(Form.Methods), + "INPUT": Object.clone(Form.Element.Methods), + "SELECT": Object.clone(Form.Element.Methods), + "TEXTAREA": Object.clone(Form.Element.Methods) + }); + } + + if (arguments.length == 2) { + var tagName = methods; + methods = arguments[1]; + } + + if (!tagName) Object.extend(Element.Methods, methods || { }); + else { + if (Object.isArray(tagName)) tagName.each(extend); + else extend(tagName); + } + + function extend(tagName) { + tagName = tagName.toUpperCase(); + if (!Element.Methods.ByTag[tagName]) + Element.Methods.ByTag[tagName] = { }; + Object.extend(Element.Methods.ByTag[tagName], methods); + } + + function copy(methods, destination, onlyIfAbsent) { + onlyIfAbsent = onlyIfAbsent || false; + for (var property in methods) { + var value = methods[property]; + if (!Object.isFunction(value)) continue; + if (!onlyIfAbsent || !(property in destination)) + destination[property] = value.methodize(); + } + } + + function findDOMClass(tagName) { + var klass; + var trans = { + "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", + "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", + "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", + "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", + "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": + "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": + "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": + "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": + "FrameSet", "IFRAME": "IFrame" + }; + if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName + 'Element'; + if (window[klass]) return window[klass]; + klass = 'HTML' + tagName.capitalize() + 'Element'; + if (window[klass]) return window[klass]; + + window[klass] = { }; + window[klass].prototype = document.createElement(tagName).__proto__; + return window[klass]; + } + + if (F.ElementExtensions) { + copy(Element.Methods, HTMLElement.prototype); + copy(Element.Methods.Simulated, HTMLElement.prototype, true); + } + + if (F.SpecificElementExtensions) { + for (var tag in Element.Methods.ByTag) { + var klass = findDOMClass(tag); + if (Object.isUndefined(klass)) continue; + copy(T[tag], klass.prototype); + } + } + + Object.extend(Element, Element.Methods); + delete Element.ByTag; + + if (Element.extend.refresh) Element.extend.refresh(); + Element.cache = { }; +}; + +document.viewport = { + getDimensions: function() { + var dimensions = { }; + var B = Prototype.Browser; + $w('width height').each(function(d) { + var D = d.capitalize(); + dimensions[d] = (B.WebKit && !document.evaluate) ? self['inner' + D] : + (B.Opera) ? document.body['client' + D] : document.documentElement['client' + D]; + }); + return dimensions; + }, + + getWidth: function() { + return this.getDimensions().width; + }, + + getHeight: function() { + return this.getDimensions().height; + }, + + getScrollOffsets: function() { + return Element._returnOffset( + window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, + window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop); + } +}; +/* Portions of the Selector class are derived from Jack Slocum’s DomQuery, + * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style + * license. Please see http://www.yui-ext.com/ for more information. */ + +var Selector = Class.create({ + initialize: function(expression) { + this.expression = expression.strip(); + this.compileMatcher(); + }, + + shouldUseXPath: function() { + if (!Prototype.BrowserFeatures.XPath) return false; + + var e = this.expression; + + // Safari 3 chokes on :*-of-type and :empty + if (Prototype.Browser.WebKit && + (e.include("-of-type") || e.include(":empty"))) + return false; + + // XPath can't do namespaced attributes, nor can it read + // the "checked" property from DOM nodes + if ((/(\[[\w-]*?:|:checked)/).test(this.expression)) + return false; + + return true; + }, + + compileMatcher: function() { + if (this.shouldUseXPath()) + return this.compileXPathMatcher(); + + var e = this.expression, ps = Selector.patterns, h = Selector.handlers, + c = Selector.criteria, le, p, m; + + if (Selector._cache[e]) { + this.matcher = Selector._cache[e]; + return; + } + + this.matcher = ["this.matcher = function(root) {", + "var r = root, h = Selector.handlers, c = false, n;"]; + + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + this.matcher.push(Object.isFunction(c[i]) ? c[i](m) : + new Template(c[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.matcher.push("return h.unique(n);\n}"); + eval(this.matcher.join('\n')); + Selector._cache[this.expression] = this.matcher; + }, + + compileXPathMatcher: function() { + var e = this.expression, ps = Selector.patterns, + x = Selector.xpath, le, m; + + if (Selector._cache[e]) { + this.xpath = Selector._cache[e]; return; + } + + this.matcher = ['.//*']; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + if (m = e.match(ps[i])) { + this.matcher.push(Object.isFunction(x[i]) ? x[i](m) : + new Template(x[i]).evaluate(m)); + e = e.replace(m[0], ''); + break; + } + } + } + + this.xpath = this.matcher.join(''); + Selector._cache[this.expression] = this.xpath; + }, + + findElements: function(root) { + root = root || document; + if (this.xpath) return document._getElementsByXPath(this.xpath, root); + return this.matcher(root); + }, + + match: function(element) { + this.tokens = []; + + var e = this.expression, ps = Selector.patterns, as = Selector.assertions; + var le, p, m; + + while (e && le !== e && (/\S/).test(e)) { + le = e; + for (var i in ps) { + p = ps[i]; + if (m = e.match(p)) { + // use the Selector.assertions methods unless the selector + // is too complex. + if (as[i]) { + this.tokens.push([i, Object.clone(m)]); + e = e.replace(m[0], ''); + } else { + // reluctantly do a document-wide search + // and look for a match in the array + return this.findElements(document).include(element); + } + } + } + } + + var match = true, name, matches; + for (var i = 0, token; token = this.tokens[i]; i++) { + name = token[0], matches = token[1]; + if (!Selector.assertions[name](element, matches)) { + match = false; break; + } + } + + return match; + }, + + toString: function() { + return this.expression; + }, + + inspect: function() { + return "#"; + } +}); + +Object.extend(Selector, { + _cache: { }, + + xpath: { + descendant: "//*", + child: "/*", + adjacent: "/following-sibling::*[1]", + laterSibling: '/following-sibling::*', + tagName: function(m) { + if (m[1] == '*') return ''; + return "[local-name()='" + m[1].toLowerCase() + + "' or local-name()='" + m[1].toUpperCase() + "']"; + }, + className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", + id: "[@id='#{1}']", + attrPresence: function(m) { + m[1] = m[1].toLowerCase(); + return new Template("[@#{1}]").evaluate(m); + }, + attr: function(m) { + m[1] = m[1].toLowerCase(); + m[3] = m[5] || m[6]; + return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + }, + pseudo: function(m) { + var h = Selector.xpath.pseudos[m[1]]; + if (!h) return ''; + if (Object.isFunction(h)) return h(m); + return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + }, + operators: { + '=': "[@#{1}='#{3}']", + '!=': "[@#{1}!='#{3}']", + '^=': "[starts-with(@#{1}, '#{3}')]", + '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", + '*=': "[contains(@#{1}, '#{3}')]", + '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", + '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + }, + pseudos: { + 'first-child': '[not(preceding-sibling::*)]', + 'last-child': '[not(following-sibling::*)]', + 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', + 'empty': "[count(*) = 0 and (count(text()) = 0 or translate(text(), ' \t\r\n', '') = '')]", + 'checked': "[@checked]", + 'disabled': "[@disabled]", + 'enabled': "[not(@disabled)]", + 'not': function(m) { + var e = m[6], p = Selector.patterns, + x = Selector.xpath, le, v; + + var exclusion = []; + while (e && le != e && (/\S/).test(e)) { + le = e; + for (var i in p) { + if (m = e.match(p[i])) { + v = Object.isFunction(x[i]) ? x[i](m) : new Template(x[i]).evaluate(m); + exclusion.push("(" + v.substring(1, v.length - 1) + ")"); + e = e.replace(m[0], ''); + break; + } + } + } + return "[not(" + exclusion.join(" and ") + ")]"; + }, + 'nth-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + }, + 'nth-last-child': function(m) { + return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + }, + 'nth-of-type': function(m) { + return Selector.xpath.pseudos.nth("position() ", m); + }, + 'nth-last-of-type': function(m) { + return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + }, + 'first-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + }, + 'last-of-type': function(m) { + m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + }, + 'only-of-type': function(m) { + var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + }, + nth: function(fragment, m) { + var mm, formula = m[6], predicate; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + if (mm = formula.match(/^(\d+)$/)) // digit only + return '[' + fragment + "= " + mm[1] + ']'; + if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (mm[1] == "-") mm[1] = -1; + var a = mm[1] ? Number(mm[1]) : 1; + var b = mm[2] ? Number(mm[2]) : 0; + predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + + "((#{fragment} - #{b}) div #{a} >= 0)]"; + return new Template(predicate).evaluate({ + fragment: fragment, a: a, b: b }); + } + } + } + }, + + criteria: { + tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', + className: 'n = h.className(n, r, "#{1}", c); c = false;', + id: 'n = h.id(n, r, "#{1}", c); c = false;', + attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', + attr: function(m) { + m[3] = (m[5] || m[6]); + return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); + }, + pseudo: function(m) { + if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); + return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); + }, + descendant: 'c = "descendant";', + child: 'c = "child";', + adjacent: 'c = "adjacent";', + laterSibling: 'c = "laterSibling";' + }, + + patterns: { + // combinators must be listed first + // (and descendant needs to be last combinator) + laterSibling: /^\s*~\s*/, + child: /^\s*>\s*/, + adjacent: /^\s*\+\s*/, + descendant: /^\s/, + + // selectors follow + tagName: /^\s*(\*|[\w\-]+)(\b|$)?/, + id: /^#([\w\-\*]+)(\b|$)/, + className: /^\.([\w\-\*]+)(\b|$)/, + pseudo: +/^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/, + attrPresence: /^\[([\w]+)\]/, + attr: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ + }, + + // for Selector.match and Element#match + assertions: { + tagName: function(element, matches) { + return matches[1].toUpperCase() == element.tagName.toUpperCase(); + }, + + className: function(element, matches) { + return Element.hasClassName(element, matches[1]); + }, + + id: function(element, matches) { + return element.id === matches[1]; + }, + + attrPresence: function(element, matches) { + return Element.hasAttribute(element, matches[1]); + }, + + attr: function(element, matches) { + var nodeValue = Element.readAttribute(element, matches[1]); + return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + } + }, + + handlers: { + // UTILITY FUNCTIONS + // joins two collections + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + a.push(node); + return a; + }, + + // marks an array of nodes for counting + mark: function(nodes) { + var _true = Prototype.emptyFunction; + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = _true; + return nodes; + }, + + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node._countedByPrototype = undefined; + return nodes; + }, + + // mark each child node with its position (for nth calls) + // "ofType" flag indicates whether we're indexing for nth-of-type + // rather than nth-child + index: function(parentNode, reverse, ofType) { + parentNode._countedByPrototype = Prototype.emptyFunction; + if (reverse) { + for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { + var node = nodes[i]; + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + } else { + for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) + if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; + } + }, + + // filters out duplicates and extends all nodes + unique: function(nodes) { + if (nodes.length == 0) return nodes; + var results = [], n; + for (var i = 0, l = nodes.length; i < l; i++) + if (!(n = nodes[i])._countedByPrototype) { + n._countedByPrototype = Prototype.emptyFunction; + results.push(Element.extend(n)); + } + return Selector.handlers.unmark(results); + }, + + // COMBINATOR FUNCTIONS + descendant: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName('*')); + return results; + }, + + child: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) { + for (var j = 0, child; child = node.childNodes[j]; j++) + if (child.nodeType == 1 && child.tagName != '!') results.push(child); + } + return results; + }, + + adjacent: function(nodes) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + var next = this.nextElementSibling(node); + if (next) results.push(next); + } + return results; + }, + + laterSibling: function(nodes) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + h.concat(results, Element.nextSiblings(node)); + return results; + }, + + nextElementSibling: function(node) { + while (node = node.nextSibling) + if (node.nodeType == 1) return node; + return null; + }, + + previousElementSibling: function(node) { + while (node = node.previousSibling) + if (node.nodeType == 1) return node; + return null; + }, + + // TOKEN FUNCTIONS + tagName: function(nodes, root, tagName, combinator) { + var uTagName = tagName.toUpperCase(); + var results = [], h = Selector.handlers; + if (nodes) { + if (combinator) { + // fastlane for ordinary descendant combinators + if (combinator == "descendant") { + for (var i = 0, node; node = nodes[i]; i++) + h.concat(results, node.getElementsByTagName(tagName)); + return results; + } else nodes = this[combinator](nodes); + if (tagName == "*") return nodes; + } + for (var i = 0, node; node = nodes[i]; i++) + if (node.tagName.toUpperCase() === uTagName) results.push(node); + return results; + } else return root.getElementsByTagName(tagName); + }, + + id: function(nodes, root, id, combinator) { + var targetNode = $(id), h = Selector.handlers; + if (!targetNode) return []; + if (!nodes && root == document) return [targetNode]; + if (nodes) { + if (combinator) { + if (combinator == 'child') { + for (var i = 0, node; node = nodes[i]; i++) + if (targetNode.parentNode == node) return [targetNode]; + } else if (combinator == 'descendant') { + for (var i = 0, node; node = nodes[i]; i++) + if (Element.descendantOf(targetNode, node)) return [targetNode]; + } else if (combinator == 'adjacent') { + for (var i = 0, node; node = nodes[i]; i++) + if (Selector.handlers.previousElementSibling(targetNode) == node) + return [targetNode]; + } else nodes = h[combinator](nodes); + } + for (var i = 0, node; node = nodes[i]; i++) + if (node == targetNode) return [targetNode]; + return []; + } + return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; + }, + + className: function(nodes, root, className, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + return Selector.handlers.byClassName(nodes, root, className); + }, + + byClassName: function(nodes, root, className) { + if (!nodes) nodes = Selector.handlers.descendant([root]); + var needle = ' ' + className + ' '; + for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { + nodeClassName = node.className; + if (nodeClassName.length == 0) continue; + if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) + results.push(node); + } + return results; + }, + + attrPresence: function(nodes, root, attr, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var results = []; + for (var i = 0, node; node = nodes[i]; i++) + if (Element.hasAttribute(node, attr)) results.push(node); + return results; + }, + + attr: function(nodes, root, attr, value, operator, combinator) { + if (!nodes) nodes = root.getElementsByTagName("*"); + if (nodes && combinator) nodes = this[combinator](nodes); + var handler = Selector.operators[operator], results = []; + for (var i = 0, node; node = nodes[i]; i++) { + var nodeValue = Element.readAttribute(node, attr); + if (nodeValue === null) continue; + if (handler(nodeValue, value)) results.push(node); + } + return results; + }, + + pseudo: function(nodes, name, value, root, combinator) { + if (nodes && combinator) nodes = this[combinator](nodes); + if (!nodes) nodes = root.getElementsByTagName("*"); + return Selector.pseudos[name](nodes, value, root); + } + }, + + pseudos: { + 'first-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.previousElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'last-child': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + if (Selector.handlers.nextElementSibling(node)) continue; + results.push(node); + } + return results; + }, + 'only-child': function(nodes, value, root) { + var h = Selector.handlers; + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) + results.push(node); + return results; + }, + 'nth-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root); + }, + 'nth-last-child': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true); + }, + 'nth-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, false, true); + }, + 'nth-last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, formula, root, true, true); + }, + 'first-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, false, true); + }, + 'last-of-type': function(nodes, formula, root) { + return Selector.pseudos.nth(nodes, "1", root, true, true); + }, + 'only-of-type': function(nodes, formula, root) { + var p = Selector.pseudos; + return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); + }, + + // handles the an+b logic + getIndices: function(a, b, total) { + if (a == 0) return b > 0 ? [b] : []; + return $R(1, total).inject([], function(memo, i) { + if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); + return memo; + }); + }, + + // handles nth(-last)-child, nth(-last)-of-type, and (first|last)-of-type + nth: function(nodes, formula, root, reverse, ofType) { + if (nodes.length == 0) return []; + if (formula == 'even') formula = '2n+0'; + if (formula == 'odd') formula = '2n+1'; + var h = Selector.handlers, results = [], indexed = [], m; + h.mark(nodes); + for (var i = 0, node; node = nodes[i]; i++) { + if (!node.parentNode._countedByPrototype) { + h.index(node.parentNode, reverse, ofType); + indexed.push(node.parentNode); + } + } + if (formula.match(/^\d+$/)) { // just a number + formula = Number(formula); + for (var i = 0, node; node = nodes[i]; i++) + if (node.nodeIndex == formula) results.push(node); + } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b + if (m[1] == "-") m[1] = -1; + var a = m[1] ? Number(m[1]) : 1; + var b = m[2] ? Number(m[2]) : 0; + var indices = Selector.pseudos.getIndices(a, b, nodes.length); + for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { + for (var j = 0; j < l; j++) + if (node.nodeIndex == indices[j]) results.push(node); + } + } + h.unmark(nodes); + h.unmark(indexed); + return results; + }, + + 'empty': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) { + // IE treats comments as element nodes + if (node.tagName == '!' || (node.firstChild && !node.innerHTML.match(/^\s*$/))) continue; + results.push(node); + } + return results; + }, + + 'not': function(nodes, selector, root) { + var h = Selector.handlers, selectorType, m; + var exclusions = new Selector(selector).findElements(root); + h.mark(exclusions); + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node._countedByPrototype) results.push(node); + h.unmark(exclusions); + return results; + }, + + 'enabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (!node.disabled) results.push(node); + return results; + }, + + 'disabled': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.disabled) results.push(node); + return results; + }, + + 'checked': function(nodes, value, root) { + for (var i = 0, results = [], node; node = nodes[i]; i++) + if (node.checked) results.push(node); + return results; + } + }, + + operators: { + '=': function(nv, v) { return nv == v; }, + '!=': function(nv, v) { return nv != v; }, + '^=': function(nv, v) { return nv.startsWith(v); }, + '$=': function(nv, v) { return nv.endsWith(v); }, + '*=': function(nv, v) { return nv.include(v); }, + '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, + '|=': function(nv, v) { return ('-' + nv.toUpperCase() + '-').include('-' + v.toUpperCase() + '-'); } + }, + + split: function(expression) { + var expressions = []; + expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { + expressions.push(m[1].strip()); + }); + return expressions; + }, + + matchElements: function(elements, expression) { + var matches = $$(expression), h = Selector.handlers; + h.mark(matches); + for (var i = 0, results = [], element; element = elements[i]; i++) + if (element._countedByPrototype) results.push(element); + h.unmark(matches); + return results; + }, + + findElement: function(elements, expression, index) { + if (Object.isNumber(expression)) { + index = expression; expression = false; + } + return Selector.matchElements(elements, expression || '*')[index || 0]; + }, + + findChildElements: function(element, expressions) { + expressions = Selector.split(expressions.join(',')); + var results = [], h = Selector.handlers; + for (var i = 0, l = expressions.length, selector; i < l; i++) { + selector = new Selector(expressions[i].strip()); + h.concat(results, selector.findElements(element)); + } + return (l > 1) ? h.unique(results) : results; + } +}); + +if (Prototype.Browser.IE) { + Object.extend(Selector.handlers, { + // IE returns comment nodes on getElementsByTagName("*"). + // Filter them out. + concat: function(a, b) { + for (var i = 0, node; node = b[i]; i++) + if (node.tagName !== "!") a.push(node); + return a; + }, + + // IE improperly serializes _countedByPrototype in (inner|outer)HTML. + unmark: function(nodes) { + for (var i = 0, node; node = nodes[i]; i++) + node.removeAttribute('_countedByPrototype'); + return nodes; + } + }); +} + +function $$() { + return Selector.findChildElements(document, $A(arguments)); +} +var Form = { + reset: function(form) { + $(form).reset(); + return form; + }, + + serializeElements: function(elements, options) { + if (typeof options != 'object') options = { hash: !!options }; + else if (Object.isUndefined(options.hash)) options.hash = true; + var key, value, submitted = false, submit = options.submit; + + var data = elements.inject({ }, function(result, element) { + if (!element.disabled && element.name) { + key = element.name; value = $(element).getValue(); + if (value != null && (element.type != 'submit' || (!submitted && + submit !== false && (!submit || key == submit) && (submitted = true)))) { + if (key in result) { + // a key is already present; construct an array of values + if (!Object.isArray(result[key])) result[key] = [result[key]]; + result[key].push(value); + } + else result[key] = value; + } + } + return result; + }); + + return options.hash ? data : Object.toQueryString(data); + } +}; + + +Form.Methods = { + serialize: function(form, options) { + return Form.serializeElements(Form.getElements(form), options); + }, + + getElements: function(form) { + return $A($(form).getElementsByTagName('*')).inject([], + function(elements, child) { + if (Form.Element.Serializers[child.tagName.toLowerCase()]) + elements.push(Element.extend(child)); + return elements; + } + ); + }, + + getInputs: function(form, typeName, name) { + form = $(form); + var inputs = form.getElementsByTagName('input'); + + if (!typeName && !name) return $A(inputs).map(Element.extend); + + for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { + var input = inputs[i]; + if ((typeName && input.type != typeName) || (name && input.name != name)) + continue; + matchingInputs.push(Element.extend(input)); + } + + return matchingInputs; + }, + + disable: function(form) { + form = $(form); + Form.getElements(form).invoke('disable'); + return form; + }, + + enable: function(form) { + form = $(form); + Form.getElements(form).invoke('enable'); + return form; + }, + + findFirstElement: function(form) { + var elements = $(form).getElements().findAll(function(element) { + return 'hidden' != element.type && !element.disabled; + }); + var firstByIndex = elements.findAll(function(element) { + return element.hasAttribute('tabIndex') && element.tabIndex >= 0; + }).sortBy(function(element) { return element.tabIndex }).first(); + + return firstByIndex ? firstByIndex : elements.find(function(element) { + return ['input', 'select', 'textarea'].include(element.tagName.toLowerCase()); + }); + }, + + focusFirstElement: function(form) { + form = $(form); + form.findFirstElement().activate(); + return form; + }, + + request: function(form, options) { + form = $(form), options = Object.clone(options || { }); + + var params = options.parameters, action = form.readAttribute('action') || ''; + if (action.blank()) action = window.location.href; + options.parameters = form.serialize(true); + + if (params) { + if (Object.isString(params)) params = params.toQueryParams(); + Object.extend(options.parameters, params); + } + + if (form.hasAttribute('method') && !options.method) + options.method = form.method; + + return new Ajax.Request(action, options); + } +}; + +/*--------------------------------------------------------------------------*/ + +Form.Element = { + focus: function(element) { + $(element).focus(); + return element; + }, + + select: function(element) { + $(element).select(); + return element; + } +}; + +Form.Element.Methods = { + serialize: function(element) { + element = $(element); + if (!element.disabled && element.name) { + var value = element.getValue(); + if (value != undefined) { + var pair = { }; + pair[element.name] = value; + return Object.toQueryString(pair); + } + } + return ''; + }, + + getValue: function(element) { + element = $(element); + var method = element.tagName.toLowerCase(); + return Form.Element.Serializers[method](element); + }, + + setValue: function(element, value) { + element = $(element); + var method = element.tagName.toLowerCase(); + Form.Element.Serializers[method](element, value); + return element; + }, + + clear: function(element) { + $(element).value = ''; + return element; + }, + + present: function(element) { + return $(element).value != ''; + }, + + activate: function(element) { + element = $(element); + try { + element.focus(); + if (element.select && (element.tagName.toLowerCase() != 'input' || + !['button', 'reset', 'submit'].include(element.type))) + element.select(); + } catch (e) { } + return element; + }, + + disable: function(element) { + element = $(element); + element.blur(); + element.disabled = true; + return element; + }, + + enable: function(element) { + element = $(element); + element.disabled = false; + return element; + } +}; + +/*--------------------------------------------------------------------------*/ + +var Field = Form.Element; +var $F = Form.Element.Methods.getValue; + +/*--------------------------------------------------------------------------*/ + +Form.Element.Serializers = { + input: function(element, value) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + return Form.Element.Serializers.inputSelector(element, value); + default: + return Form.Element.Serializers.textarea(element, value); + } + }, + + inputSelector: function(element, value) { + if (Object.isUndefined(value)) return element.checked ? element.value : null; + else element.checked = !!value; + }, + + textarea: function(element, value) { + if (Object.isUndefined(value)) return element.value; + else element.value = value; + }, + + select: function(element, index) { + if (Object.isUndefined(index)) + return this[element.type == 'select-one' ? + 'selectOne' : 'selectMany'](element); + else { + var opt, value, single = !Object.isArray(index); + for (var i = 0, length = element.length; i < length; i++) { + opt = element.options[i]; + value = this.optionValue(opt); + if (single) { + if (value == index) { + opt.selected = true; + return; + } + } + else opt.selected = index.include(value); + } + } + }, + + selectOne: function(element) { + var index = element.selectedIndex; + return index >= 0 ? this.optionValue(element.options[index]) : null; + }, + + selectMany: function(element) { + var values, length = element.length; + if (!length) return null; + + for (var i = 0, values = []; i < length; i++) { + var opt = element.options[i]; + if (opt.selected) values.push(this.optionValue(opt)); + } + return values; + }, + + optionValue: function(opt) { + // extend element because hasAttribute may not be native + return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + } +}; + +/*--------------------------------------------------------------------------*/ + +Abstract.TimedObserver = Class.create(PeriodicalExecuter, { + initialize: function($super, element, frequency, callback) { + $super(callback, frequency); + this.element = $(element); + this.lastValue = this.getValue(); + }, + + execute: function() { + var value = this.getValue(); + if (Object.isString(this.lastValue) && Object.isString(value) ? + this.lastValue != value : String(this.lastValue) != String(value)) { + this.callback(this.element, value); + this.lastValue = value; + } + } +}); + +Form.Element.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.Observer = Class.create(Abstract.TimedObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); + +/*--------------------------------------------------------------------------*/ + +Abstract.EventObserver = Class.create({ + initialize: function(element, callback) { + this.element = $(element); + this.callback = callback; + + this.lastValue = this.getValue(); + if (this.element.tagName.toLowerCase() == 'form') + this.registerFormCallbacks(); + else + this.registerCallback(this.element); + }, + + onElementEvent: function() { + var value = this.getValue(); + if (this.lastValue != value) { + this.callback(this.element, value); + this.lastValue = value; + } + }, + + registerFormCallbacks: function() { + Form.getElements(this.element).each(this.registerCallback, this); + }, + + registerCallback: function(element) { + if (element.type) { + switch (element.type.toLowerCase()) { + case 'checkbox': + case 'radio': + Event.observe(element, 'click', this.onElementEvent.bind(this)); + break; + default: + Event.observe(element, 'change', this.onElementEvent.bind(this)); + break; + } + } + } +}); + +Form.Element.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.Element.getValue(this.element); + } +}); + +Form.EventObserver = Class.create(Abstract.EventObserver, { + getValue: function() { + return Form.serialize(this.element); + } +}); +if (!window.Event) var Event = { }; + +Object.extend(Event, { + KEY_BACKSPACE: 8, + KEY_TAB: 9, + KEY_RETURN: 13, + KEY_ESC: 27, + KEY_LEFT: 37, + KEY_UP: 38, + KEY_RIGHT: 39, + KEY_DOWN: 40, + KEY_DELETE: 46, + KEY_HOME: 36, + KEY_END: 35, + KEY_PAGEUP: 33, + KEY_PAGEDOWN: 34, + KEY_INSERT: 45, + + cache: { }, + + relatedTarget: function(event) { + var element; + switch(event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } +}); + +Event.Methods = (function() { + var isButton; + + if (Prototype.Browser.IE) { + var buttonMap = { 0: 1, 1: 4, 2: 2 }; + isButton = function(event, code) { + return event.button == buttonMap[code]; + }; + + } else if (Prototype.Browser.WebKit) { + isButton = function(event, code) { + switch (code) { + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; + default: return false; + } + }; + + } else { + isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } + + return { + isLeftClick: function(event) { return isButton(event, 0) }, + isMiddleClick: function(event) { return isButton(event, 1) }, + isRightClick: function(event) { return isButton(event, 2) }, + + element: function(event) { + var node = Event.extend(event).target; + return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); + }, + + findElement: function(event, expression) { + var element = Event.element(event); + if (!expression) return element; + var elements = [element].concat(element.ancestors()); + return Selector.findElement(elements, expression, 0); + }, + + pointer: function(event) { + return { + x: event.pageX || (event.clientX + + (document.documentElement.scrollLeft || document.body.scrollLeft)), + y: event.pageY || (event.clientY + + (document.documentElement.scrollTop || document.body.scrollTop)) + }; + }, + + pointerX: function(event) { return Event.pointer(event).x }, + pointerY: function(event) { return Event.pointer(event).y }, + + stop: function(event) { + Event.extend(event); + event.preventDefault(); + event.stopPropagation(); + event.stopped = true; + } + }; +})(); + +Event.extend = (function() { + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + m[name] = Event.Methods[name].methodize(); + return m; + }); + + if (Prototype.Browser.IE) { + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return "[object Event]" } + }); + + return function(event) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + Object.extend(event, { + target: event.srcElement, + relatedTarget: Event.relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + return Object.extend(event, methods); + }; + + } else { + Event.prototype = Event.prototype || document.createEvent("HTMLEvents").__proto__; + Object.extend(Event.prototype, methods); + return Prototype.K; + } +})(); + +Object.extend(Event, (function() { + var cache = Event.cache; + + function getEventID(element) { + if (element._prototypeEventID) return element._prototypeEventID[0]; + arguments.callee.id = arguments.callee.id || 1; + return element._prototypeEventID = [++arguments.callee.id]; + } + + function getDOMEventName(eventName) { + if (eventName && eventName.include(':')) return "dataavailable"; + return eventName; + } + + function getCacheForID(id) { + return cache[id] = cache[id] || { }; + } + + function getWrappersForEventName(id, eventName) { + var c = getCacheForID(id); + return c[eventName] = c[eventName] || []; + } + + function createWrapper(element, eventName, handler) { + var id = getEventID(element); + var c = getWrappersForEventName(id, eventName); + if (c.pluck("handler").include(handler)) return false; + + var wrapper = function(event) { + if (!Event || !Event.extend || + (event.eventName && event.eventName != eventName)) + return false; + + Event.extend(event); + handler.call(element, event); + }; + + wrapper.handler = handler; + c.push(wrapper); + return wrapper; + } + + function findWrapper(id, eventName, handler) { + var c = getWrappersForEventName(id, eventName); + return c.find(function(wrapper) { return wrapper.handler == handler }); + } + + function destroyWrapper(id, eventName, handler) { + var c = getCacheForID(id); + if (!c[eventName]) return false; + c[eventName] = c[eventName].without(findWrapper(id, eventName, handler)); + } + + function destroyCache() { + for (var id in cache) + for (var eventName in cache[id]) + cache[id][eventName] = null; + } + + if (window.attachEvent) { + window.attachEvent("onunload", destroyCache); + } + + return { + observe: function(element, eventName, handler) { + element = $(element); + var name = getDOMEventName(eventName); + + var wrapper = createWrapper(element, eventName, handler); + if (!wrapper) return element; + + if (element.addEventListener) { + element.addEventListener(name, wrapper, false); + } else { + element.attachEvent("on" + name, wrapper); + } + + return element; + }, + + stopObserving: function(element, eventName, handler) { + element = $(element); + var id = getEventID(element), name = getDOMEventName(eventName); + + if (!handler && eventName) { + getWrappersForEventName(id, eventName).each(function(wrapper) { + element.stopObserving(eventName, wrapper.handler); + }); + return element; + + } else if (!eventName) { + Object.keys(getCacheForID(id)).each(function(eventName) { + element.stopObserving(eventName); + }); + return element; + } + + var wrapper = findWrapper(id, eventName, handler); + if (!wrapper) return element; + + if (element.removeEventListener) { + element.removeEventListener(name, wrapper, false); + } else { + element.detachEvent("on" + name, wrapper); + } + + destroyWrapper(id, eventName, handler); + + return element; + }, + + fire: function(element, eventName, memo) { + element = $(element); + if (element == document && document.createEvent && !element.dispatchEvent) + element = document.documentElement; + + var event; + if (document.createEvent) { + event = document.createEvent("HTMLEvents"); + event.initEvent("dataavailable", true, true); + } else { + event = document.createEventObject(); + event.eventType = "ondataavailable"; + } + + event.eventName = eventName; + event.memo = memo || { }; + + if (document.createEvent) { + element.dispatchEvent(event); + } else { + element.fireEvent(event.eventType, event); + } + + return Event.extend(event); + } + }; +})()); + +Object.extend(Event, Event.Methods); + +Element.addMethods({ + fire: Event.fire, + observe: Event.observe, + stopObserving: Event.stopObserving +}); + +Object.extend(document, { + fire: Element.Methods.fire.methodize(), + observe: Element.Methods.observe.methodize(), + stopObserving: Element.Methods.stopObserving.methodize(), + loaded: false +}); + +(function() { + /* Support for the DOMContentLoaded event is based on work by Dan Webb, + Matthias Miller, Dean Edwards and John Resig. */ + + var timer; + + function fireContentLoadedEvent() { + if (document.loaded) return; + if (timer) window.clearInterval(timer); + document.fire("dom:loaded"); + document.loaded = true; + } + + if (document.addEventListener) { + if (Prototype.Browser.WebKit) { + timer = window.setInterval(function() { + if (/loaded|complete/.test(document.readyState)) + fireContentLoadedEvent(); + }, 0); + + Event.observe(window, "load", fireContentLoadedEvent); + + } else { + document.addEventListener("DOMContentLoaded", + fireContentLoadedEvent, false); + } + + } else { + document.write(" + + + + + +
+
+ + + + + +
+Region Stats +
+
+
+ + +
+Sessions +
+
+
+ + +
+Log File +
+
+
+ + +
+
+
+ + diff --git a/bin/data/updater.js b/bin/data/updater.js new file mode 100644 index 0000000000..20201f8486 --- /dev/null +++ b/bin/data/updater.js @@ -0,0 +1,20 @@ +var updater = Class.create({ + initialize: function(divToUpdate, interval, file) { + this.divToUpdate = divToUpdate; + this.interval = interval; + this.file = file; + new PeriodicalExecuter(this.getUpdate.bind(this), this.interval); + }, + + getUpdate: function() { + var oOptions = { + method: "POST", + parameters: "intervalPeriod="+this.interval, + asynchronous: true, + onComplete: function (oXHR, Json) { + $(this.divToUpdate).innerHTML = oXHR.responseText; + } + }; + var oRequest = new Ajax.Updater(this.divToUpdate, this.file, oOptions); + } +}); \ No newline at end of file diff --git a/bin/defaultstripe.png b/bin/defaultstripe.png new file mode 100644 index 0000000000..9db19c84be Binary files /dev/null and b/bin/defaultstripe.png differ diff --git a/bin/excuses b/bin/excuses new file mode 100644 index 0000000000..d831e06318 --- /dev/null +++ b/bin/excuses @@ -0,0 +1,460 @@ +clock speed +solar flares +electromagnetic radiation from satellite debris +static from nylon underwear +static from plastic slide rules +global warming +poor power conditioning +static buildup +doppler effect +hardware stress fractures +magnetic interference from money/credit cards +dry joints on cable plug +we're waiting for [the phone company] to fix that line +sounds like a Windows problem, try calling Microsoft support +temporary routing anomaly +somebody was calculating pi on the server +fat electrons in the lines +excess surge protection +floating point processor overflow +divide-by-zero error +POSIX compliance problem +monitor resolution too high +improperly oriented keyboard +network packets travelling uphill (use a carrier pigeon) +Decreasing electron flux +first Saturday after first full moon in Winter +radiosity depletion +CPU radiator broken +It works the way the Wang did, what's the problem +positron router malfunction +cellular telephone interference +techtonic stress +piezo-electric interference +(l)user error +working as designed +dynamic software linking table corrupted +heavy gravity fluctuation, move computer to floor rapidly +secretary plugged hairdryer into UPS +terrorist activities +not enough memory, go get system upgrade +interrupt configuration error +spaghetti cable cause packet failure +boss forgot system password +bank holiday - system operating credits not recharged +virus attack, luser responsible +waste water tank overflowed onto computer +Complete Transient Lockout +bad ether in the cables +Bogon emissions +Change in Earth's rotational speed +Cosmic ray particles crashed through the hard disk platter +Smell from unhygienic janitorial staff wrecked the tape heads +Little hamster in running wheel had coronary; waiting for replacement to be Fedexed from Wyoming +Evil dogs hypnotised the night shift +Plumber mistook routing panel for decorative wall fixture +Electricians made popcorn in the power supply +Groundskeepers stole the root password +high pressure system failure +failed trials, system needs redesigned +system has been recalled +not approved by the FCC +need to wrap system in aluminum foil to fix problem +not properly grounded, please bury computer +CPU needs recalibration +system needs to be rebooted +bit bucket overflow +descramble code needed from software company +only available on a need to know basis +knot in cables caused data stream to become twisted and kinked +nesting roaches shorted out the ether cable +The file system is full of it +Satan did it +Daemons did it +You're out of memory +There isn't any problem +Unoptimized hard drive +Typo in the code +Yes, yes, its called a design limitation +Look, buddy: Windows 3.1 IS A General Protection Fault. +That's a great computer you have there; have you considered how it would work as a BSD machine? +Please excuse me, I have to circuit an AC line through my head to get this database working. +Yeah, yo mama dresses you funny and you need a mouse to delete files. +Support staff hung over, send aspirin and come back LATER. +Someone is standing on the ethernet cable, causing a kink in the cable +Windows 95 undocumented "feature" +Runt packets +Password is too complex to decrypt +Boss' kid fucked up the machine +Electromagnetic energy loss +Budget cuts +Mouse chewed through power cable +Stale file handle (next time use Tupperware(tm)!) +Feature not yet implemented +Internet outage +Pentium FDIV bug +Vendor no longer supports the product +Small animal kamikaze attack on power supplies +The vendor put the bug there. +SIMM crosstalk. +IRQ dropout +Collapsed Backbone +Power company testing new voltage spike (creation) equipment +operators on strike due to broken coffee machine +backup tape overwritten with copy of system manager's favourite CD +UPS interrupted the server's power +The electrician didn't know what the yellow cable was so he yanked the ethernet out. +The keyboard isn't plugged in +The air conditioning water supply pipe ruptured over the machine room +The electricity substation in the car park blew up. +The rolling stones concert down the road caused a brown out +The salesman drove over the CPU board. +The monitor is plugged into the serial port +Root nameservers are out of sync +electro-magnetic pulses from French above ground nuke testing. +your keyboard's space bar is generating spurious keycodes. +the real ttys became pseudo ttys and vice-versa. +the printer thinks its a router. +the router thinks its a printer. +evil hackers from Serbia. +we just switched to FDDI. +halon system went off and killed the operators. +because Bill Gates is a Jehovah's witness and so nothing can work on St. Swithin's day. +user to computer ratio too high. +user to computer ration too low. +we just switched to Sprint. +it has Intel Inside +Sticky bits on disk. +Power Company having EMP problems with their reactor +The ring needs another token +new management +telnet: Unable to connect to remote host: Connection refused +SCSI Chain overterminated +It's not plugged in. +because of network lag due to too many people playing deathmatch +You put the disk in upside down. +Daemons loose in system. +User was distributing pornography on server; system seized by FBI. +BNC (brain not connected) +UBNC (user brain not connected) +LBNC (luser brain not connected) +disks spinning backwards - toggle the hemisphere jumper. +new guy cross-connected phone lines with ac power bus. +had to use hammer to free stuck disk drive heads. +Too few computrons available. +Flat tire on station wagon with tapes. ("Never underestimate the bandwidth of a station wagon full of tapes hurling down the highway" Andrew S. Tannenbaum) +Communications satellite used by the military for star wars. +Party-bug in the Aloha protocol. +Insert coin for new game +Dew on the telephone lines. +Arcserve crashed the server again. +Some one needed the powerstrip, so they pulled the switch plug. +My pony-tail hit the on/off switch on the power strip. +Big to little endian conversion error +You can tune a file system, but you can't tune a fish (from most tunefs man pages) +Dumb terminal +Zombie processes haunting the computer +Incorrect time synchronization +Defunct processes +Stubborn processes +non-redundant fan failure +monitor VLF leakage +bugs in the RAID +no "any" key on keyboard +root rot +Backbone Scoliosis +/pub/lunch +excessive collisions & not enough packet ambulances +le0: no carrier: transceiver cable problem? +broadcast packets on wrong frequency +popper unable to process jumbo kernel +NOTICE: alloc: /dev/null: filesystem full +pseudo-user on a pseudo-terminal +Recursive traversal of loopback mount points +Backbone adjustment +OS swapped to disk +vapors from evaporating sticky-note adhesives +sticktion +short leg on process table +multicasts on broken packets +ether leak +Atilla the Hub +endothermal recalibration +filesystem not big enough for Jumbo Kernel Patch +loop found in loop in redundant loopback +system consumed all the paper for paging +permission denied +Reformatting Page. Wait... +..disk or the processor is on fire. +SCSI's too wide. +Proprietary Information. +Just type 'mv * /dev/null'. +runaway cat on system. +Did you pay the new Support Fee? +We only support a 1200 bps connection. +We only support a 28000 bps connection. +Me no internet, only janitor, me just wax floors. +I'm sorry a pentium won't do, you need an SGI to connect with us. +Post-it Note Sludge leaked into the monitor. +the curls in your keyboard cord are losing electricity. +The monitor needs another box of pixels. +RPC_PMAP_FAILURE +kernel panic: write-only-memory (/dev/wom0) capacity exceeded. +Write-only-memory subsystem too slow for this machine. Contact your local dealer. +Just pick up the phone and give modem connect sounds. "Well you said we should get more lines so we don't have voice lines." +Quantum dynamics are affecting the transistors +Police are examining all internet packets in the search for a narco-net-trafficker +We are currently trying a new concept of using a live mouse. Unfortunately, one has yet to survive being hooked up to the computer.....please bear with us. +Your mail is being routed through Germany ... and they're censoring us. +Only people with names beginning with 'A' are getting mail this week (a la Microsoft) +We didn't pay the Internet bill and it's been cut off. +Lightning strikes. +Of course it doesn't work. We've performed a software upgrade. +Change your language to Finnish. +Fluorescent lights are generating negative ions. If turning them off doesn't work, take them out and put tin foil on the ends. +High nuclear activity in your area. +What office are you in? Oh, that one. Did you know that your building was built over the universities first nuclear research site? And wow, aren't you the lucky one, your office is right over where the core is buried! +The MGs ran out of gas. +The UPS doesn't have a battery backup. +Recursivity. Call back if it happens again. +Someone thought The Big Red Button was a light switch. +The mainframe needs to rest. It's getting old, you know. +I'm not sure. Try calling the Internet's head office -- it's in the book. +The lines are all busy (busied out, that is -- why let them in to begin with?). +Jan 9 16:41:27 huber su: 'su root' succeeded for .... on /dev/pts/1 +It's those computer people in X {city of world}. They keep stuffing things up. +A star wars satellite accidently blew up the WAN. +Fatal error right in front of screen +That function is not currently supported, but Bill Gates assures us it will be featured in the next upgrade. +wrong polarity of neutron flow +Lusers learning curve appears to be fractal +We had to turn off that service to comply with the CDA Bill. +Ionization from the air-conditioning +TCP/IP UDP alarm threshold is set too low. +Someone is broadcasting pygmy packets and the router doesn't know how to deal with them. +The new frame relay network hasn't bedded down the software loop transmitter yet. +Fanout dropping voltage too much, try cutting some of those little traces +Plate voltage too low on demodulator tube +You did wha... oh _dear_.... +CPU needs bearings repacked +Too many little pins on CPU confusing it, bend back and forth until 10-20% are neatly removed. Do _not_ leave metal bits visible! +_Rosin_ core solder? But... +Software uses US measurements, but the OS is in metric... +The computer fleetly, mouse and all. +Your cat tried to eat the mouse. +The Borg tried to assimilate your system. Resistance is futile. +It must have been the lightning storm we had (yesterday) (last week) (last month) +Due to Federal Budget problems we have been forced to cut back on the number of users able to access the system at one time. (namely none allowed....) +Too much radiation coming from the soil. +Unfortunately we have run out of bits/bytes/whatever. Don't worry, the next supply will be coming next week. +Program load too heavy for processor to lift. +Processes running slowly due to weak power supply +Our ISP is having {switching,routing,SMDS,frame relay} problems +We've run out of licenses +Interference from lunar radiation +Standing room only on the bus. +You need to install an RTFM interface. +That would be because the software doesn't work. +That's easy to fix, but I can't be bothered. +Someone's tie is caught in the printer, and if anything else gets printed, he'll be in it too. +We're upgrading /dev/null +The Usenet news is out of date +Our POP server was kidnapped by a weasel. +It's stuck in the Web. +Your modem doesn't speak English. +The mouse escaped. +All of the packets are empty. +The UPS is on strike. +Neutrino overload on the nameserver +Melting hard drives +Someone has messed up the kernel pointers +The kernel license has expired +Netscape has crashed +The cord jumped over and hit the power switch. +It was OK before you touched it. +Bit rot +U.S. Postal Service +Your Flux Capacitor has gone bad. +The Dilithium Crystals need to be rotated. +The static electricity routing is acting up... +Traceroute says that there is a routing problem in the backbone. It's not our problem. +The co-locator cannot verify the frame-relay gateway to the ISDN server. +High altitude condensation from U.S.A.F prototype aircraft has contaminated the primary subnet mask. Turn off your computer for 9 days to avoid damaging it. +Lawn mower blade in your fan need sharpening +Electrons on a bender +Telecommunications is upgrading. +Telecommunications is downgrading. +Telecommunications is downshifting. +Hard drive sleeping. Let it wake up on it's own... +Interference between the keyboard and the chair. +The CPU has shifted, and become decentralized. +Due to the CDA, we no longer have a root account. +We ran out of dial tone and we're and waiting for the phone company to deliver another bottle. +You must've hit the wrong any key. +PCMCIA slave driver +The Token fell out of the ring. Call us when you find it. +The hardware bus needs a new token. +Too many interrupts +Not enough interrupts +The data on your hard drive is out of balance. +Digital Manipulator exceeding velocity parameters +appears to be a Slow/Narrow SCSI-0 Interface problem +microelectronic Riemannian curved-space fault in write-only file system +fractal radiation jamming the backbone +routing problems on the neural net +IRQ-problems with the Un-Interruptible-Power-Supply +CPU-angle has to be adjusted because of vibrations coming from the nearby road +emissions from GSM-phones +CD-ROM server needs recalibration +firewall needs cooling +asynchronous inode failure +transient bus protocol violation +incompatible bit-registration operators +your process is not ISO 9000 compliant +You need to upgrade your VESA local bus to a MasterCard local bus. +The recent proliferation of Nuclear Testing +Elves on strike. (Why do they call EMAG Elf Magic) +Internet exceeded Luser level, please wait until a luser logs off before attempting to log back on. +Your EMAIL is now being delivered by the USPS. +Your computer hasn't been returning all the bits it gets from the Internet. +You've been infected by the Telescoping Hubble virus. +Scheduled global CPU outage +Your Pentium has a heating problem - try cooling it with ice cold water.(Do not turn off your computer, you do not want to cool down the Pentium Chip while he isn't working, do you?) +Your processor has processed too many instructions. Turn it off immediately, do not type any commands!! +Your packets were eaten by the terminator +Your processor does not develop enough heat. +We need a licensed electrician to replace the light bulbs in the computer room. +The POP server is out of Coke +Fiber optics caused gas main leak +Server depressed, needs Prozac +quantum decoherence +those damn raccoons! +suboptimal routing experience +A plumber is needed, the network drain is clogged +50% of the manual is in .pdf readme files +the AA battery in the wallclock sends magnetic interference +the xy axis in the trackball is coordinated with the summer solstice +the butane lighter causes the pincushioning +old inkjet cartridges emanate barium-based fumes +manager in the cable duct +We'll fix that in the next (upgrade, update, patch release, service pack). +HTTPD Error 666 : BOFH was here +HTTPD Error 4004 : very old Intel cpu - insufficient processing power +The ATM board has run out of 10 pound notes. We are having a whip round to refill it, care to contribute ? +Network failure - call NBC +Having to manually track the satellite. +Your/our computer(s) had suffered a memory leak, and we are waiting for them to be topped up. +The rubber band broke +We're on Token Ring, and it looks like the token got loose. +Stray Alpha Particles from memory packaging caused Hard Memory Error on Server. +paradigm shift...without a clutch +PEBKAC (Problem Exists Between Keyboard And Chair) +The cables are not the same length. +Second-system effect. +Chewing gum on /dev/sd3c +Boredom in the Kernel. +the daemons! the daemons! the terrible daemons! +I'd love to help you -- it's just that the Boss won't let me near the computer. +struck by the Good Times virus +YOU HAVE AN I/O ERROR -> Incompetent Operator error +Your parity check is overdrawn and you're out of cache. +Communist revolutionaries taking over the server room and demanding all the computers in the building or they shoot the sysadmin. Poor misguided fools. +Plasma conduit breach +Out of cards on drive D: +Sand fleas eating the Internet cables +parallel processors running perpendicular today +ATM cell has no roaming feature turned on, notebooks can't connect +Webmasters kidnapped by evil cult. +Failure to adjust for daylight savings time. +Virus transmitted from computer to sysadmins. +Virus due to computers having unsafe sex. +Incorrectly configured static routes on the corerouters. +Forced to support NT servers; sysadmins quit. +Suspicious pointer corrupted virtual machine +It's the InterNIC's fault. +Root name servers corrupted. +Budget cuts forced us to sell all the power cords for the servers. +Someone hooked the twisted pair wires into the answering machine. +Operators killed by year 2000 bug bite. +We've picked COBOL as the language of choice. +Operators killed when huge stack of backup tapes fell over. +Robotic tape changer mistook operator's tie for a backup tape. +Someone was smoking in the computer room and set off the halon systems. +Your processor has taken a ride to Heaven's Gate on the UFO behind Hale-Bopp's comet. +it's an ID-10-T error +Dyslexics retyping hosts file on servers +The Internet is being scanned for viruses. +Your computer's union contract is set to expire at midnight. +Bad user karma. +/dev/clue was linked to /dev/null +Increased sunspot activity. +We already sent around a notice about that. +It's union rules. There's nothing we can do about it. Sorry. +Interference from the Van Allen Belt. +Jupiter is aligned with Mars. +Redundant ACLs. +Mail server hit by UniSpammer. +T-1's congested due to porn traffic to the news server. +Data for intranet got routed through the extranet and landed on the internet. +We are a 100% Microsoft Shop. +We are Microsoft. What you are experiencing is not a problem; it is an undocumented feature. +Sales staff sold a product we don't offer. +Secretary sent chain letter to all 5000 employees. +Sysadmin didn't hear pager go off due to loud music from bar-room speakers. +Sysadmin accidentally destroyed pager with a large hammer. +Sysadmins unavailable because they are in a meeting talking about why they are unavailable so much. +Bad cafeteria food landed all the sysadmins in the hospital. +Route flapping at the NAP. +Computers under water due to SYN flooding. +The vulcan-death-grip ping has been applied. +Electrical conduits in machine room are melting. +Traffic jam on the Information Superhighway. +Radial Telemetry Infiltration +Cow-tippers tipped a cow onto the server. +tachyon emissions overloading the system +Maintenance window broken +We're out of slots on the server +Computer room being moved. Our systems are down for the weekend. +Sysadmins busy fighting SPAM. +Repeated reboots of the system failed to solve problem +Feature was not beta tested +Domain controller not responding +Someone else stole your IP address, call the Internet detectives! +It's not RFC-822 compliant. +operation failed because: there is no message for this error (#1014) +stop bit received +internet is needed to catch the etherbunny +network down, IP packets delivered via UPS +Firmware update in the coffee machine +Temporal anomaly +Mouse has out-of-cheese-error +Borg implants are failing +Borg nanites have infested the server +error: one bad user found in front of screen +Please state the nature of the technical emergency +Internet shut down due to maintenance +Daemon escaped from pentagram +crop circles in the corn shell +sticky bit has come loose +Hot Java has gone cold +Cache miss - please take better aim next time +Hash table has woodworm +Trojan horse ran out of hay +Zombie processes detected, machine is haunted. +overflow error in /dev/null +Browser's cookie is corrupted -- someone's been nibbling on it. +Mailer-daemon is busy burning your message in hell. +According to Microsoft, it's by design +vi needs to be upgraded to vii +greenpeace free'd the mallocs +Terrorists crashed an airplane into the server room, have to remove /bin/laden. (rm -rf /bin/laden) +astropneumatic oscillations in the water-cooling +Somebody ran the operating system through a spelling checker. +Rhythmic variations in the voltage reaching the power supply. +Keyboard Actuator Failure. Order and Replace. +Packet held up at customs. +Propagation delay. +High line impedance. +Someone set us up the bomb. +Power surges on the Underground. +I'm saying stuff based upon the The Bastard Operator From Hell Stories written by Simon Paul Travaglia. Datamation magazine owns the Electronic Rights to the orgional works of the BOFH. \ No newline at end of file diff --git a/bin/http_404.html.example b/bin/http_404.html.example new file mode 100644 index 0000000000..a3216f33c1 --- /dev/null +++ b/bin/http_404.html.example @@ -0,0 +1 @@ +404 Page not found

Ooops!

The page you requested has been obsconded with by knomes. Find hippos quick!

\ No newline at end of file diff --git a/bin/http_500.html.example b/bin/http_500.html.example new file mode 100644 index 0000000000..6b5402d9d9 --- /dev/null +++ b/bin/http_500.html.example @@ -0,0 +1 @@ +500 Internal Server Error

Ooops!

The server you requested is overun by knomes! Find hippos quick!

\ No newline at end of file diff --git a/bin/http_loginform.html.example b/bin/http_loginform.html.example new file mode 100644 index 0000000000..54f8f099b1 --- /dev/null +++ b/bin/http_loginform.html.example @@ -0,0 +1,61 @@ + + + + + + +OpenSim Login +
+
+ +
+ +
[$errors]
+
+First Name: + +
+
+Last Name: + +
+
+Password: + + + + + + + + +
+
+ + + + + + + + +
+ +
+ + + + + +
[$channel] | [$version]=[$lang]
+
+ +
+ + + diff --git a/bin/inventory/AnimationsLibrary/AnimationsLibraryFolders.xml b/bin/inventory/AnimationsLibrary/AnimationsLibraryFolders.xml new file mode 100644 index 0000000000..e064c5b337 --- /dev/null +++ b/bin/inventory/AnimationsLibrary/AnimationsLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/AnimationsLibrary/AnimationsLibraryItems.xml b/bin/inventory/AnimationsLibrary/AnimationsLibraryItems.xml new file mode 100644 index 0000000000..255bb3ee1a --- /dev/null +++ b/bin/inventory/AnimationsLibrary/AnimationsLibraryItems.xml @@ -0,0 +1,134 @@ + + + +
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + +
+ + + +
diff --git a/bin/inventory/BodyPartsLibrary/BodyPartsLibraryFolders.xml b/bin/inventory/BodyPartsLibrary/BodyPartsLibraryFolders.xml new file mode 100644 index 0000000000..213a4ab93e --- /dev/null +++ b/bin/inventory/BodyPartsLibrary/BodyPartsLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/BodyPartsLibrary/BodyPartsLibraryItems.xml b/bin/inventory/BodyPartsLibrary/BodyPartsLibraryItems.xml new file mode 100644 index 0000000000..9faafc24e7 --- /dev/null +++ b/bin/inventory/BodyPartsLibrary/BodyPartsLibraryItems.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + diff --git a/bin/inventory/ClothingLibrary/ClothingLibraryFolders.xml b/bin/inventory/ClothingLibrary/ClothingLibraryFolders.xml new file mode 100644 index 0000000000..488c395c13 --- /dev/null +++ b/bin/inventory/ClothingLibrary/ClothingLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/ClothingLibrary/ClothingLibraryItems.xml b/bin/inventory/ClothingLibrary/ClothingLibraryItems.xml new file mode 100644 index 0000000000..b2fc0055e1 --- /dev/null +++ b/bin/inventory/ClothingLibrary/ClothingLibraryItems.xml @@ -0,0 +1,51 @@ + + + + + + + + diff --git a/bin/inventory/GesturesLibrary/GesturesLibraryFolders.xml b/bin/inventory/GesturesLibrary/GesturesLibraryFolders.xml new file mode 100644 index 0000000000..2ae7cecf7d --- /dev/null +++ b/bin/inventory/GesturesLibrary/GesturesLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/GesturesLibrary/GesturesLibraryItems.xml b/bin/inventory/GesturesLibrary/GesturesLibraryItems.xml new file mode 100644 index 0000000000..8443eafb74 --- /dev/null +++ b/bin/inventory/GesturesLibrary/GesturesLibraryItems.xml @@ -0,0 +1,174 @@ + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
diff --git a/bin/inventory/LandmarksLibrary/LandmarksLibraryFolders.xml b/bin/inventory/LandmarksLibrary/LandmarksLibraryFolders.xml new file mode 100644 index 0000000000..eb1b5954ff --- /dev/null +++ b/bin/inventory/LandmarksLibrary/LandmarksLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/LandmarksLibrary/LandmarksLibraryItems.xml b/bin/inventory/LandmarksLibrary/LandmarksLibraryItems.xml new file mode 100644 index 0000000000..907c83cd2a --- /dev/null +++ b/bin/inventory/LandmarksLibrary/LandmarksLibraryItems.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/bin/inventory/Libraries.xml b/bin/inventory/Libraries.xml new file mode 100644 index 0000000000..d9592a4a56 --- /dev/null +++ b/bin/inventory/Libraries.xml @@ -0,0 +1,86 @@ + +
+ + +
+ + + + + + + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
diff --git a/bin/inventory/NotecardsLibrary/NotecardsLibraryFolders.xml b/bin/inventory/NotecardsLibrary/NotecardsLibraryFolders.xml new file mode 100644 index 0000000000..fb5dc8942f --- /dev/null +++ b/bin/inventory/NotecardsLibrary/NotecardsLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/NotecardsLibrary/NotecardsLibraryItems.xml b/bin/inventory/NotecardsLibrary/NotecardsLibraryItems.xml new file mode 100644 index 0000000000..53dc2536cb --- /dev/null +++ b/bin/inventory/NotecardsLibrary/NotecardsLibraryItems.xml @@ -0,0 +1,47 @@ + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
diff --git a/bin/inventory/ObjectsLibrary/ObjectsLibraryFolders.xml b/bin/inventory/ObjectsLibrary/ObjectsLibraryFolders.xml new file mode 100644 index 0000000000..4136e5c424 --- /dev/null +++ b/bin/inventory/ObjectsLibrary/ObjectsLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/ObjectsLibrary/ObjectsLibraryItems.xml b/bin/inventory/ObjectsLibrary/ObjectsLibraryItems.xml new file mode 100644 index 0000000000..907c83cd2a --- /dev/null +++ b/bin/inventory/ObjectsLibrary/ObjectsLibraryItems.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/bin/inventory/OpenSimLibrary/OpenSimLibrary.xml b/bin/inventory/OpenSimLibrary/OpenSimLibrary.xml new file mode 100644 index 0000000000..bef59d8add --- /dev/null +++ b/bin/inventory/OpenSimLibrary/OpenSimLibrary.xml @@ -0,0 +1,16 @@ + + + + + + diff --git a/bin/inventory/OpenSimLibrary/OpenSimLibraryFolders.xml b/bin/inventory/OpenSimLibrary/OpenSimLibraryFolders.xml new file mode 100644 index 0000000000..41fce01cdd --- /dev/null +++ b/bin/inventory/OpenSimLibrary/OpenSimLibraryFolders.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/bin/inventory/PhotosLibrary/PhotosLibraryFolders.xml b/bin/inventory/PhotosLibrary/PhotosLibraryFolders.xml new file mode 100644 index 0000000000..5e42c6dfef --- /dev/null +++ b/bin/inventory/PhotosLibrary/PhotosLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/PhotosLibrary/PhotosLibraryItems.xml b/bin/inventory/PhotosLibrary/PhotosLibraryItems.xml new file mode 100644 index 0000000000..907c83cd2a --- /dev/null +++ b/bin/inventory/PhotosLibrary/PhotosLibraryItems.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/bin/inventory/README.txt b/bin/inventory/README.txt new file mode 100644 index 0000000000..ecd81d13c0 --- /dev/null +++ b/bin/inventory/README.txt @@ -0,0 +1,21 @@ +README + +Folders and items which will appear in the standard common library for all +avatars can be configured here. The root folder (currently called OpenSim +Library) is hardcoded, but you can add your own configuration of folders and +items directly beneath this, in addition to (or instead of) the contents of the +default OpenSim library. + +To add a new library, edit Libraries.xml. The entry in here needs to point to +two further xml files, one which details your library inventory folders and another +which details your library inventory items. Each inventory item will need to be +associated with an asset. Assets are configured separately in the bin/assets +directory. + +If you are running in grid mode, any library you add must be present in both +your grid servers installation and in +every region installation, otherwise library items will fail in the regions +where the inventory configuration is not present. The reasons for this are historical +and will probably be lifted in a future revision. + +Files in the attic directory are currently unused. diff --git a/bin/inventory/ScriptsLibrary/ScriptsLibraryFolders.xml b/bin/inventory/ScriptsLibrary/ScriptsLibraryFolders.xml new file mode 100644 index 0000000000..a9f2a7af0f --- /dev/null +++ b/bin/inventory/ScriptsLibrary/ScriptsLibraryFolders.xml @@ -0,0 +1,62 @@ + + + +
+ + + + +
+ + + +
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
+ + + + +
+
diff --git a/bin/inventory/ScriptsLibrary/ScriptsLibraryItems.xml b/bin/inventory/ScriptsLibrary/ScriptsLibraryItems.xml new file mode 100644 index 0000000000..42ad006219 --- /dev/null +++ b/bin/inventory/ScriptsLibrary/ScriptsLibraryItems.xml @@ -0,0 +1,436 @@ + + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+
diff --git a/bin/inventory/SoundsLibrary/SoundsLibraryFolders.xml b/bin/inventory/SoundsLibrary/SoundsLibraryFolders.xml new file mode 100644 index 0000000000..3404667bc3 --- /dev/null +++ b/bin/inventory/SoundsLibrary/SoundsLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/SoundsLibrary/SoundsLibraryItems.xml b/bin/inventory/SoundsLibrary/SoundsLibraryItems.xml new file mode 100644 index 0000000000..907c83cd2a --- /dev/null +++ b/bin/inventory/SoundsLibrary/SoundsLibraryItems.xml @@ -0,0 +1,27 @@ + + + + + + + diff --git a/bin/inventory/TexturesLibrary/TexturesLibraryFolders.xml b/bin/inventory/TexturesLibrary/TexturesLibraryFolders.xml new file mode 100644 index 0000000000..33b4f5f4fb --- /dev/null +++ b/bin/inventory/TexturesLibrary/TexturesLibraryFolders.xml @@ -0,0 +1,17 @@ + + + +
+ + + + +
+
diff --git a/bin/inventory/TexturesLibrary/TexturesLibraryItems.xml b/bin/inventory/TexturesLibrary/TexturesLibraryItems.xml new file mode 100644 index 0000000000..adab5d81b2 --- /dev/null +++ b/bin/inventory/TexturesLibrary/TexturesLibraryItems.xml @@ -0,0 +1,586 @@ + + + + + + +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + +
+
diff --git a/bin/lib/NET/Mono.Security.dll b/bin/lib/NET/Mono.Security.dll new file mode 100644 index 0000000000..6accde7911 Binary files /dev/null and b/bin/lib/NET/Mono.Security.dll differ diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll new file mode 100755 index 0000000000..6d006bf593 Binary files /dev/null and b/bin/lib32/BulletSim.dll differ diff --git a/bin/lib32/libBulletSim.dylib b/bin/lib32/libBulletSim.dylib new file mode 100755 index 0000000000..b7a42e374b Binary files /dev/null and b/bin/lib32/libBulletSim.dylib differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so new file mode 100755 index 0000000000..ec29f58f61 Binary files /dev/null and b/bin/lib32/libBulletSim.so differ diff --git a/bin/lib32/libode.so b/bin/lib32/libode.so new file mode 100644 index 0000000000..6bb85fb72a Binary files /dev/null and b/bin/lib32/libode.so differ diff --git a/bin/lib32/libopenjpeg-dotnet.so b/bin/lib32/libopenjpeg-dotnet.so new file mode 100755 index 0000000000..193eca4b34 Binary files /dev/null and b/bin/lib32/libopenjpeg-dotnet.so differ diff --git a/bin/lib32/libsqlite3.txt b/bin/lib32/libsqlite3.txt new file mode 100644 index 0000000000..8ef66bdf06 --- /dev/null +++ b/bin/lib32/libsqlite3.txt @@ -0,0 +1 @@ +libsqlite version: 3.7.5 diff --git a/bin/lib32/libsqlite3_32.so b/bin/lib32/libsqlite3_32.so new file mode 100755 index 0000000000..171ffcd1cc Binary files /dev/null and b/bin/lib32/libsqlite3_32.so differ diff --git a/bin/lib32/ode.dll b/bin/lib32/ode.dll new file mode 100755 index 0000000000..f310358693 Binary files /dev/null and b/bin/lib32/ode.dll differ diff --git a/bin/lib32/sqlite3.dll b/bin/lib32/sqlite3.dll new file mode 100755 index 0000000000..f29dc625fa Binary files /dev/null and b/bin/lib32/sqlite3.dll differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll new file mode 100755 index 0000000000..82774a22f0 Binary files /dev/null and b/bin/lib64/BulletSim.dll differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so new file mode 100755 index 0000000000..8b092751d7 Binary files /dev/null and b/bin/lib64/libBulletSim.so differ diff --git a/bin/lib64/libode-x86_64.so b/bin/lib64/libode-x86_64.so new file mode 100644 index 0000000000..9c3070abf4 Binary files /dev/null and b/bin/lib64/libode-x86_64.so differ diff --git a/bin/lib64/libode.dylib b/bin/lib64/libode.dylib new file mode 100644 index 0000000000..958d2021fb Binary files /dev/null and b/bin/lib64/libode.dylib differ diff --git a/bin/lib64/libopenjpeg-dotnet-x86_64.so b/bin/lib64/libopenjpeg-dotnet-x86_64.so new file mode 100755 index 0000000000..7a9bdfcd1d Binary files /dev/null and b/bin/lib64/libopenjpeg-dotnet-x86_64.so differ diff --git a/bin/lib64/libopenjpeg-dotnet.dylib b/bin/lib64/libopenjpeg-dotnet.dylib new file mode 100755 index 0000000000..91f7264fdb Binary files /dev/null and b/bin/lib64/libopenjpeg-dotnet.dylib differ diff --git a/bin/lib64/libsqlite3.dylib b/bin/lib64/libsqlite3.dylib new file mode 100755 index 0000000000..94dcca8e10 Binary files /dev/null and b/bin/lib64/libsqlite3.dylib differ diff --git a/bin/lib64/libsqlite3_64.so b/bin/lib64/libsqlite3_64.so new file mode 100755 index 0000000000..2646a9c41e Binary files /dev/null and b/bin/lib64/libsqlite3_64.so differ diff --git a/bin/lib64/ode.dll b/bin/lib64/ode.dll new file mode 100755 index 0000000000..df3a6c4a8e Binary files /dev/null and b/bin/lib64/ode.dll differ diff --git a/bin/lib64/sqlite3.dll b/bin/lib64/sqlite3.dll new file mode 100755 index 0000000000..815c4d303d Binary files /dev/null and b/bin/lib64/sqlite3.dll differ diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so new file mode 100644 index 0000000000..193eca4b34 Binary files /dev/null and b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-i686.so differ diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so new file mode 100644 index 0000000000..7a9bdfcd1d Binary files /dev/null and b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1-x86_64.so differ diff --git a/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib new file mode 100644 index 0000000000..91f7264fdb Binary files /dev/null and b/bin/libopenjpeg-dotnet-2-1.5.0-dotnet-1.dylib differ diff --git a/bin/log4net.dll b/bin/log4net.dll new file mode 100755 index 0000000000..ffc57e1125 Binary files /dev/null and b/bin/log4net.dll differ diff --git a/bin/mautil.exe b/bin/mautil.exe new file mode 100755 index 0000000000..9086c6d149 Binary files /dev/null and b/bin/mautil.exe differ diff --git a/bin/nunit.framework.dll b/bin/nunit.framework.dll new file mode 100755 index 0000000000..875e098420 Binary files /dev/null and b/bin/nunit.framework.dll differ diff --git a/bin/openjpeg-dotnet-x86_64.dll b/bin/openjpeg-dotnet-x86_64.dll new file mode 100755 index 0000000000..9e8cd21507 Binary files /dev/null and b/bin/openjpeg-dotnet-x86_64.dll differ diff --git a/bin/openjpeg-dotnet.dll b/bin/openjpeg-dotnet.dll new file mode 100755 index 0000000000..6377b8d970 Binary files /dev/null and b/bin/openjpeg-dotnet.dll differ diff --git a/bin/openmetaverse_data/avatar_lad.xml b/bin/openmetaverse_data/avatar_lad.xml new file mode 100644 index 0000000000..3bd7ba72eb --- /dev/null +++ b/bin/openmetaverse_data/avatar_lad.xml @@ -0,0 +1,12308 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/openmetaverse_data/blush_alpha.tga b/bin/openmetaverse_data/blush_alpha.tga new file mode 100644 index 0000000000..05be7e7e3d Binary files /dev/null and b/bin/openmetaverse_data/blush_alpha.tga differ diff --git a/bin/openmetaverse_data/body_skingrain.tga b/bin/openmetaverse_data/body_skingrain.tga new file mode 100644 index 0000000000..7264baac14 Binary files /dev/null and b/bin/openmetaverse_data/body_skingrain.tga differ diff --git a/bin/openmetaverse_data/bodyfreckles_alpha.tga b/bin/openmetaverse_data/bodyfreckles_alpha.tga new file mode 100644 index 0000000000..d30ab3d122 Binary files /dev/null and b/bin/openmetaverse_data/bodyfreckles_alpha.tga differ diff --git a/bin/openmetaverse_data/bump_face_wrinkles.tga b/bin/openmetaverse_data/bump_face_wrinkles.tga new file mode 100644 index 0000000000..54bf7a55be Binary files /dev/null and b/bin/openmetaverse_data/bump_face_wrinkles.tga differ diff --git a/bin/openmetaverse_data/bump_head_base.tga b/bin/openmetaverse_data/bump_head_base.tga new file mode 100644 index 0000000000..fa3568573a Binary files /dev/null and b/bin/openmetaverse_data/bump_head_base.tga differ diff --git a/bin/openmetaverse_data/bump_lowerbody_base.tga b/bin/openmetaverse_data/bump_lowerbody_base.tga new file mode 100644 index 0000000000..498ea3c721 Binary files /dev/null and b/bin/openmetaverse_data/bump_lowerbody_base.tga differ diff --git a/bin/openmetaverse_data/bump_pants_wrinkles.tga b/bin/openmetaverse_data/bump_pants_wrinkles.tga new file mode 100644 index 0000000000..cca72415e8 Binary files /dev/null and b/bin/openmetaverse_data/bump_pants_wrinkles.tga differ diff --git a/bin/openmetaverse_data/bump_shirt_wrinkles.tga b/bin/openmetaverse_data/bump_shirt_wrinkles.tga new file mode 100644 index 0000000000..9e0d757a48 Binary files /dev/null and b/bin/openmetaverse_data/bump_shirt_wrinkles.tga differ diff --git a/bin/openmetaverse_data/bump_upperbody_base.tga b/bin/openmetaverse_data/bump_upperbody_base.tga new file mode 100644 index 0000000000..e57d6352e6 Binary files /dev/null and b/bin/openmetaverse_data/bump_upperbody_base.tga differ diff --git a/bin/openmetaverse_data/eyebrows_alpha.tga b/bin/openmetaverse_data/eyebrows_alpha.tga new file mode 100644 index 0000000000..c363e482e1 Binary files /dev/null and b/bin/openmetaverse_data/eyebrows_alpha.tga differ diff --git a/bin/openmetaverse_data/eyeliner_alpha.tga b/bin/openmetaverse_data/eyeliner_alpha.tga new file mode 100644 index 0000000000..1611eb3355 Binary files /dev/null and b/bin/openmetaverse_data/eyeliner_alpha.tga differ diff --git a/bin/openmetaverse_data/eyeshadow_inner_alpha.tga b/bin/openmetaverse_data/eyeshadow_inner_alpha.tga new file mode 100644 index 0000000000..37d7919395 Binary files /dev/null and b/bin/openmetaverse_data/eyeshadow_inner_alpha.tga differ diff --git a/bin/openmetaverse_data/eyeshadow_outer_alpha.tga b/bin/openmetaverse_data/eyeshadow_outer_alpha.tga new file mode 100644 index 0000000000..00eef9d9f7 Binary files /dev/null and b/bin/openmetaverse_data/eyeshadow_outer_alpha.tga differ diff --git a/bin/openmetaverse_data/eyewhite.tga b/bin/openmetaverse_data/eyewhite.tga new file mode 100644 index 0000000000..a720496988 Binary files /dev/null and b/bin/openmetaverse_data/eyewhite.tga differ diff --git a/bin/openmetaverse_data/facehair_chincurtains_alpha.tga b/bin/openmetaverse_data/facehair_chincurtains_alpha.tga new file mode 100644 index 0000000000..b10397063c Binary files /dev/null and b/bin/openmetaverse_data/facehair_chincurtains_alpha.tga differ diff --git a/bin/openmetaverse_data/facehair_moustache_alpha.tga b/bin/openmetaverse_data/facehair_moustache_alpha.tga new file mode 100644 index 0000000000..4068c4f2b1 Binary files /dev/null and b/bin/openmetaverse_data/facehair_moustache_alpha.tga differ diff --git a/bin/openmetaverse_data/facehair_sideburns_alpha.tga b/bin/openmetaverse_data/facehair_sideburns_alpha.tga new file mode 100644 index 0000000000..acddc2d9bd Binary files /dev/null and b/bin/openmetaverse_data/facehair_sideburns_alpha.tga differ diff --git a/bin/openmetaverse_data/facehair_soulpatch_alpha.tga b/bin/openmetaverse_data/facehair_soulpatch_alpha.tga new file mode 100644 index 0000000000..687091a29f Binary files /dev/null and b/bin/openmetaverse_data/facehair_soulpatch_alpha.tga differ diff --git a/bin/openmetaverse_data/freckles_alpha.tga b/bin/openmetaverse_data/freckles_alpha.tga new file mode 100644 index 0000000000..a9a4ec0735 Binary files /dev/null and b/bin/openmetaverse_data/freckles_alpha.tga differ diff --git a/bin/openmetaverse_data/glove_length_alpha.tga b/bin/openmetaverse_data/glove_length_alpha.tga new file mode 100644 index 0000000000..db89ad57e7 Binary files /dev/null and b/bin/openmetaverse_data/glove_length_alpha.tga differ diff --git a/bin/openmetaverse_data/gloves_fingers_alpha.tga b/bin/openmetaverse_data/gloves_fingers_alpha.tga new file mode 100644 index 0000000000..dba2eec277 Binary files /dev/null and b/bin/openmetaverse_data/gloves_fingers_alpha.tga differ diff --git a/bin/openmetaverse_data/head_alpha.tga b/bin/openmetaverse_data/head_alpha.tga new file mode 100644 index 0000000000..8164525353 Binary files /dev/null and b/bin/openmetaverse_data/head_alpha.tga differ diff --git a/bin/openmetaverse_data/head_color.tga b/bin/openmetaverse_data/head_color.tga new file mode 100644 index 0000000000..74b1b3078b Binary files /dev/null and b/bin/openmetaverse_data/head_color.tga differ diff --git a/bin/openmetaverse_data/head_hair.tga b/bin/openmetaverse_data/head_hair.tga new file mode 100644 index 0000000000..5321f35204 Binary files /dev/null and b/bin/openmetaverse_data/head_hair.tga differ diff --git a/bin/openmetaverse_data/head_highlights_alpha.tga b/bin/openmetaverse_data/head_highlights_alpha.tga new file mode 100644 index 0000000000..8dc5239f97 Binary files /dev/null and b/bin/openmetaverse_data/head_highlights_alpha.tga differ diff --git a/bin/openmetaverse_data/head_shading_alpha.tga b/bin/openmetaverse_data/head_shading_alpha.tga new file mode 100644 index 0000000000..e8ea490109 Binary files /dev/null and b/bin/openmetaverse_data/head_shading_alpha.tga differ diff --git a/bin/openmetaverse_data/head_skingrain.tga b/bin/openmetaverse_data/head_skingrain.tga new file mode 100644 index 0000000000..b42dee0809 Binary files /dev/null and b/bin/openmetaverse_data/head_skingrain.tga differ diff --git a/bin/openmetaverse_data/jacket_length_lower_alpha.tga b/bin/openmetaverse_data/jacket_length_lower_alpha.tga new file mode 100644 index 0000000000..722bc192a8 Binary files /dev/null and b/bin/openmetaverse_data/jacket_length_lower_alpha.tga differ diff --git a/bin/openmetaverse_data/jacket_length_upper_alpha.tga b/bin/openmetaverse_data/jacket_length_upper_alpha.tga new file mode 100644 index 0000000000..e9db7e7b1f Binary files /dev/null and b/bin/openmetaverse_data/jacket_length_upper_alpha.tga differ diff --git a/bin/openmetaverse_data/jacket_open_lower_alpha.tga b/bin/openmetaverse_data/jacket_open_lower_alpha.tga new file mode 100644 index 0000000000..db0c2fb0e3 Binary files /dev/null and b/bin/openmetaverse_data/jacket_open_lower_alpha.tga differ diff --git a/bin/openmetaverse_data/jacket_open_upper_alpha.tga b/bin/openmetaverse_data/jacket_open_upper_alpha.tga new file mode 100644 index 0000000000..71b8a0b805 Binary files /dev/null and b/bin/openmetaverse_data/jacket_open_upper_alpha.tga differ diff --git a/bin/openmetaverse_data/lipgloss_alpha.tga b/bin/openmetaverse_data/lipgloss_alpha.tga new file mode 100644 index 0000000000..78ceecaf85 Binary files /dev/null and b/bin/openmetaverse_data/lipgloss_alpha.tga differ diff --git a/bin/openmetaverse_data/lips_mask.tga b/bin/openmetaverse_data/lips_mask.tga new file mode 100644 index 0000000000..ae1401c006 Binary files /dev/null and b/bin/openmetaverse_data/lips_mask.tga differ diff --git a/bin/openmetaverse_data/lipstick_alpha.tga b/bin/openmetaverse_data/lipstick_alpha.tga new file mode 100644 index 0000000000..2795f1bd40 Binary files /dev/null and b/bin/openmetaverse_data/lipstick_alpha.tga differ diff --git a/bin/openmetaverse_data/lowerbody_color.tga b/bin/openmetaverse_data/lowerbody_color.tga new file mode 100644 index 0000000000..a63aa12fca Binary files /dev/null and b/bin/openmetaverse_data/lowerbody_color.tga differ diff --git a/bin/openmetaverse_data/lowerbody_highlights_alpha.tga b/bin/openmetaverse_data/lowerbody_highlights_alpha.tga new file mode 100644 index 0000000000..ae3413ac8b Binary files /dev/null and b/bin/openmetaverse_data/lowerbody_highlights_alpha.tga differ diff --git a/bin/openmetaverse_data/lowerbody_shading_alpha.tga b/bin/openmetaverse_data/lowerbody_shading_alpha.tga new file mode 100644 index 0000000000..0242663a7d Binary files /dev/null and b/bin/openmetaverse_data/lowerbody_shading_alpha.tga differ diff --git a/bin/openmetaverse_data/nailpolish_alpha.tga b/bin/openmetaverse_data/nailpolish_alpha.tga new file mode 100644 index 0000000000..91af762902 Binary files /dev/null and b/bin/openmetaverse_data/nailpolish_alpha.tga differ diff --git a/bin/openmetaverse_data/pants_length_alpha.tga b/bin/openmetaverse_data/pants_length_alpha.tga new file mode 100644 index 0000000000..3c4f21c0f2 Binary files /dev/null and b/bin/openmetaverse_data/pants_length_alpha.tga differ diff --git a/bin/openmetaverse_data/pants_waist_alpha.tga b/bin/openmetaverse_data/pants_waist_alpha.tga new file mode 100644 index 0000000000..35658c0896 Binary files /dev/null and b/bin/openmetaverse_data/pants_waist_alpha.tga differ diff --git a/bin/openmetaverse_data/rosyface_alpha.tga b/bin/openmetaverse_data/rosyface_alpha.tga new file mode 100644 index 0000000000..a0c8513da2 Binary files /dev/null and b/bin/openmetaverse_data/rosyface_alpha.tga differ diff --git a/bin/openmetaverse_data/rouge_alpha.tga b/bin/openmetaverse_data/rouge_alpha.tga new file mode 100644 index 0000000000..a0c8513da2 Binary files /dev/null and b/bin/openmetaverse_data/rouge_alpha.tga differ diff --git a/bin/openmetaverse_data/shirt_bottom_alpha.tga b/bin/openmetaverse_data/shirt_bottom_alpha.tga new file mode 100644 index 0000000000..7cce03dbe0 Binary files /dev/null and b/bin/openmetaverse_data/shirt_bottom_alpha.tga differ diff --git a/bin/openmetaverse_data/shirt_collar_alpha.tga b/bin/openmetaverse_data/shirt_collar_alpha.tga new file mode 100644 index 0000000000..f55f635473 Binary files /dev/null and b/bin/openmetaverse_data/shirt_collar_alpha.tga differ diff --git a/bin/openmetaverse_data/shirt_collar_back_alpha.tga b/bin/openmetaverse_data/shirt_collar_back_alpha.tga new file mode 100644 index 0000000000..43a6453107 Binary files /dev/null and b/bin/openmetaverse_data/shirt_collar_back_alpha.tga differ diff --git a/bin/openmetaverse_data/shirt_sleeve_alpha.tga b/bin/openmetaverse_data/shirt_sleeve_alpha.tga new file mode 100644 index 0000000000..e3b18f4fc6 Binary files /dev/null and b/bin/openmetaverse_data/shirt_sleeve_alpha.tga differ diff --git a/bin/openmetaverse_data/shoe_height_alpha.tga b/bin/openmetaverse_data/shoe_height_alpha.tga new file mode 100644 index 0000000000..d08dd750f3 Binary files /dev/null and b/bin/openmetaverse_data/shoe_height_alpha.tga differ diff --git a/bin/openmetaverse_data/skirt_length_alpha.tga b/bin/openmetaverse_data/skirt_length_alpha.tga new file mode 100644 index 0000000000..c86799469d Binary files /dev/null and b/bin/openmetaverse_data/skirt_length_alpha.tga differ diff --git a/bin/openmetaverse_data/skirt_slit_back_alpha.tga b/bin/openmetaverse_data/skirt_slit_back_alpha.tga new file mode 100644 index 0000000000..0e49688b14 Binary files /dev/null and b/bin/openmetaverse_data/skirt_slit_back_alpha.tga differ diff --git a/bin/openmetaverse_data/skirt_slit_front_alpha.tga b/bin/openmetaverse_data/skirt_slit_front_alpha.tga new file mode 100644 index 0000000000..888bbf71a1 Binary files /dev/null and b/bin/openmetaverse_data/skirt_slit_front_alpha.tga differ diff --git a/bin/openmetaverse_data/skirt_slit_left_alpha.tga b/bin/openmetaverse_data/skirt_slit_left_alpha.tga new file mode 100644 index 0000000000..210feac1ea Binary files /dev/null and b/bin/openmetaverse_data/skirt_slit_left_alpha.tga differ diff --git a/bin/openmetaverse_data/skirt_slit_right_alpha.tga b/bin/openmetaverse_data/skirt_slit_right_alpha.tga new file mode 100644 index 0000000000..ce11c64bf6 Binary files /dev/null and b/bin/openmetaverse_data/skirt_slit_right_alpha.tga differ diff --git a/bin/openmetaverse_data/underpants_trial_female.tga b/bin/openmetaverse_data/underpants_trial_female.tga new file mode 100644 index 0000000000..96bf732351 Binary files /dev/null and b/bin/openmetaverse_data/underpants_trial_female.tga differ diff --git a/bin/openmetaverse_data/underpants_trial_male.tga b/bin/openmetaverse_data/underpants_trial_male.tga new file mode 100644 index 0000000000..095695ca1c Binary files /dev/null and b/bin/openmetaverse_data/underpants_trial_male.tga differ diff --git a/bin/openmetaverse_data/undershirt_trial_female.tga b/bin/openmetaverse_data/undershirt_trial_female.tga new file mode 100644 index 0000000000..e17a309531 Binary files /dev/null and b/bin/openmetaverse_data/undershirt_trial_female.tga differ diff --git a/bin/openmetaverse_data/upperbody_color.tga b/bin/openmetaverse_data/upperbody_color.tga new file mode 100644 index 0000000000..85fcc41142 Binary files /dev/null and b/bin/openmetaverse_data/upperbody_color.tga differ diff --git a/bin/openmetaverse_data/upperbody_highlights_alpha.tga b/bin/openmetaverse_data/upperbody_highlights_alpha.tga new file mode 100644 index 0000000000..2d8102b583 Binary files /dev/null and b/bin/openmetaverse_data/upperbody_highlights_alpha.tga differ diff --git a/bin/openmetaverse_data/upperbody_shading_alpha.tga b/bin/openmetaverse_data/upperbody_shading_alpha.tga new file mode 100644 index 0000000000..b420506b3e Binary files /dev/null and b/bin/openmetaverse_data/upperbody_shading_alpha.tga differ diff --git a/bin/openmetaverse_data/upperbodyfreckles_alpha.tga b/bin/openmetaverse_data/upperbodyfreckles_alpha.tga new file mode 100644 index 0000000000..76c7ce8849 Binary files /dev/null and b/bin/openmetaverse_data/upperbodyfreckles_alpha.tga differ diff --git a/bin/opensim-ode.sh b/bin/opensim-ode.sh new file mode 100755 index 0000000000..b901425fc1 --- /dev/null +++ b/bin/opensim-ode.sh @@ -0,0 +1,4 @@ +#!/bin/sh +echo "Starting OpenSimulator with ODE. If you get an error saying limit: Operation not permitted. Then you will need to chmod 0600 /etc/limits" +ulimit -s 262144 +mono OpenSim.exe -physics=OpenDynamicsEngine diff --git a/bin/pCampBot.exe.config b/bin/pCampBot.exe.config new file mode 100755 index 0000000000..f0173092b0 --- /dev/null +++ b/bin/pCampBot.exe.config @@ -0,0 +1,52 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/pCampBot.ini.example b/bin/pCampBot.ini.example new file mode 100644 index 0000000000..2952bb002e --- /dev/null +++ b/bin/pCampBot.ini.example @@ -0,0 +1,19 @@ +; This is the example config file for pCampbot +; To use it, copy this file to pCampbot.ini and change settings if required + +[BotManager] + ; Number of milliseconds to wait between bot logins + LoginDelay = 5000 + +[Bot] + ; Control whether bots should regularly send agent updates + ; Not doing this will reduce CPU requirements for pCampbot but greatly + ; reduce the realism compared to viewers which are constantly sending AgentUpdates UDP packets. + ; Defaults to true. + SendAgentUpdates = true + + ; Control whether bots will requests textures when receiving object information + ; Not doing this will reduce CPU requirements for pCampbot but greatly + ; reduce the realism compared to viewers which requests such texture data if not already cached. + ; Defaults to true. + RequestObjectTextures = true diff --git a/bin/pCampBotSentences.txt b/bin/pCampBotSentences.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/shutdown_commands.txt.example b/bin/shutdown_commands.txt.example new file mode 100644 index 0000000000..dc07dc93db --- /dev/null +++ b/bin/shutdown_commands.txt.example @@ -0,0 +1,3 @@ +; Copy this file to shutdown_commands.txt to execute region console commands when the simulator is asked to shut down +; e.g. show stats +; Lines that start with ; are comments diff --git a/bin/startup_commands.txt.example b/bin/startup_commands.txt.example new file mode 100644 index 0000000000..677109c5a2 --- /dev/null +++ b/bin/startup_commands.txt.example @@ -0,0 +1,3 @@ +; Copy this file to startup_commands.txt to run region console commands once the simulator has finished starting up +; e.g. show stats +; Lines that start with ; are comments. diff --git a/bin/startuplogo.txt b/bin/startuplogo.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/bin/zlib.net.dll b/bin/zlib.net.dll new file mode 100755 index 0000000000..9d156547f3 Binary files /dev/null and b/bin/zlib.net.dll differ diff --git a/doc/README b/doc/README new file mode 100644 index 0000000000..9c95557f79 --- /dev/null +++ b/doc/README @@ -0,0 +1,20 @@ +README + +This directory does not currently contain any OpenSimulator user level documentation. Such +documentation can be found at + +http://opensimulator.org + +instead. + +Rather, this directory can contain source-code documentation as generated via the doxygen package. + +To do this, either execute + +doxygen doxygen.conf + +on the command line or run the target + +nant doxygen + +if you have nant installed. diff --git a/doc/doxygen.conf b/doc/doxygen.conf new file mode 100644 index 0000000000..148de9c659 --- /dev/null +++ b/doc/doxygen.conf @@ -0,0 +1,290 @@ +# Doxyfile 1.8.2 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = OpenSim +PROJECT_NUMBER = GIT +PROJECT_BRIEF = +PROJECT_LOGO = +OUTPUT_DIRECTORY = +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +TCL_SUBST = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +OPTIMIZE_FOR_FORTRAN = NO +OPTIMIZE_OUTPUT_VHDL = NO +EXTENSION_MAPPING = +MARKDOWN_SUPPORT = YES +AUTOLINK_SUPPORT = YES +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +SIP_SUPPORT = NO +IDL_PROPERTY_SUPPORT = YES +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +INLINE_GROUPED_CLASSES = NO +INLINE_SIMPLE_STRUCTS = NO +TYPEDEF_HIDES_STRUCT = NO +SYMBOL_CACHE_SIZE = 0 +LOOKUP_CACHE_SIZE = 0 +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_PACKAGE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +FORCE_LOCAL_INCLUDES = NO +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_MEMBERS_CTORS_1ST = NO +SORT_GROUP_NAMES = NO +SORT_BY_SCOPE_NAME = NO +STRICT_PROTO_MATCHING = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_FILES = YES +SHOW_NAMESPACES = YES +FILE_VERSION_FILTER = +LAYOUT_FILE = +CITE_BIB_FILES = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = doxygen.error.log +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = ../OpenSim +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +FILTER_SOURCE_PATTERNS = +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_EXTRA_STYLESHEET = +HTML_EXTRA_FILES = +HTML_COLORSTYLE_HUE = 220 +HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_GAMMA = 80 +HTML_TIMESTAMP = YES +HTML_DYNAMIC_SECTIONS = NO +HTML_INDEX_NUM_ENTRIES = 100 +GENERATE_DOCSET = NO +DOCSET_FEEDNAME = "OpenSimulator docs" +DOCSET_BUNDLE_ID = org.opensimulator.OpenSim +DOCSET_PUBLISHER_ID = org.opensimulator.OpenSim +DOCSET_PUBLISHER_NAME = OpenSim +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +CHM_INDEX_ENCODING = +BINARY_TOC = NO +TOC_EXPAND = NO +GENERATE_QHP = NO +QCH_FILE = +QHP_NAMESPACE = org.opensimulator.OpenSim +QHP_VIRTUAL_FOLDER = doc +QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = +QHG_LOCATION = +GENERATE_ECLIPSEHELP = NO +ECLIPSE_DOC_ID = org.opensimulator.OpenSim +DISABLE_INDEX = NO +GENERATE_TREEVIEW = NO +ENUM_VALUES_PER_LINE = 4 +TREEVIEW_WIDTH = 250 +EXT_LINKS_IN_WINDOW = NO +FORMULA_FONTSIZE = 10 +FORMULA_TRANSPARENT = YES +USE_MATHJAX = NO +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest +MATHJAX_EXTENSIONS = +SEARCHENGINE = YES +SERVER_BASED_SEARCH = NO +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = NO +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +LATEX_FOOTER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +LATEX_SOURCE_CODE = NO +LATEX_BIB_STYLE = plain +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +DOT_NUM_THREADS = 0 +DOT_FONTNAME = Helvetica +DOT_FONTSIZE = 10 +DOT_FONTPATH = +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +UML_LIMIT_NUM_FIELDS = 10 +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +INTERACTIVE_SVG = NO +DOT_PATH = +DOTFILE_DIRS = +MSCFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = YES +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES diff --git a/nant-color b/nant-color new file mode 100755 index 0000000000..b36898681b --- /dev/null +++ b/nant-color @@ -0,0 +1,57 @@ +#!/usr/bin/env ruby + +def main + IO.popen("nant #{ARGV.join(' ')}") { |pipe| + pipe.sync = true + while str = pipe.gets + str.sub!(/\n+/, '') + puts colorize(str) + end + } +end + +def clear + return "\e[0m" +end + +def red(str) + return "\e[31m" + str + clear +end + +def green(str) + return "\e[32m" + str + clear +end + +def yellow(str) + return "\e[33m" + str + clear +end + +def black + return "\e[30m" +end + +def hide + return "\e[8m" +end + +def bright + return "\e[1m" +end + +def colorize(str) + str.sub!(/(error \w+:.*)/, red('\1')) + str.sub!(/(warning \w+:.*)/, yellow('\1')) + + str.sub!(/(Build Succeeded)/i, green('\1')) + str.sub!(/(Compilation succeeded)/, green('\1')) + str.sub!(/(\d+ warning\(s\))/, yellow('\1')) + str.sub!(/(Build Failed)/i, red('\1')) + + str.sub!(/(Tests run: \d+, Failures: 0, Not run: 0,.*)/, green('\1')) + str.sub!(/(Tests run: \d+, Failures: 0, Not run: [1-9].*)/, yellow('\1')) + str.sub!(/(Tests run: \d+, Failures: [1-9].*)/, red('\1')) + str.sub!(/(Test Case Failures:)/, red('\1')) + return str +end + +main() diff --git a/prebuild.xml b/prebuild.xml new file mode 100644 index 0000000000..b5ebed1958 --- /dev/null +++ b/prebuild.xml @@ -0,0 +1,3568 @@ + + + + + + TRACE;DEBUG + false + false + false + 4 + false + + bin + true + true + false + + + + + TRACE + true + false + false + 4 + false + + bin + false + true + false + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + ../../../../bin/Physics/ + + + + + ../../../../bin/Physics/ + + + + ../../../../bin/ + + + + + + + + + + + + + + ../../../../bin/Physics/ + + + + + ../../../../bin/Physics/ + + + + ../../../../bin/ + + + + + + + + + + + + + + ../../../../bin/Physics/ + + + + + ../../../../bin/Physics/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../../../bin/Physics/ + + + + + ../../../../bin/Physics/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + true + + + + + ../../../bin/ + true + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + ../../../../bin/Physics/ + true + + + + + ../../../../bin/Physics/ + true + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../../bin/ + + + + + ../../../../../../bin/ + + + + ../../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../../bin/ + + + + + ../../../../../../bin/ + + + + ../../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../../bin/ + + + + + ../../../../../../bin/ + + + + ../../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../bin/ + + + + + ../../bin/ + + + + ../../bin/ + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../../bin/ + + + + + ../../../../../../bin/ + + + + ../../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + ../../../../../bin/ + + + + + ../../../../../bin/ + + + + ../../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/runprebuild.bat b/runprebuild.bat new file mode 100755 index 0000000000..ed2def1d57 --- /dev/null +++ b/runprebuild.bat @@ -0,0 +1,38 @@ +@echo OFF + +bin\Prebuild.exe /target nant +bin\Prebuild.exe /target vs2010 + +setlocal ENABLEEXTENSIONS +set KEY_NAME="HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0" +set VALUE_NAME=MSBuildToolsPath + +rem We have to use grep or find to locate the correct line, because reg query spits +rem out 4 lines before Windows 7 but 2 lines after Windows 7. +rem We use grep if it's on the path; otherwise we use the built-in find command +rem from Windows. (We must use grep on Cygwin because it overrides the "find" command.) + +for %%X in (grep.exe) do (set FOUNDGREP=%%~$PATH:X) +if defined FOUNDGREP ( + set FINDCMD=grep +) else ( + set FINDCMD=find +) + +FOR /F "usebackq tokens=1-3" %%A IN (`REG QUERY %KEY_NAME% /v %VALUE_NAME% 2^>nul ^| %FINDCMD% "%VALUE_NAME%"`) DO ( + set ValueName=%%A + set ValueType=%%B + set ValueValue=%%C +) + +if defined ValueName ( + @echo Found msbuild path registry entry + @echo Value Name = %ValueName% + @echo Value Type = %ValueType% + @echo Value Value = %ValueValue% + @echo Creating compile.bat + @echo %ValueValue%\msbuild opensim.sln > compile.bat +) else ( + @echo %KEY_NAME%\%VALUE_NAME% not found. + @echo Not creating compile.bat +) diff --git a/runprebuild.sh b/runprebuild.sh new file mode 100755 index 0000000000..8c18db6867 --- /dev/null +++ b/runprebuild.sh @@ -0,0 +1,32 @@ +#!/bin/sh + +case "$1" in + + 'clean') + + mono bin/Prebuild.exe /clean + + ;; + + + 'autoclean') + + echo y|mono bin/Prebuild.exe /clean + + ;; + + + 'vs2010') + + mono bin/Prebuild.exe /target vs2010 + + ;; + + *) + + mono bin/Prebuild.exe /target nant + mono bin/Prebuild.exe /target vs2010 + + ;; + +esac diff --git a/share/32BitLaunch/OpenSim.32BitLaunch.exe b/share/32BitLaunch/OpenSim.32BitLaunch.exe new file mode 100755 index 0000000000..62c14af416 Binary files /dev/null and b/share/32BitLaunch/OpenSim.32BitLaunch.exe differ diff --git a/share/32BitLaunch/OpenSim.32BitLaunch/Program.cs b/share/32BitLaunch/OpenSim.32BitLaunch/Program.cs new file mode 100644 index 0000000000..52806b81bf --- /dev/null +++ b/share/32BitLaunch/OpenSim.32BitLaunch/Program.cs @@ -0,0 +1,59 @@ +/* + * 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; + +namespace OpenSim._32BitLaunch +{ + class Program + { + static void Main(string[] args) + { + log4net.Config.XmlConfigurator.Configure(); + + System.Console.WriteLine("32-bit OpenSim executor"); + System.Console.WriteLine("-----------------------"); + System.Console.WriteLine(""); + System.Console.WriteLine("This application is compiled for 32-bit CPU and will run under WOW32 or similar."); + System.Console.WriteLine("All 64-bit incompatibilities should be gone."); + System.Console.WriteLine(""); + System.Threading.Thread.Sleep(300); + try + { + global::OpenSim.Application.Main(args); + } + catch (Exception ex) + { + System.Console.WriteLine("OpenSim threw an exception:"); + System.Console.WriteLine(ex.ToString()); + System.Console.WriteLine(""); + System.Console.WriteLine("Application will now terminate!"); + System.Console.WriteLine(""); + } + } + } +} diff --git a/share/32BitLaunch/OpenSim.32BitLaunch/Properties/AssemblyInfo.cs b/share/32BitLaunch/OpenSim.32BitLaunch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..e81870f92f --- /dev/null +++ b/share/32BitLaunch/OpenSim.32BitLaunch/Properties/AssemblyInfo.cs @@ -0,0 +1,63 @@ +/* + * 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.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.32BitLaunch")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("OpenSim.32BitLaunch")] +[assembly: AssemblyCopyright("Copyright (c) 2008")] +[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("5072e919-46ab-47e6-8a63-08108324ccdf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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.3.*")] +[assembly: AssemblyVersion("0.6.3.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/share/32BitLaunch/README b/share/32BitLaunch/README new file mode 100644 index 0000000000..443cde0ab9 --- /dev/null +++ b/share/32BitLaunch/README @@ -0,0 +1,5 @@ +Many issues appear in the support channels because of a misunderstanding of the use of these utilities. And through discussion at OpenSimulator Office Hours it was determined that these tools probably serve no useful purpose anymore. + +Instead of removing them immediately, we move them here, for a time, in case there is a useful purpose that has escaped us during conversations. + +If a need to compile these arises, the OpenSim.32BitLaunch and Robust.32BitLaunch directories may be placed under the ./OpenSim/Tools sources subdirectory, run the prebuild script and compile. diff --git a/share/32BitLaunch/Robust.32BitLaunch.exe b/share/32BitLaunch/Robust.32BitLaunch.exe new file mode 100755 index 0000000000..affedb4bca Binary files /dev/null and b/share/32BitLaunch/Robust.32BitLaunch.exe differ diff --git a/share/32BitLaunch/Robust.32BitLaunch/Program.cs b/share/32BitLaunch/Robust.32BitLaunch/Program.cs new file mode 100644 index 0000000000..490414c456 --- /dev/null +++ b/share/32BitLaunch/Robust.32BitLaunch/Program.cs @@ -0,0 +1,60 @@ +/* + * 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 log4net; + +namespace Robust._32BitLaunch +{ + class Program + { + static void Main(string[] args) + { + log4net.Config.XmlConfigurator.Configure(); + + System.Console.WriteLine("32-bit OpenSim executor"); + System.Console.WriteLine("-----------------------"); + System.Console.WriteLine(""); + System.Console.WriteLine("This application is compiled for 32-bit CPU and will run under WOW32 or similar."); + System.Console.WriteLine("All 64-bit incompatibilities should be gone."); + System.Console.WriteLine(""); + System.Threading.Thread.Sleep(300); + try + { + global::OpenSim.Server.OpenSimServer.Main(args); + } + catch (Exception ex) + { + System.Console.WriteLine("OpenSim threw an exception:"); + System.Console.WriteLine(ex.ToString()); + System.Console.WriteLine(""); + System.Console.WriteLine("Application will now terminate!"); + System.Console.WriteLine(""); + } + } + } +} diff --git a/share/32BitLaunch/Robust.32BitLaunch/Properties/AssemblyInfo.cs b/share/32BitLaunch/Robust.32BitLaunch/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cf80f472ef --- /dev/null +++ b/share/32BitLaunch/Robust.32BitLaunch/Properties/AssemblyInfo.cs @@ -0,0 +1,63 @@ +/* + * 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.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("Robust.32BitLaunch")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("http://opensimulator.org")] +[assembly: AssemblyProduct("Robust.32BitLaunch")] +[assembly: AssemblyCopyright("Copyright (c) 2008")] +[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("5072e919-46ab-47e6-8a63-08108324ccdf")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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.3.*")] +[assembly: AssemblyVersion("0.6.3.*")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/share/32BitLaunch/Robust.32BitLaunch/Robust.32BitLaunch.sln b/share/32BitLaunch/Robust.32BitLaunch/Robust.32BitLaunch.sln new file mode 100644 index 0000000000..a48a2d34fc --- /dev/null +++ b/share/32BitLaunch/Robust.32BitLaunch/Robust.32BitLaunch.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Robust.32BitLaunch", "Robust.32BitLaunch.csproj", "{595D67F3-B413-4A43-8568-5B5930E3B31D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {595D67F3-B413-4A43-8568-5B5930E3B31D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {595D67F3-B413-4A43-8568-5B5930E3B31D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {595D67F3-B413-4A43-8568-5B5930E3B31D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {595D67F3-B413-4A43-8568-5B5930E3B31D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/share/RegionLoading/HOWTO_REMOTE_REGION_LOADING.txt b/share/RegionLoading/HOWTO_REMOTE_REGION_LOADING.txt new file mode 100644 index 0000000000..75afcdf69c --- /dev/null +++ b/share/RegionLoading/HOWTO_REMOTE_REGION_LOADING.txt @@ -0,0 +1,9 @@ +The remote region loading ability allows easier management of what regions a simulator run s from a webserver. +In OpenSim.ini, change the 'region_info_source = filesystem' under [Startup] to 'region_info_source = web'. +Then change the line 'regionload_webserver_url = ' to 'regionload_webserver_url = http://127.0.0.1/default.xml' +replacing 'http://127.0.0.1/default.xml' with the URL of the region XML file. + +The XML file of a remote region is similar to the filesystem version, except it is in one file instead of multiple +region_xxx.xml files. + +See example_web.xml for an example on how to make a web version for region loading. \ No newline at end of file diff --git a/share/RegionLoading/example_web.xml b/share/RegionLoading/example_web.xml new file mode 100644 index 0000000000..f7ed25e3b0 --- /dev/null +++ b/share/RegionLoading/example_web.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/share/junkCA/CA.crt b/share/junkCA/CA.crt new file mode 100644 index 0000000000..8e2f099206 --- /dev/null +++ b/share/junkCA/CA.crt @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFJzCCBA+gAwIBAgIJAK3s6O4dAEQSMA0GCSqGSIb3DQEBBQUAMIG9MQswCQYD +VQQGEwJVUzEUMBIGA1UECBMLTXVsdGktc3RhdGUxEzARBgNVBAcTCm11bHRpLWNp +dHkxJTAjBgNVBAoTHE9wZW5TaW11bGF0b3IgRGV2IERPTlQgVFJVU1QxEzARBgNV +BAsTCkRPTlQgVFJVU1QxJTAjBgNVBAMTHE9wZW5TaW11bGF0b3IgRGV2IERPTlQg +VFJVU1QxIDAeBgkqhkiG9w0BCQEWEXRlcmF2dXNAZ21haWwuY29tMB4XDTA4MDkx +MjE2MTEwNVoXDTE4MDgxMTE2MTEwNVowgb0xCzAJBgNVBAYTAlVTMRQwEgYDVQQI +EwtNdWx0aS1zdGF0ZTETMBEGA1UEBxMKbXVsdGktY2l0eTElMCMGA1UEChMcT3Bl +blNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDETMBEGA1UECxMKRE9OVCBUUlVTVDEl +MCMGA1UEAxMcT3BlblNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDEgMB4GCSqGSIb3 +DQEJARYRdGVyYXZ1c0BnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCwpTIw01Y6Lg7INnmDp9KHLC1p0Udg4Y9Ux232zd2tOTpjF7QnlHIO +GKg8jE6SiaV4NPC9nRqgVCOMa6H7crbr/IrXcTUq0ZYyIG07ZkbUb+4aNNJLh/vq +xHj0kXfRKGxq1QzjmNO7kfCzR4vQudI4F/Hw6HL2vIqRI3sNepn+j3VKCILyTLQP +b0mJi6EfRizqLtIgQwaaMN3AgZWF8rAANNLerzkYLM7+uT5sQYX/sGPi16x/NgFr +UfCI6Ag2Sbufj8VOOp08kDwVZNq7Vr44y+l1gJySPnLMbBrTb/erc+UJv4xgVsRI +opMKP/DG+z3eRbxKcPZ0hpPWJS4JhG0bAgMBAAGjggEmMIIBIjAdBgNVHQ4EFgQU +u0ZSqD+MrxiuSy0IsX5Iye8lHZswgfIGA1UdIwSB6jCB54AUu0ZSqD+MrxiuSy0I +sX5Iye8lHZuhgcOkgcAwgb0xCzAJBgNVBAYTAlVTMRQwEgYDVQQIEwtNdWx0aS1z +dGF0ZTETMBEGA1UEBxMKbXVsdGktY2l0eTElMCMGA1UEChMcT3BlblNpbXVsYXRv +ciBEZXYgRE9OVCBUUlVTVDETMBEGA1UECxMKRE9OVCBUUlVTVDElMCMGA1UEAxMc +T3BlblNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDEgMB4GCSqGSIb3DQEJARYRdGVy +YXZ1c0BnbWFpbC5jb22CCQCt7OjuHQBEEjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQAaI69OZmjTVcZxtWLASB9nv3WNEOxJW+aBjseUhyM4H9pJ5bkh +MmgiG9JgnBUpNzL3/1EV2Ud8ZCBy7JxhvwWnJMjxJL67US16sKpCLVvNAD2pCZ6f +iaT/qorLYP/yJ7OieYmAh5lZsvG8xJM44ZZyvtYEVBB+qZw1gHkb4hhf3roUCV67 +aHMDRRolWyWm6weid7wTWz38QfRohVWidH9CPwubG7K4zPrDpBJAZV1cKra1YTrM +eje1GuIyHzpIAAYP5z1hgI9p/0oTrWnG7w7Ydkpm9lu50WMt1DScsYnh0MhW/uas +e24cQsvz0m9PZlfAsJQeX6pbqlJppoX+XeVC +-----END CERTIFICATE----- diff --git a/share/junkCA/CA.key b/share/junkCA/CA.key new file mode 100644 index 0000000000..59a7a5e179 --- /dev/null +++ b/share/junkCA/CA.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAsKUyMNNWOi4OyDZ5g6fShywtadFHYOGPVMdt9s3drTk6Yxe0 +J5RyDhioPIxOkomleDTwvZ0aoFQjjGuh+3K26/yK13E1KtGWMiBtO2ZG1G/uGjTS +S4f76sR49JF30ShsatUM45jTu5Hws0eL0LnSOBfx8Ohy9ryKkSN7DXqZ/o91SgiC +8ky0D29JiYuhH0Ys6i7SIEMGmjDdwIGVhfKwADTS3q85GCzO/rk+bEGF/7Bj4tes +fzYBa1HwiOgINkm7n4/FTjqdPJA8FWTau1a+OMvpdYCckj5yzGwa02/3q3PlCb+M +YFbESKKTCj/wxvs93kW8SnD2dIaT1iUuCYRtGwIDAQABAoIBAFNoXU+iqodkMgSl +fDEHMCg1WugpMjvzpXsRg8HSqQZfDEu36I/7zvMK/30/fuZAakpdLQNLSERGFlb6 +h4y0ON0q7OAXi1RBjFr05r7yZyVuCI6FPHr/pZrP1JEekuXG4ZJ8MM7S3b8mhPIS +KVmQNEvaOppXF9mbYw5vI25U4pvIljfAKZxkeU7aHb9asrnuBOwLjFRtLDTo13Nc +dHTT3X+G+74mU8rYTV3njAmh9iE+PmDlc2mJckS/0TqpJbZgFueCCBIK5iJSc7lO ++DFFgRcouvnCdZW9fp6/8Hz4FGa2TX6jsYj/H1dGWELioUOoBwkdqFP9JaBvd7ni +Nx2PObkCgYEA31rYJJ5jUiosf1I894MuEg2HWosXd0pVAPW3QjHdx7oiVUBRS5ZB +YAOy5zeleLckfWKJiE4z/5CMdsEM/Q9F0X2xg3TDhxUM7A4px0AXAsbyJT7AcE0O +kZBZjhluIF8O3Lic/LqzT39KgG35zvvd+H42Je1WvsCLSREL1MQDwCUCgYEAynak +x41uazl5UaDwL+mahIVW+n/Bko3e9BhD7ZRkLI2+R7y180Fw7dMmnxG/jVw7hotk +Ylx3Oa+JjnEplxTd1TShnP1aQ0nhnxnhS9EbIW8SjsazeK8V8zezJ54uZziVedgg +x/ISvQM0yPbvkrSo4mQEjl3q4DjmIyg5Nx+cVD8CgYBGD0vPKLOE2V+9zED9bnNs +DDxRxWFl9LX3KBwEsnmbpaIRVaxqZkY5ZM+gQU8xL1lNzzPOwqEC4Ad/VIzLcBf5 +X1DoKB8Q5yR3gvXN3yeYomjgD+/zCeiw9jNxJD7r/oU97NapW7LVE9t9r4F1UIHO +6V/4w5q7GNBX6fXpFlcK1QKBgQCYNbYP5/4ZUm4otiucea0W7//B94YZndr9+7gl +xqfA7xcca30G0i4KPfINKJSvu6VssyLW59kiXxu1INI5qRBVF2pg0f+oEsUyjYxZ +KW2SJyT2fd+zXT3NShTANiWAqIOHxLpwV0dLHjvy0eKukm9dNABQ376Sr3Qk/jp1 +fKhUlQKBgAj6o2lw0vLOuQmqV08YF/UFWN/TZAcBzDE353fypi16aqY35pYSvUez +64d1anTTwuq5fLGaQlH0XgGor/XbBqgif8eVyTRdfmA/2YQjwMIFyrWyxLpTiuiO +0P6lO4B9NCT2N/gDPomdlOfkA2g063C21CPa43lr8lGx8oaQW95W +-----END RSA PRIVATE KEY----- diff --git a/share/junkCA/CA.srl b/share/junkCA/CA.srl new file mode 100644 index 0000000000..ea34835075 --- /dev/null +++ b/share/junkCA/CA.srl @@ -0,0 +1 @@ +F10DF59AD0EE66E0 diff --git a/share/junkCA/CA2.pem b/share/junkCA/CA2.pem new file mode 100644 index 0000000000..8e2f099206 --- /dev/null +++ b/share/junkCA/CA2.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFJzCCBA+gAwIBAgIJAK3s6O4dAEQSMA0GCSqGSIb3DQEBBQUAMIG9MQswCQYD +VQQGEwJVUzEUMBIGA1UECBMLTXVsdGktc3RhdGUxEzARBgNVBAcTCm11bHRpLWNp +dHkxJTAjBgNVBAoTHE9wZW5TaW11bGF0b3IgRGV2IERPTlQgVFJVU1QxEzARBgNV +BAsTCkRPTlQgVFJVU1QxJTAjBgNVBAMTHE9wZW5TaW11bGF0b3IgRGV2IERPTlQg +VFJVU1QxIDAeBgkqhkiG9w0BCQEWEXRlcmF2dXNAZ21haWwuY29tMB4XDTA4MDkx +MjE2MTEwNVoXDTE4MDgxMTE2MTEwNVowgb0xCzAJBgNVBAYTAlVTMRQwEgYDVQQI +EwtNdWx0aS1zdGF0ZTETMBEGA1UEBxMKbXVsdGktY2l0eTElMCMGA1UEChMcT3Bl +blNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDETMBEGA1UECxMKRE9OVCBUUlVTVDEl +MCMGA1UEAxMcT3BlblNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDEgMB4GCSqGSIb3 +DQEJARYRdGVyYXZ1c0BnbWFpbC5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCwpTIw01Y6Lg7INnmDp9KHLC1p0Udg4Y9Ux232zd2tOTpjF7QnlHIO +GKg8jE6SiaV4NPC9nRqgVCOMa6H7crbr/IrXcTUq0ZYyIG07ZkbUb+4aNNJLh/vq +xHj0kXfRKGxq1QzjmNO7kfCzR4vQudI4F/Hw6HL2vIqRI3sNepn+j3VKCILyTLQP +b0mJi6EfRizqLtIgQwaaMN3AgZWF8rAANNLerzkYLM7+uT5sQYX/sGPi16x/NgFr +UfCI6Ag2Sbufj8VOOp08kDwVZNq7Vr44y+l1gJySPnLMbBrTb/erc+UJv4xgVsRI +opMKP/DG+z3eRbxKcPZ0hpPWJS4JhG0bAgMBAAGjggEmMIIBIjAdBgNVHQ4EFgQU +u0ZSqD+MrxiuSy0IsX5Iye8lHZswgfIGA1UdIwSB6jCB54AUu0ZSqD+MrxiuSy0I +sX5Iye8lHZuhgcOkgcAwgb0xCzAJBgNVBAYTAlVTMRQwEgYDVQQIEwtNdWx0aS1z +dGF0ZTETMBEGA1UEBxMKbXVsdGktY2l0eTElMCMGA1UEChMcT3BlblNpbXVsYXRv +ciBEZXYgRE9OVCBUUlVTVDETMBEGA1UECxMKRE9OVCBUUlVTVDElMCMGA1UEAxMc +T3BlblNpbXVsYXRvciBEZXYgRE9OVCBUUlVTVDEgMB4GCSqGSIb3DQEJARYRdGVy +YXZ1c0BnbWFpbC5jb22CCQCt7OjuHQBEEjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3 +DQEBBQUAA4IBAQAaI69OZmjTVcZxtWLASB9nv3WNEOxJW+aBjseUhyM4H9pJ5bkh +MmgiG9JgnBUpNzL3/1EV2Ud8ZCBy7JxhvwWnJMjxJL67US16sKpCLVvNAD2pCZ6f +iaT/qorLYP/yJ7OieYmAh5lZsvG8xJM44ZZyvtYEVBB+qZw1gHkb4hhf3roUCV67 +aHMDRRolWyWm6weid7wTWz38QfRohVWidH9CPwubG7K4zPrDpBJAZV1cKra1YTrM +eje1GuIyHzpIAAYP5z1hgI9p/0oTrWnG7w7Ydkpm9lu50WMt1DScsYnh0MhW/uas +e24cQsvz0m9PZlfAsJQeX6pbqlJppoX+XeVC +-----END CERTIFICATE----- diff --git a/share/junkCA/Certificate commands OpenSSL.txt b/share/junkCA/Certificate commands OpenSSL.txt new file mode 100644 index 0000000000..0448d76e80 --- /dev/null +++ b/share/junkCA/Certificate commands OpenSSL.txt @@ -0,0 +1,86 @@ +To generate a cert request and sign it with the JunkCA + +REMEMBER TO APPEND THE CA2.pem file to the bottom of the app_settings/CA.pem in the Linden client folders or you won't be able to connect! + +Generate a Host Key: + openssl genrsa -out host.key 2048 + +Generate a Certificate signing request with *OpenSSL*: + openssl req -new -nodes -key host.key -out host.csr + When prompted for: 'Common Name (eg, YOUR name) []:', please type the domain name that this certificate will be used on. + +Or you could; + +Generate a Certificate request with the *IIS Snapin*: + Go to Control Panel ---> Administrative tools ---> Internet Information Services + Pick a web site on your server. + right click, choose properties from the context menu + Go to the Directory Security tab + Click On the 'Server Certificate...' button + Click 'Prepare the request now, but send it later' and then follow the wizard. + Be sure to type the common name as the domain name that you will be servicing. www.osgrid.org or whatever server will be using this cert + +Sign the certificate request with the junkCA; +openssl x509 -req -days 3620 -CA CA.crt -CAkey CA.key -CAcreateserial -in host.csr -out signed.cer + +Import it into your MY store on windows. + + If you used OpenSSL to generate the certificate; + openssl pkcs12 -export -in server.crt -inkey server.key.unsecure -out server.pfx -name "My Lovely Cert" + server.crt is the signed cert from the CA. + server.key.unsecure is the *unencrypted* private key. + + You will be asked for a password, set this if you want. + + In Windows, fire up "mmc", add the certificates Snap-in, set it to manage the local computer. Go to personal certificates folder, import server.pfx, enter password if you gave it one earlier. + + In IIS, get it to let you choose from currently installed certs. You should now be able to choose the one you just installed. + + If you used the IIS Snap-in, + Go to Control Panel ---> Administrative tools ---> Internet Information Services + Pick a web site on your server. + right click, choose properties from the context menu + Go to the Directory Security tab + Click On the 'Server Certificate...' button + Choose the radio button that says, 'Assign an existing certificate' + + +Mono, you must use httpcfg in the Mono-1.9.1/lib/mono/2.0 folder. + httpcfg -add -port -pvk -cert MyCert + From Lexa: + It expect the key to be in the windows pvk format + pvk -in host.key -nocrypt -out host.pvk -topvk" and use -pvk host.pvk + pvk is a package on FreeBSD that gets installed as part of the Mono installation + +After that, make sure to set-up your opensim.ini! + + +OpenSSL can be found: +http://www.slproweb.com/products/Win32OpenSSL.html + +httpcfg.exe for windowsXP can be found: +http://www.microsoft.com/downloads/details.aspx?FamilyID=49ae8576-9bb9-4126-9761-ba8011fabf38&displaylang=en + +Windows Vista users need to use netsh http! + +--------------------------------------------------- + +Additional notes + +To create your own CA + +openssl genrsa -out yourCA.key 2048 +openssl req -new -key yourCA.key -x509 -days 3620 -out yourCA.crt + +and the final step.. (AND THIS IS IMPORTANT) + +openssl x509 -in CA.crt -out yourCA.pem -outform PEM + +The last step will produce a certificate in the PEM format that you can append to the Linden client's app_settings/CA.pem file +so that it can validate certificates that are generated from your CA. + +One last important thing! + +All users that connect with linden clients +using SSL NEED the pem file you created in that last step appended to theirs, or their client will give them a weird error about +their clock being wrong! diff --git a/share/junkCA/README.txt b/share/junkCA/README.txt new file mode 100644 index 0000000000..8367bd08d8 --- /dev/null +++ b/share/junkCA/README.txt @@ -0,0 +1,2 @@ +This Folder contains junk CA files and directions for signing with it. +Comply with export laws!