Compare commits
638 Commits
master
...
0.7.4-exte
Author | SHA1 | Date |
---|---|---|
Justin Clark-Casey (justincc) | 761fe07f1e | |
Justin Clark-Casey (justincc) | f603cbec02 | |
Justin Clark-Casey (justincc) | 9d6ff81a07 | |
Justin Clark-Casey (justincc) | 2a76b08ebd | |
Justin Clark-Casey (justincc) | a49447832e | |
Justin Clark-Casey (justincc) | 86bdc4eca4 | |
Justin Clark-Casey (justincc) | d71b097396 | |
Justin Clark-Casey (justincc) | d25d36ec33 | |
Justin Clark-Casey (justincc) | 92cedc083f | |
Justin Clark-Casey (justincc) | edfcec5041 | |
Justin Clark-Casey (justincc) | 15c79a3f16 | |
Justin Clark-Casey (justincc) | 0d67aa23d5 | |
Justin Clark-Casey (justincc) | 44434e8200 | |
Justin Clark-Casey (justincc) | 0f569d2359 | |
Justin Clark-Casey (justincc) | f6a0294bbc | |
Justin Clark-Casey (justincc) | fa4ea2db17 | |
Justin Clark-Casey (justincc) | 0fa04289d5 | |
Justin Clark-Casey (justincc) | debc266e75 | |
Justin Clark-Casey (justincc) | b87c5140a1 | |
Justin Clark-Casey (justincc) | 6f0cab8538 | |
Justin Clark-Casey (justincc) | f57e29372b | |
Justin Clark-Casey (justincc) | e185ed6e69 | |
Justin Clark-Casey (justincc) | 27d5a3ce35 | |
Justin Clark-Casey (justincc) | 78ae1d34ec | |
Justin Clark-Casey (justincc) | 44ca6c01ea | |
Justin Clark-Casey (justincc) | 9dd7fb8142 | |
Justin Clark-Casey (justincc) | e50b27047d | |
Robert Adams | 67ffb085e1 | |
Justin Clark-Casey (justincc) | 38767d5984 | |
Melanie | fbb738a627 | |
Justin Clark-Casey (justincc) | 95cdaf81a3 | |
Justin Clark-Casey (justincc) | 2e9c57f644 | |
Justin Clark-Casey (justincc) | eafcc6f2b2 | |
Robert Adams | f8067f02fe | |
Justin Clark-Casey (justincc) | 515324a1aa | |
Justin Clark-Casey (justincc) | cc24ba3a91 | |
Justin Clark-Casey (justincc) | 556817e279 | |
Justin Clark-Casey (justincc) | 2a81f4db2e | |
Justin Clark-Casey (justincc) | 3098ad4ab9 | |
Justin Clark-Casey (justincc) | 9f1aac82b2 | |
Justin Clark-Casey (justincc) | 60060e7be9 | |
Justin Clark-Casey (justincc) | 785abe070d | |
Justin Clark-Casey (justincc) | 5526177c32 | |
Justin Clark-Casey (justincc) | cbb4086e82 | |
Justin Clark-Casey (justincc) | 6804549428 | |
Justin Clark-Casey (justincc) | cf52e8072c | |
Justin Clark-Casey (justincc) | ac063b4681 | |
Justin Clark-Casey (justincc) | eb29597c6e | |
Justin Clark-Casey (justincc) | a935440513 | |
Justin Clark-Casey (justincc) | b9d4266400 | |
Justin Clark-Casey (justincc) | 3b13dd56cd | |
Justin Clark-Casey (justincc) | 213f0e3a12 | |
Justin Clark-Casey (justincc) | 75c169ffe5 | |
Justin Clark-Casey (justincc) | 1c12697232 | |
Justin Clark-Casey (justincc) | 051f21ff65 | |
Justin Clark-Casey (justincc) | c46a00a1fe | |
SignpostMarv | 49a2cfacb9 | |
SignpostMarv | 4a35f0a305 | |
Justin Clark-Casey (justincc) | 078617cff4 | |
Justin Clark-Casey (justincc) | 6ed5561057 | |
Justin Clark-Casey (justincc) | 4531b2274e | |
Justin Clark-Casey (justincc) | b025cdf6be | |
Justin Clark-Casey (justincc) | 99a0f76998 | |
Justin Clark-Casey (justincc) | 681b07f668 | |
Justin Clark-Casey (justincc) | 608a569bd5 | |
Justin Clark-Casey (justincc) | 66749eafdd | |
Justin Clark-Casey (justincc) | 2fd9456f4f | |
Justin Clark-Casey (justincc) | 7af13bcca3 | |
Justin Clark-Casey (justincc) | 85a327dc59 | |
Justin Clark-Casey (justincc) | adfc8ade9a | |
Justin Clark-Casey (justincc) | 03983edc3b | |
Justin Clark-Casey (justincc) | 49bb9b383f | |
Justin Clark-Casey (justincc) | 248cb10b24 | |
teravus | 5201433d8b | |
teravus | 0793e6df25 | |
Justin Clark-Casey (justincc) | a74aef6a81 | |
Justin Clark-Casey (justincc) | 6fb29865f5 | |
Mic Bowman | c5a6b61218 | |
Mic Bowman | e6a97e9864 | |
teravus | ca7959c82f | |
Robert Adams | 5d187b9b28 | |
Robert Adams | 663809479d | |
Robert Adams | 452f6894ef | |
Robert Adams | caceca87e4 | |
Robert Adams | f3943a3677 | |
Robert Adams | 7d63676cfc | |
Robert Adams | d99441de25 | |
Robert Adams | dee74834be | |
Justin Clark-Casey (justincc) | a46b02a3ae | |
Robert Adams | 1c14a9d883 | |
Justin Clark-Casey (justincc) | b265eefd01 | |
Robert Adams | eab30e4b49 | |
Justin Clark-Casey (justincc) | f8a57edb2c | |
Justin Clark-Casey (justincc) | ff199668fe | |
Justin Clark-Casey (justincc) | f95c5dd58e | |
Diva Canto | 75790c2393 | |
Justin Clark-Casey (justincc) | 1de04b94e0 | |
Melanie | 74c8cf0406 | |
Justin Clark-Casey (justincc) | f80ce30f4b | |
Justin Clark-Casey (justincc) | 4d9307753a | |
Justin Clark-Casey (justincc) | 889efb6bef | |
Justin Clark-Casey (justincc) | fd501ae45f | |
Justin Clark-Casey (justincc) | 93d16b90a4 | |
Justin Clark-Casey (justincc) | b53362cdb0 | |
Justin Clark-Casey (justincc) | bbfde60ad5 | |
Justin Clark-Casey (justincc) | ce2bf496a4 | |
Justin Clark-Casey (justincc) | c80009e86c | |
Justin Clark-Casey (justincc) | cfd0a5d34c | |
Justin Clark-Casey (justincc) | 28219d622f | |
Justin Clark-Casey (justincc) | e13f71048d | |
Justin Clark-Casey (justincc) | 772a0d785d | |
Justin Clark-Casey (justincc) | 2bde9e4f89 | |
Mic Bowman | ee26b56cc6 | |
Justin Clark-Casey (justincc) | baf1af2abf | |
Justin Clark-Casey (justincc) | 83ea45c567 | |
Justin Clark-Casey (justincc) | 420b94d358 | |
Justin Clark-Casey (justincc) | dd5f9b0103 | |
Justin Clark-Casey (justincc) | 7d5613bc16 | |
Justin Clark-Casey (justincc) | f7de17c9d9 | |
Justin Clark-Casey (justincc) | f1b49aaa99 | |
Dan Lake | b779ab041b | |
Justin Clark-Casey (justincc) | a402571a60 | |
Justin Clark-Casey (justincc) | 7e75fd7dcb | |
Justin Clark-Casey (justincc) | 282123cb00 | |
Justin Clark-Casey (justincc) | 476ad0550f | |
Justin Clark-Casey (justincc) | 6e612937c7 | |
Justin Clark-Casey (justincc) | c992037d50 | |
Justin Clark-Casey (justincc) | fe06b49dd6 | |
Justin Clark-Casey (justincc) | 1df97c2385 | |
Justin Clark-Casey (justincc) | 37ab587f27 | |
Mic Bowman | 12201bf7f4 | |
Mic Bowman | c7179ff454 | |
Justin Clark-Casey (justincc) | 841ca0fe38 | |
Justin Clark-Casey (justincc) | 1ee6822e39 | |
Justin Clark-Casey (justincc) | 3fdeb559f0 | |
Justin Clark-Casey (justincc) | 50811b02ab | |
Justin Clark-Casey (justincc) | 009178d7dd | |
Justin Clark-Casey (justincc) | 7cacf5f8a0 | |
Justin Clark-Casey (justincc) | b1740e11de | |
Mic Bowman | b6cbda61b3 | |
Mic Bowman | 914ebd7476 | |
Justin Clark-Casey (justincc) | 7d68559e15 | |
Justin Clark-Casey (justincc) | 5c75e43a70 | |
Justin Clark-Casey (justincc) | 79dd5f2692 | |
Justin Clark-Casey (justincc) | 216ef7522a | |
Justin Clark-Casey (justincc) | ccd1bac994 | |
Justin Clark-Casey (justincc) | 182c66cea1 | |
Justin Clark-Casey (justincc) | 7fe768a98d | |
Melanie | 4fd176f479 | |
BlueWall | 2be88cd46c | |
Mic Bowman | 5a2895977a | |
Oren Hurvitz | 7fc820b3fd | |
Oren Hurvitz | 72838a04d6 | |
Mic Bowman | 9ee6c06ec8 | |
Justin Clark-Casey (justincc) | 5393ecfa75 | |
Mic Bowman | f6ddd20413 | |
Justin Clark-Casey (justincc) | b46a9cf57f | |
Justin Clark-Casey (justincc) | aab719dc18 | |
Justin Clark-Casey (justincc) | 90d71d423c | |
Justin Clark-Casey (justincc) | 3f6feec914 | |
Justin Clark-Casey (justincc) | 6874d56452 | |
Justin Clark-Casey (justincc) | 23df5768c3 | |
Justin Clark-Casey (justincc) | f52cee9732 | |
Justin Clark-Casey (justincc) | 37e4186ad8 | |
Justin Clark-Casey (justincc) | 5be2483e93 | |
Justin Clark-Casey (justincc) | 6418b89fd6 | |
Justin Clark-Casey (justincc) | c67f791097 | |
Justin Clark-Casey (justincc) | 001bbb0b16 | |
Justin Clark-Casey (justincc) | 365292e38f | |
Justin Clark-Casey (justincc) | fb2de29f77 | |
Dan Lake | 251a70338a | |
Justin Clark-Casey (justincc) | e210dc188a | |
Justin Clark-Casey (justincc) | e61117669e | |
Justin Clark-Casey (justincc) | 97fcc97902 | |
Justin Clark-Casey (justincc) | eab1b1b9f8 | |
Justin Clark-Casey (justincc) | 87d50974f8 | |
Justin Clark-Casey (justincc) | 78814adf01 | |
SignpostMarv | 341dcbede6 | |
teravus | 901aa4153e | |
Melanie | f99ba6f506 | |
Justin Clark-Casey (justincc) | 85f32f184c | |
Justin Clark-Casey (justincc) | 982d3d3faa | |
Justin Clark-Casey (justincc) | 744ed1b313 | |
Mic Bowman | 69ca1498ef | |
Mic Bowman | 9a60039d36 | |
Justin Clark-Casey (justincc) | f030f1dbfb | |
Justin Clark-Casey (justincc) | 16641e5bd0 | |
Justin Clark-Casey (justincc) | 10db078aa4 | |
Justin Clark-Casey (justincc) | afc93d7ac4 | |
Justin Clark-Casey (justincc) | fa5e1dde64 | |
Justin Clark-Casey (justincc) | 0810f5f449 | |
Justin Clark-Casey (justincc) | 56e245e5e9 | |
Justin Clark-Casey (justincc) | 89362dbc4e | |
Melanie | 2f6ddc68b0 | |
Justin Clark-Casey (justincc) | 25b539e593 | |
Talun | 25a9ad1722 | |
Justin Clark-Casey (justincc) | d3bbfcc97b | |
Justin Clark-Casey (justincc) | c3cbba7778 | |
Justin Clark-Casey (justincc) | 648932458a | |
Justin Clark-Casey (justincc) | f1b978cdf0 | |
Justin Clark-Casey (justincc) | cb26e8a6c6 | |
Justin Clark-Casey (justincc) | 7b8dc102d2 | |
Justin Clark-Casey (justincc) | cf04e09521 | |
Justin Clark-Casey (justincc) | 3f50de6445 | |
Justin Clark-Casey (justincc) | 18659dfaea | |
Justin Clark-Casey (justincc) | ba175ee50b | |
Justin Clark-Casey (justincc) | 995976c6db | |
Melanie | cfc9270bae | |
Melanie | b2b6fd6aad | |
Mic Bowman | 96454ddee2 | |
Justin Clark-Casey (justincc) | 4a0b9c411e | |
Justin Clark-Casey (justincc) | e67b84613d | |
Justin Clark-Casey (justincc) | d4f9982936 | |
Justin Clark-Casey (justincc) | 873ce644d3 | |
Justin Clark-Casey (justincc) | 1f8e43dd93 | |
Justin Clark-Casey (justincc) | 7bfd369b24 | |
Justin Clark-Casey (justincc) | 1da5a1ab21 | |
Justin Clark-Casey (justincc) | af30d9b07e | |
Justin Clark-Casey (justincc) | b2c893a7be | |
Justin Clark-Casey (justincc) | 3f80ac23b6 | |
Justin Clark-Casey (justincc) | 701109c8c9 | |
Justin Clark-Casey (justincc) | 5cbda393cd | |
Justin Clark-Casey (justincc) | 98d0440c42 | |
Justin Clark-Casey (justincc) | 8148d7e204 | |
Justin Clark-Casey (justincc) | b43a6b0199 | |
Justin Clark-Casey (justincc) | d01943fa40 | |
Justin Clark-Casey (justincc) | b6daf4d4c6 | |
Justin Clark-Casey (justincc) | 4495ecd158 | |
Justin Clark-Casey (justincc) | 85df66db51 | |
Justin Clark-Casey (justincc) | 3024d0e61c | |
Justin Clark-Casey (justincc) | 5ac0447e0f | |
Oren Hurvitz | bb42862639 | |
Justin Clark-Casey (justincc) | 39b8f339ce | |
Justin Clark-Casey (justincc) | af99e5e74a | |
Justin Clark-Casey (justincc) | f79800246a | |
Melanie | 4c1b1d89eb | |
Diva Canto | a473482413 | |
Justin Clark-Casey (justincc) | 535ec23646 | |
Justin Clark-Casey (justincc) | 245d6435e3 | |
Justin Clark-Casey (justincc) | 8728d4ce6a | |
Justin Clark-Casey (justincc) | 44901f0b31 | |
Justin Clark-Casey (justincc) | 5054a07be2 | |
Justin Clark-Casey (justincc) | 2f1cc6a06a | |
Robert Adams | ff7693a14b | |
Justin Clark-Casey (justincc) | e89f93c78c | |
Justin Clark-Casey (justincc) | d51aee876d | |
Justin Clark-Casey (justincc) | 3ad03d41e6 | |
Justin Clark-Casey (justincc) | 69104f38f9 | |
Justin Clark-Casey (justincc) | c6bb0d9662 | |
Justin Clark-Casey (justincc) | 73717f2ce7 | |
Justin Clark-Casey (justincc) | 03bc92d112 | |
Justin Clark-Casey (justincc) | fd015c1ed7 | |
Justin Clark-Casey (justincc) | 8e548e4c8a | |
Justin Clark-Casey (justincc) | 44adf909b0 | |
Justin Clark-Casey (justincc) | d43863af78 | |
Justin Clark-Casey (justincc) | 83c70dc914 | |
Justin Clark-Casey (justincc) | 10c1b15f12 | |
Justin Clark-Casey (justincc) | be5c6658bb | |
Justin Clark-Casey (justincc) | f0a936832b | |
Justin Clark-Casey (justincc) | ce5c2ee506 | |
Justin Clark-Casey (justincc) | bd33953c60 | |
Justin Clark-Casey (justincc) | ab22de03b8 | |
Melanie | 61af272956 | |
Justin Clark-Casey (justincc) | e43ddcd5ea | |
Justin Clark-Casey (justincc) | 4041cb54f3 | |
Justin Clark-Casey (justincc) | 87b4f335af | |
SignpostMarv | f9bb9191cf | |
SignpostMarv | 4ab7697f00 | |
SignpostMarv | d1ba3ea60d | |
SignpostMarv | e597b33926 | |
Justin Clark-Casey (justincc) | 3bb3a8f39b | |
Justin Clark-Casey (justincc) | 561626fe1b | |
Oren Hurvitz | 86519bd407 | |
Justin Clark-Casey (justincc) | 35a1949fb8 | |
Justin Clark-Casey (justincc) | 586a331a95 | |
Justin Clark-Casey (justincc) | 9b0f784c63 | |
Oren Hurvitz | ac0f5e75bd | |
Justin Clark-Casey (justincc) | f283ff5949 | |
Justin Clark-Casey (justincc) | 04ee863c33 | |
Oren Hurvitz | 16349c1368 | |
Justin Clark-Casey (justincc) | 7caf21c8a8 | |
Justin Clark-Casey (justincc) | c98d215242 | |
Justin Clark-Casey (justincc) | 76cfab35a5 | |
Justin Clark-Casey (justincc) | 2e02f49fd9 | |
Justin Clark-Casey (justincc) | 548deb9153 | |
Justin Clark-Casey (justincc) | 150c4faa79 | |
nebadon | 6693ef8288 | |
Justin Clark-Casey (justincc) | ba93e36fce | |
Justin Clark-Casey (justincc) | 9ab580d1ea | |
Justin Clark-Casey (justincc) | 7deb2d9646 | |
Justin Clark-Casey (justincc) | c77728ebf8 | |
Justin Clark-Casey (justincc) | 83487bfbec | |
Justin Clark-Casey (justincc) | e6f475735f | |
Justin Clark-Casey (justincc) | 48486b137a | |
Justin Clark-Casey (justincc) | 4490020197 | |
Justin Clark-Casey (justincc) | 770caad0ad | |
Justin Clark-Casey (justincc) | 13e29ae3f7 | |
Justin Clark-Casey (justincc) | b52ac8c5d1 | |
Justin Clark-Casey (justincc) | 97a907ad84 | |
Justin Clark-Casey (justincc) | 7ced077821 | |
Justin Clark-Casey (justincc) | fdbd448cc7 | |
Justin Clark-Casey (justincc) | d8c88f4894 | |
Justin Clark-Casey (justincc) | d558bbfa35 | |
Justin Clark-Casey (justincc) | 992dc1c2c7 | |
Justin Clark-Casey (justincc) | ce1d3e9c96 | |
Justin Clark-Casey (justincc) | a8a712e1f8 | |
Justin Clark-Casey (justincc) | 5a71cbe530 | |
Justin Clark-Casey (justincc) | c2bdb36c11 | |
Justin Clark-Casey (justincc) | 6d03a5d01b | |
Justin Clark-Casey (justincc) | ad75cb2682 | |
Justin Clark-Casey (justincc) | f46478f1df | |
Justin Clark-Casey (justincc) | 7c811c39c8 | |
Justin Clark-Casey (justincc) | e18d6c0956 | |
Justin Clark-Casey (justincc) | a7af8345a5 | |
Justin Clark-Casey (justincc) | b5ecc31096 | |
Justin Clark-Casey (justincc) | 3a297c7fe6 | |
Justin Clark-Casey (justincc) | c3243ce0ce | |
Justin Clark-Casey (justincc) | ace552ecc5 | |
Justin Clark-Casey (justincc) | 1d75570c59 | |
Justin Clark-Casey (justincc) | 70ccc63b83 | |
Justin Clark-Casey (justincc) | 31373ee099 | |
Justin Clark-Casey (justincc) | afaf7b5b94 | |
Justin Clark-Casey (justincc) | 6d0f66ae87 | |
Justin Clark-Casey (justincc) | 207233335e | |
Justin Clark-Casey (justincc) | e9fbfd0905 | |
Justin Clark-Casey (justincc) | c35f974637 | |
Justin Clark-Casey (justincc) | a4007cbe71 | |
Justin Clark-Casey (justincc) | d70e0b1189 | |
Justin Clark-Casey (justincc) | a9ce40a722 | |
Justin Clark-Casey (justincc) | 8d207fd8e6 | |
Justin Clark-Casey (justincc) | 2487adf0b1 | |
Justin Clark-Casey (justincc) | 4aa725f60b | |
Justin Clark-Casey (justincc) | 0686a2fba9 | |
Justin Clark-Casey (justincc) | b6305011db | |
Justin Clark-Casey (justincc) | 9cdf5199df | |
Justin Clark-Casey (justincc) | 632dad337b | |
Justin Clark-Casey (justincc) | 08234d0097 | |
Justin Clark-Casey (justincc) | d68ba391fc | |
Justin Clark-Casey (justincc) | 603a140eb7 | |
Justin Clark-Casey (justincc) | fd31f05cf0 | |
Justin Clark-Casey (justincc) | a8152c57b3 | |
Justin Clark-Casey (justincc) | 4753d14a19 | |
Justin Clark-Casey (justincc) | ee5e61d448 | |
Justin Clark-Casey (justincc) | 41b76c4b9e | |
Justin Clark-Casey (justincc) | 314de0fc49 | |
Justin Clark-Casey (justincc) | 93f7e4fb9d | |
Justin Clark-Casey (justincc) | f8adf4de2f | |
Justin Clark-Casey (justincc) | 08f0274b5a | |
Justin Clark-Casey (justincc) | 94748aab84 | |
Justin Clark-Casey (justincc) | 468d1cf03e | |
Justin Clark-Casey (justincc) | e8e6bc0c6a | |
Justin Clark-Casey (justincc) | c7b9d460e0 | |
Justin Clark-Casey (justincc) | e263368656 | |
Justin Clark-Casey (justincc) | 4b6a9d107d | |
Justin Clark-Casey (justincc) | ee5454fd4a | |
Justin Clark-Casey (justincc) | 38d2d6a20c | |
Justin Clark-Casey (justincc) | 7e493b9665 | |
Justin Clark-Casey (justincc) | b7c1a37676 | |
Justin Clark-Casey (justincc) | 4fe042aa7f | |
Justin Clark-Casey (justincc) | a83218fd08 | |
Justin Clark-Casey (justincc) | 221af2da70 | |
Diva Canto | c6c6ac0a3e | |
Justin Clark-Casey (justincc) | a5fdfd6343 | |
Diva Canto | 72e0c77f91 | |
Justin Clark-Casey (justincc) | 862f595e5d | |
Justin Clark-Casey (justincc) | db045e69f9 | |
Justin Clark-Casey (justincc) | 6a54cb871b | |
Justin Clark-Casey (justincc) | 3619a83b28 | |
Justin Clark-Casey (justincc) | e3927d2868 | |
Justin Clark-Casey (justincc) | fe503de17a | |
SignpostMarv | 5498a64a9f | |
SignpostMarv | 3148a231db | |
Justin Clark-Casey (justincc) | bffdf20721 | |
Justin Clark-Casey (justincc) | a4b64d4854 | |
Melanie | fe195e0a9c | |
Justin Clark-Casey (justincc) | 9d70f48207 | |
Justin Clark-Casey (justincc) | d83df8c1cf | |
Justin Clark-Casey (justincc) | c7c6c12146 | |
SignpostMarv | e83a4c24ee | |
Justin Clark-Casey (justincc) | a1669be6c3 | |
SignpostMarv | e36799f515 | |
Justin Clark-Casey (justincc) | 186fe5f7b0 | |
Justin Clark-Casey (justincc) | 27206eccef | |
Oren Hurvitz | 62c0d60d3e | |
Oren Hurvitz | c39342df3e | |
Justin Clark-Casey (justincc) | 458a103529 | |
Justin Clark-Casey (justincc) | c22a37e7a6 | |
Justin Clark-Casey (justincc) | 01a8a65d75 | |
Justin Clark-Casey (justincc) | 28ced402d8 | |
Justin Clark-Casey (justincc) | 95e35fe84a | |
Justin Clark-Casey (justincc) | 3692ff2bdb | |
Justin Clark-Casey (justincc) | 7235a5fed4 | |
Justin Clark-Casey (justincc) | 614e5b52b8 | |
Justin Clark-Casey (justincc) | 35ab31fb5f | |
Justin Clark-Casey (justincc) | 636994eea6 | |
Justin Clark-Casey (justincc) | 146ac5ceda | |
Justin Clark-Casey (justincc) | 5413bfec30 | |
Justin Clark-Casey (justincc) | f39c2cd714 | |
Justin Clark-Casey (justincc) | e0ef2bdf81 | |
PixelTomsen | efc09e8022 | |
Justin Clark-Casey (justincc) | 1790355f3e | |
Justin Clark-Casey (justincc) | d0d004c6ff | |
Justin Clark-Casey (justincc) | ee4abb681e | |
Justin Clark-Casey (justincc) | 9e3605d952 | |
Justin Clark-Casey (justincc) | 0df4927710 | |
Justin Clark-Casey (justincc) | 9d4415429d | |
SignpostMarv | c86ac36876 | |
SignpostMarv | 4a4d800523 | |
Justin Clark-Casey (justincc) | 01c148bbce | |
Justin Clark-Casey (justincc) | 1cbe4363a3 | |
Justin Clark-Casey (justincc) | c0254d914f | |
Justin Clark-Casey (justincc) | 53112f1e80 | |
Justin Clark-Casey (justincc) | 0f312c58ca | |
Justin Clark-Casey (justincc) | 2ca520c1eb | |
Justin Clark-Casey (justincc) | c9b7ac2ae5 | |
Justin Clark-Casey (justincc) | 6e2ffd7050 | |
Justin Clark-Casey (justincc) | 8726748e22 | |
Justin Clark-Casey (justincc) | 73c2db9e8f | |
Justin Clark-Casey (justincc) | c1cab3e752 | |
Justin Clark-Casey (justincc) | 713fdda7f8 | |
Justin Clark-Casey (justincc) | baa599e5b0 | |
Justin Clark-Casey (justincc) | 014c533ebe | |
Justin Clark-Casey (justincc) | 53d87510ce | |
Justin Clark-Casey (justincc) | 43aa74d139 | |
Justin Clark-Casey (justincc) | ada9238907 | |
Justin Clark-Casey (justincc) | 4d98cdf829 | |
Justin Clark-Casey (justincc) | 547043047e | |
Justin Clark-Casey (justincc) | 9d3b68411a | |
Justin Clark-Casey (justincc) | 470f8d3c04 | |
Justin Clark-Casey (justincc) | da00b57d4b | |
Justin Clark-Casey (justincc) | caedac67e0 | |
Justin Clark-Casey (justincc) | 2b5dc4eba4 | |
Justin Clark-Casey (justincc) | 39b8d01e71 | |
Justin Clark-Casey (justincc) | fca3154982 | |
Justin Clark-Casey (justincc) | cef158c42d | |
Justin Clark-Casey (justincc) | 177c3bcfe6 | |
Justin Clark-Casey (justincc) | 35766f2c3a | |
Justin Clark-Casey (justincc) | e175cf543c | |
Justin Clark-Casey (justincc) | 96b0d1276e | |
Justin Clark-Casey (justincc) | 301546289e | |
Justin Clark-Casey (justincc) | 813778b39e | |
Justin Clark-Casey (justincc) | 6c3eceb197 | |
Justin Clark-Casey (justincc) | 0afc43eed7 | |
Justin Clark-Casey (justincc) | 6eb260d4eb | |
Justin Clark-Casey (justincc) | 63f1efc414 | |
Justin Clark-Casey (justincc) | e685f7ab68 | |
Justin Clark-Casey (justincc) | ac8f420adb | |
SignpostMarv | fa16f132e3 | |
Justin Clark-Casey (justincc) | 7a5782c3e3 | |
Justin Clark-Casey (justincc) | cf98691a3d | |
Justin Clark-Casey (justincc) | 09aa87ba26 | |
Justin Clark-Casey (justincc) | 38fb0430bf | |
Justin Clark-Casey (justincc) | ec82e2fde8 | |
Justin Clark-Casey (justincc) | fba2864cbc | |
Justin Clark-Casey (justincc) | 60c6d3e108 | |
Justin Clark-Casey (justincc) | 92f9750ded | |
Justin Clark-Casey (justincc) | 23106bce76 | |
Justin Clark-Casey (justincc) | 15acba1972 | |
Justin Clark-Casey (justincc) | 909d4dca82 | |
Justin Clark-Casey (justincc) | 5676f1d037 | |
Justin Clark-Casey (justincc) | d864d76254 | |
Justin Clark-Casey (justincc) | 9ff4d38cc9 | |
Justin Clark-Casey (justincc) | 8dadbf706d | |
Justin Clark-Casey (justincc) | ff968cbe43 | |
Justin Clark-Casey (justincc) | 582540657f | |
Justin Clark-Casey (justincc) | 05eeee6ee8 | |
Justin Clark-Casey (justincc) | cd6f3b147d | |
Justin Clark-Casey (justincc) | 86f519ba57 | |
Justin Clark-Casey (justincc) | febc6bae30 | |
Justin Clark-Casey (justincc) | 48f818bf07 | |
Justin Clark-Casey (justincc) | c2d21bb8cc | |
Justin Clark-Casey (justincc) | 39fe1ba028 | |
Justin Clark-Casey (justincc) | 7112e860dc | |
Justin Clark-Casey (justincc) | 4606137882 | |
Justin Clark-Casey (justincc) | 8eab6b7701 | |
Justin Clark-Casey (justincc) | 71cd68eec1 | |
Justin Clark-Casey (justincc) | 168c3b78f4 | |
Justin Clark-Casey (justincc) | d175e49e15 | |
Justin Clark-Casey (justincc) | 79624d762e | |
Justin Clark-Casey (justincc) | 3ec9eec257 | |
Justin Clark-Casey (justincc) | e5ac4a72b7 | |
Justin Clark-Casey (justincc) | 470ea6cf70 | |
Justin Clark-Casey (justincc) | 45dc7ac7d1 | |
Justin Clark-Casey (justincc) | 2905eaa8ff | |
Justin Clark-Casey (justincc) | deb068050b | |
Justin Clark-Casey (justincc) | 8f803c5a7d | |
Justin Clark-Casey (justincc) | 601c7998eb | |
Justin Clark-Casey (justincc) | 5a686d40de | |
Justin Clark-Casey (justincc) | 57837a1e81 | |
Justin Clark-Casey (justincc) | 5162ebd2cc | |
Justin Clark-Casey (justincc) | 1f869ab36d | |
Justin Clark-Casey (justincc) | e912e52e15 | |
Justin Clark-Casey (justincc) | 14b0c03d5b | |
Justin Clark-Casey (justincc) | 8414ec9429 | |
Justin Clark-Casey (justincc) | ac03b1b82f | |
Justin Clark-Casey (justincc) | d5c999553e | |
Justin Clark-Casey (justincc) | 371df42d3f | |
Justin Clark-Casey (justincc) | a4a0396850 | |
Justin Clark-Casey (justincc) | 83ad75b997 | |
Justin Clark-Casey (justincc) | 3f45d4ba9d | |
Justin Clark-Casey (justincc) | 2fa8bc201a | |
Justin Clark-Casey (justincc) | e6b99ec849 | |
Justin Clark-Casey (justincc) | e5c665384c | |
Justin Clark-Casey (justincc) | 5bb1273b3d | |
Justin Clark-Casey (justincc) | 5629f5141e | |
SignpostMarv | 15aef01b34 | |
SignpostMarv | 67a010298f | |
SignpostMarv | 7c398a532b | |
Justin Clark-Casey (justincc) | eefd39a0d5 | |
Justin Clark-Casey (justincc) | 176b1c85c0 | |
Justin Clark-Casey (justincc) | 2da6cfde80 | |
Justin Clark-Casey (justincc) | 8068c083f6 | |
Justin Clark-Casey (justincc) | bbe04aab95 | |
Justin Clark-Casey (justincc) | d31a951d94 | |
Justin Clark-Casey (justincc) | c27a158961 | |
SignpostMarv | 068e97cf0e | |
SignpostMarv | aa45d831f3 | |
SignpostMarv | eff8448154 | |
SignpostMarv | badd7f1578 | |
SignpostMarv | e12cb7bca9 | |
Justin Clark-Casey (justincc) | 23fe2a2103 | |
Justin Clark-Casey (justincc) | e684e426af | |
Justin Clark-Casey (justincc) | e65959b6f7 | |
Justin Clark-Casey (justincc) | 8041af6d18 | |
SignpostMarv | 56873b319b | |
SignpostMarv | b315fab771 | |
Justin Clark-Casey (justincc) | 05342ed677 | |
SignpostMarv | 487bf31e06 | |
SignpostMarv | 6f3b2ea632 | |
SignpostMarv | 307fdeff78 | |
SignpostMarv | 8df3edcc30 | |
SignpostMarv | a43e282efa | |
SignpostMarv | 7374690957 | |
SignpostMarv | 0c69575670 | |
SignpostMarv | 148cad1976 | |
SignpostMarv | 7db4e00b14 | |
BlueWall | a00f745eba | |
SignpostMarv | 9ae335fafc | |
Justin Clark-Casey (justincc) | 6bd7639d66 | |
Justin Clark-Casey (justincc) | c336309234 | |
SignpostMarv | 41d217321d | |
Justin Clark-Casey (justincc) | 2226dd1efb | |
Justin Clark-Casey (justincc) | 44699b07e4 | |
Justin Clark-Casey (justincc) | 6739f0752b | |
Justin Clark-Casey (justincc) | 1ebde81d6a | |
Justin Clark-Casey (justincc) | 195625b5e7 | |
Justin Clark-Casey (justincc) | 951c2702ff | |
SignpostMarv | 02b90df6ff | |
SignpostMarv | f7c01c8e90 | |
SignpostMarv | e8c964efaf | |
SignpostMarv | 74baab6f0f | |
SignpostMarv | bff0ea2fc3 | |
Justin Clark-Casey (justincc) | e474c19c40 | |
Justin Clark-Casey (justincc) | a2a81f1bb7 | |
Justin Clark-Casey (justincc) | c291284629 | |
Justin Clark-Casey (justincc) | 7f7ad71def | |
justincc | 7dfc0f30cc | |
Justin Clark-Casey (justincc) | c14e67a8bb | |
Justin Clark-Casey (justincc) | 75b7e6009a | |
Justin Clark-Casey (justincc) | 63df56d613 | |
SignpostMarv | f2e1f3e659 | |
SignpostMarv | b1b7d3b004 | |
Justin Clark-Casey (justincc) | f2c12d4fba | |
Justin Clark-Casey (justincc) | 3a60c9c8c8 | |
Justin Clark-Casey (justincc) | 44cd864205 | |
SignpostMarv | f70d7013a5 | |
Mic Bowman | ac1e902d59 | |
Justin Clark-Casey (justincc) | b67bdee3cf | |
Justin Clark-Casey (justincc) | 83d4fa98f0 | |
SignpostMarv | 70a968b342 | |
SignpostMarv | 8e576f7511 | |
SignpostMarv | 4526424436 | |
SignpostMarv | fd43620e87 | |
SignpostMarv | 56b8d331cb | |
SignpostMarv | 14bfba8b3b | |
SignpostMarv | af8d3ae790 | |
SignpostMarv | e6bb7e99be | |
SignpostMarv | 97c56adb9d | |
SignpostMarv | f8612b0d1b | |
SignpostMarv | a2a46a18ae | |
Justin Clark-Casey (justincc) | 1cf888b71f | |
Justin Clark-Casey (justincc) | 0109603cdc | |
SignpostMarv | c7c750d127 | |
SignpostMarv | b738b50cd6 | |
SignpostMarv | 351daf90f1 | |
BlueWall | 19d9acf63a | |
Justin Clark-Casey (justincc) | eb40d3b6fc | |
BlueWall | f5da23d9db | |
SignpostMarv | b412bce095 | |
Justin Clark-Casey (justincc) | 0b57ddd753 | |
Justin Clark-Casey (justincc) | d0a6d82a23 | |
Justin Clark-Casey (justincc) | ba2792bd1f | |
Justin Clark-Casey (justincc) | 3f8c09e006 | |
SignpostMarv | 8f39268761 | |
Justin Clark-Casey (justincc) | cd8c8d78a9 | |
Justin Clark-Casey (justincc) | 980678846d | |
Justin Clark-Casey (justincc) | 9a92d8d57e | |
Justin Clark-Casey (justincc) | b155c601d7 | |
Justin Clark-Casey (justincc) | 536f2c085a | |
SignpostMarv | 2602d4f16e | |
SignpostMarv | 174addc426 | |
Justin Clark-Casey (justincc) | 849053681e | |
Justin Clark-Casey (justincc) | a8d8ea7990 | |
Justin Clark-Casey (justincc) | 10e6493f9f | |
Melanie | 8c4f911935 | |
Justin Clark-Casey (justincc) | 62bc85b5c7 | |
SignpostMarv | 76eba917f9 | |
SignpostMarv | 84a046eaf5 | |
Justin Clark-Casey (justincc) | 8d1d314f49 | |
Justin Clark-Casey (justincc) | 9395f12584 | |
Justin Clark-Casey (justincc) | b97f58d597 | |
Justin Clark-Casey (justincc) | 30c36a3960 | |
Justin Clark-Casey (justincc) | 206eccc2b6 | |
Justin Clark-Casey (justincc) | 2358a623e3 | |
Justin Clark-Casey (justincc) | 87850bd6dc | |
Justin Clark-Casey (justincc) | d80eda202f | |
Justin Clark-Casey (justincc) | 3edfa585ec | |
Justin Clark-Casey (justincc) | fa13a6a8da | |
Justin Clark-Casey (justincc) | 9d5e7c89d9 | |
SignpostMarv | 590bf0bcc0 | |
Justin Clark-Casey (justincc) | b199a2dea3 | |
Justin Clark-Casey (justincc) | f1d4b8d83e | |
Justin Clark-Casey (justincc) | 0088661356 | |
Justin Clark-Casey (justincc) | 1a7e3cabc0 | |
Justin Clark-Casey (justincc) | 132d701b3e | |
Justin Clark-Casey (justincc) | 3b97241716 | |
Melanie | f82d090df3 | |
Justin Clark-Casey (justincc) | 7399d3e953 | |
Justin Clark-Casey (justincc) | cab546ecee | |
Justin Clark-Casey (justincc) | 7dc1c7d841 | |
Robert Adams | fbff51f387 | |
SignpostMarv | b714fb0c39 | |
Justin Clark-Casey (justincc) | f37038013d | |
SignpostMarv | 5e98e2b7c7 | |
Justin Clark-Casey (justincc) | 35f8f3ff79 | |
Justin Clark-Casey (justincc) | 7ec8e7e025 | |
Melanie | c6efebdd8c | |
Justin Clark-Casey (justincc) | 760047abc5 |
|
@ -26,9 +26,14 @@
|
||||||
bin/Debug/*.dll
|
bin/Debug/*.dll
|
||||||
bin/*.dll.mdb
|
bin/*.dll.mdb
|
||||||
bin/*.db
|
bin/*.db
|
||||||
|
bin/*.db-journal
|
||||||
bin/addin-db-*
|
bin/addin-db-*
|
||||||
bin/*.dll
|
bin/*.dll
|
||||||
bin/OpenSim.vshost.exe.config
|
bin/OpenSim.vshost.exe.config
|
||||||
|
bin/OpenSim.32BitLaunch.vshost.exe.config
|
||||||
|
bin/OpenSim.32BitLaunch.log
|
||||||
|
UpgradeLog.XML
|
||||||
|
_UpgradeReport_Files/
|
||||||
bin/ScriptEngines/*-*-*-*-*
|
bin/ScriptEngines/*-*-*-*-*
|
||||||
bin/ScriptEngines/*.dll
|
bin/ScriptEngines/*.dll
|
||||||
bin/ScriptEngines/*/*.dll
|
bin/ScriptEngines/*/*.dll
|
||||||
|
@ -61,6 +66,7 @@ Examples/*.dll
|
||||||
OpenSim.build
|
OpenSim.build
|
||||||
OpenSim.sln
|
OpenSim.sln
|
||||||
OpenSim.suo
|
OpenSim.suo
|
||||||
|
OpenSim.userprefs
|
||||||
Prebuild/Prebuild.build
|
Prebuild/Prebuild.build
|
||||||
Prebuild/Prebuild.sln
|
Prebuild/Prebuild.sln
|
||||||
TestResult.xml
|
TestResult.xml
|
||||||
|
@ -89,3 +95,5 @@ OpenSim/Region/ScriptEngine/test-results/
|
||||||
OpenSim/Tests/Common/test-results/
|
OpenSim/Tests/Common/test-results/
|
||||||
OpenSim/Tests/test-results/
|
OpenSim/Tests/test-results/
|
||||||
test-results/
|
test-results/
|
||||||
|
doc/html
|
||||||
|
doc/doxygen.error.log
|
||||||
|
|
|
@ -43,10 +43,11 @@
|
||||||
<delete dir="${distbindir}/Prebuild"/>
|
<delete dir="${distbindir}/Prebuild"/>
|
||||||
<delete dir="${distbindir}/%temp%"/>
|
<delete dir="${distbindir}/%temp%"/>
|
||||||
<delete dir="${distbindir}/.nant"/>
|
<delete dir="${distbindir}/.nant"/>
|
||||||
|
<delete dir="${distbindir}/ThirdParty"/>
|
||||||
<delete>
|
<delete>
|
||||||
<fileset basedir="${distbindir}">
|
<fileset basedir="${distbindir}">
|
||||||
<include name="compile.bat"/>
|
<include name="compile.bat"/>
|
||||||
<include name="BUILDING.txt"/>
|
<include name="BUILDING.md"/>
|
||||||
<include name="Makefile"/>
|
<include name="Makefile"/>
|
||||||
<include name="nant-color"/>
|
<include name="nant-color"/>
|
||||||
<include name="OpenSim.*"/>
|
<include name="OpenSim.*"/>
|
||||||
|
@ -132,17 +133,38 @@
|
||||||
</exec>
|
</exec>
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
|
||||||
|
|
||||||
|
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.server.handlers.tests">
|
||||||
|
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
|
||||||
|
</exec>
|
||||||
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.server.handlers.tests)==0}" />
|
||||||
|
|
||||||
|
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.services.inventoryservice.tests">
|
||||||
|
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
|
||||||
|
</exec>
|
||||||
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.services.inventoryservice.tests)==0}" />
|
||||||
|
|
||||||
<delete dir="%temp%"/>
|
<delete dir="%temp%"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="torture" depends="build, find-nunit">
|
<target name="test-stress" depends="build, find-nunit">
|
||||||
<setenv name="MONO_THREADS_PER_CPU" value="100" />
|
<setenv name="MONO_THREADS_PER_CPU" value="100" />
|
||||||
|
|
||||||
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.torture">
|
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.stress">
|
||||||
<arg value="./bin/OpenSim.Tests.Torture.dll" />
|
<arg value="./bin/OpenSim.Tests.Stress.dll" />
|
||||||
</exec>
|
</exec>
|
||||||
|
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests.torture)==0}" />
|
<fail message="Failures reported in stress tests." unless="${int::parse(testresult.opensim.tests.stress)==0}" />
|
||||||
|
<delete dir="%temp%"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="test-perf" depends="build, find-nunit">
|
||||||
|
<setenv name="MONO_THREADS_PER_CPU" value="100" />
|
||||||
|
|
||||||
|
<exec program="${nunitcmd}" failonerror="true" resultproperty="testresult.opensim.tests.performance">
|
||||||
|
<arg value="./bin/OpenSim.Tests.Performance.dll" />
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<fail message="Failures reported in performance tests." unless="${int::parse(testresult.opensim.tests.performance)==0}" />
|
||||||
<delete dir="%temp%"/>
|
<delete dir="%temp%"/>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
@ -224,6 +246,16 @@
|
||||||
<arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" />
|
<arg value="-xml=test-results/OpenSim.Capabilities.Handlers.Tests.dll-Results.xml" />
|
||||||
</exec>
|
</exec>
|
||||||
|
|
||||||
|
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.server.handlers.tests">
|
||||||
|
<arg value="./bin/OpenSim.Server.Handlers.Tests.dll" />
|
||||||
|
<arg value="-xml=test-results/OpenSim.Server.Handlers.Tests.dll-Results.xml" />
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec program="${nunitcmd}" failonerror="false" resultproperty="testresult.opensim.services.inventoryservice.tests">
|
||||||
|
<arg value="./bin/OpenSim.Services.InventoryService.Tests.dll" />
|
||||||
|
<arg value="-xml=test-results/OpenSim.Services.InventoryService.Tests.dll-Results.xml" />
|
||||||
|
</exec>
|
||||||
|
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.tests)==0}" />
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.tests)==0}" />
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.servers.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.framework.servers.tests)==0}" />
|
||||||
|
@ -234,6 +266,7 @@
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.framework.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.region.framework.tests)==0}" />
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.data.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.data.tests)==0}" />
|
||||||
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.capabilities.handlers.tests)==0}" />
|
||||||
|
<fail message="Failures reported in unit tests." unless="${int::parse(testresult.opensim.services.inventoryservice.tests)==0}" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="doxygen">
|
<target name="doxygen">
|
||||||
|
|
|
@ -91,6 +91,7 @@ what it is today.
|
||||||
* Fly-Man
|
* Fly-Man
|
||||||
* Flyte Xevious
|
* Flyte Xevious
|
||||||
* Garmin Kawaguichi
|
* Garmin Kawaguichi
|
||||||
|
* Gryc Ueusp
|
||||||
* Imaze Rhiano
|
* Imaze Rhiano
|
||||||
* Intimidated
|
* Intimidated
|
||||||
* Jeremy Bongio (IBM)
|
* Jeremy Bongio (IBM)
|
||||||
|
|
|
@ -136,6 +136,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
availableMethods["admin_save_heightmap"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcSaveHeightmapMethod);
|
availableMethods["admin_save_heightmap"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcSaveHeightmapMethod);
|
||||||
|
|
||||||
// Agent management
|
// Agent management
|
||||||
|
availableMethods["admin_get_agents"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcGetAgentsMethod);
|
||||||
availableMethods["admin_teleport_agent"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcTeleportAgentMethod);
|
availableMethods["admin_teleport_agent"] = (req, ep) => InvokeXmlRpcMethod(req, ep, XmlRpcTeleportAgentMethod);
|
||||||
|
|
||||||
// User management
|
// User management
|
||||||
|
@ -572,7 +573,7 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
|
|
||||||
region.ExternalHostName = (string) requestData["external_address"];
|
region.ExternalHostName = (string) requestData["external_address"];
|
||||||
|
|
||||||
bool persist = Convert.ToBoolean((string) requestData["persist"]);
|
bool persist = Convert.ToBoolean(requestData["persist"]);
|
||||||
if (persist)
|
if (persist)
|
||||||
{
|
{
|
||||||
// default place for region configuration files is in the
|
// default place for region configuration files is in the
|
||||||
|
@ -982,8 +983,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
string lastName = (string) requestData["user_lastname"];
|
string lastName = (string) requestData["user_lastname"];
|
||||||
string password = (string) requestData["user_password"];
|
string password = (string) requestData["user_password"];
|
||||||
|
|
||||||
uint regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
|
uint regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
|
||||||
uint regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
|
uint regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
|
||||||
|
|
||||||
string email = ""; // empty string for email
|
string email = ""; // empty string for email
|
||||||
if (requestData.Contains("user_email"))
|
if (requestData.Contains("user_email"))
|
||||||
|
@ -1180,9 +1181,9 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
|
|
||||||
if (requestData.ContainsKey("user_password")) password = (string) requestData["user_password"];
|
if (requestData.ContainsKey("user_password")) password = (string) requestData["user_password"];
|
||||||
if (requestData.ContainsKey("start_region_x"))
|
if (requestData.ContainsKey("start_region_x"))
|
||||||
regionXLocation = Convert.ToUInt32((Int32) requestData["start_region_x"]);
|
regionXLocation = Convert.ToUInt32(requestData["start_region_x"]);
|
||||||
if (requestData.ContainsKey("start_region_y"))
|
if (requestData.ContainsKey("start_region_y"))
|
||||||
regionYLocation = Convert.ToUInt32((Int32) requestData["start_region_y"]);
|
regionYLocation = Convert.ToUInt32(requestData["start_region_y"]);
|
||||||
|
|
||||||
// if (requestData.ContainsKey("start_lookat_x"))
|
// if (requestData.ContainsKey("start_lookat_x"))
|
||||||
// ulaX = Convert.ToUInt32((Int32) requestData["start_lookat_x"]);
|
// ulaX = Convert.ToUInt32((Int32) requestData["start_lookat_x"]);
|
||||||
|
@ -1753,6 +1754,71 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
m_log.Info("[RADMIN]: Access List List Request complete");
|
m_log.Info("[RADMIN]: Access List List 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<ScenePresence> 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)
|
private void XmlRpcTeleportAgentMethod(XmlRpcRequest request, XmlRpcResponse response, IPEndPoint remoteClient)
|
||||||
{
|
{
|
||||||
Hashtable responseData = (Hashtable)response.Value;
|
Hashtable responseData = (Hashtable)response.Value;
|
||||||
|
@ -2381,7 +2447,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
destinationItem.Description = item.Description;
|
destinationItem.Description = item.Description;
|
||||||
destinationItem.InvType = item.InvType;
|
destinationItem.InvType = item.InvType;
|
||||||
destinationItem.CreatorId = item.CreatorId;
|
destinationItem.CreatorId = item.CreatorId;
|
||||||
destinationItem.CreatorIdAsUuid = item.CreatorIdAsUuid;
|
|
||||||
destinationItem.CreatorData = item.CreatorData;
|
destinationItem.CreatorData = item.CreatorData;
|
||||||
destinationItem.NextPermissions = item.NextPermissions;
|
destinationItem.NextPermissions = item.NextPermissions;
|
||||||
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
||||||
|
@ -2436,7 +2501,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
destinationItem.Description = item.Description;
|
destinationItem.Description = item.Description;
|
||||||
destinationItem.InvType = item.InvType;
|
destinationItem.InvType = item.InvType;
|
||||||
destinationItem.CreatorId = item.CreatorId;
|
destinationItem.CreatorId = item.CreatorId;
|
||||||
destinationItem.CreatorIdAsUuid = item.CreatorIdAsUuid;
|
|
||||||
destinationItem.CreatorData = item.CreatorData;
|
destinationItem.CreatorData = item.CreatorData;
|
||||||
destinationItem.NextPermissions = item.NextPermissions;
|
destinationItem.NextPermissions = item.NextPermissions;
|
||||||
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
||||||
|
@ -2549,7 +2613,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
destinationItem.Description = item.Description;
|
destinationItem.Description = item.Description;
|
||||||
destinationItem.InvType = item.InvType;
|
destinationItem.InvType = item.InvType;
|
||||||
destinationItem.CreatorId = item.CreatorId;
|
destinationItem.CreatorId = item.CreatorId;
|
||||||
destinationItem.CreatorIdAsUuid = item.CreatorIdAsUuid;
|
|
||||||
destinationItem.CreatorData = item.CreatorData;
|
destinationItem.CreatorData = item.CreatorData;
|
||||||
destinationItem.NextPermissions = item.NextPermissions;
|
destinationItem.NextPermissions = item.NextPermissions;
|
||||||
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
destinationItem.CurrentPermissions = item.CurrentPermissions;
|
||||||
|
@ -2855,7 +2918,6 @@ namespace OpenSim.ApplicationPlugins.RemoteController
|
||||||
inventoryItem.Description = GetStringAttribute(item,"desc","");
|
inventoryItem.Description = GetStringAttribute(item,"desc","");
|
||||||
inventoryItem.InvType = GetIntegerAttribute(item,"invtype",-1);
|
inventoryItem.InvType = GetIntegerAttribute(item,"invtype",-1);
|
||||||
inventoryItem.CreatorId = GetStringAttribute(item,"creatorid","");
|
inventoryItem.CreatorId = GetStringAttribute(item,"creatorid","");
|
||||||
inventoryItem.CreatorIdAsUuid = (UUID)GetStringAttribute(item,"creatoruuid","");
|
|
||||||
inventoryItem.CreatorData = GetStringAttribute(item, "creatordata", "");
|
inventoryItem.CreatorData = GetStringAttribute(item, "creatordata", "");
|
||||||
inventoryItem.NextPermissions = GetUnsignedAttribute(perms, "next", 0x7fffffff);
|
inventoryItem.NextPermissions = GetUnsignedAttribute(perms, "next", 0x7fffffff);
|
||||||
inventoryItem.CurrentPermissions = GetUnsignedAttribute(perms,"current",0x7fffffff);
|
inventoryItem.CurrentPermissions = GetUnsignedAttribute(perms,"current",0x7fffffff);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,11 +0,0 @@
|
||||||
<Addin id="OpenSim.ApplicationPlugins.Rest.Inventory" version="0.1">
|
|
||||||
<Runtime>
|
|
||||||
<Import assembly="OpenSim.ApplicationPlugins.Rest.Inventory.dll"/>
|
|
||||||
</Runtime>
|
|
||||||
<Dependencies>
|
|
||||||
<Addin id="OpenSim" version="0.5" />
|
|
||||||
</Dependencies>
|
|
||||||
<Extension path = "/OpenSim/Startup">
|
|
||||||
<Plugin id="RestInventory" type="OpenSim.ApplicationPlugins.Rest.Inventory.RestHandler" />
|
|
||||||
</Extension>
|
|
||||||
</Addin>
|
|
|
@ -1,551 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using log4net;
|
|
||||||
using Nini.Config;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Communications;
|
|
||||||
using OpenSim.Services.Interfaces;
|
|
||||||
using IAvatarService = OpenSim.Services.Interfaces.IAvatarService;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
public class Rest
|
|
||||||
{
|
|
||||||
internal static readonly ILog Log =
|
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
internal static bool DEBUG = Log.IsDebugEnabled;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Supported authentication schemes
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public const string AS_BASIC = "Basic"; // simple user/password verification
|
|
||||||
public const string AS_DIGEST = "Digest"; // password safe authentication
|
|
||||||
|
|
||||||
/// Supported Digest algorithms
|
|
||||||
|
|
||||||
public const string Digest_MD5 = "MD5"; // assumed default if omitted
|
|
||||||
public const string Digest_MD5Sess = "MD5-sess"; // session-span - not good for REST?
|
|
||||||
|
|
||||||
public const string Qop_Auth = "auth"; // authentication only
|
|
||||||
public const string Qop_Int = "auth-int"; // TODO
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These values have a single value for the whole
|
|
||||||
/// domain and lifetime of the plugin handler. We
|
|
||||||
/// make them static for ease of reference within
|
|
||||||
/// the assembly. These are initialized by the
|
|
||||||
/// RestHandler class during start-up.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static IRestHandler Plugin = null;
|
|
||||||
internal static OpenSimBase main = null;
|
|
||||||
internal static string Prefix = null;
|
|
||||||
internal static IConfig Config = null;
|
|
||||||
internal static string GodKey = null;
|
|
||||||
internal static bool Authenticate = true;
|
|
||||||
internal static bool Secure = true;
|
|
||||||
internal static bool ExtendedEscape = true;
|
|
||||||
internal static bool DumpAsset = false;
|
|
||||||
internal static bool Fill = true;
|
|
||||||
internal static bool FlushEnabled = true;
|
|
||||||
internal static string Realm = "OpenSim REST";
|
|
||||||
internal static string Scheme = AS_BASIC;
|
|
||||||
internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These are all dependent upon the Comms manager
|
|
||||||
/// being initialized. So they have to be properties
|
|
||||||
/// because the comms manager is now a module and is
|
|
||||||
/// not guaranteed to be there when the rest handler
|
|
||||||
/// initializes.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static IInventoryService InventoryServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.InventoryService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IUserAccountService UserServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.UserAccountService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAuthenticationService AuthServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AuthenticationService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAvatarService AvatarServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AvatarService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IAssetService AssetServices
|
|
||||||
{
|
|
||||||
get { return main.SceneManager.CurrentOrFirstScene.AssetService; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTTP requires that status information be generated for PUT
|
|
||||||
/// and POST opertaions. This is in support of that. The
|
|
||||||
/// operation verb gets substituted into the first string,
|
|
||||||
/// and the completion code is inserted into the tail. The
|
|
||||||
/// strings are put here to encourage consistency.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal static string statusHead = "<html><body><title>{0} status</title><break>";
|
|
||||||
internal static string statusTail = "</body></html>";
|
|
||||||
|
|
||||||
internal static Dictionary<int,string> HttpStatusDesc;
|
|
||||||
|
|
||||||
static Rest()
|
|
||||||
{
|
|
||||||
HttpStatusDesc = new Dictionary<int,string>();
|
|
||||||
if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length)
|
|
||||||
{
|
|
||||||
Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match");
|
|
||||||
throw new Exception("HTTP Status array discrepancy");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Repackage the data into something more tractable. The sparse
|
|
||||||
// nature of HTTP return codes makes an array a bad choice.
|
|
||||||
|
|
||||||
for (int i=0; i<HttpStatusCodeArray.Length; i++)
|
|
||||||
{
|
|
||||||
HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int CreationDate
|
|
||||||
{
|
|
||||||
get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string MsgId
|
|
||||||
{
|
|
||||||
get { return Plugin.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string RequestId
|
|
||||||
{
|
|
||||||
get { return Plugin.RequestId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Encoding Encoding = Util.UTF8;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Version control for REST implementation. This
|
|
||||||
/// refers to the overall infrastructure represented
|
|
||||||
/// by the following classes
|
|
||||||
/// RequestData
|
|
||||||
/// RequestInventoryPlugin
|
|
||||||
/// Rest
|
|
||||||
/// It does no describe implementation classes such as
|
|
||||||
/// RestInventoryServices, which may morph much more
|
|
||||||
/// often. Such classes ARE dependent upon this however
|
|
||||||
/// and should check it in their Initialize method.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public static readonly float Version = 1.0F;
|
|
||||||
public const string Name = "REST 1.0";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Currently defined HTTP methods.
|
|
||||||
/// Only GET and HEAD are required to be
|
|
||||||
/// supported by all servers. See Respond
|
|
||||||
/// to see how these are handled.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// REST AGENT 1.0 interpretations
|
|
||||||
public const string GET = "get"; // information retrieval - server state unchanged
|
|
||||||
public const string HEAD = "head"; // same as get except only the headers are returned.
|
|
||||||
public const string POST = "post"; // Replace the URI designated resource with the entity.
|
|
||||||
public const string PUT = "put"; // Add the entity to the context represented by the URI
|
|
||||||
public const string DELETE = "delete"; // Remove the URI designated resource from the server.
|
|
||||||
|
|
||||||
public const string OPTIONS = "options"; //
|
|
||||||
public const string TRACE = "trace"; //
|
|
||||||
public const string CONNECT = "connect"; //
|
|
||||||
|
|
||||||
// Define this in one place...
|
|
||||||
|
|
||||||
public const string UrlPathSeparator = "/";
|
|
||||||
public const string UrlMethodSeparator = ":";
|
|
||||||
|
|
||||||
// Redirection qualifications
|
|
||||||
|
|
||||||
public const bool PERMANENT = false;
|
|
||||||
public const bool TEMPORARY = true;
|
|
||||||
|
|
||||||
// Constant arrays used by String.Split
|
|
||||||
|
|
||||||
public static readonly char C_SPACE = ' ';
|
|
||||||
public static readonly char C_SLASH = '/';
|
|
||||||
public static readonly char C_PATHSEP = '/';
|
|
||||||
public static readonly char C_COLON = ':';
|
|
||||||
public static readonly char C_PLUS = '+';
|
|
||||||
public static readonly char C_PERIOD = '.';
|
|
||||||
public static readonly char C_COMMA = ',';
|
|
||||||
public static readonly char C_DQUOTE = '"';
|
|
||||||
|
|
||||||
public static readonly string CS_SPACE = " ";
|
|
||||||
public static readonly string CS_SLASH = "/";
|
|
||||||
public static readonly string CS_PATHSEP = "/";
|
|
||||||
public static readonly string CS_COLON = ":";
|
|
||||||
public static readonly string CS_PLUS = "+";
|
|
||||||
public static readonly string CS_PERIOD = ".";
|
|
||||||
public static readonly string CS_COMMA = ",";
|
|
||||||
public static readonly string CS_DQUOTE = "\"";
|
|
||||||
|
|
||||||
public static readonly char[] CA_SPACE = { C_SPACE };
|
|
||||||
public static readonly char[] CA_SLASH = { C_SLASH };
|
|
||||||
public static readonly char[] CA_PATHSEP = { C_PATHSEP };
|
|
||||||
public static readonly char[] CA_COLON = { C_COLON };
|
|
||||||
public static readonly char[] CA_PERIOD = { C_PERIOD };
|
|
||||||
public static readonly char[] CA_PLUS = { C_PLUS };
|
|
||||||
public static readonly char[] CA_COMMA = { C_COMMA };
|
|
||||||
public static readonly char[] CA_DQUOTE = { C_DQUOTE };
|
|
||||||
|
|
||||||
// HTTP Code Values (in value order)
|
|
||||||
|
|
||||||
public const int HttpStatusCodeContinue = 100;
|
|
||||||
public const int HttpStatusCodeSwitchingProtocols = 101;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeOK = 200;
|
|
||||||
public const int HttpStatusCodeCreated = 201;
|
|
||||||
public const int HttpStatusCodeAccepted = 202;
|
|
||||||
public const int HttpStatusCodeNonAuthoritative = 203;
|
|
||||||
public const int HttpStatusCodeNoContent = 204;
|
|
||||||
public const int HttpStatusCodeResetContent = 205;
|
|
||||||
public const int HttpStatusCodePartialContent = 206;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeMultipleChoices = 300;
|
|
||||||
public const int HttpStatusCodePermanentRedirect = 301;
|
|
||||||
public const int HttpStatusCodeFound = 302;
|
|
||||||
public const int HttpStatusCodeSeeOther = 303;
|
|
||||||
public const int HttpStatusCodeNotModified = 304;
|
|
||||||
public const int HttpStatusCodeUseProxy = 305;
|
|
||||||
public const int HttpStatusCodeReserved306 = 306;
|
|
||||||
public const int HttpStatusCodeTemporaryRedirect = 307;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeBadRequest = 400;
|
|
||||||
public const int HttpStatusCodeNotAuthorized = 401;
|
|
||||||
public const int HttpStatusCodePaymentRequired = 402;
|
|
||||||
public const int HttpStatusCodeForbidden = 403;
|
|
||||||
public const int HttpStatusCodeNotFound = 404;
|
|
||||||
public const int HttpStatusCodeMethodNotAllowed = 405;
|
|
||||||
public const int HttpStatusCodeNotAcceptable = 406;
|
|
||||||
public const int HttpStatusCodeProxyAuthenticate = 407;
|
|
||||||
public const int HttpStatusCodeTimeOut = 408;
|
|
||||||
public const int HttpStatusCodeConflict = 409;
|
|
||||||
public const int HttpStatusCodeGone = 410;
|
|
||||||
public const int HttpStatusCodeLengthRequired = 411;
|
|
||||||
public const int HttpStatusCodePreconditionFailed = 412;
|
|
||||||
public const int HttpStatusCodeEntityTooLarge = 413;
|
|
||||||
public const int HttpStatusCodeUriTooLarge = 414;
|
|
||||||
public const int HttpStatusCodeUnsupportedMedia = 415;
|
|
||||||
public const int HttpStatusCodeRangeNotSatsified = 416;
|
|
||||||
public const int HttpStatusCodeExpectationFailed = 417;
|
|
||||||
|
|
||||||
public const int HttpStatusCodeServerError = 500;
|
|
||||||
public const int HttpStatusCodeNotImplemented = 501;
|
|
||||||
public const int HttpStatusCodeBadGateway = 502;
|
|
||||||
public const int HttpStatusCodeServiceUnavailable = 503;
|
|
||||||
public const int HttpStatusCodeGatewayTimeout = 504;
|
|
||||||
public const int HttpStatusCodeHttpVersionError = 505;
|
|
||||||
|
|
||||||
public static readonly int[] HttpStatusCodeArray = {
|
|
||||||
HttpStatusCodeContinue,
|
|
||||||
HttpStatusCodeSwitchingProtocols,
|
|
||||||
HttpStatusCodeOK,
|
|
||||||
HttpStatusCodeCreated,
|
|
||||||
HttpStatusCodeAccepted,
|
|
||||||
HttpStatusCodeNonAuthoritative,
|
|
||||||
HttpStatusCodeNoContent,
|
|
||||||
HttpStatusCodeResetContent,
|
|
||||||
HttpStatusCodePartialContent,
|
|
||||||
HttpStatusCodeMultipleChoices,
|
|
||||||
HttpStatusCodePermanentRedirect,
|
|
||||||
HttpStatusCodeFound,
|
|
||||||
HttpStatusCodeSeeOther,
|
|
||||||
HttpStatusCodeNotModified,
|
|
||||||
HttpStatusCodeUseProxy,
|
|
||||||
HttpStatusCodeReserved306,
|
|
||||||
HttpStatusCodeTemporaryRedirect,
|
|
||||||
HttpStatusCodeBadRequest,
|
|
||||||
HttpStatusCodeNotAuthorized,
|
|
||||||
HttpStatusCodePaymentRequired,
|
|
||||||
HttpStatusCodeForbidden,
|
|
||||||
HttpStatusCodeNotFound,
|
|
||||||
HttpStatusCodeMethodNotAllowed,
|
|
||||||
HttpStatusCodeNotAcceptable,
|
|
||||||
HttpStatusCodeProxyAuthenticate,
|
|
||||||
HttpStatusCodeTimeOut,
|
|
||||||
HttpStatusCodeConflict,
|
|
||||||
HttpStatusCodeGone,
|
|
||||||
HttpStatusCodeLengthRequired,
|
|
||||||
HttpStatusCodePreconditionFailed,
|
|
||||||
HttpStatusCodeEntityTooLarge,
|
|
||||||
HttpStatusCodeUriTooLarge,
|
|
||||||
HttpStatusCodeUnsupportedMedia,
|
|
||||||
HttpStatusCodeRangeNotSatsified,
|
|
||||||
HttpStatusCodeExpectationFailed,
|
|
||||||
HttpStatusCodeServerError,
|
|
||||||
HttpStatusCodeNotImplemented,
|
|
||||||
HttpStatusCodeBadGateway,
|
|
||||||
HttpStatusCodeServiceUnavailable,
|
|
||||||
HttpStatusCodeGatewayTimeout,
|
|
||||||
HttpStatusCodeHttpVersionError
|
|
||||||
};
|
|
||||||
|
|
||||||
// HTTP Status Descriptions (in status code order)
|
|
||||||
// This array must be kept strictly consistent with respect
|
|
||||||
// to the status code array above.
|
|
||||||
|
|
||||||
public static readonly string[] HttpStatusDescArray = {
|
|
||||||
"Continue Request",
|
|
||||||
"Switching Protocols",
|
|
||||||
"OK",
|
|
||||||
"CREATED",
|
|
||||||
"ACCEPTED",
|
|
||||||
"NON-AUTHORITATIVE INFORMATION",
|
|
||||||
"NO CONTENT",
|
|
||||||
"RESET CONTENT",
|
|
||||||
"PARTIAL CONTENT",
|
|
||||||
"MULTIPLE CHOICES",
|
|
||||||
"PERMANENT REDIRECT",
|
|
||||||
"FOUND",
|
|
||||||
"SEE OTHER",
|
|
||||||
"NOT MODIFIED",
|
|
||||||
"USE PROXY",
|
|
||||||
"RESERVED CODE 306",
|
|
||||||
"TEMPORARY REDIRECT",
|
|
||||||
"BAD REQUEST",
|
|
||||||
"NOT AUTHORIZED",
|
|
||||||
"PAYMENT REQUIRED",
|
|
||||||
"FORBIDDEN",
|
|
||||||
"NOT FOUND",
|
|
||||||
"METHOD NOT ALLOWED",
|
|
||||||
"NOT ACCEPTABLE",
|
|
||||||
"PROXY AUTHENTICATION REQUIRED",
|
|
||||||
"TIMEOUT",
|
|
||||||
"CONFLICT",
|
|
||||||
"GONE",
|
|
||||||
"LENGTH REQUIRED",
|
|
||||||
"PRECONDITION FAILED",
|
|
||||||
"ENTITY TOO LARGE",
|
|
||||||
"URI TOO LARGE",
|
|
||||||
"UNSUPPORTED MEDIA",
|
|
||||||
"RANGE NOT SATISFIED",
|
|
||||||
"EXPECTATION FAILED",
|
|
||||||
"SERVER ERROR",
|
|
||||||
"NOT IMPLEMENTED",
|
|
||||||
"BAD GATEWAY",
|
|
||||||
"SERVICE UNAVAILABLE",
|
|
||||||
"GATEWAY TIMEOUT",
|
|
||||||
"HTTP VERSION NOT SUPPORTED"
|
|
||||||
};
|
|
||||||
|
|
||||||
// HTTP Headers
|
|
||||||
|
|
||||||
public const string HttpHeaderAccept = "Accept";
|
|
||||||
public const string HttpHeaderAcceptCharset = "Accept-Charset";
|
|
||||||
public const string HttpHeaderAcceptEncoding = "Accept-Encoding";
|
|
||||||
public const string HttpHeaderAcceptLanguage = "Accept-Language";
|
|
||||||
public const string HttpHeaderAcceptRanges = "Accept-Ranges";
|
|
||||||
public const string HttpHeaderAge = "Age";
|
|
||||||
public const string HttpHeaderAllow = "Allow";
|
|
||||||
public const string HttpHeaderAuthorization = "Authorization";
|
|
||||||
public const string HttpHeaderCacheControl = "Cache-Control";
|
|
||||||
public const string HttpHeaderConnection = "Connection";
|
|
||||||
public const string HttpHeaderContentEncoding = "Content-Encoding";
|
|
||||||
public const string HttpHeaderContentLanguage = "Content-Language";
|
|
||||||
public const string HttpHeaderContentLength = "Content-Length";
|
|
||||||
public const string HttpHeaderContentLocation = "Content-Location";
|
|
||||||
public const string HttpHeaderContentMD5 = "Content-MD5";
|
|
||||||
public const string HttpHeaderContentRange = "Content-Range";
|
|
||||||
public const string HttpHeaderContentType = "Content-Type";
|
|
||||||
public const string HttpHeaderDate = "Date";
|
|
||||||
public const string HttpHeaderETag = "ETag";
|
|
||||||
public const string HttpHeaderExpect = "Expect";
|
|
||||||
public const string HttpHeaderExpires = "Expires";
|
|
||||||
public const string HttpHeaderFrom = "From";
|
|
||||||
public const string HttpHeaderHost = "Host";
|
|
||||||
public const string HttpHeaderIfMatch = "If-Match";
|
|
||||||
public const string HttpHeaderIfModifiedSince = "If-Modified-Since";
|
|
||||||
public const string HttpHeaderIfNoneMatch = "If-None-Match";
|
|
||||||
public const string HttpHeaderIfRange = "If-Range";
|
|
||||||
public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since";
|
|
||||||
public const string HttpHeaderLastModified = "Last-Modified";
|
|
||||||
public const string HttpHeaderLocation = "Location";
|
|
||||||
public const string HttpHeaderMaxForwards = "Max-Forwards";
|
|
||||||
public const string HttpHeaderPragma = "Pragma";
|
|
||||||
public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate";
|
|
||||||
public const string HttpHeaderProxyAuthorization = "Proxy-Authorization";
|
|
||||||
public const string HttpHeaderRange = "Range";
|
|
||||||
public const string HttpHeaderReferer = "Referer";
|
|
||||||
public const string HttpHeaderRetryAfter = "Retry-After";
|
|
||||||
public const string HttpHeaderServer = "Server";
|
|
||||||
public const string HttpHeaderTE = "TE";
|
|
||||||
public const string HttpHeaderTrailer = "Trailer";
|
|
||||||
public const string HttpHeaderTransferEncoding = "Transfer-Encoding";
|
|
||||||
public const string HttpHeaderUpgrade = "Upgrade";
|
|
||||||
public const string HttpHeaderUserAgent = "User-Agent";
|
|
||||||
public const string HttpHeaderVary = "Vary";
|
|
||||||
public const string HttpHeaderVia = "Via";
|
|
||||||
public const string HttpHeaderWarning = "Warning";
|
|
||||||
public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate";
|
|
||||||
|
|
||||||
/// Utility routines
|
|
||||||
|
|
||||||
public static string StringToBase64(string str)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
byte[] encData_byte = new byte[str.Length];
|
|
||||||
encData_byte = Util.UTF8.GetBytes(str);
|
|
||||||
return Convert.ToBase64String(encData_byte);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string Base64ToString(string str)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Util.Base64ToString(str);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return String.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string hvals = "0123456789abcdef";
|
|
||||||
|
|
||||||
public static int Hex2Int(string hex)
|
|
||||||
{
|
|
||||||
int val = 0;
|
|
||||||
int sum = 0;
|
|
||||||
string tmp = null;
|
|
||||||
|
|
||||||
if (hex != null)
|
|
||||||
{
|
|
||||||
tmp = hex.ToLower();
|
|
||||||
for (int i = 0; i < tmp.Length; i++)
|
|
||||||
{
|
|
||||||
val = hvals.IndexOf(tmp[i]);
|
|
||||||
if (val == -1)
|
|
||||||
break;
|
|
||||||
sum *= 16;
|
|
||||||
sum += val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return sum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nonce management
|
|
||||||
|
|
||||||
public static string NonceGenerator()
|
|
||||||
{
|
|
||||||
return StringToBase64(CreationDate + Guid.NewGuid().ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump the specified data stream
|
|
||||||
|
|
||||||
public static void Dump(byte[] data)
|
|
||||||
{
|
|
||||||
char[] buffer = new char[DumpLineSize];
|
|
||||||
int cc = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < data.Length; i++)
|
|
||||||
{
|
|
||||||
if (i % DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
|
|
||||||
|
|
||||||
if (i % 4 == 0) Console.Write(" ");
|
|
||||||
|
|
||||||
Console.Write("{0}",data[i].ToString("x2"));
|
|
||||||
|
|
||||||
if (data[i] < 127 && data[i] > 31)
|
|
||||||
buffer[i % DumpLineSize] = (char) data[i];
|
|
||||||
else
|
|
||||||
buffer[i % DumpLineSize] = '.';
|
|
||||||
|
|
||||||
cc++;
|
|
||||||
|
|
||||||
if (i != 0 && (i + 1) % DumpLineSize == 0)
|
|
||||||
{
|
|
||||||
Console.Write(" |"+(new String(buffer))+"|");
|
|
||||||
cc = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finish off any incomplete line
|
|
||||||
|
|
||||||
if (cc != 0)
|
|
||||||
{
|
|
||||||
for (int i = cc ; i < DumpLineSize; i++)
|
|
||||||
{
|
|
||||||
if (i % 4 == 0) Console.Write(" ");
|
|
||||||
Console.Write(" ");
|
|
||||||
buffer[i % DumpLineSize] = ' ';
|
|
||||||
}
|
|
||||||
Console.WriteLine(" |"+(new String(buffer))+"|");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.Write("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Local exception type
|
|
||||||
|
|
||||||
public class RestException : Exception
|
|
||||||
{
|
|
||||||
internal int statusCode;
|
|
||||||
internal string statusDesc;
|
|
||||||
internal string httpmethod;
|
|
||||||
internal string httppath;
|
|
||||||
|
|
||||||
public RestException(string msg) : base(msg)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,860 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Xml;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Services.Interfaces;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
|
|
||||||
public class RestAppearanceServices : IRest
|
|
||||||
{
|
|
||||||
// private static readonly int PARM_USERID = 0;
|
|
||||||
|
|
||||||
// private static readonly int PARM_PATH = 1;
|
|
||||||
|
|
||||||
// private bool enabled = false;
|
|
||||||
private string qPrefix = "appearance";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The constructor makes sure that the service prefix is absolute
|
|
||||||
/// and the registers the service handler and the allocator.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public RestAppearanceServices()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance services initializing", MsgId);
|
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
|
||||||
|
|
||||||
// If a relative path was specified for the handler's domain,
|
|
||||||
// add the standard prefix to make it absolute, e.g. /admin
|
|
||||||
|
|
||||||
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
|
|
||||||
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
|
|
||||||
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
|
|
||||||
Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register interface using the absolute URI.
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoAppearance,qPrefix,Allocate);
|
|
||||||
|
|
||||||
// Activate if everything went OK
|
|
||||||
|
|
||||||
// enabled = true;
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance services initialization complete", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Post-construction, pre-enabled initialization opportunity
|
|
||||||
/// Not currently exploited.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called by the plug-in to halt service processing. Local processing is
|
|
||||||
/// disabled.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
// enabled = false;
|
|
||||||
Rest.Log.InfoFormat("{0} User appearance services closing down", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This property is declared locally because it is used a lot and
|
|
||||||
/// brevity is nice.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The plugin (RestHandler) calls this method to allocate the request
|
|
||||||
/// state carrier for a new request. It is destroyed when the request
|
|
||||||
/// completes. All request-instance specific state is kept here. This
|
|
||||||
/// is registered when this service provider is registered.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=request>Inbound HTTP request information</param>
|
|
||||||
/// <param name=response>Outbound HTTP request information</param>
|
|
||||||
/// <param name=qPrefix>REST service domain prefix</param>
|
|
||||||
/// <returns>A RequestData instance suitable for this service</returns>
|
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
{
|
|
||||||
return (RequestData) new AppearanceRequestData(request, response, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is registered with the handler when this service provider
|
|
||||||
/// is initialized. It is called whenever the plug-in identifies this service
|
|
||||||
/// provider as the best match for a given request.
|
|
||||||
/// It handles all aspects of inventory REST processing, i.e. /admin/inventory
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=hdata>A consolidated HTTP request work area</param>
|
|
||||||
|
|
||||||
private void DoAppearance(RequestData hdata)
|
|
||||||
{
|
|
||||||
// !!! REFACTORIMG PROBLEM. This needs rewriting for 0.7
|
|
||||||
|
|
||||||
//AppearanceRequestData rdata = (AppearanceRequestData) hdata;
|
|
||||||
|
|
||||||
//Rest.Log.DebugFormat("{0} DoAppearance ENTRY", MsgId);
|
|
||||||
|
|
||||||
//// If we're disabled, do nothing.
|
|
||||||
|
|
||||||
//if (!enabled)
|
|
||||||
//{
|
|
||||||
// return;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// Now that we know this is a serious attempt to
|
|
||||||
//// access inventory data, we should find out who
|
|
||||||
//// is asking, and make sure they are authorized
|
|
||||||
//// to do so. We need to validate the caller's
|
|
||||||
//// identity before revealing anything about the
|
|
||||||
//// status quo. Authenticate throws an exception
|
|
||||||
//// via Fail if no identity information is present.
|
|
||||||
////
|
|
||||||
//// With the present HTTP server we can't use the
|
|
||||||
//// builtin authentication mechanisms because they
|
|
||||||
//// would be enforced for all in-bound requests.
|
|
||||||
//// Instead we look at the headers ourselves and
|
|
||||||
//// handle authentication directly.
|
|
||||||
|
|
||||||
//try
|
|
||||||
//{
|
|
||||||
// if (!rdata.IsAuthenticated)
|
|
||||||
// {
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNotAuthorized,String.Format("user \"{0}\" could not be authenticated", rdata.userName));
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//catch (RestException e)
|
|
||||||
//{
|
|
||||||
// if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
|
||||||
// {
|
|
||||||
// Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
|
|
||||||
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
|
|
||||||
// Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
// }
|
|
||||||
// throw (e);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//Rest.Log.DebugFormat("{0} Authenticated {1}", MsgId, rdata.userName);
|
|
||||||
|
|
||||||
//// We can only get here if we are authorized
|
|
||||||
////
|
|
||||||
//// The requestor may have specified an UUID or
|
|
||||||
//// a conjoined FirstName LastName string. We'll
|
|
||||||
//// try both. If we fail with the first, UUID,
|
|
||||||
//// attempt, we try the other. As an example, the
|
|
||||||
//// URI for a valid inventory request might be:
|
|
||||||
////
|
|
||||||
//// http://<host>:<port>/admin/inventory/Arthur Dent
|
|
||||||
////
|
|
||||||
//// Indicating that this is an inventory request for
|
|
||||||
//// an avatar named Arthur Dent. This is ALL that is
|
|
||||||
//// required to designate a GET for an entire
|
|
||||||
//// inventory.
|
|
||||||
////
|
|
||||||
|
|
||||||
//// Do we have at least a user agent name?
|
|
||||||
|
|
||||||
//if (rdata.Parameters.Length < 1)
|
|
||||||
//{
|
|
||||||
// Rest.Log.WarnFormat("{0} Appearance: No user agent identifier specified", MsgId);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "no user identity specified");
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// The first parameter MUST be the agent identification, either an UUID
|
|
||||||
//// or a space-separated First-name Last-Name specification. We check for
|
|
||||||
//// an UUID first, if anyone names their character using a valid UUID
|
|
||||||
//// that identifies another existing avatar will cause this a problem...
|
|
||||||
|
|
||||||
//try
|
|
||||||
//{
|
|
||||||
// rdata.uuid = new UUID(rdata.Parameters[PARM_USERID]);
|
|
||||||
// Rest.Log.DebugFormat("{0} UUID supplied", MsgId);
|
|
||||||
// rdata.userProfile = Rest.UserServices.GetUserProfile(rdata.uuid);
|
|
||||||
//}
|
|
||||||
//catch
|
|
||||||
//{
|
|
||||||
// string[] names = rdata.Parameters[PARM_USERID].Split(Rest.CA_SPACE);
|
|
||||||
// if (names.Length == 2)
|
|
||||||
// {
|
|
||||||
// Rest.Log.DebugFormat("{0} Agent Name supplied [2]", MsgId);
|
|
||||||
// rdata.userProfile = Rest.UserServices.GetUserProfile(names[0],names[1]);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// Rest.Log.WarnFormat("{0} A Valid UUID or both first and last names must be specified", MsgId);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeBadRequest, "invalid user identity");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// If the user profile is null then either the server is broken, or the
|
|
||||||
//// user is not known. We always assume the latter case.
|
|
||||||
|
|
||||||
//if (rdata.userProfile != null)
|
|
||||||
//{
|
|
||||||
// Rest.Log.DebugFormat("{0} User profile obtained for agent {1} {2}",
|
|
||||||
// MsgId, rdata.userProfile.FirstName, rdata.userProfile.SurName);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// Rest.Log.WarnFormat("{0} No user profile for {1}", MsgId, rdata.path);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNotFound, "unrecognized user identity");
|
|
||||||
//}
|
|
||||||
|
|
||||||
//// If we get to here, then we have effectively validated the user's
|
|
||||||
|
|
||||||
//switch (rdata.method)
|
|
||||||
//{
|
|
||||||
// case Rest.HEAD : // Do the processing, set the status code, suppress entity
|
|
||||||
// DoGet(rdata);
|
|
||||||
// rdata.buffer = null;
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.GET : // Do the processing, set the status code, return entity
|
|
||||||
// DoGet(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.PUT : // Update named element
|
|
||||||
// DoUpdate(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.POST : // Add new information to identified context.
|
|
||||||
// DoExtend(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// case Rest.DELETE : // Delete information
|
|
||||||
// DoDelete(rdata);
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// default :
|
|
||||||
// Rest.Log.WarnFormat("{0} Method {1} not supported for {2}",
|
|
||||||
// MsgId, rdata.method, rdata.path);
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeMethodNotAllowed,
|
|
||||||
// String.Format("{0} not supported", rdata.method));
|
|
||||||
// break;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Interface
|
|
||||||
|
|
||||||
#region method-specific processing
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method implements GET processing for user's appearance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name=rdata>HTTP service request work area</param>
|
|
||||||
|
|
||||||
// private void DoGet(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// if (adata == null)
|
|
||||||
// {
|
|
||||||
// rdata.Fail(Rest.HttpStatusCodeNoContent,
|
|
||||||
// String.Format("appearance data not found for user {0} {1}",
|
|
||||||
// rdata.userProfile.FirstName, rdata.userProfile.SurName));
|
|
||||||
// }
|
|
||||||
// rdata.userAppearance = adata.ToAvatarAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// rdata.initXmlWriter();
|
|
||||||
//
|
|
||||||
// FormatUserAppearance(rdata);
|
|
||||||
//
|
|
||||||
// // Indicate a successful request
|
|
||||||
//
|
|
||||||
// rdata.Complete();
|
|
||||||
//
|
|
||||||
// // Send the response to the user. The body will be implicitly
|
|
||||||
// // constructed from the result of the XML writer.
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} Normal completion", rdata.method));
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// POST adds NEW information to the user profile database.
|
|
||||||
/// This effectively resets the appearance before applying those
|
|
||||||
/// characteristics supplied in the request.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// private void DoExtend(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// bool created = false;
|
|
||||||
// bool modified = false;
|
|
||||||
// string newnode = String.Empty;
|
|
||||||
//
|
|
||||||
// Rest.Log.DebugFormat("{0} POST ENTRY", MsgId);
|
|
||||||
//
|
|
||||||
// //AvatarAppearance old = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// rdata.userAppearance = new AvatarAppearance();
|
|
||||||
//
|
|
||||||
// // Although the following behavior is admitted by HTTP I am becoming
|
|
||||||
// // increasingly doubtful that it is appropriate for REST. If I attempt to
|
|
||||||
// // add a new record, and it already exists, then it seems to me that the
|
|
||||||
// // attempt should fail, rather than update the existing record.
|
|
||||||
// AvatarData adata = null;
|
|
||||||
// if (GetUserAppearance(rdata))
|
|
||||||
// {
|
|
||||||
// modified = rdata.userAppearance != null;
|
|
||||||
// created = !modified;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// created = true;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if (created)
|
|
||||||
// {
|
|
||||||
// newnode = String.Format("{0} {1}", rdata.userProfile.FirstName,
|
|
||||||
// rdata.userProfile.SurName);
|
|
||||||
// // Must include a location header with a URI that identifies the new resource.
|
|
||||||
//
|
|
||||||
// rdata.AddHeader(Rest.HttpHeaderLocation,String.Format("http://{0}{1}:{2}{3}{4}",
|
|
||||||
// rdata.hostname,rdata.port,rdata.path,Rest.UrlPathSeparator, newnode));
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (modified)
|
|
||||||
// {
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This updates the user's appearance. not all aspects need to be provided,
|
|
||||||
/// only those supplied will be changed.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
// private void DoUpdate(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// // REFACTORING PROBLEM This was commented out. It doesn't work for 0.7
|
|
||||||
//
|
|
||||||
// //bool created = false;
|
|
||||||
// //bool modified = false;
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// //rdata.userAppearance = Rest.AvatarServices.GetUserAppearance(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// //// If the user exists then this is considered a modification regardless
|
|
||||||
// //// of what may, or may not be, specified in the payload.
|
|
||||||
//
|
|
||||||
// //if (rdata.userAppearance != null)
|
|
||||||
// //{
|
|
||||||
// // modified = true;
|
|
||||||
// // Rest.AvatarServices.UpdateUserAppearance(rdata.userProfile.ID, rdata.userAppearance);
|
|
||||||
// // Rest.UserServices.UpdateUserProfile(rdata.userProfile);
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// //if (created)
|
|
||||||
// //{
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
// //}
|
|
||||||
// //else
|
|
||||||
// //{
|
|
||||||
// // if (modified)
|
|
||||||
// // {
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
// // }
|
|
||||||
// // else
|
|
||||||
// // {
|
|
||||||
// // rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// // }
|
|
||||||
// //}
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delete the specified user's appearance. This actually performs a reset
|
|
||||||
/// to the default avatar appearance, if the info is already there.
|
|
||||||
/// Existing ownership is preserved. All prior updates are lost and can not
|
|
||||||
/// be recovered.
|
|
||||||
/// </summary>
|
|
||||||
// private void DoDelete(AppearanceRequestData rdata)
|
|
||||||
// {
|
|
||||||
// AvatarData adata = Rest.AvatarServices.GetAvatar(rdata.userProfile.ID);
|
|
||||||
//
|
|
||||||
// if (adata != null)
|
|
||||||
// {
|
|
||||||
// AvatarAppearance old = adata.ToAvatarAppearance(rdata.userProfile.ID);
|
|
||||||
// rdata.userAppearance = new AvatarAppearance();
|
|
||||||
// rdata.userAppearance.Owner = old.Owner;
|
|
||||||
// adata = new AvatarData(rdata.userAppearance);
|
|
||||||
//
|
|
||||||
// Rest.AvatarServices.SetAvatar(rdata.userProfile.ID, adata);
|
|
||||||
//
|
|
||||||
// rdata.Complete();
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
//
|
|
||||||
// rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// rdata.Respond(String.Format("Appearance {0} : Normal completion", rdata.method));
|
|
||||||
// }
|
|
||||||
|
|
||||||
#endregion method-specific processing
|
|
||||||
|
|
||||||
private bool GetUserAppearance(AppearanceRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
XmlReader xml;
|
|
||||||
bool indata = false;
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
xml = rdata.reader;
|
|
||||||
|
|
||||||
while (xml.Read())
|
|
||||||
{
|
|
||||||
switch (xml.NodeType)
|
|
||||||
{
|
|
||||||
case XmlNodeType.Element :
|
|
||||||
switch (xml.Name)
|
|
||||||
{
|
|
||||||
case "Appearance" :
|
|
||||||
if (xml.MoveToAttribute("Height"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.AvatarHeight = (float) Convert.ToDouble(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
// if (xml.MoveToAttribute("Owner"))
|
|
||||||
// {
|
|
||||||
// rdata.userAppearance.Owner = (UUID)xml.Value;
|
|
||||||
// indata = true;
|
|
||||||
// }
|
|
||||||
if (xml.MoveToAttribute("Serial"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Serial = Convert.ToInt32(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
/*
|
|
||||||
case "Body" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.BodyItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.BodyAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Skin" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkinItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkinAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Hair" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.HairItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.HairAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Eyes" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.EyesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.EyesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Shirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Pants" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.PantsItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.PantsAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Shoes" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShoesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.ShoesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Socks" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SocksItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SocksAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Jacket" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.JacketItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.JacketAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Gloves" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.GlovesItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.GlovesAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "UnderShirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderShirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderShirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "UnderPants" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderPantsItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.UnderPantsAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Skirt" :
|
|
||||||
if (xml.MoveToAttribute("Item"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkirtItem = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.SkirtAsset = (UUID)xml.Value;
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
case "Attachment" :
|
|
||||||
{
|
|
||||||
|
|
||||||
int ap;
|
|
||||||
UUID asset;
|
|
||||||
UUID item;
|
|
||||||
|
|
||||||
if (xml.MoveToAttribute("AtPoint"))
|
|
||||||
{
|
|
||||||
ap = Convert.ToInt32(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
asset = new UUID(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Asset"))
|
|
||||||
{
|
|
||||||
item = new UUID(xml.Value);
|
|
||||||
rdata.userAppearance.SetAttachment(ap, item, asset);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Texture" :
|
|
||||||
if (xml.MoveToAttribute("Default"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Texture = new Primitive.TextureEntry(new UUID(xml.Value));
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "Face" :
|
|
||||||
{
|
|
||||||
uint index;
|
|
||||||
if (xml.MoveToAttribute("Index"))
|
|
||||||
{
|
|
||||||
index = Convert.ToUInt32(xml.Value);
|
|
||||||
if (xml.MoveToAttribute("Id"))
|
|
||||||
{
|
|
||||||
rdata.userAppearance.Texture.CreateFace(index).TextureID = new UUID(xml.Value);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "VisualParameters" :
|
|
||||||
{
|
|
||||||
xml.ReadContentAsBase64(rdata.userAppearance.VisualParams,
|
|
||||||
0, rdata.userAppearance.VisualParams.Length);
|
|
||||||
indata = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return indata;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FormatPart(AppearanceRequestData rdata, string part, UUID item, UUID asset)
|
|
||||||
{
|
|
||||||
if (item != UUID.Zero || asset != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement(part);
|
|
||||||
if (item != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteAttributeString("Item",item.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (asset != UUID.Zero)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteAttributeString("Asset",asset.ToString());
|
|
||||||
}
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FormatUserAppearance(AppearanceRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance", MsgId);
|
|
||||||
|
|
||||||
if (rdata.userAppearance != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: appearance object exists", MsgId);
|
|
||||||
rdata.writer.WriteStartElement("Appearance");
|
|
||||||
|
|
||||||
rdata.writer.WriteAttributeString("Height", rdata.userAppearance.AvatarHeight.ToString());
|
|
||||||
// if (rdata.userAppearance.Owner != UUID.Zero)
|
|
||||||
// rdata.writer.WriteAttributeString("Owner", rdata.userAppearance.Owner.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Serial", rdata.userAppearance.Serial.ToString());
|
|
||||||
|
|
||||||
/*
|
|
||||||
FormatPart(rdata, "Body", rdata.userAppearance.BodyItem, rdata.userAppearance.BodyAsset);
|
|
||||||
FormatPart(rdata, "Skin", rdata.userAppearance.SkinItem, rdata.userAppearance.SkinAsset);
|
|
||||||
FormatPart(rdata, "Hair", rdata.userAppearance.HairItem, rdata.userAppearance.HairAsset);
|
|
||||||
FormatPart(rdata, "Eyes", rdata.userAppearance.EyesItem, rdata.userAppearance.EyesAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "Shirt", rdata.userAppearance.ShirtItem, rdata.userAppearance.ShirtAsset);
|
|
||||||
FormatPart(rdata, "Pants", rdata.userAppearance.PantsItem, rdata.userAppearance.PantsAsset);
|
|
||||||
FormatPart(rdata, "Skirt", rdata.userAppearance.SkirtItem, rdata.userAppearance.SkirtAsset);
|
|
||||||
FormatPart(rdata, "Shoes", rdata.userAppearance.ShoesItem, rdata.userAppearance.ShoesAsset);
|
|
||||||
FormatPart(rdata, "Socks", rdata.userAppearance.SocksItem, rdata.userAppearance.SocksAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "Jacket", rdata.userAppearance.JacketItem, rdata.userAppearance.JacketAsset);
|
|
||||||
FormatPart(rdata, "Gloves", rdata.userAppearance.GlovesItem, rdata.userAppearance.GlovesAsset);
|
|
||||||
|
|
||||||
FormatPart(rdata, "UnderShirt", rdata.userAppearance.UnderShirtItem, rdata.userAppearance.UnderShirtAsset);
|
|
||||||
FormatPart(rdata, "UnderPants", rdata.userAppearance.UnderPantsItem, rdata.userAppearance.UnderPantsAsset);
|
|
||||||
*/
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting attachments", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("Attachments");
|
|
||||||
List<AvatarAttachment> attachments = rdata.userAppearance.GetAttachments();
|
|
||||||
foreach (AvatarAttachment attach in attachments)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement("Attachment");
|
|
||||||
rdata.writer.WriteAttributeString("AtPoint", attach.AttachPoint.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Item", attach.ItemID.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Asset", attach.AssetID.ToString());
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
|
|
||||||
Primitive.TextureEntry texture = rdata.userAppearance.Texture;
|
|
||||||
|
|
||||||
if (texture != null && (texture.DefaultTexture != null || texture.FaceTextures != null))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting textures", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("Texture");
|
|
||||||
|
|
||||||
if (texture.DefaultTexture != null)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting default texture", MsgId);
|
|
||||||
rdata.writer.WriteAttributeString("Default",
|
|
||||||
texture.DefaultTexture.TextureID.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texture.FaceTextures != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting face textures", MsgId);
|
|
||||||
|
|
||||||
for (int i=0; i<texture.FaceTextures.Length;i++)
|
|
||||||
{
|
|
||||||
if (texture.FaceTextures[i] != null)
|
|
||||||
{
|
|
||||||
rdata.writer.WriteStartElement("Face");
|
|
||||||
rdata.writer.WriteAttributeString("Index", i.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("Id",
|
|
||||||
texture.FaceTextures[i].TextureID.ToString());
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: Formatting visual parameters", MsgId);
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement("VisualParameters");
|
|
||||||
rdata.writer.WriteBase64(rdata.userAppearance.VisualParams,0,
|
|
||||||
rdata.userAppearance.VisualParams.Length);
|
|
||||||
rdata.writer.WriteEndElement();
|
|
||||||
rdata.writer.WriteFullEndElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} FormatUserAppearance: completed", MsgId);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region appearance RequestData extension
|
|
||||||
|
|
||||||
internal class AppearanceRequestData : RequestData
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// These are the inventory specific request/response state
|
|
||||||
/// extensions.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal UUID uuid = UUID.Zero;
|
|
||||||
internal UserProfileData userProfile = null;
|
|
||||||
internal AvatarAppearance userAppearance = null;
|
|
||||||
|
|
||||||
internal AppearanceRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
: base(request, response, prefix)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Appearance RequestData extension
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,383 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Xml;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
public class RestAssetServices : IRest
|
|
||||||
{
|
|
||||||
private bool enabled = false;
|
|
||||||
private string qPrefix = "assets";
|
|
||||||
|
|
||||||
// A simple constructor is used to handle any once-only
|
|
||||||
// initialization of working classes.
|
|
||||||
|
|
||||||
public RestAssetServices()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Asset services initializing", MsgId);
|
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
|
||||||
|
|
||||||
// If the handler specifies a relative path for its domain
|
|
||||||
// then we must add the standard absolute prefix, e.g. /admin
|
|
||||||
|
|
||||||
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
|
|
||||||
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
|
|
||||||
Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register interface using the fully-qualified prefix
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoAsset, qPrefix, Allocate);
|
|
||||||
|
|
||||||
// Activate if all went OK
|
|
||||||
|
|
||||||
enabled = true;
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Asset services initialization complete", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-construction, pre-enabled initialization opportunity
|
|
||||||
// Not currently exploited.
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the plug-in to halt REST processing. Local processing is
|
|
||||||
// disabled, and control blocks until all current processing has
|
|
||||||
// completed. No new processing will be started
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
enabled = false;
|
|
||||||
Rest.Log.InfoFormat("{0} Asset services ({1}) closing down", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface
|
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
{
|
|
||||||
return (RequestData) new AssetRequestData(request, response, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asset Handler
|
|
||||||
|
|
||||||
private void DoAsset(RequestData rparm)
|
|
||||||
{
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
AssetRequestData rdata = (AssetRequestData) rparm;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST Asset handler ({1}) ENTRY", MsgId, qPrefix);
|
|
||||||
|
|
||||||
// Now that we know this is a serious attempt to
|
|
||||||
// access inventory data, we should find out who
|
|
||||||
// is asking, and make sure they are authorized
|
|
||||||
// to do so. We need to validate the caller's
|
|
||||||
// identity before revealing anything about the
|
|
||||||
// status quo. Authenticate throws an exception
|
|
||||||
// via Fail if no identity information is present.
|
|
||||||
//
|
|
||||||
// With the present HTTP server we can't use the
|
|
||||||
// builtin authentication mechanisms because they
|
|
||||||
// would be enforced for all in-bound requests.
|
|
||||||
// Instead we look at the headers ourselves and
|
|
||||||
// handle authentication directly.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!rdata.IsAuthenticated)
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RestException e)
|
|
||||||
{
|
|
||||||
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
|
|
||||||
rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
|
|
||||||
rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
throw (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the prefix and what's left are the parameters. If we don't have
|
|
||||||
// the parameters we need, fail the request. Parameters do NOT include
|
|
||||||
// any supplied query values.
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 0)
|
|
||||||
{
|
|
||||||
switch (rdata.method)
|
|
||||||
{
|
|
||||||
case "get" :
|
|
||||||
DoGet(rdata);
|
|
||||||
break;
|
|
||||||
case "put" :
|
|
||||||
DoPut(rdata);
|
|
||||||
break;
|
|
||||||
case "post" :
|
|
||||||
DoPost(rdata);
|
|
||||||
break;
|
|
||||||
case "delete" :
|
|
||||||
default :
|
|
||||||
Rest.Log.WarnFormat("{0} Asset: Method not supported: {1}",
|
|
||||||
MsgId, rdata.method);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Asset: No agent information provided", MsgId);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST Asset handler EXIT", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Interface
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The only parameter we recognize is a UUID.If an asset with this identification is
|
|
||||||
/// found, it's content, base-64 encoded, is returned to the client.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void DoGet(AssetRequestData rdata)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length == 1)
|
|
||||||
{
|
|
||||||
UUID uuid = new UUID(rdata.Parameters[0]);
|
|
||||||
AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
|
|
||||||
|
|
||||||
if (asset != null)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Asset located <{1}>", MsgId, rdata.Parameters[0]);
|
|
||||||
|
|
||||||
rdata.initXmlWriter();
|
|
||||||
|
|
||||||
rdata.writer.WriteStartElement(String.Empty,"Asset",String.Empty);
|
|
||||||
|
|
||||||
rdata.writer.WriteAttributeString("id", asset.ID);
|
|
||||||
rdata.writer.WriteAttributeString("name", asset.Name);
|
|
||||||
rdata.writer.WriteAttributeString("desc", asset.Description);
|
|
||||||
rdata.writer.WriteAttributeString("type", asset.Type.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("local", asset.Local.ToString());
|
|
||||||
rdata.writer.WriteAttributeString("temporary", asset.Temporary.ToString());
|
|
||||||
|
|
||||||
rdata.writer.WriteBase64(asset.Data,0,asset.Data.Length);
|
|
||||||
|
|
||||||
rdata.writer.WriteFullEndElement();
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Complete();
|
|
||||||
rdata.Respond(String.Format("Asset <{0}> : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UPDATE existing item, if it exists. URI identifies the item in question.
|
|
||||||
/// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
|
|
||||||
/// is decoded and stored in the database, identified by the supplied UUID.
|
|
||||||
/// </summary>
|
|
||||||
private void DoPut(AssetRequestData rdata)
|
|
||||||
{
|
|
||||||
bool modified = false;
|
|
||||||
bool created = false;
|
|
||||||
|
|
||||||
AssetBase asset = null;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length == 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
XmlReader xml = rdata.reader;
|
|
||||||
|
|
||||||
if (!xml.ReadToFollowing("Asset"))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID uuid = new UUID(rdata.Parameters[0]);
|
|
||||||
asset = Rest.AssetServices.Get(uuid.ToString());
|
|
||||||
|
|
||||||
modified = (asset != null);
|
|
||||||
created = !modified;
|
|
||||||
|
|
||||||
asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
|
|
||||||
asset.Description = xml.GetAttribute("desc");
|
|
||||||
asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
|
|
||||||
asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
|
|
||||||
asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
|
|
||||||
|
|
||||||
if (asset.ID != rdata.Parameters[0])
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} URI and payload disagree on UUID U:{1} vs P:{2}",
|
|
||||||
MsgId, rdata.Parameters[0], asset.ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.AssetServices.Store(asset);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void DoPost(AssetRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
bool created = false;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST Asset handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length != 0)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Parameters ignored <{1}>", MsgId, rdata.path);
|
|
||||||
Rest.Log.InfoFormat("{0} POST of an asset has no parameters", MsgId, rdata.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
XmlReader xml = rdata.reader;
|
|
||||||
|
|
||||||
if (!xml.ReadToFollowing("Asset"))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID uuid = new UUID(xml.GetAttribute("id"));
|
|
||||||
AssetBase asset = Rest.AssetServices.Get(uuid.ToString());
|
|
||||||
|
|
||||||
modified = (asset != null);
|
|
||||||
created = !modified;
|
|
||||||
|
|
||||||
asset = new AssetBase(uuid, xml.GetAttribute("name"), SByte.Parse(xml.GetAttribute("type")), UUID.Zero.ToString());
|
|
||||||
asset.Description = xml.GetAttribute("desc");
|
|
||||||
asset.Local = Int32.Parse(xml.GetAttribute("local")) != 0;
|
|
||||||
asset.Temporary = Int32.Parse(xml.GetAttribute("temporary")) != 0;
|
|
||||||
asset.Data = Convert.FromBase64String(xml.ReadElementContentAsString("Asset", ""));
|
|
||||||
|
|
||||||
Rest.AssetServices.Store(asset);
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Created asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified asset {0}, UUID {1} <p>", asset.Name, asset.FullID));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("Asset {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Asset processing has no special data area requirements.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal class AssetRequestData : RequestData
|
|
||||||
{
|
|
||||||
internal AssetRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
: base(request, response, prefix)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,448 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Xml;
|
|
||||||
using System.IO;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
public class RestFileServices : IRest
|
|
||||||
{
|
|
||||||
private bool enabled = false;
|
|
||||||
private string qPrefix = "files";
|
|
||||||
|
|
||||||
// A simple constructor is used to handle any once-only
|
|
||||||
// initialization of working classes.
|
|
||||||
|
|
||||||
public RestFileServices()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} File services initializing", MsgId);
|
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
|
||||||
|
|
||||||
// If the handler specifies a relative path for its domain
|
|
||||||
// then we must add the standard absolute prefix, e.g. /admin
|
|
||||||
|
|
||||||
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Prefixing domain name ({1})", MsgId, qPrefix);
|
|
||||||
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
|
|
||||||
Rest.Log.InfoFormat("{0} Fully qualified domain name is <{1}>", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register interface using the fully-qualified prefix
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoFile, qPrefix, Allocate);
|
|
||||||
|
|
||||||
// Activate if all went OK
|
|
||||||
|
|
||||||
enabled = true;
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} File services initialization complete", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-construction, pre-enabled initialization opportunity
|
|
||||||
// Not currently exploited.
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the plug-in to halt REST processing. Local processing is
|
|
||||||
// disabled, and control blocks until all current processing has
|
|
||||||
// completed. No new processing will be started
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
enabled = false;
|
|
||||||
Rest.Log.InfoFormat("{0} File services ({1}) closing down", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface
|
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
{
|
|
||||||
return (RequestData) new FileRequestData(request, response, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Asset Handler
|
|
||||||
|
|
||||||
private void DoFile(RequestData rparm)
|
|
||||||
{
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
FileRequestData rdata = (FileRequestData) rparm;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler ({1}) ENTRY", MsgId, qPrefix);
|
|
||||||
|
|
||||||
// Now that we know this is a serious attempt to
|
|
||||||
// access file data, we should find out who
|
|
||||||
// is asking, and make sure they are authorized
|
|
||||||
// to do so. We need to validate the caller's
|
|
||||||
// identity before revealing anything about the
|
|
||||||
// status quo. Authenticate throws an exception
|
|
||||||
// via Fail if no identity information is present.
|
|
||||||
//
|
|
||||||
// With the present HTTP server we can't use the
|
|
||||||
// builtin authentication mechanisms because they
|
|
||||||
// would be enforced for all in-bound requests.
|
|
||||||
// Instead we look at the headers ourselves and
|
|
||||||
// handle authentication directly.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!rdata.IsAuthenticated)
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotAuthorized, String.Format("user \"{0}\" could not be authenticated"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RestException e)
|
|
||||||
{
|
|
||||||
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
|
|
||||||
rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId,
|
|
||||||
rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
throw (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the prefix and what's left are the parameters. If we don't have
|
|
||||||
// the parameters we need, fail the request. Parameters do NOT include
|
|
||||||
// any supplied query values.
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 0)
|
|
||||||
{
|
|
||||||
switch (rdata.method)
|
|
||||||
{
|
|
||||||
case "get" :
|
|
||||||
DoGet(rdata);
|
|
||||||
break;
|
|
||||||
case "put" :
|
|
||||||
DoPut(rdata);
|
|
||||||
break;
|
|
||||||
case "post" :
|
|
||||||
DoPost(rdata);
|
|
||||||
break;
|
|
||||||
case "delete" :
|
|
||||||
DoDelete(rdata);
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
Rest.Log.WarnFormat("{0} File: Method not supported: {1}",
|
|
||||||
MsgId, rdata.method);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,String.Format("method <{0}> not supported", rdata.method));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} File: No agent information provided", MsgId);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no agent information provided");
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler EXIT", MsgId);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Interface
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The only parameter we recognize is a UUID.If an asset with this identification is
|
|
||||||
/// found, it's content, base-64 encoded, is returned to the client.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void DoGet(FileRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
string path = String.Empty;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 1)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
|
|
||||||
if (File.Exists(path))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} File located <{1}>", MsgId, path);
|
|
||||||
Byte[] data = File.ReadAllBytes(path);
|
|
||||||
rdata.initXmlWriter();
|
|
||||||
rdata.writer.WriteStartElement(String.Empty,"File",String.Empty);
|
|
||||||
rdata.writer.WriteAttributeString("name", path);
|
|
||||||
rdata.writer.WriteBase64(data,0,data.Length);
|
|
||||||
rdata.writer.WriteFullEndElement();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0}", path));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, e.Message);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
|
|
||||||
path, e.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Complete();
|
|
||||||
rdata.Respond(String.Format("File <{0}> : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UPDATE existing item, if it exists. URI identifies the item in question.
|
|
||||||
/// The only parameter we recognize is a UUID. The enclosed asset data (base-64 encoded)
|
|
||||||
/// is decoded and stored in the database, identified by the supplied UUID.
|
|
||||||
/// </summary>
|
|
||||||
private void DoPut(FileRequestData rdata)
|
|
||||||
{
|
|
||||||
bool modified = false;
|
|
||||||
bool created = false;
|
|
||||||
string path = String.Empty;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 1)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
|
|
||||||
bool maymod = File.Exists(path);
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
XmlReader xml = rdata.reader;
|
|
||||||
|
|
||||||
if (!xml.ReadToFollowing("File"))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
|
|
||||||
}
|
|
||||||
|
|
||||||
Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
|
|
||||||
|
|
||||||
File.WriteAllBytes(path,data);
|
|
||||||
modified = maymod;
|
|
||||||
created = ! maymod;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
|
|
||||||
e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void DoPost(FileRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
bool created = false;
|
|
||||||
string path = String.Empty;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 1)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
|
|
||||||
bool maymod = File.Exists(path);
|
|
||||||
|
|
||||||
rdata.initXmlReader();
|
|
||||||
XmlReader xml = rdata.reader;
|
|
||||||
|
|
||||||
if (!xml.ReadToFollowing("File"))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid request data: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,"invalid request data");
|
|
||||||
}
|
|
||||||
|
|
||||||
Byte[] data = Convert.FromBase64String(xml.ReadElementContentAsString("File", ""));
|
|
||||||
|
|
||||||
File.WriteAllBytes(path,data);
|
|
||||||
modified = maymod;
|
|
||||||
created = ! maymod;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
|
|
||||||
e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CREATE new item, replace if it exists. URI identifies the context for the item in question.
|
|
||||||
/// No parameters are required for POST, just thepayload.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void DoDelete(FileRequestData rdata)
|
|
||||||
{
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
bool created = false;
|
|
||||||
string path = String.Empty;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST File handler, Method = <{1}> ENTRY", MsgId, rdata.method);
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 1)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
path = rdata.path.Substring(rdata.Parameters[0].Length+qPrefix.Length+2);
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
{
|
|
||||||
File.Delete(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Exception during file processing : {1}", MsgId,
|
|
||||||
e.Message);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, String.Format("invalid parameters : {0} {1}",
|
|
||||||
path, e.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Invalid parameters: <{1}>", MsgId, rdata.path);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotFound, "invalid parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (created)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Created file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeCreated);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (modified)
|
|
||||||
{
|
|
||||||
rdata.appendStatus(String.Format("<p> Modified file {0} <p>", path));
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeOK);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Complete(Rest.HttpStatusCodeNoContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Respond(String.Format("File {0} : Normal completion", rdata.method));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// File processing has no special data area requirements.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal class FileRequestData : RequestData
|
|
||||||
{
|
|
||||||
internal FileRequestData(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
: base(request, response, prefix)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,658 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
/// <remarks>
|
|
||||||
/// The class signature reveals the roles that RestHandler plays.
|
|
||||||
///
|
|
||||||
/// [1] It is a sub-class of RestPlugin. It inherits and extends
|
|
||||||
/// the functionality of this class, constraining it to the
|
|
||||||
/// specific needs of this REST implementation. This relates
|
|
||||||
/// to the plug-in mechanism supported by OpenSim, the specifics
|
|
||||||
/// of which are mostly hidden by RestPlugin.
|
|
||||||
/// [2] IRestHandler describes the interface that this class
|
|
||||||
/// exports to service implementations. This is the services
|
|
||||||
/// management interface.
|
|
||||||
/// [3] IHttpAgentHandler describes the interface that is exported
|
|
||||||
/// to the BaseHttpServer in support of this particular HTTP
|
|
||||||
/// processing model. This is the request interface of the
|
|
||||||
/// handler.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler
|
|
||||||
{
|
|
||||||
// Handler tables: both stream and REST are supported. The path handlers and their
|
|
||||||
// respective allocators are stored in separate tables.
|
|
||||||
|
|
||||||
internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>();
|
|
||||||
internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>();
|
|
||||||
internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>();
|
|
||||||
|
|
||||||
#region local static state
|
|
||||||
|
|
||||||
private static bool handlersLoaded = false;
|
|
||||||
private static List<Type> classes = new List<Type>();
|
|
||||||
private static List<IRest> handlers = new List<IRest>();
|
|
||||||
private static Type[] parms = new Type[0];
|
|
||||||
private static Object[] args = new Object[0];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This static initializer scans the ASSEMBLY for classes that
|
|
||||||
/// export the IRest interface and builds a list of them. These
|
|
||||||
/// are later activated by the handler. To add a new handler it
|
|
||||||
/// is only necessary to create a new services class that implements
|
|
||||||
/// the IRest interface, and recompile the handler. This gives
|
|
||||||
/// all of the build-time flexibility of a modular approach
|
|
||||||
/// while not introducing yet-another module loader. Note that
|
|
||||||
/// multiple assembles can still be built, each with its own set
|
|
||||||
/// of handlers. Examples of services classes are RestInventoryServices
|
|
||||||
/// and RestSkeleton.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
static RestHandler()
|
|
||||||
{
|
|
||||||
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
|
||||||
|
|
||||||
foreach (Module m in mods)
|
|
||||||
{
|
|
||||||
Type[] types = m.GetTypes();
|
|
||||||
foreach (Type t in types)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (t.GetInterface("IRest") != null)
|
|
||||||
{
|
|
||||||
classes.Add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t);
|
|
||||||
Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion local static state
|
|
||||||
|
|
||||||
#region local instance state
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This routine loads all of the handlers discovered during
|
|
||||||
/// instance initialization.
|
|
||||||
/// A table of all loaded and successfully constructed handlers
|
|
||||||
/// is built, and this table is then used by the constructor to
|
|
||||||
/// initialize each of the handlers in turn.
|
|
||||||
/// NOTE: The loading process does not automatically imply that
|
|
||||||
/// the handler has registered any kind of an interface, that
|
|
||||||
/// may be (optionally) done by the handler either during
|
|
||||||
/// construction, or during initialization.
|
|
||||||
///
|
|
||||||
/// I was not able to make this code work within a constructor
|
|
||||||
/// so it is isolated within this method.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void LoadHandlers()
|
|
||||||
{
|
|
||||||
lock (handlers)
|
|
||||||
{
|
|
||||||
if (!handlersLoaded)
|
|
||||||
{
|
|
||||||
ConstructorInfo ci;
|
|
||||||
Object ht;
|
|
||||||
|
|
||||||
foreach (Type t in classes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ci = t.GetConstructor(parms);
|
|
||||||
ht = ci.Invoke(args);
|
|
||||||
handlers.Add((IRest)ht);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handlersLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion local instance state
|
|
||||||
|
|
||||||
#region overriding properties
|
|
||||||
|
|
||||||
// These properties override definitions
|
|
||||||
// in the base class.
|
|
||||||
|
|
||||||
// Name is used to differentiate the message header.
|
|
||||||
|
|
||||||
public override string Name
|
|
||||||
{
|
|
||||||
get { return "HANDLER"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to partition the .ini configuration space.
|
|
||||||
|
|
||||||
public override string ConfigName
|
|
||||||
{
|
|
||||||
get { return "RestHandler"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to rename these because we want
|
|
||||||
// to be able to share the values with other
|
|
||||||
// classes in our assembly and the base
|
|
||||||
// names are protected.
|
|
||||||
|
|
||||||
public string MsgId
|
|
||||||
{
|
|
||||||
get { return base.MsgID; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RequestId
|
|
||||||
{
|
|
||||||
get { return base.RequestID; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion overriding properties
|
|
||||||
|
|
||||||
#region overriding methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
|
|
||||||
public override void Initialise(OpenSimBase openSim)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// This plugin will only be enabled if the broader
|
|
||||||
// REST plugin mechanism is enabled.
|
|
||||||
|
|
||||||
//Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId);
|
|
||||||
|
|
||||||
base.Initialise(openSim);
|
|
||||||
|
|
||||||
// IsEnabled is implemented by the base class and
|
|
||||||
// reflects an overall RestPlugin status
|
|
||||||
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
//Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Rest <{1}> plugin will be enabled", MsgId, Name);
|
|
||||||
Rest.Log.InfoFormat("{0} Configuration parameters read from <{1}>", MsgId, ConfigName);
|
|
||||||
|
|
||||||
// These are stored in static variables to make
|
|
||||||
// them easy to reach from anywhere in the assembly.
|
|
||||||
|
|
||||||
Rest.main = openSim;
|
|
||||||
if (Rest.main == null)
|
|
||||||
throw new Exception("OpenSim base pointer is null");
|
|
||||||
|
|
||||||
Rest.Plugin = this;
|
|
||||||
Rest.Config = Config;
|
|
||||||
Rest.Prefix = Prefix;
|
|
||||||
Rest.GodKey = GodKey;
|
|
||||||
Rest.Authenticate = Rest.Config.GetBoolean("authenticate", Rest.Authenticate);
|
|
||||||
Rest.Scheme = Rest.Config.GetString("auth-scheme", Rest.Scheme);
|
|
||||||
Rest.Secure = Rest.Config.GetBoolean("secured", Rest.Secure);
|
|
||||||
Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape", Rest.ExtendedEscape);
|
|
||||||
Rest.Realm = Rest.Config.GetString("realm", Rest.Realm);
|
|
||||||
Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset", Rest.DumpAsset);
|
|
||||||
Rest.Fill = Rest.Config.GetBoolean("path-fill", Rest.Fill);
|
|
||||||
Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size", Rest.DumpLineSize);
|
|
||||||
Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error", Rest.FlushEnabled);
|
|
||||||
|
|
||||||
// Note: Odd spacing is required in the following strings
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId,
|
|
||||||
(Rest.Authenticate ? "" : "not "));
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId,
|
|
||||||
(Rest.Secure ? "" : "not "));
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId,
|
|
||||||
(Rest.ExtendedEscape ? "" : "not "));
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId,
|
|
||||||
(Rest.DumpAsset ? "" : "not "));
|
|
||||||
|
|
||||||
// The supplied prefix MUST be absolute
|
|
||||||
|
|
||||||
if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Prefix <{1}> is not absolute and must be", MsgId, Rest.Prefix);
|
|
||||||
Rest.Log.InfoFormat("{0} Prefix changed to </{1}>", MsgId, Rest.Prefix);
|
|
||||||
Rest.Prefix = String.Format("{0}{1}", Rest.UrlPathSeparator, Rest.Prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If data dumping is requested, report on the chosen line
|
|
||||||
// length.
|
|
||||||
|
|
||||||
if (Rest.DumpAsset)
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, Rest.DumpLineSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load all of the handlers present in the
|
|
||||||
// assembly
|
|
||||||
|
|
||||||
// In principle, as we're an application plug-in,
|
|
||||||
// most of what needs to be done could be done using
|
|
||||||
// static resources, however the Open Sim plug-in
|
|
||||||
// model makes this an instance, so that's what we
|
|
||||||
// need to be.
|
|
||||||
// There is only one Communications manager per
|
|
||||||
// server, and by inference, only one each of the
|
|
||||||
// user, asset, and inventory servers. So we can cache
|
|
||||||
// those using a static initializer.
|
|
||||||
// We move all of this processing off to another
|
|
||||||
// services class to minimize overlap between function
|
|
||||||
// and infrastructure.
|
|
||||||
|
|
||||||
LoadHandlers();
|
|
||||||
|
|
||||||
// The intention of a post construction initializer
|
|
||||||
// is to allow for setup that is dependent upon other
|
|
||||||
// activities outside of the agency.
|
|
||||||
|
|
||||||
foreach (IRest handler in handlers)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handler.Initialize();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now that everything is setup we can proceed to
|
|
||||||
// add THIS agent to the HTTP server's handler list
|
|
||||||
|
|
||||||
if (!AddAgentHandler(Rest.Name,this))
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId);
|
|
||||||
foreach (IRest handler in handlers)
|
|
||||||
{
|
|
||||||
handler.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In the interests of efficiency, and because we cannot determine whether
|
|
||||||
/// or not this instance will actually be harvested, we clobber the only
|
|
||||||
/// anchoring reference to the working state for this plug-in. What the
|
|
||||||
/// call to close does is irrelevant to this class beyond knowing that it
|
|
||||||
/// can nullify the reference when it returns.
|
|
||||||
/// To make sure everything is copacetic we make sure the primary interface
|
|
||||||
/// is disabled by deleting the handler from the HTTP server tables.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RemoveAgentHandler(Rest.Name, this);
|
|
||||||
}
|
|
||||||
catch (KeyNotFoundException){}
|
|
||||||
|
|
||||||
foreach (IRest handler in handlers)
|
|
||||||
{
|
|
||||||
handler.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion overriding methods
|
|
||||||
|
|
||||||
#region interface methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by the HTTP server to match an incoming
|
|
||||||
/// request. It scans all of the strings registered by the
|
|
||||||
/// underlying handlers and looks for the best match. It returns
|
|
||||||
/// true if a match is found.
|
|
||||||
/// The matching process could be made arbitrarily complex.
|
|
||||||
/// Note: The match is case-insensitive.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public bool Match(OSHttpRequest request, OSHttpResponse response)
|
|
||||||
{
|
|
||||||
|
|
||||||
string path = request.RawUrl.ToLower();
|
|
||||||
|
|
||||||
// Rest.Log.DebugFormat("{0} Match ENTRY", MsgId);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (string key in pathHandlers.Keys)
|
|
||||||
{
|
|
||||||
// Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key);
|
|
||||||
|
|
||||||
// Note that Match will not necessarily find the handler that will
|
|
||||||
// actually be used - it does no test for the "closest" fit. It
|
|
||||||
// simply reflects that at least one possible handler exists.
|
|
||||||
|
|
||||||
if (path.StartsWith(key))
|
|
||||||
{
|
|
||||||
// Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
|
|
||||||
|
|
||||||
// This apparently odd evaluation is needed to prevent a match
|
|
||||||
// on anything other than a URI token boundary. Otherwise we
|
|
||||||
// may match on URL's that were not intended for this handler.
|
|
||||||
|
|
||||||
return (path.Length == key.Length ||
|
|
||||||
path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path);
|
|
||||||
|
|
||||||
foreach (string key in streamHandlers.Keys)
|
|
||||||
{
|
|
||||||
// Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key);
|
|
||||||
|
|
||||||
// Note that Match will not necessarily find the handler that will
|
|
||||||
// actually be used - it does no test for the "closest" fit. It
|
|
||||||
// simply reflects that at least one possible handler exists.
|
|
||||||
|
|
||||||
if (path.StartsWith(key))
|
|
||||||
{
|
|
||||||
// Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key);
|
|
||||||
|
|
||||||
// This apparently odd evaluation is needed to prevent a match
|
|
||||||
// on anything other than a URI token boundary. Otherwise we
|
|
||||||
// may match on URL's that were not intended for this handler.
|
|
||||||
|
|
||||||
return (path.Length == key.Length ||
|
|
||||||
path.Substring(key.Length, 1) == Rest.UrlPathSeparator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This is called by the HTTP server once the handler has indicated
|
|
||||||
/// that it is able to handle the request.
|
|
||||||
/// Preconditions:
|
|
||||||
/// [1] request != null and is a valid request object
|
|
||||||
/// [2] response != null and is a valid response object
|
|
||||||
/// Behavior is undefined if preconditions are not satisfied.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public bool Handle(OSHttpRequest request, OSHttpResponse response)
|
|
||||||
{
|
|
||||||
bool handled;
|
|
||||||
base.MsgID = base.RequestID;
|
|
||||||
|
|
||||||
// Debug only
|
|
||||||
|
|
||||||
if (Rest.DEBUG)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} ENTRY", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent);
|
|
||||||
Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod);
|
|
||||||
|
|
||||||
for (int i = 0; i < request.Headers.Count; i++)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>",
|
|
||||||
MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i));
|
|
||||||
}
|
|
||||||
Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a path handler worked we're done, otherwise try any
|
|
||||||
// available stream handlers too.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handled = (FindPathHandler(request, response) ||
|
|
||||||
FindStreamHandler(request, response));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// A raw exception indicates that something we weren't expecting has
|
|
||||||
// happened. This should always reflect a shortcoming in the plugin,
|
|
||||||
// or a failure to satisfy the preconditions. It should not reflect
|
|
||||||
// an error in the request itself. Under such circumstances the state
|
|
||||||
// of the request cannot be determined and we are obliged to mark it
|
|
||||||
// as 'handled'.
|
|
||||||
|
|
||||||
Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message);
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} EXIT", MsgId);
|
|
||||||
|
|
||||||
return handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion interface methods
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If there is a stream handler registered that can handle the
|
|
||||||
/// request, then fine. If the request is not matched, do
|
|
||||||
/// nothing.
|
|
||||||
/// Note: The selection is case-insensitive
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response)
|
|
||||||
{
|
|
||||||
RequestData rdata = new RequestData(request, response, String.Empty);
|
|
||||||
|
|
||||||
string bestMatch = String.Empty;
|
|
||||||
string path = String.Format("{0}:{1}", rdata.method, rdata.path).ToLower();
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path);
|
|
||||||
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string pattern in streamHandlers.Keys)
|
|
||||||
{
|
|
||||||
if (path.StartsWith(pattern))
|
|
||||||
{
|
|
||||||
if (pattern.Length > bestMatch.Length)
|
|
||||||
{
|
|
||||||
bestMatch = pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle using the best match available
|
|
||||||
|
|
||||||
if (bestMatch.Length > 0)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch);
|
|
||||||
RestStreamHandler handler = streamHandlers[bestMatch];
|
|
||||||
rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response);
|
|
||||||
rdata.AddHeader(rdata.response.ContentType,handler.ContentType);
|
|
||||||
rdata.Respond("FindStreamHandler Completion");
|
|
||||||
}
|
|
||||||
|
|
||||||
return rdata.handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a stream handler for the designated HTTP method and path prefix.
|
|
||||||
/// If the handler is not enabled, the request is ignored. If the path
|
|
||||||
/// does not start with the REST prefix, it is added. If method-qualified
|
|
||||||
/// path has not already been registered, the method is added to the active
|
|
||||||
/// handler table.
|
|
||||||
/// </summary>
|
|
||||||
public void AddStreamHandler(string httpMethod, string path, RestMethod method)
|
|
||||||
{
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path.StartsWith(Rest.Prefix))
|
|
||||||
{
|
|
||||||
path = String.Format("{0}{1}", Rest.Prefix, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path);
|
|
||||||
|
|
||||||
// Conditionally add to the list
|
|
||||||
|
|
||||||
if (!streamHandlers.ContainsKey(path))
|
|
||||||
{
|
|
||||||
streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method));
|
|
||||||
Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Given the supplied request/response, if the handler is enabled, the inbound
|
|
||||||
/// information is used to match an entry in the active path handler tables, using
|
|
||||||
/// the method-qualified path information. If a match is found, then the handler is
|
|
||||||
/// invoked. The result is the boolean result of the handler, or false if no
|
|
||||||
/// handler was located. The boolean indicates whether or not the request has been
|
|
||||||
/// handled, not whether or not the request was successful - that information is in
|
|
||||||
/// the response.
|
|
||||||
/// Note: The selection process is case-insensitive
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response)
|
|
||||||
{
|
|
||||||
RequestData rdata = null;
|
|
||||||
string bestMatch = null;
|
|
||||||
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conditionally add to the list
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl);
|
|
||||||
|
|
||||||
foreach (string pattern in pathHandlers.Keys)
|
|
||||||
{
|
|
||||||
if (request.RawUrl.ToLower().StartsWith(pattern))
|
|
||||||
{
|
|
||||||
if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
|
|
||||||
{
|
|
||||||
bestMatch = pattern;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!String.IsNullOrEmpty(bestMatch))
|
|
||||||
{
|
|
||||||
rdata = pathAllocators[bestMatch](request, response, bestMatch);
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
pathHandlers[bestMatch](rdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A plugin generated error indicates a request-related error
|
|
||||||
// that has been handled by the plugin.
|
|
||||||
|
|
||||||
catch (RestException r)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (rdata == null) ? false : rdata.handled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A method handler and a request allocator are stored using the designated
|
|
||||||
/// path as a key. If an entry already exists, it is replaced by the new one.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra)
|
|
||||||
{
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathHandlers.ContainsKey(path))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path);
|
|
||||||
pathHandlers.Remove(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pathAllocators.ContainsKey(path))
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path);
|
|
||||||
pathAllocators.Remove(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path);
|
|
||||||
|
|
||||||
pathHandlers.Add(path, mh);
|
|
||||||
pathAllocators.Add(path, ra);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,246 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
public class RestTestServices : IRest
|
|
||||||
{
|
|
||||||
private bool enabled = false;
|
|
||||||
private string qPrefix = "test";
|
|
||||||
|
|
||||||
// A simple constructor is used to handle any once-only
|
|
||||||
// initialization of working classes.
|
|
||||||
|
|
||||||
public RestTestServices()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Test services initializing", MsgId);
|
|
||||||
Rest.Log.InfoFormat("{0} Using REST Implementation Version {1}", MsgId, Rest.Version);
|
|
||||||
|
|
||||||
// If a relative path was specified, make it absolute by adding
|
|
||||||
// the standard prefix, e.g. /admin
|
|
||||||
|
|
||||||
if (!qPrefix.StartsWith(Rest.UrlPathSeparator))
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Domain is relative, adding absolute prefix", MsgId);
|
|
||||||
qPrefix = String.Format("{0}{1}{2}", Rest.Prefix, Rest.UrlPathSeparator, qPrefix);
|
|
||||||
Rest.Log.InfoFormat("{0} Domain is now <{1}>", MsgId, qPrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load test cases
|
|
||||||
|
|
||||||
loadTests();
|
|
||||||
foreach (ITest test in tests)
|
|
||||||
{
|
|
||||||
test.Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register interface
|
|
||||||
|
|
||||||
Rest.Plugin.AddPathHandler(DoTests,qPrefix,Allocate);
|
|
||||||
|
|
||||||
// Activate
|
|
||||||
|
|
||||||
enabled = true;
|
|
||||||
|
|
||||||
Rest.Log.InfoFormat("{0} Test services initialization complete", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-construction, pre-enabled initialization opportunity
|
|
||||||
// Not currently exploited.
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the plug-in to halt REST processing. Local processing is
|
|
||||||
// disabled, and control blocks until all current processing has
|
|
||||||
// completed. No new processing will be started
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
enabled = false;
|
|
||||||
foreach (ITest test in tests)
|
|
||||||
{
|
|
||||||
test.Close();
|
|
||||||
}
|
|
||||||
Rest.Log.InfoFormat("{0} Test services closing down", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Interface
|
|
||||||
|
|
||||||
private RequestData Allocate(OSHttpRequest request, OSHttpResponse response, string prefix)
|
|
||||||
{
|
|
||||||
return new RequestData(request, response, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inventory Handler
|
|
||||||
|
|
||||||
private void DoTests(RequestData rdata)
|
|
||||||
{
|
|
||||||
if (!enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Now that we know this is a serious attempt to
|
|
||||||
// access inventory data, we should find out who
|
|
||||||
// is asking, and make sure they are authorized
|
|
||||||
// to do so. We need to validate the caller's
|
|
||||||
// identity before revealing anything about the
|
|
||||||
// status quo. Authenticate throws an exception
|
|
||||||
// via Fail if no identity information is present.
|
|
||||||
//
|
|
||||||
// With the present HTTP server we can't use the
|
|
||||||
// builtin authentication mechanisms because they
|
|
||||||
// would be enforced for all in-bound requests.
|
|
||||||
// Instead we look at the headers ourselves and
|
|
||||||
// handle authentication directly.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!rdata.IsAuthenticated)
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeNotAuthorized,
|
|
||||||
String.Format("user \"{0}\" could not be authenticated", rdata.userName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (RestException e)
|
|
||||||
{
|
|
||||||
if (e.statusCode == Rest.HttpStatusCodeNotAuthorized)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} User not authenticated", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Rest.Log.ErrorFormat("{0} User authentication failed", MsgId);
|
|
||||||
Rest.Log.DebugFormat("{0} Authorization header: {1}", MsgId, rdata.request.Headers.Get("Authorization"));
|
|
||||||
}
|
|
||||||
throw (e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that a test was specified
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length < 1)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Insufficient parameters", MsgId);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, "not enough parameters");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the test
|
|
||||||
|
|
||||||
foreach (ITest test in tests)
|
|
||||||
{
|
|
||||||
if (!rdata.handled)
|
|
||||||
test.Execute(rdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion Interface
|
|
||||||
|
|
||||||
private static bool testsLoaded = false;
|
|
||||||
private static List<Type> classes = new List<Type>();
|
|
||||||
private static List<ITest> tests = new List<ITest>();
|
|
||||||
private static Type[] parms = new Type[0];
|
|
||||||
private static Object[] args = new Object[0];
|
|
||||||
|
|
||||||
static RestTestServices()
|
|
||||||
{
|
|
||||||
Module[] mods = Assembly.GetExecutingAssembly().GetModules();
|
|
||||||
foreach (Module m in mods)
|
|
||||||
{
|
|
||||||
Type[] types = m.GetTypes();
|
|
||||||
foreach (Type t in types)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (t.GetInterface("ITest") != null)
|
|
||||||
{
|
|
||||||
classes.Add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("[STATIC-TEST] Unable to include test {0} : {1}", t, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This routine loads all of the handlers discovered during
|
|
||||||
/// instance initialization. Each handler is responsible for
|
|
||||||
/// registering itself with this handler.
|
|
||||||
/// I was not able to make this code work in a constructor.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private void loadTests()
|
|
||||||
{
|
|
||||||
lock (tests)
|
|
||||||
{
|
|
||||||
if (!testsLoaded)
|
|
||||||
{
|
|
||||||
|
|
||||||
ConstructorInfo ci;
|
|
||||||
Object ht;
|
|
||||||
|
|
||||||
foreach (Type t in classes)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (t.GetInterface("ITest") != null)
|
|
||||||
{
|
|
||||||
ci = t.GetConstructor(parms);
|
|
||||||
ht = ci.Invoke(args);
|
|
||||||
tests.Add((ITest)ht);
|
|
||||||
Rest.Log.InfoFormat("{0} Test {1} added", MsgId, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Unable to load test {1} : {2}", MsgId, t, e.Message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
testsLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This interface represents the boundary between the general purpose
|
|
||||||
/// REST plugin handling, and the functionally specific handlers. The
|
|
||||||
/// handler knows only to initialzie and terminate all such handlers
|
|
||||||
/// that it finds.
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
internal interface ITest
|
|
||||||
{
|
|
||||||
void Initialize();
|
|
||||||
void Execute(RequestData rdata);
|
|
||||||
void Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,204 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Inventory.Tests
|
|
||||||
{
|
|
||||||
public class Remote : ITest
|
|
||||||
{
|
|
||||||
private static readonly int PARM_TESTID = 0;
|
|
||||||
private static readonly int PARM_COMMAND = 1;
|
|
||||||
|
|
||||||
private static readonly int PARM_MOVE_AVATAR = 2;
|
|
||||||
private static readonly int PARM_MOVE_X = 3;
|
|
||||||
private static readonly int PARM_MOVE_Y = 4;
|
|
||||||
private static readonly int PARM_MOVE_Z = 5;
|
|
||||||
|
|
||||||
private bool enabled = false;
|
|
||||||
|
|
||||||
// No constructor code is required.
|
|
||||||
|
|
||||||
public Remote()
|
|
||||||
{
|
|
||||||
Rest.Log.InfoFormat("{0} Remote services constructor", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post-construction, pre-enabled initialization opportunity
|
|
||||||
// Not currently exploited.
|
|
||||||
|
|
||||||
public void Initialize()
|
|
||||||
{
|
|
||||||
enabled = true;
|
|
||||||
Rest.Log.InfoFormat("{0} Remote services initialized", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by the plug-in to halt REST processing. Local processing is
|
|
||||||
// disabled, and control blocks until all current processing has
|
|
||||||
// completed. No new processing will be started
|
|
||||||
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
enabled = false;
|
|
||||||
Rest.Log.InfoFormat("{0} Remote services closing down", MsgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
|
|
||||||
internal string MsgId
|
|
||||||
{
|
|
||||||
get { return Rest.MsgId; }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remote Handler
|
|
||||||
// Key information of interest here is the Parameters array, each
|
|
||||||
// entry represents an element of the URI, with element zero being
|
|
||||||
// the
|
|
||||||
|
|
||||||
public void Execute(RequestData rdata)
|
|
||||||
{
|
|
||||||
if (!enabled) return;
|
|
||||||
|
|
||||||
// If we can't relate to what's there, leave it for others.
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length == 0 || rdata.Parameters[PARM_TESTID] != "remote")
|
|
||||||
return;
|
|
||||||
|
|
||||||
Rest.Log.DebugFormat("{0} REST Remote handler ENTRY", MsgId);
|
|
||||||
|
|
||||||
// Remove the prefix and what's left are the parameters. If we don't have
|
|
||||||
// the parameters we need, fail the request. Parameters do NOT include
|
|
||||||
// any supplied query values.
|
|
||||||
|
|
||||||
if (rdata.Parameters.Length > 1)
|
|
||||||
{
|
|
||||||
switch (rdata.Parameters[PARM_COMMAND].ToLower())
|
|
||||||
{
|
|
||||||
case "move" :
|
|
||||||
DoMove(rdata);
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
DoHelp(rdata);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DoHelp(rdata);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DoHelp(RequestData rdata)
|
|
||||||
{
|
|
||||||
rdata.body = Help;
|
|
||||||
rdata.Complete();
|
|
||||||
rdata.Respond("Help");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DoMove(RequestData rdata)
|
|
||||||
{
|
|
||||||
if (rdata.Parameters.Length < 6)
|
|
||||||
{
|
|
||||||
Rest.Log.WarnFormat("{0} Move: No movement information provided", MsgId);
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest, "no movement information provided");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string[] names = rdata.Parameters[PARM_MOVE_AVATAR].Split(Rest.CA_SPACE);
|
|
||||||
ScenePresence presence = null;
|
|
||||||
Scene scene = null;
|
|
||||||
|
|
||||||
if (names.Length != 2)
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
|
||||||
String.Format("invalid avatar name: <{0}>",rdata.Parameters[PARM_MOVE_AVATAR]));
|
|
||||||
}
|
|
||||||
|
|
||||||
Rest.Log.WarnFormat("{0} '{1}' command received for {2} {3}",
|
|
||||||
MsgId, rdata.Parameters[0], names[0], names[1]);
|
|
||||||
|
|
||||||
// The first parameter should be an avatar name, look for the
|
|
||||||
// avatar in the known regions first.
|
|
||||||
|
|
||||||
Rest.main.SceneManager.ForEachScene(delegate(Scene s)
|
|
||||||
{
|
|
||||||
s.ForEachRootScenePresence(delegate(ScenePresence sp)
|
|
||||||
{
|
|
||||||
if (sp.Firstname == names[0] && sp.Lastname == names[1])
|
|
||||||
{
|
|
||||||
scene = s;
|
|
||||||
presence = sp;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (presence != null)
|
|
||||||
{
|
|
||||||
Rest.Log.DebugFormat("{0} Move : Avatar {1} located in region {2}",
|
|
||||||
MsgId, rdata.Parameters[PARM_MOVE_AVATAR], scene.RegionInfo.RegionName);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
float x = Convert.ToSingle(rdata.Parameters[PARM_MOVE_X]);
|
|
||||||
float y = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Y]);
|
|
||||||
float z = Convert.ToSingle(rdata.Parameters[PARM_MOVE_Z]);
|
|
||||||
Vector3 vector = new Vector3(x, y, z);
|
|
||||||
presence.MoveToTarget(vector, false, false);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
|
||||||
String.Format("invalid parameters: {0}", e.Message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rdata.Fail(Rest.HttpStatusCodeBadRequest,
|
|
||||||
String.Format("avatar {0} not present", rdata.Parameters[PARM_MOVE_AVATAR]));
|
|
||||||
}
|
|
||||||
|
|
||||||
rdata.Complete();
|
|
||||||
rdata.Respond("OK");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly string Help =
|
|
||||||
"<html>"
|
|
||||||
+ "<head><title>Remote Command Usage</title></head>"
|
|
||||||
+ "<body>"
|
|
||||||
+ "<p>Supported commands are:</p>"
|
|
||||||
+ "<dl>"
|
|
||||||
+ "<dt>move/avatar-name/x/y/z</dt>"
|
|
||||||
+ "<dd>moves the specified avatar to another location</dd>"
|
|
||||||
+ "</dl>"
|
|
||||||
+ "</body>"
|
|
||||||
+ "</html>"
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,228 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region GET methods
|
|
||||||
public string GetHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: regions list
|
|
||||||
if (String.IsNullOrEmpty(param)) return GetHandlerRegions(httpResponse);
|
|
||||||
|
|
||||||
// param not empty: specific region
|
|
||||||
return GetHandlerRegion(httpResponse, param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetHandlerRegions(IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string ShortRegionInfo(string key, string value)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(value) ||
|
|
||||||
String.IsNullOrEmpty(key)) return null;
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
rxw.WriteStartElement(String.Empty, key, String.Empty);
|
|
||||||
rxw.WriteString(value);
|
|
||||||
rxw.WriteEndDocument();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetHandlerRegion(IOSHttpResponse httpResponse, string param)
|
|
||||||
{
|
|
||||||
// be resilient and don't get confused by a terminating '/'
|
|
||||||
param = param.TrimEnd(new char[]{'/'});
|
|
||||||
string[] comps = param.Split('/');
|
|
||||||
UUID regionID = (UUID)comps[0];
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} GET region UUID {1}", MsgID, regionID.ToString());
|
|
||||||
|
|
||||||
if (UUID.Zero == regionID) throw new Exception("missing region ID");
|
|
||||||
|
|
||||||
Scene scene = null;
|
|
||||||
App.SceneManager.TryGetScene(regionID, out scene);
|
|
||||||
if (null == scene) return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"GET", "cannot find region {0}", regionID.ToString());
|
|
||||||
|
|
||||||
RegionDetails details = new RegionDetails(scene.RegionInfo);
|
|
||||||
|
|
||||||
// m_log.DebugFormat("{0} GET comps {1}", MsgID, comps.Length);
|
|
||||||
// for (int i = 0; i < comps.Length; i++) m_log.DebugFormat("{0} GET comps[{1}] >{2}<", MsgID, i, comps[i]);
|
|
||||||
|
|
||||||
if (1 == comps.Length)
|
|
||||||
{
|
|
||||||
// complete region details requested
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
XmlSerializer xs = new XmlSerializer(typeof(RegionDetails));
|
|
||||||
xs.Serialize(rxw, details, _xmlNs);
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (2 == comps.Length)
|
|
||||||
{
|
|
||||||
string resp = ShortRegionInfo(comps[1], details[comps[1]]);
|
|
||||||
if (null != resp) return resp;
|
|
||||||
|
|
||||||
// m_log.DebugFormat("{0} GET comps advanced: >{1}<", MsgID, comps[1]);
|
|
||||||
|
|
||||||
// check for {terrain,stats,prims}
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "terrain":
|
|
||||||
return RegionTerrain(httpResponse, scene);
|
|
||||||
|
|
||||||
case "stats":
|
|
||||||
return RegionStats(httpResponse, scene);
|
|
||||||
|
|
||||||
case "prims":
|
|
||||||
return RegionPrims(httpResponse, scene, Vector3.Zero, Vector3.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (3 == comps.Length)
|
|
||||||
{
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "prims":
|
|
||||||
string[] subregion = comps[2].Split(',');
|
|
||||||
if (subregion.Length == 6)
|
|
||||||
{
|
|
||||||
Vector3 min, max;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
min = new Vector3((float)Double.Parse(subregion[0], Culture.NumberFormatInfo), (float)Double.Parse(subregion[1], Culture.NumberFormatInfo), (float)Double.Parse(subregion[2], Culture.NumberFormatInfo));
|
|
||||||
max = new Vector3((float)Double.Parse(subregion[3], Culture.NumberFormatInfo), (float)Double.Parse(subregion[4], Culture.NumberFormatInfo), (float)Double.Parse(subregion[5], Culture.NumberFormatInfo));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "invalid subregion parameter");
|
|
||||||
}
|
|
||||||
return RegionPrims(httpResponse, scene, min, max);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "invalid subregion parameter");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorBadRequest,
|
|
||||||
"GET", "too many parameters {0}", param);
|
|
||||||
}
|
|
||||||
#endregion GET methods
|
|
||||||
|
|
||||||
protected string RegionTerrain(IOSHttpResponse httpResponse, Scene scene)
|
|
||||||
{
|
|
||||||
httpResponse.SendChunked = true;
|
|
||||||
httpResponse.ContentType = "text/xml";
|
|
||||||
|
|
||||||
return scene.Heightmap.SaveToXmlString();
|
|
||||||
//return Failure(httpResponse, OSHttpStatusCode.ServerErrorNotImplemented,
|
|
||||||
// "GET", "terrain not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string RegionStats(IOSHttpResponse httpResponse, Scene scene)
|
|
||||||
{
|
|
||||||
int users = scene.GetRootAgentCount();
|
|
||||||
int objects = scene.Entities.Count - users;
|
|
||||||
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
rxw.WriteStartElement(String.Empty, "stats", String.Empty);
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "users", String.Empty);
|
|
||||||
rxw.WriteString(users.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "objects", String.Empty);
|
|
||||||
rxw.WriteString(objects.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
rxw.WriteEndDocument();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string RegionPrims(IOSHttpResponse httpResponse, Scene scene, Vector3 min, Vector3 max)
|
|
||||||
{
|
|
||||||
httpResponse.SendChunked = true;
|
|
||||||
httpResponse.ContentType = "text/xml";
|
|
||||||
|
|
||||||
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
|
||||||
if (serialiser != null)
|
|
||||||
serialiser.SavePrimsToXml2(scene, new StreamWriter(httpResponse.OutputStream), min, max);
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region GET methods
|
|
||||||
public string GetRegionInfoHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} GET path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: regions list
|
|
||||||
// if (String.IsNullOrEmpty(param))
|
|
||||||
return GetRegionInfoHandlerRegions(httpResponse);
|
|
||||||
|
|
||||||
// // param not empty: specific region
|
|
||||||
// return GetRegionInfoHandlerRegion(httpResponse, param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "GET", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetRegionInfoHandlerRegions(IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
// regions info
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
{
|
|
||||||
// regions info: number of regions
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "number", String.Empty);
|
|
||||||
rxw.WriteValue(App.SceneManager.Scenes.Count);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
// regions info: max number of regions
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "max", String.Empty);
|
|
||||||
if (App.ConfigSource.Source.Configs["RemoteAdmin"] != null)
|
|
||||||
{
|
|
||||||
rxw.WriteValue(App.ConfigSource.Source.Configs["RemoteAdmin"].GetInt("region_limit", -1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rxw.WriteValue(-1);
|
|
||||||
}
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
// regions info: region
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "region", String.Empty);
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "name", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionName);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "x", String.Empty);
|
|
||||||
rxw.WriteValue(s.RegionInfo.RegionLocX);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "y", String.Empty);
|
|
||||||
rxw.WriteValue(s.RegionInfo.RegionLocY);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "external_hostname", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.ExternalHostName);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "ip", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.InternalEndPoint.ToString());
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
int users = s.GetRootAgentCount();
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "avatars", String.Empty);
|
|
||||||
rxw.WriteValue(users);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteStartAttribute(String.Empty, "objects", String.Empty);
|
|
||||||
rxw.WriteValue(s.Entities.Count - users);
|
|
||||||
rxw.WriteEndAttribute();
|
|
||||||
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
#endregion GET methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,122 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
using OpenSim.Region.Framework.Interfaces;
|
|
||||||
using OpenSim.Region.Framework.Scenes;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
#region POST methods
|
|
||||||
|
|
||||||
public string PostHandler(string request, string path, string param,
|
|
||||||
IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
|
|
||||||
{
|
|
||||||
// foreach (string h in httpRequest.Headers.AllKeys)
|
|
||||||
// foreach (string v in httpRequest.Headers.GetValues(h))
|
|
||||||
// m_log.DebugFormat("{0} IsGod: {1} -> {2}", MsgID, h, v);
|
|
||||||
|
|
||||||
MsgID = RequestID;
|
|
||||||
m_log.DebugFormat("{0} POST path {1} param {2}", MsgID, path, param);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// param empty: new region post
|
|
||||||
if (!IsGod(httpRequest))
|
|
||||||
// XXX: this needs to be turned into a FailureUnauthorized(...)
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorUnauthorized,
|
|
||||||
"GET", "you are not god");
|
|
||||||
|
|
||||||
if (String.IsNullOrEmpty(param)) return CreateRegion(httpRequest, httpResponse);
|
|
||||||
|
|
||||||
// Parse region ID and other parameters
|
|
||||||
param = param.TrimEnd(new char[] {'/'});
|
|
||||||
string[] comps = param.Split('/');
|
|
||||||
UUID regionID = (UUID) comps[0];
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} POST region UUID {1}", MsgID, regionID.ToString());
|
|
||||||
if (UUID.Zero == regionID) throw new Exception("missing region ID");
|
|
||||||
|
|
||||||
Scene scene = null;
|
|
||||||
App.SceneManager.TryGetScene(regionID, out scene);
|
|
||||||
if (null == scene)
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"POST", "cannot find region {0}", regionID.ToString());
|
|
||||||
|
|
||||||
if (2 == comps.Length)
|
|
||||||
{
|
|
||||||
// check for {prims}
|
|
||||||
switch (comps[1].ToLower())
|
|
||||||
{
|
|
||||||
case "prims":
|
|
||||||
return LoadPrims(request, httpRequest, httpResponse, scene);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ClientErrorNotFound,
|
|
||||||
"POST", "url {0} not supported", param);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return Failure(httpResponse, OSHttpStatusCode.ServerErrorInternalError, "POST", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string CreateRegion(IOSHttpRequest request, IOSHttpResponse response)
|
|
||||||
{
|
|
||||||
RestXmlWriter rxw = new RestXmlWriter(new StringWriter());
|
|
||||||
|
|
||||||
rxw.WriteStartElement(String.Empty, "regions", String.Empty);
|
|
||||||
foreach (Scene s in App.SceneManager.Scenes)
|
|
||||||
{
|
|
||||||
rxw.WriteStartElement(String.Empty, "uuid", String.Empty);
|
|
||||||
rxw.WriteString(s.RegionInfo.RegionID.ToString());
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
}
|
|
||||||
rxw.WriteEndElement();
|
|
||||||
|
|
||||||
return rxw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string LoadPrims(string requestBody, IOSHttpRequest request, IOSHttpResponse response, Scene scene)
|
|
||||||
{
|
|
||||||
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
|
|
||||||
if (serialiser != null)
|
|
||||||
serialiser.LoadPrimsFromXml2(scene, new StringReader(requestBody), true);
|
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion POST methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
[XmlRoot(ElementName="region", IsNullable = false)]
|
|
||||||
public class RegionDetails
|
|
||||||
{
|
|
||||||
public string region_name;
|
|
||||||
public string region_id;
|
|
||||||
public uint region_x;
|
|
||||||
public uint region_y;
|
|
||||||
public string region_owner;
|
|
||||||
public string region_owner_id;
|
|
||||||
public uint region_http_port;
|
|
||||||
public uint region_port;
|
|
||||||
public string region_server_uri;
|
|
||||||
public string region_external_hostname;
|
|
||||||
|
|
||||||
public RegionDetails()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public RegionDetails(RegionInfo regInfo)
|
|
||||||
{
|
|
||||||
region_name = regInfo.RegionName;
|
|
||||||
region_id = regInfo.RegionID.ToString();
|
|
||||||
region_x = regInfo.RegionLocX;
|
|
||||||
region_y = regInfo.RegionLocY;
|
|
||||||
region_owner_id = regInfo.EstateSettings.EstateOwner.ToString();
|
|
||||||
region_http_port = regInfo.HttpPort;
|
|
||||||
region_server_uri = regInfo.ServerURI;
|
|
||||||
region_external_hostname = regInfo.ExternalHostName;
|
|
||||||
|
|
||||||
Uri uri = new Uri(region_server_uri);
|
|
||||||
region_port = (uint)uri.Port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string this[string idx]
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
switch (idx.ToLower())
|
|
||||||
{
|
|
||||||
case "name":
|
|
||||||
return region_name;
|
|
||||||
case "id":
|
|
||||||
return region_id;
|
|
||||||
case "location":
|
|
||||||
return String.Format("<x>{0}</x><y>{1}</y>", region_x, region_y);
|
|
||||||
case "owner":
|
|
||||||
return region_owner;
|
|
||||||
case "owner_id":
|
|
||||||
return region_owner_id;
|
|
||||||
case "http_port":
|
|
||||||
return region_http_port.ToString();
|
|
||||||
case "server_uri":
|
|
||||||
return region_server_uri;
|
|
||||||
case "external_hostname":
|
|
||||||
case "hostname":
|
|
||||||
return region_external_hostname;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
<Addin id="OpenSim.ApplicationPlugins.Rest.Regions" version="0.1">
|
|
||||||
<Runtime>
|
|
||||||
<Import assembly="OpenSim.ApplicationPlugins.Rest.Regions.dll"/>
|
|
||||||
</Runtime>
|
|
||||||
<Dependencies>
|
|
||||||
<Addin id="OpenSim" version="0.5" />
|
|
||||||
</Dependencies>
|
|
||||||
<Extension path = "/OpenSim/Startup">
|
|
||||||
<Plugin id="RestRegions" type="OpenSim.ApplicationPlugins.Rest.Regions.RestRegionPlugin" />
|
|
||||||
</Extension>
|
|
||||||
</Addin>
|
|
|
@ -1,94 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Xml.Serialization;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest.Regions
|
|
||||||
{
|
|
||||||
public partial class RestRegionPlugin : RestPlugin
|
|
||||||
{
|
|
||||||
private static XmlSerializerNamespaces _xmlNs;
|
|
||||||
|
|
||||||
static RestRegionPlugin()
|
|
||||||
{
|
|
||||||
_xmlNs = new XmlSerializerNamespaces();
|
|
||||||
_xmlNs.Add(String.Empty, String.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region overriding properties
|
|
||||||
public override string Name
|
|
||||||
{
|
|
||||||
get { return "REGION"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ConfigName
|
|
||||||
{
|
|
||||||
get { return "RestRegionPlugin"; }
|
|
||||||
}
|
|
||||||
#endregion overriding properties
|
|
||||||
|
|
||||||
#region overriding methods
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
public override void Initialise(OpenSimBase openSim)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
base.Initialise(openSim);
|
|
||||||
if (!IsEnabled)
|
|
||||||
{
|
|
||||||
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_log.InfoFormat("{0} REST region plugin enabled", MsgID);
|
|
||||||
|
|
||||||
// add REST method handlers
|
|
||||||
AddRestStreamHandler("GET", "/regions/", GetHandler);
|
|
||||||
AddRestStreamHandler("POST", "/regions/", PostHandler);
|
|
||||||
AddRestStreamHandler("GET", "/regioninfo/", GetRegionInfoHandler);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
|
|
||||||
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endregion overriding methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,415 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) Contributors, http://opensimulator.org/
|
|
||||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name of the OpenSimulator Project nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Xml;
|
|
||||||
using log4net;
|
|
||||||
using Nini.Config;
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Framework.Servers;
|
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
|
||||||
|
|
||||||
namespace OpenSim.ApplicationPlugins.Rest
|
|
||||||
{
|
|
||||||
public abstract class RestPlugin : IApplicationPlugin
|
|
||||||
{
|
|
||||||
#region properties
|
|
||||||
|
|
||||||
protected static readonly ILog m_log =
|
|
||||||
LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
|
||||||
|
|
||||||
private IConfig _config; // Configuration source: Rest Plugins
|
|
||||||
private IConfig _pluginConfig; // Configuration source: Plugin specific
|
|
||||||
private OpenSimBase _app; // The 'server'
|
|
||||||
private BaseHttpServer _httpd; // The server's RPC interface
|
|
||||||
private string _prefix; // URL prefix below
|
|
||||||
// which all REST URLs
|
|
||||||
// are living
|
|
||||||
// private StringWriter _sw = null;
|
|
||||||
// private RestXmlWriter _xw = null;
|
|
||||||
|
|
||||||
private string _godkey;
|
|
||||||
private int _reqk;
|
|
||||||
|
|
||||||
[ThreadStatic]
|
|
||||||
private static string _threadRequestID = String.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return an ever increasing request ID for logging
|
|
||||||
/// </summary>
|
|
||||||
protected string RequestID
|
|
||||||
{
|
|
||||||
get { return _reqk++.ToString(); }
|
|
||||||
set { _reqk = Convert.ToInt32(value); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Thread-constant message IDs for logging.
|
|
||||||
/// </summary>
|
|
||||||
protected string MsgID
|
|
||||||
{
|
|
||||||
get { return String.Format("[REST-{0}] #{1}", Name, _threadRequestID); }
|
|
||||||
set { _threadRequestID = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if Rest Plugins are enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool PluginsAreEnabled
|
|
||||||
{
|
|
||||||
get { return null != _config; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if specific Rest Plugin is enabled.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsEnabled
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return (null != _pluginConfig) && _pluginConfig.GetBoolean("enabled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// OpenSimMain application
|
|
||||||
/// </summary>
|
|
||||||
public OpenSimBase App
|
|
||||||
{
|
|
||||||
get { return _app; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// RPC server
|
|
||||||
/// </summary>
|
|
||||||
public BaseHttpServer HttpServer
|
|
||||||
{
|
|
||||||
get { return _httpd; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// URL prefix to use for all REST handlers
|
|
||||||
/// </summary>
|
|
||||||
public string Prefix
|
|
||||||
{
|
|
||||||
get { return _prefix; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Access to GOD password string
|
|
||||||
/// </summary>
|
|
||||||
protected string GodKey
|
|
||||||
{
|
|
||||||
get { return _godkey; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Configuration of the plugin
|
|
||||||
/// </summary>
|
|
||||||
public IConfig Config
|
|
||||||
{
|
|
||||||
get { return _pluginConfig; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Name of the plugin
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return the config section name
|
|
||||||
/// </summary>
|
|
||||||
public abstract string ConfigName { get; }
|
|
||||||
|
|
||||||
// public XmlTextWriter XmlWriter
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// if (null == _xw)
|
|
||||||
// {
|
|
||||||
// _sw = new StringWriter();
|
|
||||||
// _xw = new RestXmlWriter(_sw);
|
|
||||||
// _xw.Formatting = Formatting.Indented;
|
|
||||||
// }
|
|
||||||
// return _xw;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public string XmlWriterResult
|
|
||||||
// {
|
|
||||||
// get
|
|
||||||
// {
|
|
||||||
// _xw.Flush();
|
|
||||||
// _xw.Close();
|
|
||||||
// _xw = null;
|
|
||||||
|
|
||||||
// return _sw.ToString();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
#endregion properties
|
|
||||||
|
|
||||||
#region methods
|
|
||||||
|
|
||||||
// TODO: required by IPlugin, but likely not at all right
|
|
||||||
private string m_version = "0.0";
|
|
||||||
|
|
||||||
public string Version
|
|
||||||
{
|
|
||||||
get { return m_version; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Initialise()
|
|
||||||
{
|
|
||||||
m_log.Info("[RESTPLUGIN]: " + Name + " cannot be default-initialized!");
|
|
||||||
throw new PluginNotInitialisedException(Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This method is called by OpenSimMain immediately after loading the
|
|
||||||
/// plugin and after basic server setup, but before running any server commands.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that entries MUST be added to the active configuration files before
|
|
||||||
/// the plugin can be enabled.
|
|
||||||
/// </remarks>
|
|
||||||
public virtual void Initialise(OpenSimBase openSim)
|
|
||||||
{
|
|
||||||
RequestID = "0";
|
|
||||||
MsgID = RequestID;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if ((_config = openSim.ConfigSource.Source.Configs["RestPlugins"]) == null)
|
|
||||||
{
|
|
||||||
m_log.WarnFormat("{0} Rest Plugins not configured", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_config.GetBoolean("enabled", false))
|
|
||||||
{
|
|
||||||
//m_log.WarnFormat("{0} Rest Plugins are disabled", MsgID);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_app = openSim;
|
|
||||||
_httpd = openSim.HttpServer;
|
|
||||||
|
|
||||||
// Retrieve GOD key value, if any.
|
|
||||||
_godkey = _config.GetString("god_key", String.Empty);
|
|
||||||
|
|
||||||
// Retrive prefix if any.
|
|
||||||
_prefix = _config.GetString("prefix", "/admin");
|
|
||||||
|
|
||||||
// Get plugin specific config
|
|
||||||
_pluginConfig = openSim.ConfigSource.Source.Configs[ConfigName];
|
|
||||||
|
|
||||||
m_log.InfoFormat("{0} Rest Plugins Enabled", MsgID);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// we can safely ignore this, as it just means that
|
|
||||||
// the key lookup in Configs failed, which signals to
|
|
||||||
// us that noone is interested in our services...they
|
|
||||||
// don't know what they are missing out on...
|
|
||||||
// NOTE: Under the present OpenSimulator implementation it is
|
|
||||||
// not possible for the openSimulator pointer to be null. However
|
|
||||||
// were the implementation to be changed, this could
|
|
||||||
// result in a silent initialization failure. Harmless
|
|
||||||
// except for lack of function and lack of any
|
|
||||||
// diagnostic indication as to why. The same is true if
|
|
||||||
// the HTTP server reference is bad.
|
|
||||||
// We should at least issue a message...
|
|
||||||
m_log.WarnFormat("{0} Initialization failed: {1}", MsgID, e.Message);
|
|
||||||
m_log.DebugFormat("{0} Initialization failed: {1}", MsgID, e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void PostInitialise()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<RestStreamHandler> _handlers = new List<RestStreamHandler>();
|
|
||||||
private Dictionary<string, IHttpAgentHandler> _agents = new Dictionary<string, IHttpAgentHandler>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a REST stream handler to the underlying HTTP server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="httpMethod">GET/PUT/POST/DELETE or
|
|
||||||
/// similar</param>
|
|
||||||
/// <param name="path">URL prefix</param>
|
|
||||||
/// <param name="method">RestMethod handler doing the actual work</param>
|
|
||||||
public virtual void AddRestStreamHandler(string httpMethod, string path, RestMethod method)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return;
|
|
||||||
|
|
||||||
if (!path.StartsWith(_prefix))
|
|
||||||
{
|
|
||||||
path = String.Format("{0}{1}", _prefix, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
RestStreamHandler h = new RestStreamHandler(httpMethod, path, method);
|
|
||||||
_httpd.AddStreamHandler(h);
|
|
||||||
_handlers.Add(h);
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} Added REST handler {1} {2}", MsgID, httpMethod, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Add a powerful Agent handler to the underlying HTTP
|
|
||||||
/// server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="agentName">name of agent handler</param>
|
|
||||||
/// <param name="handler">agent handler method</param>
|
|
||||||
/// <returns>false when the plugin is disabled or the agent
|
|
||||||
/// handler could not be added. Any generated exceptions are
|
|
||||||
/// allowed to drop through to the caller, i.e. ArgumentException.
|
|
||||||
/// </returns>
|
|
||||||
public bool AddAgentHandler(string agentName, IHttpAgentHandler handler)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return false;
|
|
||||||
_agents.Add(agentName, handler);
|
|
||||||
return _httpd.AddAgentHandler(agentName, handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove a powerful Agent handler from the underlying HTTP
|
|
||||||
/// server.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="agentName">name of agent handler</param>
|
|
||||||
/// <param name="handler">agent handler method</param>
|
|
||||||
/// <returns>false when the plugin is disabled or the agent
|
|
||||||
/// handler could not be removed. Any generated exceptions are
|
|
||||||
/// allowed to drop through to the caller, i.e. KeyNotFound.
|
|
||||||
/// </returns>
|
|
||||||
public bool RemoveAgentHandler(string agentName, IHttpAgentHandler handler)
|
|
||||||
{
|
|
||||||
if (!IsEnabled) return false;
|
|
||||||
if (_agents[agentName] == handler)
|
|
||||||
{
|
|
||||||
_agents.Remove(agentName);
|
|
||||||
return _httpd.RemoveAgentHandler(agentName, handler);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check whether the HTTP request came from god; that is, is
|
|
||||||
/// the god_key as configured in the config section supplied
|
|
||||||
/// via X-OpenSim-Godkey?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">HTTP request header</param>
|
|
||||||
/// <returns>true when the HTTP request came from god.</returns>
|
|
||||||
protected bool IsGod(IOSHttpRequest request)
|
|
||||||
{
|
|
||||||
string[] keys = request.Headers.GetValues("X-OpenSim-Godkey");
|
|
||||||
if (null == keys) return false;
|
|
||||||
|
|
||||||
// we take the last key supplied
|
|
||||||
return keys[keys.Length - 1] == _godkey;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks wether the X-OpenSim-Password value provided in the
|
|
||||||
/// HTTP header is indeed the password on file for the avatar
|
|
||||||
/// specified by the UUID
|
|
||||||
/// </summary>
|
|
||||||
protected bool IsVerifiedUser(IOSHttpRequest request, UUID uuid)
|
|
||||||
{
|
|
||||||
// XXX under construction
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Clean up and remove all handlers that were added earlier.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Close()
|
|
||||||
{
|
|
||||||
foreach (RestStreamHandler h in _handlers)
|
|
||||||
{
|
|
||||||
_httpd.RemoveStreamHandler(h.HttpMethod, h.Path);
|
|
||||||
}
|
|
||||||
_handlers = null;
|
|
||||||
foreach (KeyValuePair<string, IHttpAgentHandler> h in _agents)
|
|
||||||
{
|
|
||||||
_httpd.RemoveAgentHandler(h.Key, h.Value);
|
|
||||||
}
|
|
||||||
_agents = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Dispose()
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a failure message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">origin of the failure message</param>
|
|
||||||
/// <param name="message">failure message</param>
|
|
||||||
/// <remarks>This should probably set a return code as
|
|
||||||
/// well. (?)</remarks>
|
|
||||||
protected string Failure(IOSHttpResponse response, OSHttpStatusCode status,
|
|
||||||
string method, string format, params string[] msg)
|
|
||||||
{
|
|
||||||
string m = String.Format(format, msg);
|
|
||||||
|
|
||||||
response.StatusCode = (int) status;
|
|
||||||
response.StatusDescription = m;
|
|
||||||
|
|
||||||
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, m);
|
|
||||||
return String.Format("<error>{0}</error>", m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a failure message.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">origin of the failure message</param>
|
|
||||||
/// <param name="e">exception causing the failure message</param>
|
|
||||||
/// <remarks>This should probably set a return code as
|
|
||||||
/// well. (?)</remarks>
|
|
||||||
public string Failure(IOSHttpResponse response, OSHttpStatusCode status,
|
|
||||||
string method, Exception e)
|
|
||||||
{
|
|
||||||
string m = String.Format("exception occurred: {0}", e.Message);
|
|
||||||
|
|
||||||
response.StatusCode = (int) status;
|
|
||||||
response.StatusDescription = m;
|
|
||||||
|
|
||||||
m_log.DebugFormat("{0} {1} failed: {2}", MsgID, method, e.ToString());
|
|
||||||
m_log.ErrorFormat("{0} {1} failed: {2}", MsgID, method, e.Message);
|
|
||||||
|
|
||||||
return String.Format("<error>{0}</error>", e.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion methods
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,276 +0,0 @@
|
||||||
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
|
||||||
|
|
||||||
<xsd:annotation>
|
|
||||||
<xsd:documentation xml:lang="en">
|
|
||||||
Open Simulator Export/Import XML schema
|
|
||||||
August 2008
|
|
||||||
</xsd:documentation>
|
|
||||||
</xsd:annotation>
|
|
||||||
|
|
||||||
<!-- WARNING!!!
|
|
||||||
This is currently a draft, it does not reflect
|
|
||||||
what is exported, nor what will be understood
|
|
||||||
on import. It is included as a working document
|
|
||||||
and this comment will be removed at such time as
|
|
||||||
the schema corresponds to reality.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
REST-related information
|
|
||||||
Inventory data is always framed by an
|
|
||||||
inventory element. Consists of zero or
|
|
||||||
more elements representing either folders
|
|
||||||
or items within those folders. The inventory
|
|
||||||
element represents the "real" root folder.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:element name="inventory" type="inventory_ct" />
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The inventory complex type is just an arbitrary
|
|
||||||
sequence of folders and items. In reality it is
|
|
||||||
typically just folders. Both item and folder
|
|
||||||
have corresponding complex types. It is distinct
|
|
||||||
from folders insofar as it has no other defining
|
|
||||||
attributes.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:complexType name="inventory_ct">
|
|
||||||
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded"/>
|
|
||||||
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="folder_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="folder_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" /> <!-- added -->
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" />
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" /> <!-- added -->
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" /> <!-- added -->
|
|
||||||
|
|
||||||
<xsd:attribute name="parent" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" /> <!-- added -->
|
|
||||||
<xsd:element name="folder" type="folder_ct" maxOccurs="unbounded" />
|
|
||||||
<xsd:element name="item" type="item_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="item_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="inventory_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" />
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" />
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="folder" type="uuid_st" />
|
|
||||||
<xsd:attribute name="groupid" type="uuid_st" />
|
|
||||||
<xsd:attribute name="groupowned" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="saletype" type="sale_st" />
|
|
||||||
<xsd:attribute name="saleprice" type="xsd:decimal" />
|
|
||||||
|
|
||||||
<xsd:element name="permissions" type="permissions_ct" maxOccurs="unbounded" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<xsd:complexType name="asset_ct">
|
|
||||||
<xsd:attribute name="UUID" type="uuid_st" />
|
|
||||||
<xsd:attribute name="name" type="name_st" />
|
|
||||||
<xsd:attribute name="type" type="asset_type_st" />
|
|
||||||
<xsd:attribute name="description" type="xsd:string" />
|
|
||||||
<xsd:attribute name="version" type="unsignedShort" /> <!-- added -->
|
|
||||||
<xsd:attribute name="owner" type="uuid_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="creator" type="uuid_st" />
|
|
||||||
<xsd:attribute name="creationdate" type="date_st" />
|
|
||||||
|
|
||||||
<xsd:attribute name="temporary" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="local" type="xsd:boolean" />
|
|
||||||
<xsd:attribute name="inline" type="xsd:boolean" />
|
|
||||||
</xsd:complexType>
|
|
||||||
|
|
||||||
<!-- Constrained Simple Data types -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
We need to specify name as a simple type because on
|
|
||||||
some platforms it is constrained by a certain length
|
|
||||||
limitation. For completeness we indicate that whitespace
|
|
||||||
should be preserved exactly as specified.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="name_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<whiteSpace value="preserve" />
|
|
||||||
<minLength value="0" />
|
|
||||||
<maxLength value="64" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Type information in the folder is meant to indicate
|
|
||||||
the preferred asset type for this folder. As such, that
|
|
||||||
currently corresponds to the type values allowed for
|
|
||||||
assets, however that is not mandated, so for
|
|
||||||
now at least I'll represent this as a distinct
|
|
||||||
enumeration.
|
|
||||||
This seems inappropriate; it seems like the folder's
|
|
||||||
content should reflect the InventoryType classifications
|
|
||||||
rather than the asset types.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="folder_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSLText" />
|
|
||||||
<xsd:enumeration value="LSLByteCode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="SoundWAV" />
|
|
||||||
<xsd:enumeration value="ImageTGA" />
|
|
||||||
<xsd:enumeration value="ImageJPEG" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Simstate" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFoundFolder" />
|
|
||||||
<xsd:enumeration value="SnapshotFolder" />
|
|
||||||
<xsd:enumeration value="TrashFolder" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="RootFolder" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Inventory item type designates an asset class, rather
|
|
||||||
than a specific asset type. For example, "SnapShot"
|
|
||||||
might include a number of asset types such as JPEG,
|
|
||||||
TGA, etc.. This is not a consistent interpretation,
|
|
||||||
classifications such as LostAndFound are meta-types
|
|
||||||
relative to asset classes.
|
|
||||||
|
|
||||||
These types should be abstract and not be tied to a
|
|
||||||
specific platform. A world's import facility should be
|
|
||||||
responsible for mapping these to meaningful internal
|
|
||||||
representations.
|
|
||||||
|
|
||||||
These types were based on information in:
|
|
||||||
libsecondlife/InventoryManager.cs
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="inventory_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSL" />
|
|
||||||
<xsd:enumeration value="LSLBytecode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="Snapshot" />
|
|
||||||
<xsd:enumeration value="Attachment" />
|
|
||||||
<xsd:enumeration value="Wearable" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFound" />
|
|
||||||
<xsd:enumeration value="Trash" />
|
|
||||||
<xsd:enumeration value="Root" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
The asset types seem to be even more disarrayed than
|
|
||||||
the inventory types. It seems to be little more than
|
|
||||||
a reiteration of the inventory type information,
|
|
||||||
which adds little or nothing to the overall data
|
|
||||||
model.
|
|
||||||
|
|
||||||
Of course, given that these are drawn from the
|
|
||||||
libsecondlife definitions, we aren't at liberty to
|
|
||||||
simply redefine them in place. But the XML definitions
|
|
||||||
here could be made more useful.
|
|
||||||
|
|
||||||
These types were based on information in:
|
|
||||||
libsecondlife/AssetManager.cs
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="asset_type_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:enumeration value="Texture" />
|
|
||||||
<xsd:enumeration value="Sound" />
|
|
||||||
<xsd:enumeration value="CallingCard" />
|
|
||||||
<xsd:enumeration value="Landmark" />
|
|
||||||
<xsd:enumeration value="Script" />
|
|
||||||
<xsd:enumeration value="Clothing" />
|
|
||||||
<xsd:enumeration value="Object" />
|
|
||||||
<xsd:enumeration value="Notecard" />
|
|
||||||
<xsd:enumeration value="LSLText" />
|
|
||||||
<xsd:enumeration value="LSLByteCode" />
|
|
||||||
<xsd:enumeration value="TextureTGA" />
|
|
||||||
<xsd:enumeration value="BodyPart" />
|
|
||||||
<xsd:enumeration value="SoundWAV" />
|
|
||||||
<xsd:enumeration value="ImageTGA" />
|
|
||||||
<xsd:enumeration value="ImageJPEG" />
|
|
||||||
<xsd:enumeration value="Animation" />
|
|
||||||
<xsd:enumeration value="Gesture" />
|
|
||||||
<xsd:enumeration value="Simstate" />
|
|
||||||
<xsd:enumeration value="Unknown" />
|
|
||||||
<xsd:enumeration value="LostAndFoundFolder" />
|
|
||||||
<xsd:enumeration value="SnapshotFolder" />
|
|
||||||
<xsd:enumeration value="TrashFolder" />
|
|
||||||
<xsd:enumeration value="Folder" />
|
|
||||||
<xsd:enumeration value="RootFolder" />
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This is describing the apparent form of a UUID. If
|
|
||||||
we ever want a more metaphysical definition we'll
|
|
||||||
need to add to it.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="uuid_st">
|
|
||||||
<xsd:restriction base="xsd:string">
|
|
||||||
<xsd:pattern value="[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}"/>
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This constrains the date representation. Currently
|
|
||||||
it is simply an integer representing the elapsed
|
|
||||||
?? since ??.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="date_st">
|
|
||||||
<xsd:restriction base="xsd:positiveInteger">
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
<!-- This constrains the representation of sale price.
|
|
||||||
Currently it is a simple decimal with no unit
|
|
||||||
specified.
|
|
||||||
Issues: interoperability.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<xsd:simpleType name="sale_st">
|
|
||||||
<xsd:restriction base="xsd:decimal">
|
|
||||||
</xsd:restriction>
|
|
||||||
</xsd:simpleType>
|
|
||||||
|
|
||||||
</xsd:schema>
|
|
|
@ -219,15 +219,38 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
int start, end;
|
int start, end;
|
||||||
if (TryParseRange(range, out start, out end))
|
if (TryParseRange(range, out start, out end))
|
||||||
{
|
{
|
||||||
|
|
||||||
// Before clamping start make sure we can satisfy it in order to avoid
|
// Before clamping start make sure we can satisfy it in order to avoid
|
||||||
// sending back the last byte instead of an error status
|
// sending back the last byte instead of an error status
|
||||||
if (start >= texture.Data.Length)
|
if (start >= texture.Data.Length)
|
||||||
{
|
{
|
||||||
response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
|
// 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
|
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);
|
end = Utils.Clamp(end, 0, texture.Data.Length - 1);
|
||||||
start = Utils.Clamp(start, 0, end);
|
start = Utils.Clamp(start, 0, end);
|
||||||
int len = end - start + 1;
|
int len = end - start + 1;
|
||||||
|
@ -238,6 +261,12 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
// We were accidentally sending back 404 before in this situation
|
// 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
|
// 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.
|
// 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.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
|
||||||
|
|
||||||
response.ContentLength = len;
|
response.ContentLength = len;
|
||||||
|
@ -275,16 +304,44 @@ namespace OpenSim.Capabilities.Handlers
|
||||||
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
|
// texture.FullID, range, response.StatusCode, response.ContentLength, texture.Data.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a range header.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// As per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html,
|
||||||
|
/// this obeys range headers with two values (e.g. 533-4165) and no second value (e.g. 533-).
|
||||||
|
/// Where there is no value, -1 is returned.
|
||||||
|
/// FIXME: Need to cover the case where only a second value is specified (e.g. -4165), probably by returning -1
|
||||||
|
/// for start.</remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <param name='header'></param>
|
||||||
|
/// <param name='start'>Start of the range. Undefined if this was not a number.</param>
|
||||||
|
/// <param name='end'>End of the range. Will be -1 if no end specified. Undefined if there was a raw string but this was not a number.</param>
|
||||||
private bool TryParseRange(string header, out int start, out int end)
|
private bool TryParseRange(string header, out int start, out int end)
|
||||||
{
|
{
|
||||||
|
start = end = 0;
|
||||||
|
|
||||||
if (header.StartsWith("bytes="))
|
if (header.StartsWith("bytes="))
|
||||||
{
|
{
|
||||||
string[] rangeValues = header.Substring(6).Split('-');
|
string[] rangeValues = header.Substring(6).Split('-');
|
||||||
|
|
||||||
if (rangeValues.Length == 2)
|
if (rangeValues.Length == 2)
|
||||||
{
|
{
|
||||||
if (Int32.TryParse(rangeValues[0], out start) && Int32.TryParse(rangeValues[1], out end))
|
if (!Int32.TryParse(rangeValues[0], out start))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
string rawEnd = rangeValues[1];
|
||||||
|
|
||||||
|
if (rawEnd == "")
|
||||||
|
{
|
||||||
|
end = -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else if (Int32.TryParse(rawEnd, out end))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
start = end = 0;
|
start = end = 0;
|
||||||
|
|
|
@ -42,7 +42,7 @@ using OpenSim.Tests.Common.Mock;
|
||||||
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
|
namespace OpenSim.Capabilities.Handlers.GetTexture.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class GetTextureHandlerTests
|
public class GetTextureHandlerTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestTextureNotFound()
|
public void TestTextureNotFound()
|
||||||
|
|
|
@ -44,7 +44,6 @@ namespace OpenSim.ConsoleClient
|
||||||
ReplyDelegate action)
|
ReplyDelegate action)
|
||||||
{
|
{
|
||||||
WebRequest request = WebRequest.Create(requestUrl);
|
WebRequest request = WebRequest.Create(requestUrl);
|
||||||
WebResponse response = null;
|
|
||||||
|
|
||||||
request.Method = "POST";
|
request.Method = "POST";
|
||||||
|
|
||||||
|
@ -64,17 +63,19 @@ namespace OpenSim.ConsoleClient
|
||||||
{
|
{
|
||||||
string reply = String.Empty;
|
string reply = String.Empty;
|
||||||
|
|
||||||
response = request.EndGetResponse(ar);
|
using (WebResponse response = request.EndGetResponse(ar))
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
StreamReader r = new StreamReader(response.GetResponseStream());
|
using (Stream s = response.GetResponseStream())
|
||||||
|
using (StreamReader r = new StreamReader(s))
|
||||||
reply = r.ReadToEnd();
|
reply = r.ReadToEnd();
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (System.InvalidOperationException)
|
catch (System.InvalidOperationException)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
action(requestUrl, data, reply);
|
action(requestUrl, data, reply);
|
||||||
}, null);
|
}, null);
|
||||||
|
|
|
@ -40,6 +40,11 @@ namespace OpenSim.Data
|
||||||
public UUID folderID;
|
public UUID folderID;
|
||||||
public UUID agentID;
|
public UUID agentID;
|
||||||
public UUID parentFolderID;
|
public UUID parentFolderID;
|
||||||
|
|
||||||
|
public XInventoryFolder Clone()
|
||||||
|
{
|
||||||
|
return (XInventoryFolder)MemberwiseClone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class XInventoryItem
|
public class XInventoryItem
|
||||||
|
@ -64,6 +69,11 @@ namespace OpenSim.Data
|
||||||
public UUID avatarID;
|
public UUID avatarID;
|
||||||
public UUID parentFolderID;
|
public UUID parentFolderID;
|
||||||
public int inventoryGroupPermissions;
|
public int inventoryGroupPermissions;
|
||||||
|
|
||||||
|
public XInventoryItem Clone()
|
||||||
|
{
|
||||||
|
return (XInventoryItem)MemberwiseClone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IXInventoryData
|
public interface IXInventoryData
|
||||||
|
@ -106,7 +116,22 @@ namespace OpenSim.Data
|
||||||
/// <returns>true if the delete was successful, false if it was not</returns>
|
/// <returns>true if the delete was successful, false if it was not</returns>
|
||||||
bool DeleteItems(string[] fields, string[] vals);
|
bool DeleteItems(string[] fields, string[] vals);
|
||||||
|
|
||||||
bool MoveItem(string id, string newParent);
|
/// <summary>
|
||||||
|
/// Move an item to another folder.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>/returns>
|
||||||
|
/// <param name='id'>UUID of the item</param>
|
||||||
|
/// <param name='newParent'>UUID of the new parent folder.</param>
|
||||||
|
bool MoveItem(string id, string newParentFolderID);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Move a folder to another folder.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>/returns>
|
||||||
|
/// <param name='id'>UUID of the item</param>
|
||||||
|
/// <param name='newParent'>UUID of the new parent folder.</param>
|
||||||
|
bool MoveFolder(string id, string newParentFolderID);
|
||||||
|
|
||||||
XInventoryItem[] GetActiveGestures(UUID principalID);
|
XInventoryItem[] GetActiveGestures(UUID principalID);
|
||||||
int GetAssetPermissions(UUID principalID, UUID assetID);
|
int GetAssetPermissions(UUID principalID, UUID assetID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,14 +163,18 @@ namespace OpenSim.Data.MSSQL
|
||||||
if (asset.Name.Length > 64)
|
if (asset.Name.Length > 64)
|
||||||
{
|
{
|
||||||
assetName = asset.Name.Substring(0, 64);
|
assetName = asset.Name.Substring(0, 64);
|
||||||
m_log.Warn("[ASSET DB]: Name field truncated from " + asset.Name.Length + " to " + assetName.Length + " characters on add");
|
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;
|
string assetDescription = asset.Description;
|
||||||
if (asset.Description.Length > 64)
|
if (asset.Description.Length > 64)
|
||||||
{
|
{
|
||||||
assetDescription = asset.Description.Substring(0, 64);
|
assetDescription = asset.Description.Substring(0, 64);
|
||||||
m_log.Warn("[ASSET DB]: Description field truncated from " + asset.Description.Length + " to " + assetDescription.Length + " characters on add");
|
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 (SqlConnection conn = new SqlConnection(m_connectionString))
|
using (SqlConnection conn = new SqlConnection(m_connectionString))
|
||||||
|
|
|
@ -43,12 +43,12 @@ namespace OpenSim.Data.MSSQL
|
||||||
// private static readonly ILog m_log = LogManager.GetLogger(
|
// private static readonly ILog m_log = LogManager.GetLogger(
|
||||||
// MethodBase.GetCurrentMethod().DeclaringType);
|
// MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
private MSSQLGenericTableHandler<XInventoryFolder> m_Folders;
|
private MSSQLFolderHandler m_Folders;
|
||||||
private MSSQLItemHandler m_Items;
|
private MSSQLItemHandler m_Items;
|
||||||
|
|
||||||
public MSSQLXInventoryData(string conn, string realm)
|
public MSSQLXInventoryData(string conn, string realm)
|
||||||
{
|
{
|
||||||
m_Folders = new MSSQLGenericTableHandler<XInventoryFolder>(
|
m_Folders = new MSSQLFolderHandler(
|
||||||
conn, "inventoryfolders", "InventoryStore");
|
conn, "inventoryfolders", "InventoryStore");
|
||||||
m_Items = new MSSQLItemHandler(
|
m_Items = new MSSQLItemHandler(
|
||||||
conn, "inventoryitems", String.Empty);
|
conn, "inventoryitems", String.Empty);
|
||||||
|
@ -85,6 +85,7 @@ namespace OpenSim.Data.MSSQL
|
||||||
{
|
{
|
||||||
return m_Folders.Delete(field, val);
|
return m_Folders.Delete(field, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DeleteFolders(string[] fields, string[] vals)
|
public bool DeleteFolders(string[] fields, string[] vals)
|
||||||
{
|
{
|
||||||
return m_Folders.Delete(fields, vals);
|
return m_Folders.Delete(fields, vals);
|
||||||
|
@ -94,15 +95,22 @@ namespace OpenSim.Data.MSSQL
|
||||||
{
|
{
|
||||||
return m_Items.Delete(field, val);
|
return m_Items.Delete(field, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool DeleteItems(string[] fields, string[] vals)
|
public bool DeleteItems(string[] fields, string[] vals)
|
||||||
{
|
{
|
||||||
return m_Items.Delete(fields, vals);
|
return m_Items.Delete(fields, vals);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MoveItem(string id, string newParent)
|
public bool MoveItem(string id, string newParent)
|
||||||
{
|
{
|
||||||
return m_Items.MoveItem(id, 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)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
{
|
{
|
||||||
return m_Items.GetActiveGestures(principalID);
|
return m_Items.GetActiveGestures(principalID);
|
||||||
|
@ -114,7 +122,7 @@ namespace OpenSim.Data.MSSQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MSSQLItemHandler : MSSQLGenericTableHandler<XInventoryItem>
|
public class MSSQLItemHandler : MSSQLInventoryHandler<XInventoryItem>
|
||||||
{
|
{
|
||||||
public MSSQLItemHandler(string c, string t, string m) :
|
public MSSQLItemHandler(string c, string t, string m) :
|
||||||
base(c, t, m)
|
base(c, t, m)
|
||||||
|
@ -123,7 +131,14 @@ namespace OpenSim.Data.MSSQL
|
||||||
|
|
||||||
public bool MoveItem(string id, string newParent)
|
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 (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
||||||
|
{
|
||||||
using (SqlCommand cmd = new SqlCommand())
|
using (SqlCommand cmd = new SqlCommand())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -132,13 +147,22 @@ namespace OpenSim.Data.MSSQL
|
||||||
cmd.Parameters.Add(m_database.CreateParameter("@InventoryID", id));
|
cmd.Parameters.Add(m_database.CreateParameter("@InventoryID", id));
|
||||||
cmd.Connection = conn;
|
cmd.Connection = conn;
|
||||||
conn.Open();
|
conn.Open();
|
||||||
return cmd.ExecuteNonQuery() == 0 ? false : true;
|
|
||||||
|
if (cmd.ExecuteNonQuery() == 0)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IncrementFolderVersion(oldParent);
|
||||||
|
IncrementFolderVersion(newParent);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
{
|
{
|
||||||
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
||||||
|
{
|
||||||
using (SqlCommand cmd = new SqlCommand())
|
using (SqlCommand cmd = new SqlCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = @uuid and assetType = @type and flags = 1", m_Realm);
|
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = @uuid and assetType = @type and flags = 1", m_Realm);
|
||||||
|
@ -150,10 +174,12 @@ namespace OpenSim.Data.MSSQL
|
||||||
return DoQuery(cmd);
|
return DoQuery(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int GetAssetPermissions(UUID principalID, UUID assetID)
|
public int GetAssetPermissions(UUID principalID, UUID assetID)
|
||||||
{
|
{
|
||||||
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
||||||
|
{
|
||||||
using (SqlCommand cmd = new SqlCommand())
|
using (SqlCommand cmd = new SqlCommand())
|
||||||
{
|
{
|
||||||
cmd.CommandText = String.Format("select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions from inventoryitems where avatarID = @PrincipalID and assetID = @AssetID group by assetID", m_Realm);
|
cmd.CommandText = String.Format("select bit_or(inventoryCurrentPermissions) as inventoryCurrentPermissions from inventoryitems where avatarID = @PrincipalID and assetID = @AssetID group by assetID", m_Realm);
|
||||||
|
@ -176,17 +202,92 @@ namespace OpenSim.Data.MSSQL
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override bool Store(XInventoryItem item)
|
public override bool Store(XInventoryItem item)
|
||||||
{
|
{
|
||||||
if (!base.Store(item))
|
if (!base.Store(item))
|
||||||
return false;
|
return false;
|
||||||
string sql = "update inventoryfolders set version=version+1 where folderID = @folderID";
|
|
||||||
|
IncrementFolderVersion(item.parentFolderID);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MSSQLFolderHandler : MSSQLInventoryHandler<XInventoryFolder>
|
||||||
|
{
|
||||||
|
public MSSQLFolderHandler(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 (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
||||||
|
{
|
||||||
|
using (SqlCommand cmd = new SqlCommand())
|
||||||
|
{
|
||||||
|
|
||||||
|
cmd.CommandText = String.Format("update {0} set parentFolderID = @ParentFolderID where folderID = @folderID", m_Realm);
|
||||||
|
cmd.Parameters.Add(m_database.CreateParameter("@ParentFolderID", newParentFolderID));
|
||||||
|
cmd.Parameters.Add(m_database.CreateParameter("@folderID", id));
|
||||||
|
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 MSSQLInventoryHandler<T> : MSSQLGenericTableHandler<T> where T: class, new()
|
||||||
|
{
|
||||||
|
public MSSQLInventoryHandler(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();
|
||||||
|
|
||||||
|
string sql = "update inventoryfolders set version=version+1 where folderID = ?folderID";
|
||||||
|
|
||||||
|
using (SqlConnection conn = new SqlConnection(m_ConnectionString))
|
||||||
|
{
|
||||||
using (SqlCommand cmd = new SqlCommand(sql, conn))
|
using (SqlCommand cmd = new SqlCommand(sql, conn))
|
||||||
{
|
{
|
||||||
conn.Open();
|
conn.Open();
|
||||||
|
|
||||||
cmd.Parameters.AddWithValue("@folderID", item.parentFolderID.ToString());
|
cmd.Parameters.AddWithValue("@folderID", folderID);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
|
@ -196,6 +297,8 @@ namespace OpenSim.Data.MSSQL
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,14 +173,18 @@ namespace OpenSim.Data.MySQL
|
||||||
if (asset.Name.Length > 64)
|
if (asset.Name.Length > 64)
|
||||||
{
|
{
|
||||||
assetName = asset.Name.Substring(0, 64);
|
assetName = asset.Name.Substring(0, 64);
|
||||||
m_log.Warn("[ASSET DB]: Name field truncated from " + asset.Name.Length + " to " + assetName.Length + " characters on add");
|
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;
|
string assetDescription = asset.Description;
|
||||||
if (asset.Description.Length > 64)
|
if (asset.Description.Length > 64)
|
||||||
{
|
{
|
||||||
assetDescription = asset.Description.Substring(0, 64);
|
assetDescription = asset.Description.Substring(0, 64);
|
||||||
m_log.Warn("[ASSET DB]: Description field truncated from " + asset.Description.Length + " to " + assetDescription.Length + " characters on add");
|
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
|
try
|
||||||
|
|
|
@ -219,6 +219,8 @@ namespace OpenSim.Data.MySQL
|
||||||
|
|
||||||
public virtual bool Store(T row)
|
public virtual bool Store(T row)
|
||||||
{
|
{
|
||||||
|
// m_log.DebugFormat("[MYSQL GENERIC TABLE HANDLER]: Store(T row) invoked");
|
||||||
|
|
||||||
using (MySqlCommand cmd = new MySqlCommand())
|
using (MySqlCommand cmd = new MySqlCommand())
|
||||||
{
|
{
|
||||||
string query = "";
|
string query = "";
|
||||||
|
@ -273,6 +275,10 @@ namespace OpenSim.Data.MySQL
|
||||||
|
|
||||||
public virtual bool Delete(string[] fields, string[] keys)
|
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)
|
if (fields.Length != keys.Length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -719,6 +719,8 @@ namespace OpenSim.Data.MySQL
|
||||||
RegionLightShareData nWP = new RegionLightShareData();
|
RegionLightShareData nWP = new RegionLightShareData();
|
||||||
nWP.OnSave += StoreRegionWindlightSettings;
|
nWP.OnSave += StoreRegionWindlightSettings;
|
||||||
|
|
||||||
|
lock (m_dbLock)
|
||||||
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
dbcon.Open();
|
dbcon.Open();
|
||||||
|
@ -808,6 +810,8 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nWP;
|
return nWP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -852,6 +856,8 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StoreRegionWindlightSettings(RegionLightShareData wl)
|
public void StoreRegionWindlightSettings(RegionLightShareData wl)
|
||||||
|
{
|
||||||
|
lock (m_dbLock)
|
||||||
{
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
|
@ -953,8 +959,11 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveRegionWindlightSettings(UUID regionID)
|
public void RemoveRegionWindlightSettings(UUID regionID)
|
||||||
|
{
|
||||||
|
lock (m_dbLock)
|
||||||
{
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
|
@ -968,9 +977,12 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region RegionEnvironmentSettings
|
#region RegionEnvironmentSettings
|
||||||
public string LoadRegionEnvironmentSettings(UUID regionUUID)
|
public string LoadRegionEnvironmentSettings(UUID regionUUID)
|
||||||
|
{
|
||||||
|
lock (m_dbLock)
|
||||||
{
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
|
@ -996,8 +1008,11 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings)
|
public void StoreRegionEnvironmentSettings(UUID regionUUID, string settings)
|
||||||
|
{
|
||||||
|
lock (m_dbLock)
|
||||||
{
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
|
@ -1014,8 +1029,11 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void RemoveRegionEnvironmentSettings(UUID regionUUID)
|
public void RemoveRegionEnvironmentSettings(UUID regionUUID)
|
||||||
|
{
|
||||||
|
lock (m_dbLock)
|
||||||
{
|
{
|
||||||
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
|
@ -1029,6 +1047,7 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void StoreRegionSettings(RegionSettings rs)
|
public void StoreRegionSettings(RegionSettings rs)
|
||||||
|
|
|
@ -204,14 +204,18 @@ namespace OpenSim.Data.MySQL
|
||||||
if (asset.Name.Length > 64)
|
if (asset.Name.Length > 64)
|
||||||
{
|
{
|
||||||
assetName = asset.Name.Substring(0, 64);
|
assetName = asset.Name.Substring(0, 64);
|
||||||
m_log.Warn("[XASSET DB]: Name field truncated from " + asset.Name.Length + " to " + assetName.Length + " characters on add");
|
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;
|
string assetDescription = asset.Description;
|
||||||
if (asset.Description.Length > 64)
|
if (asset.Description.Length > 64)
|
||||||
{
|
{
|
||||||
assetDescription = asset.Description.Substring(0, 64);
|
assetDescription = asset.Description.Substring(0, 64);
|
||||||
m_log.Warn("[XASSET DB]: Description field truncated from " + asset.Description.Length + " to " + assetDescription.Length + " characters on add");
|
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)
|
if (m_enableCompression)
|
||||||
|
|
|
@ -26,9 +26,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Data;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using log4net;
|
using log4net;
|
||||||
using MySql.Data.MySqlClient;
|
using MySql.Data.MySqlClient;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
|
@ -41,12 +42,12 @@ namespace OpenSim.Data.MySQL
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MySQLXInventoryData : IXInventoryData
|
public class MySQLXInventoryData : IXInventoryData
|
||||||
{
|
{
|
||||||
private MySQLGenericTableHandler<XInventoryFolder> m_Folders;
|
private MySqlFolderHandler m_Folders;
|
||||||
private MySqlItemHandler m_Items;
|
private MySqlItemHandler m_Items;
|
||||||
|
|
||||||
public MySQLXInventoryData(string conn, string realm)
|
public MySQLXInventoryData(string conn, string realm)
|
||||||
{
|
{
|
||||||
m_Folders = new MySQLGenericTableHandler<XInventoryFolder>(
|
m_Folders = new MySqlFolderHandler(
|
||||||
conn, "inventoryfolders", "InventoryStore");
|
conn, "inventoryfolders", "InventoryStore");
|
||||||
m_Items = new MySqlItemHandler(
|
m_Items = new MySqlItemHandler(
|
||||||
conn, "inventoryitems", String.Empty);
|
conn, "inventoryitems", String.Empty);
|
||||||
|
@ -105,6 +106,11 @@ namespace OpenSim.Data.MySQL
|
||||||
return m_Items.MoveItem(id, 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)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
{
|
{
|
||||||
return m_Items.GetActiveGestures(principalID);
|
return m_Items.GetActiveGestures(principalID);
|
||||||
|
@ -116,24 +122,71 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class MySqlItemHandler : MySQLGenericTableHandler<XInventoryItem>
|
public class MySqlItemHandler : MySqlInventoryHandler<XInventoryItem>
|
||||||
{
|
{
|
||||||
|
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
public MySqlItemHandler(string c, string t, string m) :
|
public MySqlItemHandler(string c, string t, string m) :
|
||||||
base(c, t, 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<UUID> deletedItemFolderUUIDs = new HashSet<UUID>();
|
||||||
|
|
||||||
|
Array.ForEach<XInventoryItem>(retrievedItems, i => deletedItemFolderUUIDs.Add(i.parentFolderID));
|
||||||
|
|
||||||
|
foreach (UUID deletedItemFolderUUID in deletedItemFolderUUIDs)
|
||||||
|
IncrementFolderVersion(deletedItemFolderUUID);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool MoveItem(string id, string newParent)
|
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())
|
using (MySqlCommand cmd = new MySqlCommand())
|
||||||
{
|
{
|
||||||
|
|
||||||
cmd.CommandText = String.Format("update {0} set parentFolderID = ?ParentFolderID where inventoryID = ?InventoryID", m_Realm);
|
cmd.CommandText = String.Format("update {0} set parentFolderID = ?ParentFolderID where inventoryID = ?InventoryID", m_Realm);
|
||||||
cmd.Parameters.AddWithValue("?ParentFolderID", newParent);
|
cmd.Parameters.AddWithValue("?ParentFolderID", newParent);
|
||||||
cmd.Parameters.AddWithValue("?InventoryID", id);
|
cmd.Parameters.AddWithValue("?InventoryID", id);
|
||||||
|
|
||||||
return ExecuteNonQuery(cmd) == 0 ? false : true;
|
if (ExecuteNonQuery(cmd) == 0)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IncrementFolderVersion(oldParent);
|
||||||
|
IncrementFolderVersion(newParent);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
|
@ -184,6 +237,73 @@ namespace OpenSim.Data.MySQL
|
||||||
if (!base.Store(item))
|
if (!base.Store(item))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
IncrementFolderVersion(item.parentFolderID);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MySqlFolderHandler : MySqlInventoryHandler<XInventoryFolder>
|
||||||
|
{
|
||||||
|
// 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<T> : MySQLGenericTableHandler<T> 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))
|
using (MySqlConnection dbcon = new MySqlConnection(m_connectionString))
|
||||||
{
|
{
|
||||||
dbcon.Open();
|
dbcon.Open();
|
||||||
|
@ -193,7 +313,7 @@ namespace OpenSim.Data.MySQL
|
||||||
cmd.Connection = dbcon;
|
cmd.Connection = dbcon;
|
||||||
|
|
||||||
cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID");
|
cmd.CommandText = String.Format("update inventoryfolders set version=version+1 where folderID = ?folderID");
|
||||||
cmd.Parameters.AddWithValue("?folderID", item.parentFolderID.ToString());
|
cmd.Parameters.AddWithValue("?folderID", folderID);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -205,8 +325,10 @@ namespace OpenSim.Data.MySQL
|
||||||
}
|
}
|
||||||
cmd.Dispose();
|
cmd.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
dbcon.Close();
|
dbcon.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,12 +112,15 @@ namespace OpenSim.Data.Null
|
||||||
// Find region data
|
// Find region data
|
||||||
List<RegionData> ret = new List<RegionData>();
|
List<RegionData> ret = new List<RegionData>();
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
foreach (RegionData r in m_regionData.Values)
|
foreach (RegionData r in m_regionData.Values)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanName, r.RegionName.ToLower());
|
// m_log.DebugFormat("[NULL REGION DATA]: comparing {0} to {1}", cleanName, r.RegionName.ToLower());
|
||||||
if (queryMatch(r.RegionName.ToLower()))
|
if (queryMatch(r.RegionName.ToLower()))
|
||||||
ret.Add(r);
|
ret.Add(r);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ret.Count > 0)
|
if (ret.Count > 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -132,11 +135,14 @@ namespace OpenSim.Data.Null
|
||||||
|
|
||||||
List<RegionData> ret = new List<RegionData>();
|
List<RegionData> ret = new List<RegionData>();
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
foreach (RegionData r in m_regionData.Values)
|
foreach (RegionData r in m_regionData.Values)
|
||||||
{
|
{
|
||||||
if (r.posX == posX && r.posY == posY)
|
if (r.posX == posX && r.posY == posY)
|
||||||
ret.Add(r);
|
ret.Add(r);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ret.Count > 0)
|
if (ret.Count > 0)
|
||||||
return ret[0];
|
return ret[0];
|
||||||
|
@ -149,8 +155,11 @@ namespace OpenSim.Data.Null
|
||||||
if (m_useStaticInstance && Instance != this)
|
if (m_useStaticInstance && Instance != this)
|
||||||
return Instance.Get(regionID, scopeID);
|
return Instance.Get(regionID, scopeID);
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
if (m_regionData.ContainsKey(regionID))
|
if (m_regionData.ContainsKey(regionID))
|
||||||
return m_regionData[regionID];
|
return m_regionData[regionID];
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -162,11 +171,14 @@ namespace OpenSim.Data.Null
|
||||||
|
|
||||||
List<RegionData> ret = new List<RegionData>();
|
List<RegionData> ret = new List<RegionData>();
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
foreach (RegionData r in m_regionData.Values)
|
foreach (RegionData r in m_regionData.Values)
|
||||||
{
|
{
|
||||||
if (r.posX >= startX && r.posX <= endX && r.posY >= startY && r.posY <= endY)
|
if (r.posX >= startX && r.posX <= endX && r.posY >= startY && r.posY <= endY)
|
||||||
ret.Add(r);
|
ret.Add(r);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +191,10 @@ namespace OpenSim.Data.Null
|
||||||
// m_log.DebugFormat(
|
// m_log.DebugFormat(
|
||||||
// "[NULL REGION DATA]: Storing region {0} {1}, scope {2}", data.RegionName, data.RegionID, data.ScopeID);
|
// "[NULL REGION DATA]: Storing region {0} {1}, scope {2}", data.RegionName, data.RegionID, data.ScopeID);
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
m_regionData[data.RegionID] = data;
|
m_regionData[data.RegionID] = data;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -189,10 +204,13 @@ namespace OpenSim.Data.Null
|
||||||
if (m_useStaticInstance && Instance != this)
|
if (m_useStaticInstance && Instance != this)
|
||||||
return Instance.SetDataItem(regionID, item, value);
|
return Instance.SetDataItem(regionID, item, value);
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
if (!m_regionData.ContainsKey(regionID))
|
if (!m_regionData.ContainsKey(regionID))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_regionData[regionID].Data[item] = value;
|
m_regionData[regionID].Data[item] = value;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -204,10 +222,13 @@ namespace OpenSim.Data.Null
|
||||||
|
|
||||||
// m_log.DebugFormat("[NULL REGION DATA]: Deleting region {0}", regionID);
|
// m_log.DebugFormat("[NULL REGION DATA]: Deleting region {0}", regionID);
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
if (!m_regionData.ContainsKey(regionID))
|
if (!m_regionData.ContainsKey(regionID))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
m_regionData.Remove(regionID);
|
m_regionData.Remove(regionID);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -237,11 +258,14 @@ namespace OpenSim.Data.Null
|
||||||
|
|
||||||
List<RegionData> ret = new List<RegionData>();
|
List<RegionData> ret = new List<RegionData>();
|
||||||
|
|
||||||
|
lock (m_regionData)
|
||||||
|
{
|
||||||
foreach (RegionData r in m_regionData.Values)
|
foreach (RegionData r in m_regionData.Values)
|
||||||
{
|
{
|
||||||
if ((Convert.ToInt32(r.Data["flags"]) & regionFlags) != 0)
|
if ((Convert.ToInt32(r.Data["flags"]) & regionFlags) != 0)
|
||||||
ret.Add(r);
|
ret.Add(r);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace OpenSim.Data.SQLite
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SQLiteAssetData : AssetDataBase
|
public class SQLiteAssetData : AssetDataBase
|
||||||
{
|
{
|
||||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
private const string SelectAssetSQL = "select * from assets where UUID=:UUID";
|
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 SelectAssetMetadataSQL = "select Name, Description, Type, Temporary, asset_flags, UUID, CreatorID from assets limit :start, :count";
|
||||||
|
@ -133,6 +133,24 @@ namespace OpenSim.Data.SQLite
|
||||||
/// <param name="asset">Asset Base</param>
|
/// <param name="asset">Asset Base</param>
|
||||||
override public void StoreAsset(AssetBase asset)
|
override public void StoreAsset(AssetBase asset)
|
||||||
{
|
{
|
||||||
|
string assetName = asset.Name;
|
||||||
|
if (asset.Name.Length > 64)
|
||||||
|
{
|
||||||
|
assetName = asset.Name.Substring(0, 64);
|
||||||
|
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 > 64)
|
||||||
|
{
|
||||||
|
assetDescription = asset.Description.Substring(0, 64);
|
||||||
|
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());
|
//m_log.Info("[ASSET DB]: Creating Asset " + asset.FullID.ToString());
|
||||||
if (ExistsAsset(asset.FullID))
|
if (ExistsAsset(asset.FullID))
|
||||||
{
|
{
|
||||||
|
@ -143,8 +161,8 @@ namespace OpenSim.Data.SQLite
|
||||||
using (SqliteCommand cmd = new SqliteCommand(UpdateAssetSQL, m_conn))
|
using (SqliteCommand cmd = new SqliteCommand(UpdateAssetSQL, m_conn))
|
||||||
{
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
|
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Name", asset.Name));
|
cmd.Parameters.Add(new SqliteParameter(":Name", assetName));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Description", asset.Description));
|
cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
|
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
|
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
|
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
|
||||||
|
@ -163,8 +181,8 @@ namespace OpenSim.Data.SQLite
|
||||||
using (SqliteCommand cmd = new SqliteCommand(InsertAssetSQL, m_conn))
|
using (SqliteCommand cmd = new SqliteCommand(InsertAssetSQL, m_conn))
|
||||||
{
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
|
cmd.Parameters.Add(new SqliteParameter(":UUID", asset.FullID.ToString()));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Name", asset.Name));
|
cmd.Parameters.Add(new SqliteParameter(":Name", assetName));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Description", asset.Description));
|
cmd.Parameters.Add(new SqliteParameter(":Description", assetDescription));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
|
cmd.Parameters.Add(new SqliteParameter(":Type", asset.Type));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
|
cmd.Parameters.Add(new SqliteParameter(":Local", asset.Local));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
|
cmd.Parameters.Add(new SqliteParameter(":Temporary", asset.Temporary));
|
||||||
|
@ -202,7 +220,8 @@ namespace OpenSim.Data.SQLite
|
||||||
/// <returns>True if exist, or false.</returns>
|
/// <returns>True if exist, or false.</returns>
|
||||||
override public bool ExistsAsset(UUID uuid)
|
override public bool ExistsAsset(UUID uuid)
|
||||||
{
|
{
|
||||||
lock (this) {
|
lock (this)
|
||||||
|
{
|
||||||
using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn))
|
using (SqliteCommand cmd = new SqliteCommand(SelectAssetSQL, m_conn))
|
||||||
{
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString()));
|
cmd.Parameters.Add(new SqliteParameter(":UUID", uuid.ToString()));
|
||||||
|
@ -359,6 +378,7 @@ namespace OpenSim.Data.SQLite
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,14 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
AuthenticationData ret = new AuthenticationData();
|
AuthenticationData ret = new AuthenticationData();
|
||||||
ret.Data = new Dictionary<string, object>();
|
ret.Data = new Dictionary<string, object>();
|
||||||
|
IDataReader result;
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID");
|
using (SqliteCommand cmd = new SqliteCommand("select * from `" + m_Realm + "` where UUID = :PrincipalID"))
|
||||||
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString()));
|
cmd.Parameters.Add(new SqliteParameter(":PrincipalID", principalID.ToString()));
|
||||||
|
|
||||||
IDataReader result = ExecuteReader(cmd, m_Connection);
|
result = ExecuteReader(cmd, m_Connection);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -121,10 +124,6 @@ namespace OpenSim.Data.SQLite
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
//CloseCommand(cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -140,8 +139,8 @@ namespace OpenSim.Data.SQLite
|
||||||
foreach (object o in data.Data.Values)
|
foreach (object o in data.Data.Values)
|
||||||
values[i++] = o.ToString();
|
values[i++] = o.ToString();
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
if (Get(data.PrincipalID) != null)
|
if (Get(data.PrincipalID) != null)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -177,7 +176,6 @@ namespace OpenSim.Data.SQLite
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string insert = "insert into `" + m_Realm + "` (`UUID`, `" +
|
string insert = "insert into `" + m_Realm + "` (`UUID`, `" +
|
||||||
|
@ -194,30 +192,28 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) < 1)
|
if (ExecuteNonQuery(cmd, m_Connection) < 1)
|
||||||
{
|
{
|
||||||
//CloseCommand(cmd);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Console.WriteLine(e.ToString());
|
Console.WriteLine(e.ToString());
|
||||||
//CloseCommand(cmd);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//CloseCommand(cmd);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SetDataItem(UUID principalID, string item, string value)
|
public bool SetDataItem(UUID principalID, string item, string value)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand("update `" + m_Realm +
|
using (SqliteCommand cmd = new SqliteCommand("update `" + m_Realm +
|
||||||
"` set `" + item + "` = " + value + " where UUID = '" + principalID.ToString() + "'");
|
"` set `" + item + "` = " + value + " where UUID = '" + principalID.ToString() + "'"))
|
||||||
|
{
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -227,16 +223,13 @@ namespace OpenSim.Data.SQLite
|
||||||
if (System.Environment.TickCount - m_LastExpire > 30000)
|
if (System.Environment.TickCount - m_LastExpire > 30000)
|
||||||
DoExpire();
|
DoExpire();
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() +
|
using (SqliteCommand cmd = new SqliteCommand("insert into tokens (UUID, token, validity) values ('" + principalID.ToString() +
|
||||||
"', '" + token + "', datetime('now', 'localtime', '+" + lifetime.ToString() + " minutes'))");
|
"', '" + token + "', datetime('now', 'localtime', '+" + lifetime.ToString() + " minutes'))"))
|
||||||
|
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
|
||||||
{
|
{
|
||||||
cmd.Dispose();
|
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Dispose();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,27 +238,21 @@ namespace OpenSim.Data.SQLite
|
||||||
if (System.Environment.TickCount - m_LastExpire > 30000)
|
if (System.Environment.TickCount - m_LastExpire > 30000)
|
||||||
DoExpire();
|
DoExpire();
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand("update tokens set validity = datetime('now', 'localtime', '+" + lifetime.ToString() +
|
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')");
|
" minutes') where UUID = '" + principalID.ToString() + "' and token = '" + token + "' and validity > datetime('now', 'localtime')"))
|
||||||
|
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
|
||||||
{
|
{
|
||||||
cmd.Dispose();
|
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Dispose();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoExpire()
|
private void DoExpire()
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand("delete from tokens where validity < datetime('now', 'localtime')");
|
using (SqliteCommand cmd = new SqliteCommand("delete from tokens where validity < datetime('now', 'localtime')"))
|
||||||
ExecuteNonQuery(cmd, m_Connection);
|
ExecuteNonQuery(cmd, m_Connection);
|
||||||
|
|
||||||
cmd.Dispose();
|
|
||||||
|
|
||||||
m_LastExpire = System.Environment.TickCount;
|
m_LastExpire = System.Environment.TickCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,23 +56,17 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
public bool Delete(UUID principalID, string name)
|
public bool Delete(UUID principalID, string name)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm);
|
cmd.CommandText = String.Format("delete from {0} where `PrincipalID` = :PrincipalID and `Name` = :Name", m_Realm);
|
||||||
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());
|
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());
|
||||||
cmd.Parameters.AddWithValue(":Name", name);
|
cmd.Parameters.AddWithValue(":Name", name);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
{
|
|
||||||
//CloseCommand(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -104,13 +104,14 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
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";
|
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";
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
cmd.Parameters.AddWithValue(":RegionID", regionID.ToString());
|
cmd.Parameters.AddWithValue(":RegionID", regionID.ToString());
|
||||||
|
|
||||||
return DoLoad(cmd, regionID, create);
|
return DoLoad(cmd, regionID, create);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private EstateSettings DoLoad(SqliteCommand cmd, UUID regionID, bool create)
|
private EstateSettings DoLoad(SqliteCommand cmd, UUID regionID, bool create)
|
||||||
{
|
{
|
||||||
|
@ -186,9 +187,10 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
List<string> names = new List<string>(FieldList);
|
List<string> names = new List<string>(FieldList);
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
|
||||||
IDataReader r = null;
|
IDataReader r = null;
|
||||||
|
|
||||||
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
names.Remove("EstateID");
|
names.Remove("EstateID");
|
||||||
|
|
||||||
string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")";
|
string sql = "insert into estate_settings ("+String.Join(",", names.ToArray())+") values ( :"+String.Join(", :", names.ToArray())+")";
|
||||||
|
@ -217,6 +219,7 @@ namespace OpenSim.Data.SQLite
|
||||||
cmd.Parameters.Clear();
|
cmd.Parameters.Clear();
|
||||||
|
|
||||||
r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
r.Read();
|
r.Read();
|
||||||
|
|
||||||
|
@ -239,8 +242,8 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
string sql = "update estate_settings set "+String.Join(", ", terms.ToArray())+" where EstateID = :EstateID";
|
string sql = "update estate_settings set "+String.Join(", ", terms.ToArray())+" where EstateID = :EstateID";
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
|
|
||||||
foreach (string name in FieldList)
|
foreach (string name in FieldList)
|
||||||
|
@ -259,6 +262,7 @@ namespace OpenSim.Data.SQLite
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.ExecuteNonQuery();
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
SaveBanList(es);
|
SaveBanList(es);
|
||||||
SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers);
|
SaveUUIDList(es.EstateID, "estate_managers", es.EstateManagers);
|
||||||
|
@ -270,12 +274,15 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
es.ClearBans();
|
es.ClearBans();
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
IDataReader r;
|
||||||
|
|
||||||
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID";
|
cmd.CommandText = "select bannedUUID from estateban where EstateID = :EstateID";
|
||||||
cmd.Parameters.AddWithValue(":EstateID", es.EstateID);
|
cmd.Parameters.AddWithValue(":EstateID", es.EstateID);
|
||||||
|
|
||||||
IDataReader r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
{
|
{
|
||||||
|
@ -294,8 +301,8 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
private void SaveBanList(EstateSettings es)
|
private void SaveBanList(EstateSettings es)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = "delete from estateban where EstateID = :EstateID";
|
cmd.CommandText = "delete from estateban where EstateID = :EstateID";
|
||||||
cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString());
|
cmd.Parameters.AddWithValue(":EstateID", es.EstateID.ToString());
|
||||||
|
|
||||||
|
@ -314,11 +321,12 @@ namespace OpenSim.Data.SQLite
|
||||||
cmd.Parameters.Clear();
|
cmd.Parameters.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void SaveUUIDList(uint EstateID, string table, UUID[] data)
|
void SaveUUIDList(uint EstateID, string table, UUID[] data)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = "delete from "+table+" where EstateID = :EstateID";
|
cmd.CommandText = "delete from "+table+" where EstateID = :EstateID";
|
||||||
cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString());
|
cmd.Parameters.AddWithValue(":EstateID", EstateID.ToString());
|
||||||
|
|
||||||
|
@ -337,17 +345,20 @@ namespace OpenSim.Data.SQLite
|
||||||
cmd.Parameters.Clear();
|
cmd.Parameters.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UUID[] LoadUUIDList(uint EstateID, string table)
|
UUID[] LoadUUIDList(uint EstateID, string table)
|
||||||
{
|
{
|
||||||
List<UUID> uuids = new List<UUID>();
|
List<UUID> uuids = new List<UUID>();
|
||||||
|
IDataReader r;
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID";
|
cmd.CommandText = "select uuid from "+table+" where EstateID = :EstateID";
|
||||||
cmd.Parameters.AddWithValue(":EstateID", EstateID);
|
cmd.Parameters.AddWithValue(":EstateID", EstateID);
|
||||||
|
|
||||||
IDataReader r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
{
|
{
|
||||||
|
@ -367,13 +378,14 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_settings where estate_settings.EstateID = :EstateID";
|
string sql = "select estate_settings."+String.Join(",estate_settings.", FieldList)+" from estate_settings where estate_settings.EstateID = :EstateID";
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
cmd.Parameters.AddWithValue(":EstateID", estateID.ToString());
|
cmd.Parameters.AddWithValue(":EstateID", estateID.ToString());
|
||||||
|
|
||||||
return DoLoad(cmd, UUID.Zero, false);
|
return DoLoad(cmd, UUID.Zero, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<EstateSettings> LoadEstateSettingsAll()
|
public List<EstateSettings> LoadEstateSettingsAll()
|
||||||
{
|
{
|
||||||
|
@ -391,13 +403,15 @@ namespace OpenSim.Data.SQLite
|
||||||
List<int> result = new List<int>();
|
List<int> result = new List<int>();
|
||||||
|
|
||||||
string sql = "select EstateID from estate_settings where estate_settings.EstateName = :EstateName";
|
string sql = "select EstateID from estate_settings where estate_settings.EstateName = :EstateName";
|
||||||
|
IDataReader r;
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
cmd.Parameters.AddWithValue(":EstateName", search);
|
cmd.Parameters.AddWithValue(":EstateName", search);
|
||||||
|
|
||||||
IDataReader r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
{
|
{
|
||||||
|
@ -413,12 +427,14 @@ namespace OpenSim.Data.SQLite
|
||||||
List<int> result = new List<int>();
|
List<int> result = new List<int>();
|
||||||
|
|
||||||
string sql = "select EstateID from estate_settings";
|
string sql = "select EstateID from estate_settings";
|
||||||
|
IDataReader r;
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
|
|
||||||
IDataReader r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
{
|
{
|
||||||
|
@ -434,13 +450,15 @@ namespace OpenSim.Data.SQLite
|
||||||
List<int> result = new List<int>();
|
List<int> result = new List<int>();
|
||||||
|
|
||||||
string sql = "select EstateID from estate_settings where estate_settings.EstateOwner = :EstateOwner";
|
string sql = "select EstateID from estate_settings where estate_settings.EstateOwner = :EstateOwner";
|
||||||
|
IDataReader r;
|
||||||
|
|
||||||
SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand();
|
using (SqliteCommand cmd = (SqliteCommand)m_connection.CreateCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = sql;
|
cmd.CommandText = sql;
|
||||||
cmd.Parameters.AddWithValue(":EstateOwner", ownerID);
|
cmd.Parameters.AddWithValue(":EstateOwner", ownerID);
|
||||||
|
|
||||||
IDataReader r = cmd.ExecuteReader();
|
r = cmd.ExecuteReader();
|
||||||
|
}
|
||||||
|
|
||||||
while (r.Read())
|
while (r.Read())
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,12 +90,5 @@ namespace OpenSim.Data.SQLite
|
||||||
return cmd.ExecuteReader();
|
return cmd.ExecuteReader();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void CloseCommand(SqliteCommand cmd)
|
|
||||||
{
|
|
||||||
cmd.Connection.Close();
|
|
||||||
cmd.Connection.Dispose();
|
|
||||||
cmd.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -53,13 +53,13 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
public FriendsData[] GetFriends(string userID)
|
public FriendsData[] GetFriends(string userID)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
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.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());
|
cmd.Parameters.AddWithValue(":PrincipalID", userID.ToString());
|
||||||
|
|
||||||
return DoQuery(cmd);
|
return DoQuery(cmd);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Delete(UUID principalID, string friend)
|
public bool Delete(UUID principalID, string friend)
|
||||||
|
@ -69,13 +69,14 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
public bool Delete(string principalID, string friend)
|
public bool Delete(string principalID, string friend)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm);
|
cmd.CommandText = String.Format("delete from {0} where PrincipalID = :PrincipalID and Friend = :Friend", m_Realm);
|
||||||
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());
|
cmd.Parameters.AddWithValue(":PrincipalID", principalID.ToString());
|
||||||
cmd.Parameters.AddWithValue(":Friend", friend);
|
cmd.Parameters.AddWithValue(":Friend", friend);
|
||||||
|
|
||||||
ExecuteNonQuery(cmd, m_Connection);
|
ExecuteNonQuery(cmd, m_Connection);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,20 +120,20 @@ namespace OpenSim.Data.SQLite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] Get(string field, string key)
|
public virtual T[] Get(string field, string key)
|
||||||
{
|
{
|
||||||
return Get(new string[] { field }, new string[] { key });
|
return Get(new string[] { field }, new string[] { key });
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] Get(string[] fields, string[] keys)
|
public virtual T[] Get(string[] fields, string[] keys)
|
||||||
{
|
{
|
||||||
if (fields.Length != keys.Length)
|
if (fields.Length != keys.Length)
|
||||||
return new T[0];
|
return new T[0];
|
||||||
|
|
||||||
List<string> terms = new List<string>();
|
List<string> terms = new List<string>();
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
for (int i = 0 ; i < fields.Length ; i++)
|
for (int i = 0 ; i < fields.Length ; i++)
|
||||||
{
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
|
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
|
||||||
|
@ -149,6 +149,7 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
return DoQuery(cmd);
|
return DoQuery(cmd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected T[] DoQuery(SqliteCommand cmd)
|
protected T[] DoQuery(SqliteCommand cmd)
|
||||||
{
|
{
|
||||||
|
@ -212,10 +213,10 @@ namespace OpenSim.Data.SQLite
|
||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public T[] Get(string where)
|
public virtual T[] Get(string where)
|
||||||
|
{
|
||||||
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
|
||||||
|
|
||||||
string query = String.Format("select * from {0} where {1}",
|
string query = String.Format("select * from {0} where {1}",
|
||||||
m_Realm, where);
|
m_Realm, where);
|
||||||
|
|
||||||
|
@ -223,11 +224,12 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
return DoQuery(cmd);
|
return DoQuery(cmd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Store(T row)
|
public virtual bool Store(T row)
|
||||||
|
{
|
||||||
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
|
||||||
|
|
||||||
string query = "";
|
string query = "";
|
||||||
List<String> names = new List<String>();
|
List<String> names = new List<String>();
|
||||||
List<String> values = new List<String>();
|
List<String> values = new List<String>();
|
||||||
|
@ -258,6 +260,7 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
if (ExecuteNonQuery(cmd, m_Connection) > 0)
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -267,15 +270,15 @@ namespace OpenSim.Data.SQLite
|
||||||
return Delete(new string[] { field }, new string[] { key });
|
return Delete(new string[] { field }, new string[] { key });
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Delete(string[] fields, string[] keys)
|
public virtual bool Delete(string[] fields, string[] keys)
|
||||||
{
|
{
|
||||||
if (fields.Length != keys.Length)
|
if (fields.Length != keys.Length)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
List<string> terms = new List<string>();
|
List<string> terms = new List<string>();
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
for (int i = 0 ; i < fields.Length ; i++)
|
for (int i = 0 ; i < fields.Length ; i++)
|
||||||
{
|
{
|
||||||
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
|
cmd.Parameters.Add(new SqliteParameter(":" + fields[i], keys[i]));
|
||||||
|
@ -292,3 +295,4 @@ namespace OpenSim.Data.SQLite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1366,6 +1366,12 @@ namespace OpenSim.Data.SQLite
|
||||||
createCol(land, "UserLookAtZ", typeof(Double));
|
createCol(land, "UserLookAtZ", typeof(Double));
|
||||||
createCol(land, "AuthbuyerID", typeof(String));
|
createCol(land, "AuthbuyerID", typeof(String));
|
||||||
createCol(land, "OtherCleanTime", typeof(Int32));
|
createCol(land, "OtherCleanTime", 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"] };
|
land.PrimaryKey = new DataColumn[] { land.Columns["UUID"] };
|
||||||
|
|
||||||
|
@ -1781,9 +1787,15 @@ namespace OpenSim.Data.SQLite
|
||||||
newData.PassHours = Convert.ToSingle(row["PassHours"]);
|
newData.PassHours = Convert.ToSingle(row["PassHours"]);
|
||||||
newData.PassPrice = Convert.ToInt32(row["PassPrice"]);
|
newData.PassPrice = Convert.ToInt32(row["PassPrice"]);
|
||||||
newData.SnapshotID = (UUID)(String)row["SnapshotUUID"];
|
newData.SnapshotID = (UUID)(String)row["SnapshotUUID"];
|
||||||
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
newData.UserLocation =
|
newData.UserLocation =
|
||||||
new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]),
|
new Vector3(Convert.ToSingle(row["UserLocationX"]), Convert.ToSingle(row["UserLocationY"]),
|
||||||
Convert.ToSingle(row["UserLocationZ"]));
|
Convert.ToSingle(row["UserLocationZ"]));
|
||||||
|
@ -2197,10 +2209,10 @@ namespace OpenSim.Data.SQLite
|
||||||
row["OtherCleanTime"] = land.OtherCleanTime;
|
row["OtherCleanTime"] = land.OtherCleanTime;
|
||||||
row["MediaType"] = land.MediaType;
|
row["MediaType"] = land.MediaType;
|
||||||
row["MediaDescription"] = land.MediaDescription;
|
row["MediaDescription"] = land.MediaDescription;
|
||||||
row["MediaSize"] = land.MediaWidth.ToString() + "," + land.MediaHeight.ToString();
|
row["MediaSize"] = String.Format("{0},{1}", land.MediaWidth, land.MediaHeight);
|
||||||
row["MediaLoop"] = land.MediaLoop.ToString();
|
row["MediaLoop"] = land.MediaLoop;
|
||||||
row["ObscureMusic"] = land.ObscureMusic.ToString();
|
row["ObscureMusic"] = land.ObscureMusic;
|
||||||
row["ObscureMedia"] = land.ObscureMedia.ToString();
|
row["ObscureMedia"] = land.ObscureMedia;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -66,8 +66,8 @@ namespace OpenSim.Data.SQLite
|
||||||
if (words.Length > 2)
|
if (words.Length > 2)
|
||||||
return new UserAccountData[0];
|
return new UserAccountData[0];
|
||||||
|
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
if (words.Length == 1)
|
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}%')",
|
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}%')",
|
||||||
|
@ -83,3 +83,4 @@ namespace OpenSim.Data.SQLite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace OpenSim.Data.SQLite
|
||||||
{
|
{
|
||||||
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
private SQLiteGenericTableHandler<XInventoryFolder> m_Folders;
|
private SqliteFolderHandler m_Folders;
|
||||||
private SqliteItemHandler m_Items;
|
private SqliteItemHandler m_Items;
|
||||||
|
|
||||||
public SQLiteXInventoryData(string conn, string realm)
|
public SQLiteXInventoryData(string conn, string realm)
|
||||||
|
@ -55,7 +55,7 @@ namespace OpenSim.Data.SQLite
|
||||||
if (Util.IsWindows())
|
if (Util.IsWindows())
|
||||||
Util.LoadArchSpecificWindowsDll("sqlite3.dll");
|
Util.LoadArchSpecificWindowsDll("sqlite3.dll");
|
||||||
|
|
||||||
m_Folders = new SQLiteGenericTableHandler<XInventoryFolder>(
|
m_Folders = new SqliteFolderHandler(
|
||||||
conn, "inventoryfolders", "XInventoryStore");
|
conn, "inventoryfolders", "XInventoryStore");
|
||||||
m_Items = new SqliteItemHandler(
|
m_Items = new SqliteItemHandler(
|
||||||
conn, "inventoryitems", String.Empty);
|
conn, "inventoryitems", String.Empty);
|
||||||
|
@ -114,6 +114,11 @@ namespace OpenSim.Data.SQLite
|
||||||
return m_Items.MoveItem(id, 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)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
{
|
{
|
||||||
return m_Items.GetActiveGestures(principalID);
|
return m_Items.GetActiveGestures(principalID);
|
||||||
|
@ -125,27 +130,85 @@ namespace OpenSim.Data.SQLite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SqliteItemHandler : SQLiteGenericTableHandler<XInventoryItem>
|
public class SqliteItemHandler : SqliteInventoryHandler<XInventoryItem>
|
||||||
{
|
{
|
||||||
public SqliteItemHandler(string c, string t, string m) :
|
public SqliteItemHandler(string c, string t, string m) :
|
||||||
base(c, t, 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<UUID> deletedItemFolderUUIDs = new HashSet<UUID>();
|
||||||
|
|
||||||
|
Array.ForEach<XInventoryItem>(retrievedItems, i => deletedItemFolderUUIDs.Add(i.parentFolderID));
|
||||||
|
|
||||||
|
foreach (UUID deletedItemFolderUUID in deletedItemFolderUUIDs)
|
||||||
|
IncrementFolderVersion(deletedItemFolderUUID);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public bool MoveItem(string id, string newParent)
|
public bool MoveItem(string id, string newParent)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
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.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(":ParentFolderID", newParent));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":InventoryID", id));
|
cmd.Parameters.Add(new SqliteParameter(":InventoryID", id));
|
||||||
|
|
||||||
return ExecuteNonQuery(cmd, m_Connection) == 0 ? false : true;
|
if (ExecuteNonQuery(cmd, m_Connection) == 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IncrementFolderVersion(oldParent);
|
||||||
|
IncrementFolderVersion(newParent);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
public XInventoryItem[] GetActiveGestures(UUID principalID)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = String.Format("select * from inventoryitems where avatarId = :uuid and assetType = :type and flags = 1", m_Realm);
|
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(":uuid", principalID.ToString()));
|
||||||
|
@ -153,16 +216,20 @@ namespace OpenSim.Data.SQLite
|
||||||
|
|
||||||
return DoQuery(cmd);
|
return DoQuery(cmd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int GetAssetPermissions(UUID principalID, UUID assetID)
|
public int GetAssetPermissions(UUID principalID, UUID assetID)
|
||||||
{
|
{
|
||||||
SqliteCommand cmd = new SqliteCommand();
|
IDataReader reader;
|
||||||
|
|
||||||
|
using (SqliteCommand cmd = new SqliteCommand())
|
||||||
|
{
|
||||||
cmd.CommandText = String.Format("select inventoryCurrentPermissions from inventoryitems where avatarID = :PrincipalID and assetID = :AssetID", m_Realm);
|
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(":PrincipalID", principalID.ToString()));
|
||||||
cmd.Parameters.Add(new SqliteParameter(":AssetID", assetID.ToString()));
|
cmd.Parameters.Add(new SqliteParameter(":AssetID", assetID.ToString()));
|
||||||
|
|
||||||
IDataReader reader = ExecuteReader(cmd, m_Connection);
|
reader = ExecuteReader(cmd, m_Connection);
|
||||||
|
}
|
||||||
|
|
||||||
int perms = 0;
|
int perms = 0;
|
||||||
|
|
||||||
|
@ -177,4 +244,81 @@ namespace OpenSim.Data.SQLite
|
||||||
return perms;
|
return perms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class SqliteFolderHandler : SqliteInventoryHandler<XInventoryFolder>
|
||||||
|
{
|
||||||
|
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<T> : SQLiteGenericTableHandler<T> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -33,6 +33,7 @@ using NUnit.Framework;
|
||||||
using NUnit.Framework.Constraints;
|
using NUnit.Framework.Constraints;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
using log4net;
|
using log4net;
|
||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Data.Common;
|
using System.Data.Common;
|
||||||
|
@ -43,6 +44,12 @@ namespace OpenSim.Data.Tests
|
||||||
/// <summary>This is a base class for testing any Data service for any DBMS.
|
/// <summary>This is a base class for testing any Data service for any DBMS.
|
||||||
/// Requires NUnit 2.5 or better (to support the generics).
|
/// Requires NUnit 2.5 or better (to support the generics).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
/// <typeparam name="TConn"></typeparam>
|
/// <typeparam name="TConn"></typeparam>
|
||||||
/// <typeparam name="TService"></typeparam>
|
/// <typeparam name="TService"></typeparam>
|
||||||
public class BasicDataServiceTest<TConn, TService>
|
public class BasicDataServiceTest<TConn, TService>
|
||||||
|
|
|
@ -36,6 +36,7 @@ using NUnit.Framework;
|
||||||
using NUnit.Framework.Constraints;
|
using NUnit.Framework.Constraints;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Data.Tests
|
namespace OpenSim.Data.Tests
|
||||||
{
|
{
|
||||||
|
@ -254,7 +255,7 @@ namespace OpenSim.Data.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class PropertyCompareConstraintTest
|
public class PropertyCompareConstraintTest : OpenSimTestCase
|
||||||
{
|
{
|
||||||
public class HasInt
|
public class HasInt
|
||||||
{
|
{
|
||||||
|
|
|
@ -34,6 +34,7 @@ using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Data.Tests
|
namespace OpenSim.Data.Tests
|
||||||
{
|
{
|
||||||
|
@ -158,7 +159,7 @@ namespace OpenSim.Data.Tests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class PropertyScramblerTests
|
public class PropertyScramblerTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScramble()
|
public void TestScramble()
|
||||||
|
|
|
@ -330,6 +330,9 @@ namespace OpenSim.Framework
|
||||||
SetVisualParams(visualParams);
|
SetVisualParams(visualParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set avatar height by a calculation based on their visual parameters.
|
||||||
|
/// </summary>
|
||||||
public virtual void SetHeight()
|
public virtual void SetHeight()
|
||||||
{
|
{
|
||||||
// Start with shortest possible female avatar height
|
// Start with shortest possible female avatar height
|
||||||
|
|
|
@ -199,7 +199,14 @@ namespace OpenSim.Framework
|
||||||
//
|
//
|
||||||
public class Cache
|
public class Cache
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Must only be accessed under lock.
|
||||||
|
/// </summary>
|
||||||
private List<CacheItemBase> m_Index = new List<CacheItemBase>();
|
private List<CacheItemBase> m_Index = new List<CacheItemBase>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Must only be accessed under m_Index lock.
|
||||||
|
/// </summary>
|
||||||
private Dictionary<string, CacheItemBase> m_Lookup =
|
private Dictionary<string, CacheItemBase> m_Lookup =
|
||||||
new Dictionary<string, CacheItemBase>();
|
new Dictionary<string, CacheItemBase>();
|
||||||
|
|
||||||
|
@ -320,7 +327,6 @@ namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
if (m_Lookup.ContainsKey(index))
|
if (m_Lookup.ContainsKey(index))
|
||||||
item = m_Lookup[index];
|
item = m_Lookup[index];
|
||||||
}
|
|
||||||
|
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
|
@ -332,6 +338,7 @@ namespace OpenSim.Framework
|
||||||
item.lastUsed = DateTime.Now;
|
item.lastUsed = DateTime.Now;
|
||||||
|
|
||||||
Expire(true);
|
Expire(true);
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
@ -385,7 +392,10 @@ namespace OpenSim.Framework
|
||||||
//
|
//
|
||||||
public Object Find(Predicate<CacheItemBase> d)
|
public Object Find(Predicate<CacheItemBase> d)
|
||||||
{
|
{
|
||||||
CacheItemBase item = m_Index.Find(d);
|
CacheItemBase item;
|
||||||
|
|
||||||
|
lock (m_Index)
|
||||||
|
item = m_Index.Find(d);
|
||||||
|
|
||||||
if (item == null)
|
if (item == null)
|
||||||
return null;
|
return null;
|
||||||
|
@ -419,12 +429,12 @@ namespace OpenSim.Framework
|
||||||
public virtual void Store(string index, Object data, Type container,
|
public virtual void Store(string index, Object data, Type container,
|
||||||
Object[] parameters)
|
Object[] parameters)
|
||||||
{
|
{
|
||||||
Expire(false);
|
|
||||||
|
|
||||||
CacheItemBase item;
|
CacheItemBase item;
|
||||||
|
|
||||||
lock (m_Index)
|
lock (m_Index)
|
||||||
{
|
{
|
||||||
|
Expire(false);
|
||||||
|
|
||||||
if (m_Index.Contains(new CacheItemBase(index)))
|
if (m_Index.Contains(new CacheItemBase(index)))
|
||||||
{
|
{
|
||||||
if ((m_Flags & CacheFlags.AllowUpdate) != 0)
|
if ((m_Flags & CacheFlags.AllowUpdate) != 0)
|
||||||
|
@ -450,9 +460,17 @@ namespace OpenSim.Framework
|
||||||
m_Index.Add(item);
|
m_Index.Add(item);
|
||||||
m_Lookup[index] = item;
|
m_Lookup[index] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
item.Store(data);
|
item.Store(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Expire items as appropriate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Callers must lock m_Index.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name='getting'></param>
|
||||||
protected virtual void Expire(bool getting)
|
protected virtual void Expire(bool getting)
|
||||||
{
|
{
|
||||||
if (getting && (m_Strategy == CacheStrategy.Aggressive))
|
if (getting && (m_Strategy == CacheStrategy.Aggressive))
|
||||||
|
@ -479,8 +497,6 @@ namespace OpenSim.Framework
|
||||||
if (Count < Size)
|
if (Count < Size)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock (m_Index)
|
|
||||||
{
|
|
||||||
m_Index.Sort(new SortLRU());
|
m_Index.Sort(new SortLRU());
|
||||||
m_Index.Reverse();
|
m_Index.Reverse();
|
||||||
|
|
||||||
|
@ -513,14 +529,17 @@ namespace OpenSim.Framework
|
||||||
foreach (CacheItemBase item in m_Index)
|
foreach (CacheItemBase item in m_Index)
|
||||||
m_Lookup[item.uuid] = item;
|
m_Lookup[item.uuid] = item;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Invalidate(string uuid)
|
public void Invalidate(string uuid)
|
||||||
|
{
|
||||||
|
lock (m_Index)
|
||||||
{
|
{
|
||||||
if (!m_Lookup.ContainsKey(uuid))
|
if (!m_Lookup.ContainsKey(uuid))
|
||||||
return;
|
return;
|
||||||
|
@ -529,11 +548,15 @@ namespace OpenSim.Framework
|
||||||
m_Lookup.Remove(uuid);
|
m_Lookup.Remove(uuid);
|
||||||
m_Index.Remove(item);
|
m_Index.Remove(item);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
|
{
|
||||||
|
lock (m_Index)
|
||||||
{
|
{
|
||||||
m_Index.Clear();
|
m_Index.Clear();
|
||||||
m_Lookup.Clear();
|
m_Lookup.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
|
@ -33,7 +33,8 @@ namespace OpenSim.Framework.Client
|
||||||
{
|
{
|
||||||
event ChatMessage OnChatFromClient;
|
event ChatMessage OnChatFromClient;
|
||||||
|
|
||||||
void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source,
|
void SendChatMessage(
|
||||||
|
string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source,
|
||||||
byte audible);
|
byte audible);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -65,10 +65,10 @@ namespace OpenSim.Framework.Configuration.HTTP
|
||||||
byte[] buf = new byte[8192];
|
byte[] buf = new byte[8192];
|
||||||
HttpWebRequest request =
|
HttpWebRequest request =
|
||||||
(HttpWebRequest) WebRequest.Create(remoteConfigSettings.baseConfigURL + configFileName);
|
(HttpWebRequest) WebRequest.Create(remoteConfigSettings.baseConfigURL + configFileName);
|
||||||
HttpWebResponse response = (HttpWebResponse) request.GetResponse();
|
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
|
||||||
|
{
|
||||||
Stream resStream = response.GetResponseStream();
|
using (Stream resStream = response.GetResponseStream())
|
||||||
|
{
|
||||||
string tempString = null;
|
string tempString = null;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
|
@ -80,9 +80,13 @@ namespace OpenSim.Framework.Configuration.HTTP
|
||||||
tempString = Util.UTF8.GetString(buf, 0, count);
|
tempString = Util.UTF8.GetString(buf, 0, count);
|
||||||
sb.Append(tempString);
|
sb.Append(tempString);
|
||||||
}
|
}
|
||||||
} while (count > 0);
|
}
|
||||||
|
while (count > 0);
|
||||||
|
|
||||||
LoadDataFromString(sb.ToString());
|
LoadDataFromString(sb.ToString());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (WebException)
|
catch (WebException)
|
||||||
{
|
{
|
||||||
m_log.Warn("Unable to connect to remote configuration file (" +
|
m_log.Warn("Unable to connect to remote configuration file (" +
|
||||||
|
|
|
@ -83,7 +83,8 @@ namespace OpenSim.Framework.Console
|
||||||
= "To enter an argument that contains spaces, surround the argument with double quotes.\nFor example, show object name \"My long object name\"\n";
|
= "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
|
public const string ItemHelpText
|
||||||
= "For more information, type 'help <item>' where <item> is one of the following:";
|
= @"For more information, type 'help all' to get a list of all commands,
|
||||||
|
or type help <item>' where <item> is one of the following:";
|
||||||
|
|
||||||
/// <value>
|
/// <value>
|
||||||
/// Commands organized by keyword in a tree
|
/// Commands organized by keyword in a tree
|
||||||
|
@ -109,19 +110,48 @@ namespace OpenSim.Framework.Console
|
||||||
// Remove initial help keyword
|
// Remove initial help keyword
|
||||||
helpParts.RemoveAt(0);
|
helpParts.RemoveAt(0);
|
||||||
|
|
||||||
|
help.Add(""); // Will become a newline.
|
||||||
|
|
||||||
// General help
|
// General help
|
||||||
if (helpParts.Count == 0)
|
if (helpParts.Count == 0)
|
||||||
{
|
{
|
||||||
help.Add(""); // Will become a newline.
|
|
||||||
help.Add(GeneralHelpText);
|
help.Add(GeneralHelpText);
|
||||||
help.Add(ItemHelpText);
|
help.Add(ItemHelpText);
|
||||||
help.AddRange(CollectModulesHelp(tree));
|
help.AddRange(CollectModulesHelp(tree));
|
||||||
}
|
}
|
||||||
|
else if (helpParts.Count == 1 && helpParts[0] == "all")
|
||||||
|
{
|
||||||
|
help.AddRange(CollectAllCommandsHelp());
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
help.AddRange(CollectHelp(helpParts));
|
help.AddRange(CollectHelp(helpParts));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
help.Add(""); // Will become a newline.
|
||||||
|
|
||||||
|
return help;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collects the help from all commands and return in alphabetical order.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
private List<string> CollectAllCommandsHelp()
|
||||||
|
{
|
||||||
|
List<string> help = new List<string>();
|
||||||
|
|
||||||
|
lock (m_modulesCommands)
|
||||||
|
{
|
||||||
|
foreach (List<CommandInfo> 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;
|
return help;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,14 +197,11 @@ namespace OpenSim.Framework.Console
|
||||||
|
|
||||||
string descriptiveHelp = commandInfo.descriptive_help;
|
string descriptiveHelp = commandInfo.descriptive_help;
|
||||||
|
|
||||||
// If we do have some descriptive help then insert a spacing line before and after for readability.
|
// If we do have some descriptive help then insert a spacing line before for readability.
|
||||||
if (descriptiveHelp != string.Empty)
|
if (descriptiveHelp != string.Empty)
|
||||||
help.Add(string.Empty);
|
help.Add(string.Empty);
|
||||||
|
|
||||||
help.Add(commandInfo.descriptive_help);
|
help.Add(commandInfo.descriptive_help);
|
||||||
|
|
||||||
if (descriptiveHelp != string.Empty)
|
|
||||||
help.Add(string.Empty);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -702,7 +729,7 @@ namespace OpenSim.Framework.Console
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Prompt()
|
public void Prompt()
|
||||||
{
|
{
|
||||||
string line = ReadLine(m_defaultPrompt + "# ", true, true);
|
string line = ReadLine(DefaultPrompt + "# ", true, true);
|
||||||
|
|
||||||
if (line != String.Empty)
|
if (line != String.Empty)
|
||||||
Output("Invalid command");
|
Output("Invalid command");
|
||||||
|
|
|
@ -43,15 +43,7 @@ namespace OpenSim.Framework.Console
|
||||||
|
|
||||||
public object ConsoleScene { get; set; }
|
public object ConsoleScene { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
public string DefaultPrompt { get; set; }
|
||||||
/// The default prompt text.
|
|
||||||
/// </summary>
|
|
||||||
public string DefaultPrompt
|
|
||||||
{
|
|
||||||
set { m_defaultPrompt = value; }
|
|
||||||
get { return m_defaultPrompt; }
|
|
||||||
}
|
|
||||||
protected string m_defaultPrompt;
|
|
||||||
|
|
||||||
public ConsoleBase(string defaultPrompt)
|
public ConsoleBase(string defaultPrompt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace OpenSim.Framework.Console
|
||||||
public List<ConsoleDisplayTableRow> Rows { get; private set; }
|
public List<ConsoleDisplayTableRow> Rows { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of spaces to indent the table.
|
/// Number of spaces to indent the whole table.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int Indent { get; set; }
|
public int Indent { get; set; }
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ namespace OpenSim.Framework.Console
|
||||||
Columns.Add(new ConsoleDisplayTableColumn(name, width));
|
Columns.Add(new ConsoleDisplayTableColumn(name, width));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddRow(params string[] cells)
|
public void AddRow(params object[] cells)
|
||||||
{
|
{
|
||||||
Rows.Add(new ConsoleDisplayTableRow(cells));
|
Rows.Add(new ConsoleDisplayTableRow(cells));
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,7 @@ namespace OpenSim.Framework.Console
|
||||||
|
|
||||||
for (int i = 0; i < Columns.Count; i++)
|
for (int i = 0; i < Columns.Count; i++)
|
||||||
{
|
{
|
||||||
|
if (i != 0)
|
||||||
formatSb.Append(' ', TableSpacing);
|
formatSb.Append(' ', TableSpacing);
|
||||||
|
|
||||||
// Can only do left formatting for now
|
// Can only do left formatting for now
|
||||||
|
@ -139,16 +140,16 @@ namespace OpenSim.Framework.Console
|
||||||
|
|
||||||
public struct ConsoleDisplayTableRow
|
public struct ConsoleDisplayTableRow
|
||||||
{
|
{
|
||||||
public List<string> Cells { get; private set; }
|
public List<object> Cells { get; private set; }
|
||||||
|
|
||||||
public ConsoleDisplayTableRow(List<string> cells) : this()
|
public ConsoleDisplayTableRow(List<object> cells) : this()
|
||||||
{
|
{
|
||||||
Cells = cells;
|
Cells = cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConsoleDisplayTableRow(params string[] cells) : this()
|
public ConsoleDisplayTableRow(params object[] cells) : this()
|
||||||
{
|
{
|
||||||
Cells = new List<string>(cells);
|
Cells = new List<object>(cells);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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.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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used by modules to display stock co-ordinate help, though possibly this should be under some general section
|
||||||
|
/// rather than in each help summary.
|
||||||
|
/// </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();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Try to parse a console UUID from the console.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Will complain to the console if parsing fails.
|
||||||
|
/// </remarks>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <param name='console'>If null then no complaint is printed.</param>
|
||||||
|
/// <param name='rawUuid'></param>
|
||||||
|
/// <param name='uuid'></param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to parse the input as either a UUID or a local ID.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if parsing succeeded, false otherwise.</returns>
|
||||||
|
/// <param name='console'></param>
|
||||||
|
/// <param name='rawId'></param>
|
||||||
|
/// <param name='uuid'></param>
|
||||||
|
/// <param name='localId'>
|
||||||
|
/// Will be set to ConsoleUtil.LocalIdNotFound if parsing result was a UUID or no parse succeeded.
|
||||||
|
/// </param>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='console'>Can be null if no console is available.</param>
|
||||||
|
/// <param name='rawConsoleVector'>/param>
|
||||||
|
/// <param name='vector'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='rawConsoleVector'>/param>
|
||||||
|
/// <param name='vector'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector)
|
||||||
|
{
|
||||||
|
return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a maximum vector input from the console to an OpenMetaverse.Vector3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='rawConsoleVector'>/param>
|
||||||
|
/// <param name='vector'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector)
|
||||||
|
{
|
||||||
|
return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a vector input from the console to an OpenMetaverse.Vector3
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='rawConsoleVector'>
|
||||||
|
/// A string in the form <x>,<y>,<z> 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.
|
||||||
|
/// </param>
|
||||||
|
/// <param name='blankComponentFunc'></param>
|
||||||
|
/// <param name='vector'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool TryParseConsoleVector(
|
||||||
|
string rawConsoleVector, Func<string, string> blankComponentFunc, out Vector3 vector)
|
||||||
|
{
|
||||||
|
List<string> components = rawConsoleVector.Split(VectorSeparatorChars).ToList();
|
||||||
|
|
||||||
|
if (components.Count < 1 || components.Count > 3)
|
||||||
|
{
|
||||||
|
vector = Vector3.Zero;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = components.Count; i < 3; i++)
|
||||||
|
components.Add("");
|
||||||
|
|
||||||
|
List<string> semiDigestedComponents
|
||||||
|
= components.ConvertAll<string>(
|
||||||
|
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;
|
||||||
|
});
|
||||||
|
|
||||||
|
string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray());
|
||||||
|
|
||||||
|
// m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector);
|
||||||
|
|
||||||
|
return Vector3.TryParse(semiDigestedConsoleVector, out vector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,13 +44,18 @@ namespace OpenSim.Framework.Console
|
||||||
|
|
||||||
public ICommands Commands { get { return m_commands; } }
|
public ICommands Commands { get { return m_commands; } }
|
||||||
|
|
||||||
|
public string DefaultPrompt { get; set; }
|
||||||
|
|
||||||
public void Prompt() {}
|
public void Prompt() {}
|
||||||
|
|
||||||
public void RunCommand(string cmd) {}
|
public void RunCommand(string cmd) {}
|
||||||
|
|
||||||
public string ReadLine(string p, bool isCommand, bool e) { return ""; }
|
public string ReadLine(string p, bool isCommand, bool e) { return ""; }
|
||||||
|
|
||||||
public object ConsoleScene { get { return null; } }
|
public object ConsoleScene {
|
||||||
|
get { return null; }
|
||||||
|
set {}
|
||||||
|
}
|
||||||
|
|
||||||
public void Output(string text, string level) {}
|
public void Output(string text, string level) {}
|
||||||
public void Output(string text) {}
|
public void Output(string text) {}
|
||||||
|
|
|
@ -31,6 +31,7 @@ namespace OpenSim.Framework
|
||||||
public class Constants
|
public class Constants
|
||||||
{
|
{
|
||||||
public const uint RegionSize = 256;
|
public const uint RegionSize = 256;
|
||||||
|
public const uint RegionHeight = 4096;
|
||||||
public const byte TerrainPatchSize = 16;
|
public const byte TerrainPatchSize = 16;
|
||||||
public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";
|
public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A double dictionary that is thread abort safe.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within
|
||||||
|
/// a finally section (which can't be interrupted by Thread.Abort()).
|
||||||
|
/// </remarks>
|
||||||
|
public class DoubleDictionaryThreadAbortSafe<TKey1, TKey2, TValue>
|
||||||
|
{
|
||||||
|
Dictionary<TKey1, TValue> Dictionary1;
|
||||||
|
Dictionary<TKey2, TValue> Dictionary2;
|
||||||
|
ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
public DoubleDictionaryThreadAbortSafe()
|
||||||
|
{
|
||||||
|
Dictionary1 = new Dictionary<TKey1,TValue>();
|
||||||
|
Dictionary2 = new Dictionary<TKey2,TValue>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleDictionaryThreadAbortSafe(int capacity)
|
||||||
|
{
|
||||||
|
Dictionary1 = new Dictionary<TKey1, TValue>(capacity);
|
||||||
|
Dictionary2 = new Dictionary<TKey2, TValue>(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<TKey2, TValue> 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<TKey1, TValue> 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<TValue> 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<KeyValuePair<TKey1, TValue>> 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<TKey1, TValue> entry in Dictionary1)
|
||||||
|
action(entry);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (gotLock)
|
||||||
|
rwLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForEach(Action<KeyValuePair<TKey2, TValue>> 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<TKey2, TValue> entry in Dictionary2)
|
||||||
|
action(entry);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (gotLock)
|
||||||
|
rwLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue FindValue(Predicate<TValue> 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<TValue> FindAll(Predicate<TValue> predicate)
|
||||||
|
{
|
||||||
|
IList<TValue> list = new List<TValue>();
|
||||||
|
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<TValue> predicate)
|
||||||
|
{
|
||||||
|
IList<TKey1> list = new List<TKey1>();
|
||||||
|
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<TKey1, TValue> kvp in Dictionary1)
|
||||||
|
{
|
||||||
|
if (predicate(kvp.Value))
|
||||||
|
list.Add(kvp.Key);
|
||||||
|
}
|
||||||
|
|
||||||
|
IList<TKey2> list2 = new List<TKey2>(list.Count);
|
||||||
|
foreach (KeyValuePair<TKey2, TValue> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -805,8 +805,23 @@ namespace OpenSim.Framework
|
||||||
event Action<IClientAPI> OnRegionHandShakeReply;
|
event Action<IClientAPI> OnRegionHandShakeReply;
|
||||||
event GenericCall1 OnRequestWearables;
|
event GenericCall1 OnRequestWearables;
|
||||||
event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when an AgentUpdate message is received and before OnAgentUpdate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
|
||||||
|
/// </remarks>
|
||||||
event UpdateAgent OnPreAgentUpdate;
|
event UpdateAgent OnPreAgentUpdate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when an AgentUpdate message is received and after OnPreAgentUpdate.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates.
|
||||||
|
/// </remarks>
|
||||||
event UpdateAgent OnAgentUpdate;
|
event UpdateAgent OnAgentUpdate;
|
||||||
|
|
||||||
event AgentRequestSit OnAgentRequestSit;
|
event AgentRequestSit OnAgentRequestSit;
|
||||||
event AgentSit OnAgentSit;
|
event AgentSit OnAgentSit;
|
||||||
event AvatarPickerRequest OnAvatarPickerRequest;
|
event AvatarPickerRequest OnAvatarPickerRequest;
|
||||||
|
@ -1033,7 +1048,21 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
void InPacket(object NewPack);
|
void InPacket(object NewPack);
|
||||||
void ProcessInPacket(Packet NewPack);
|
void ProcessInPacket(Packet NewPack);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close this client
|
||||||
|
/// </summary>
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Close this client
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='force'>
|
||||||
|
/// 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.
|
||||||
|
/// </param>
|
||||||
|
void Close(bool force);
|
||||||
|
|
||||||
void Kick(string message);
|
void Kick(string message);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1070,7 +1099,19 @@ namespace OpenSim.Framework
|
||||||
void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs);
|
void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs);
|
||||||
void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args);
|
void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args);
|
||||||
|
|
||||||
void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source,
|
/// <summary>
|
||||||
|
/// Send chat to the viewer.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='message'></param>
|
||||||
|
/// <param name='type'></param>
|
||||||
|
/// <param name='fromPos'></param>
|
||||||
|
/// <param name='fromName'></param>
|
||||||
|
/// <param name='fromAgentID'></param>
|
||||||
|
/// <param name='ownerID'></param>
|
||||||
|
/// <param name='source'></param>
|
||||||
|
/// <param name='audible'></param>
|
||||||
|
void SendChatMessage(
|
||||||
|
string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source,
|
||||||
byte audible);
|
byte audible);
|
||||||
|
|
||||||
void SendInstantMessage(GridInstantMessage im);
|
void SendInstantMessage(GridInstantMessage im);
|
||||||
|
|
|
@ -78,6 +78,11 @@ namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
ICommands Commands { get; }
|
ICommands Commands { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The default prompt text.
|
||||||
|
/// </summary>
|
||||||
|
string DefaultPrompt { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Display a command prompt on the console and wait for user input
|
/// Display a command prompt on the console and wait for user input
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
public interface IConsole
|
public interface IConsole
|
||||||
{
|
{
|
||||||
object ConsoleScene { get; }
|
object ConsoleScene { get; set; }
|
||||||
|
|
||||||
void Output(string text, string level);
|
void Output(string text, string level);
|
||||||
void Output(string text);
|
void Output(string text);
|
||||||
|
|
|
@ -55,6 +55,13 @@ namespace OpenSim.Region.Framework.Interfaces
|
||||||
/// <returns>Land object at the point supplied</returns>
|
/// <returns>Land object at the point supplied</returns>
|
||||||
ILandObject GetLandObject(float x, float y);
|
ILandObject GetLandObject(float x, float y);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the parcel at the specified point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">Vector where x and y components are between 0 and 256. z component is ignored.</param>
|
||||||
|
/// <returns>Land object at the point supplied</returns>
|
||||||
|
ILandObject GetLandObject(Vector3 position);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the parcels near the specified point
|
/// Get the parcels near the specified point
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -73,32 +73,26 @@ namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryFolderBase(UUID id)
|
public InventoryFolderBase(UUID id) : this()
|
||||||
{
|
{
|
||||||
ID = id;
|
ID = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryFolderBase(UUID id, UUID owner)
|
public InventoryFolderBase(UUID id, UUID owner) : this(id)
|
||||||
{
|
{
|
||||||
ID = id;
|
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent)
|
public InventoryFolderBase(UUID id, string name, UUID owner, UUID parent) : this(id, owner)
|
||||||
{
|
{
|
||||||
ID = id;
|
|
||||||
Name = name;
|
Name = name;
|
||||||
Owner = owner;
|
|
||||||
ParentID = parent;
|
ParentID = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InventoryFolderBase(UUID id, string name, UUID owner, short type, UUID parent, ushort version)
|
public InventoryFolderBase(
|
||||||
|
UUID id, string name, UUID owner, short type, UUID parent, ushort version) : this(id, name, owner, parent)
|
||||||
{
|
{
|
||||||
ID = id;
|
|
||||||
Name = name;
|
|
||||||
Owner = owner;
|
|
||||||
Type = type;
|
Type = type;
|
||||||
ParentID = parent;
|
|
||||||
Version = version;
|
Version = version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,16 +87,7 @@ namespace OpenSim.Framework
|
||||||
protected string m_creatorId;
|
protected string m_creatorId;
|
||||||
|
|
||||||
/// <value>
|
/// <value>
|
||||||
/// The UUID for the creator. This may be different from the canonical CreatorId. This property is used
|
/// The CreatorId expressed as a UUID.tely
|
||||||
/// for communication with the client over the Second Life protocol, since that protocol can only understand
|
|
||||||
/// UUIDs. As this is a basic framework class, this means that both the string creator id and the uuid
|
|
||||||
/// reference have to be settable separately
|
|
||||||
///
|
|
||||||
/// Database plugins don't need to set this, it will be set by
|
|
||||||
/// upstream code (or set by the get accessor if left unset).
|
|
||||||
///
|
|
||||||
/// XXX: An alternative to having a separate uuid property would be to hash the CreatorId appropriately
|
|
||||||
/// every time there was communication with a UUID-only client. This may be much more expensive.
|
|
||||||
/// </value>
|
/// </value>
|
||||||
public UUID CreatorIdAsUuid
|
public UUID CreatorIdAsUuid
|
||||||
{
|
{
|
||||||
|
@ -109,20 +100,18 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
return m_creatorIdAsUuid;
|
return m_creatorIdAsUuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
|
||||||
{
|
|
||||||
m_creatorIdAsUuid = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
protected UUID m_creatorIdAsUuid = UUID.Zero;
|
protected UUID m_creatorIdAsUuid = UUID.Zero;
|
||||||
|
|
||||||
protected string m_creatorData = string.Empty;
|
/// <summary>
|
||||||
|
/// Extended creator information of the form <profile url>;<name>
|
||||||
|
/// </summary>
|
||||||
public string CreatorData // = <profile url>;<name>
|
public string CreatorData // = <profile url>;<name>
|
||||||
{
|
{
|
||||||
get { return m_creatorData; }
|
get { return m_creatorData; }
|
||||||
set { m_creatorData = value; }
|
set { m_creatorData = value; }
|
||||||
}
|
}
|
||||||
|
protected string m_creatorData = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used by the DB layer to retrieve / store the entire user identification.
|
/// Used by the DB layer to retrieve / store the entire user identification.
|
||||||
|
@ -162,7 +151,6 @@ namespace OpenSim.Framework
|
||||||
name = parts[2];
|
name = parts[2];
|
||||||
|
|
||||||
m_creatorData += ';' + name;
|
m_creatorData += ';' + name;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Monitoring
|
namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -100,5 +102,29 @@ Asset requests yesterday : {3} ({4} per hour) of which {5} were not found",
|
||||||
AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday,
|
AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday,
|
||||||
AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring
|
||||||
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
|
Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0));
|
||||||
|
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
"OpenSim object memory churn : {0} MB/s\n",
|
"OpenSim last object memory churn : {0} MB/s\n",
|
||||||
|
Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3));
|
||||||
|
|
||||||
|
sb.AppendFormat(
|
||||||
|
"OpenSim average object memory churn : {0} MB/s\n",
|
||||||
Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
|
Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3));
|
||||||
|
|
||||||
sb.AppendFormat(
|
sb.AppendFormat(
|
||||||
|
@ -63,5 +67,12 @@ namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Monitoring
|
namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -45,5 +47,12 @@ namespace OpenSim.Framework.Monitoring
|
||||||
/// A <see cref="System.String"/>
|
/// A <see cref="System.String"/>
|
||||||
/// </returns>
|
/// </returns>
|
||||||
string XReport(string uptime, string version);
|
string XReport(string uptime, string version);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Report back collected statistical information as an OSDMap of key/values
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>
|
||||||
|
/// </returns>
|
||||||
|
OSDMap OReport(string uptime, string version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring
|
||||||
private static bool m_enabled;
|
private static bool m_enabled;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Average memory churn in bytes per millisecond.
|
/// Last memory churn in bytes per millisecond.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static double AverageMemoryChurn
|
public static double AverageMemoryChurn
|
||||||
{
|
{
|
||||||
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
|
get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Average memory churn in bytes per millisecond.
|
||||||
|
/// </summary>
|
||||||
|
public static double LastMemoryChurn
|
||||||
|
{
|
||||||
|
get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Maximum number of statistical samples.
|
/// Maximum number of statistical samples.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
@ -355,10 +355,27 @@ Asset service request failures: {3}" + Environment.NewLine,
|
||||||
sb.Append(Environment.NewLine);
|
sb.Append(Environment.NewLine);
|
||||||
sb.Append(
|
sb.Append(
|
||||||
string.Format(
|
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}",
|
"{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,
|
inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
|
||||||
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
|
netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
|
||||||
sb.Append(Environment.NewLine);
|
|
||||||
|
/* 20130319 RA: For the moment, disable the dump of 'scene' catagory as they are mostly output by
|
||||||
|
* the two formatted printouts above.
|
||||||
|
SortedDictionary<string, SortedDictionary<string, Stat>> sceneStats;
|
||||||
|
if (StatsManager.TryGetStats("scene", out sceneStats))
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, SortedDictionary<string, Stat>> 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(Environment.NewLine);
|
||||||
|
@ -389,6 +406,15 @@ Asset service request failures: {3}" + Environment.NewLine,
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public override string XReport(string uptime, string version)
|
public override string XReport(string uptime, string version)
|
||||||
|
{
|
||||||
|
return OSDParser.SerializeJsonString(OReport(uptime, version));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Report back collected statistical information as an OSDMap
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public override OSDMap OReport(string uptime, string version)
|
||||||
{
|
{
|
||||||
OSDMap args = new OSDMap(30);
|
OSDMap args = new OSDMap(30);
|
||||||
// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache));
|
// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache));
|
||||||
|
@ -427,12 +453,10 @@ Asset service request failures: {3}" + Environment.NewLine,
|
||||||
args["Uptime"] = OSD.FromString (uptime);
|
args["Uptime"] = OSD.FromString (uptime);
|
||||||
args["Version"] = OSD.FromString (version);
|
args["Version"] = OSD.FromString (version);
|
||||||
|
|
||||||
string strBuffer = "";
|
return args;
|
||||||
strBuffer = OSDParser.SerializeJsonString(args);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return strBuffer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pull packet queue stats from packet queues and report
|
/// Pull packet queue stats from packet queues and report
|
||||||
|
@ -459,5 +483,11 @@ Asset service request failures: {3}" + Environment.NewLine,
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OSDMap OReport(string uptime, string version)
|
||||||
|
{
|
||||||
|
OSDMap ret = new OSDMap();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A statistic that wraps a counter.
|
||||||
|
// Built this way mostly so histograms and history can be created.
|
||||||
|
public class CounterStat : Stat
|
||||||
|
{
|
||||||
|
private SortedDictionary<string, EventHistogram> 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<string, EventHistogram>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<string, EventHistogram> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,56 +27,62 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Text;
|
||||||
using Nini.Config;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
using OpenSim.Data;
|
|
||||||
using OpenSim.Services.Interfaces;
|
|
||||||
using OpenSim.Services.Base;
|
|
||||||
|
|
||||||
namespace OpenSim.Services.InventoryService
|
namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
public class InventoryServiceBase : ServiceBase
|
public class PercentageStat : Stat
|
||||||
{
|
{
|
||||||
protected IInventoryDataPlugin m_Database = null;
|
public long Antecedent { get; set; }
|
||||||
|
public long Consequent { get; set; }
|
||||||
|
|
||||||
public InventoryServiceBase(IConfigSource config) : base(config)
|
public override double Value
|
||||||
{
|
{
|
||||||
string dllName = String.Empty;
|
get
|
||||||
string connString = String.Empty;
|
{
|
||||||
|
// 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;
|
||||||
// Try reading the [DatabaseService] section first, if it exists
|
|
||||||
//
|
// Avoid any chance of a multi-threaded divide-by-zero
|
||||||
IConfig dbConfig = config.Configs["DatabaseService"];
|
if (c == 0)
|
||||||
if (dbConfig != null)
|
return 0;
|
||||||
{
|
|
||||||
dllName = dbConfig.GetString("StorageProvider", String.Empty);
|
return (double)Antecedent / c * 100;
|
||||||
connString = dbConfig.GetString("ConnectionString", String.Empty);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
set
|
||||||
// Try reading the more specific [InventoryService] section, if it exists
|
|
||||||
//
|
|
||||||
IConfig inventoryConfig = config.Configs["InventoryService"];
|
|
||||||
if (inventoryConfig != null)
|
|
||||||
{
|
{
|
||||||
dllName = inventoryConfig.GetString("StorageProvider", dllName);
|
throw new InvalidOperationException("Cannot set value on a PercentageStat");
|
||||||
connString = inventoryConfig.GetString("ConnectionString", connString);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
public PercentageStat(
|
||||||
// We tried, but this doesn't exist. We can't proceed.
|
string shortName,
|
||||||
//
|
string name,
|
||||||
if (dllName.Equals(String.Empty))
|
string description,
|
||||||
throw new Exception("No InventoryService configuration");
|
string category,
|
||||||
|
string container,
|
||||||
m_Database = LoadPlugin<IInventoryDataPlugin>(dllName);
|
StatType type,
|
||||||
if (m_Database == null)
|
Action<Stat> pullAction,
|
||||||
throw new Exception("Could not find a storage interface in the given module");
|
StatVerbosity verbosity)
|
||||||
|
: base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {}
|
||||||
m_Database.Initialise(connString);
|
|
||||||
}
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds individual statistic details
|
||||||
|
/// </summary>
|
||||||
|
public class Stat : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Category of this stat (e.g. cache, scene, etc).
|
||||||
|
/// </summary>
|
||||||
|
public string Category { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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).
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// The container.
|
||||||
|
/// </value>
|
||||||
|
public string Container { get; private set; }
|
||||||
|
|
||||||
|
public StatType StatType { get; private set; }
|
||||||
|
|
||||||
|
public MeasuresOfInterest MeasuresOfInterest { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Action used to update this stat when the value is requested if it's a pull type.
|
||||||
|
/// </summary>
|
||||||
|
public Action<Stat> 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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Historical samples for calculating measures of interest average.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Will be null if no measures of interest require samples.
|
||||||
|
/// </remarks>
|
||||||
|
private static Queue<double> m_samples;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum number of statistical samples.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from
|
||||||
|
/// the main Watchdog.
|
||||||
|
/// </remarks>
|
||||||
|
private static int m_maxSamples = 24;
|
||||||
|
|
||||||
|
public Stat(
|
||||||
|
string shortName,
|
||||||
|
string name,
|
||||||
|
string description,
|
||||||
|
string unitName,
|
||||||
|
string category,
|
||||||
|
string container,
|
||||||
|
StatType type,
|
||||||
|
Action<Stat> pullAction,
|
||||||
|
StatVerbosity verbosity)
|
||||||
|
: this(
|
||||||
|
shortName,
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
unitName,
|
||||||
|
category,
|
||||||
|
container,
|
||||||
|
type,
|
||||||
|
MeasuresOfInterest.None,
|
||||||
|
pullAction,
|
||||||
|
verbosity)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructor
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='shortName'>Short name for the stat. Must not contain spaces. e.g. "LongFrames"</param>
|
||||||
|
/// <param name='name'>Human readable name for the stat. e.g. "Long frames"</param>
|
||||||
|
/// <param name='description'>Description of stat</param>
|
||||||
|
/// <param name='unitName'>
|
||||||
|
/// 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"
|
||||||
|
/// </param>
|
||||||
|
/// <param name='category'>Category under which this stat should appear, e.g. "scene". Do not capitalize.</param>
|
||||||
|
/// <param name='container'>Entity to which this stat relates. e.g. scene name if this is a per scene stat.</param>
|
||||||
|
/// <param name='type'>Push or pull</param>
|
||||||
|
/// <param name='pullAction'>Pull stats need an action to update the stat on request. Push stats should set null here.</param>
|
||||||
|
/// <param name='moi'>Measures of interest</param>
|
||||||
|
/// <param name='verbosity'>Verbosity of stat. Controls whether it will appear in short stat display or only full display.</param>
|
||||||
|
public Stat(
|
||||||
|
string shortName,
|
||||||
|
string name,
|
||||||
|
string description,
|
||||||
|
string unitName,
|
||||||
|
string category,
|
||||||
|
string container,
|
||||||
|
StatType type,
|
||||||
|
MeasuresOfInterest moi,
|
||||||
|
Action<Stat> 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));
|
||||||
|
|
||||||
|
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<double>(m_maxSamples);
|
||||||
|
|
||||||
|
Verbosity = verbosity;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDisposable.Dispose()
|
||||||
|
public virtual void Dispose()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Record a value in the sample set.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Do not call this if MeasuresOfInterest.None
|
||||||
|
/// </remarks>
|
||||||
|
public void RecordValue()
|
||||||
|
{
|
||||||
|
double newValue = Value;
|
||||||
|
|
||||||
|
lock (m_samples)
|
||||||
|
{
|
||||||
|
if (m_samples.Count >= m_maxSamples)
|
||||||
|
m_samples.Dequeue();
|
||||||
|
|
||||||
|
m_samples.Enqueue(newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual string ToConsoleString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName);
|
||||||
|
|
||||||
|
AppendMeasuresOfInterest(sb);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual OSDMap ToOSDMap()
|
||||||
|
{
|
||||||
|
OSDMap ret = new OSDMap();
|
||||||
|
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));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void AppendMeasuresOfInterest(StringBuilder sb)
|
||||||
|
{
|
||||||
|
if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime)
|
||||||
|
== MeasuresOfInterest.AverageChangeOverTime)
|
||||||
|
{
|
||||||
|
double totalChange = 0;
|
||||||
|
double? lastSample = null;
|
||||||
|
|
||||||
|
lock (m_samples)
|
||||||
|
{
|
||||||
|
foreach (double s in m_samples)
|
||||||
|
{
|
||||||
|
if (lastSample != null)
|
||||||
|
totalChange += s - (double)lastSample;
|
||||||
|
|
||||||
|
lastSample = s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1;
|
||||||
|
|
||||||
|
sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,10 @@
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Monitoring
|
namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -32,6 +36,24 @@ namespace OpenSim.Framework.Monitoring
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StatsManager
|
public 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<string> SubCommands = new HashSet<string> { AllSubCommand, ListSubCommand };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registered stats categorized by category/container/shortname
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Do not add or remove directly from this dictionary.
|
||||||
|
/// </remarks>
|
||||||
|
public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
|
||||||
|
= new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
|
||||||
|
|
||||||
private static AssetStatsCollector assetStats;
|
private static AssetStatsCollector assetStats;
|
||||||
private static UserStatsCollector userStats;
|
private static UserStatsCollector userStats;
|
||||||
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
|
private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector();
|
||||||
|
@ -40,6 +62,94 @@ namespace OpenSim.Framework.Monitoring
|
||||||
public static UserStatsCollector UserStats { get { return userStats; } }
|
public static UserStatsCollector UserStats { get { return userStats; } }
|
||||||
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
|
public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } }
|
||||||
|
|
||||||
|
public static void RegisterConsoleCommands(ICommandConsole console)
|
||||||
|
{
|
||||||
|
console.Commands.AddCommand(
|
||||||
|
"General",
|
||||||
|
false,
|
||||||
|
"show stats",
|
||||||
|
"show stats [list|all|<category>]",
|
||||||
|
"Show statistical information for this server",
|
||||||
|
"If no final argument is specified then legacy statistics information is currently shown.\n"
|
||||||
|
+ "If list is specified then statistic categories are shown.\n"
|
||||||
|
+ "If all is specified then all registered statistics are shown.\n"
|
||||||
|
+ "If a category name is specified then only statistics from that category are shown.\n"
|
||||||
|
+ "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS",
|
||||||
|
HandleShowStatsCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void HandleShowStatsCommand(string module, string[] cmd)
|
||||||
|
{
|
||||||
|
ICommandConsole con = MainConsole.Instance;
|
||||||
|
|
||||||
|
if (cmd.Length > 2)
|
||||||
|
{
|
||||||
|
var categoryName = cmd[2];
|
||||||
|
var containerName = cmd.Length > 3 ? cmd[3] : String.Empty;
|
||||||
|
|
||||||
|
if (categoryName == AllSubCommand)
|
||||||
|
{
|
||||||
|
foreach (var category in RegisteredStats.Values)
|
||||||
|
{
|
||||||
|
OutputCategoryStatsToConsole(con, category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (categoryName == ListSubCommand)
|
||||||
|
{
|
||||||
|
con.Output("Statistic categories available are:");
|
||||||
|
foreach (string category in RegisteredStats.Keys)
|
||||||
|
con.OutputFormat(" {0}", category);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SortedDictionary<string, SortedDictionary<string, Stat>> 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<string, Stat> container;
|
||||||
|
if (category.TryGetValue(containerName, out container))
|
||||||
|
{
|
||||||
|
OutputContainerStatsToConsole(con, container);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Legacy
|
||||||
|
con.Output(SimExtraStats.Report());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OutputCategoryStatsToConsole(
|
||||||
|
ICommandConsole con, SortedDictionary<string, SortedDictionary<string, Stat>> category)
|
||||||
|
{
|
||||||
|
foreach (var container in category.Values)
|
||||||
|
{
|
||||||
|
OutputContainerStatsToConsole(con, container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OutputContainerStatsToConsole( ICommandConsole con, SortedDictionary<string, Stat> container)
|
||||||
|
{
|
||||||
|
foreach (Stat stat in container.Values)
|
||||||
|
{
|
||||||
|
con.Output(stat.ToConsoleString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start collecting statistics related to assets.
|
/// Start collecting statistics related to assets.
|
||||||
/// Should only be called once.
|
/// Should only be called once.
|
||||||
|
@ -61,5 +171,153 @@ namespace OpenSim.Framework.Monitoring
|
||||||
|
|
||||||
return userStats;
|
return userStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a statistic.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name='stat'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool RegisterStat(Stat stat)
|
||||||
|
{
|
||||||
|
SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
|
||||||
|
SortedDictionary<string, Stat> 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 (TryGetStat(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<string, Stat>(container);
|
||||||
|
else
|
||||||
|
newContainer = new SortedDictionary<string, Stat>();
|
||||||
|
|
||||||
|
if (category != null)
|
||||||
|
newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
|
||||||
|
else
|
||||||
|
newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>();
|
||||||
|
|
||||||
|
newContainer[stat.ShortName] = stat;
|
||||||
|
newCategory[stat.Container] = newContainer;
|
||||||
|
RegisteredStats[stat.Category] = newCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deregister a statistic
|
||||||
|
/// </summary>>
|
||||||
|
/// <param name='stat'></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static bool DeregisterStat(Stat stat)
|
||||||
|
{
|
||||||
|
SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
|
||||||
|
SortedDictionary<string, Stat> container = null, newContainer;
|
||||||
|
|
||||||
|
lock (RegisteredStats)
|
||||||
|
{
|
||||||
|
if (!TryGetStat(stat, out category, out container))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
newContainer = new SortedDictionary<string, Stat>(container);
|
||||||
|
newContainer.Remove(stat.ShortName);
|
||||||
|
|
||||||
|
newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
|
||||||
|
newCategory.Remove(stat.Container);
|
||||||
|
|
||||||
|
newCategory[stat.Container] = newContainer;
|
||||||
|
RegisteredStats[stat.Category] = newCategory;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetStats(string category, out SortedDictionary<string, SortedDictionary<string, Stat>> stats)
|
||||||
|
{
|
||||||
|
return RegisteredStats.TryGetValue(category, out stats);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetStat(
|
||||||
|
Stat stat,
|
||||||
|
out SortedDictionary<string, SortedDictionary<string, Stat>> category,
|
||||||
|
out SortedDictionary<string, Stat> 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<string, SortedDictionary<string, Stat>> category in RegisteredStats.Values)
|
||||||
|
{
|
||||||
|
foreach (SortedDictionary<string, Stat> container in category.Values)
|
||||||
|
{
|
||||||
|
foreach (Stat stat in container.Values)
|
||||||
|
{
|
||||||
|
if (stat.MeasuresOfInterest != MeasuresOfInterest.None)
|
||||||
|
stat.RecordValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stat type.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
public enum StatType
|
||||||
|
{
|
||||||
|
Push,
|
||||||
|
Pull
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Measures of interest for this stat.
|
||||||
|
/// </summary>
|
||||||
|
[Flags]
|
||||||
|
public enum MeasuresOfInterest
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
AverageChangeOverTime
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Verbosity of stat.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Info will always be displayed.
|
||||||
|
/// </remarks>
|
||||||
|
public enum StatVerbosity
|
||||||
|
{
|
||||||
|
Debug,
|
||||||
|
Info
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Monitoring
|
namespace OpenSim.Framework.Monitoring
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -88,5 +90,21 @@ namespace OpenSim.Framework.Monitoring
|
||||||
Logouts total : {3}",
|
Logouts total : {3}",
|
||||||
SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ namespace OpenSim.Framework.Monitoring
|
||||||
public static class Watchdog
|
public static class Watchdog
|
||||||
{
|
{
|
||||||
/// <summary>Timer interval in milliseconds for the watchdog timer</summary>
|
/// <summary>Timer interval in milliseconds for the watchdog timer</summary>
|
||||||
const double WATCHDOG_INTERVAL_MS = 2500.0d;
|
public const double WATCHDOG_INTERVAL_MS = 2500.0d;
|
||||||
|
|
||||||
/// <summary>Default timeout in milliseconds before a thread is considered dead</summary>
|
/// <summary>Default timeout in milliseconds before a thread is considered dead</summary>
|
||||||
public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000;
|
public const int DEFAULT_WATCHDOG_TIMEOUT_MS = 5000;
|
||||||
|
@ -89,6 +89,17 @@ namespace OpenSim.Framework.Monitoring
|
||||||
FirstTick = Environment.TickCount & Int32.MaxValue;
|
FirstTick = Environment.TickCount & Int32.MaxValue;
|
||||||
LastTick = FirstTick;
|
LastTick = FirstTick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -97,6 +108,32 @@ namespace OpenSim.Framework.Monitoring
|
||||||
/// /summary>
|
/// /summary>
|
||||||
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
|
public static event Action<ThreadWatchdogInfo> OnWatchdogTimeout;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this watchdog active?
|
||||||
|
/// </summary>
|
||||||
|
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 readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
private static Dictionary<int, ThreadWatchdogInfo> m_threads;
|
private static Dictionary<int, ThreadWatchdogInfo> m_threads;
|
||||||
private static System.Timers.Timer m_watchdogTimer;
|
private static System.Timers.Timer m_watchdogTimer;
|
||||||
|
@ -115,11 +152,6 @@ namespace OpenSim.Framework.Monitoring
|
||||||
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
|
m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
|
||||||
m_watchdogTimer.AutoReset = false;
|
m_watchdogTimer.AutoReset = false;
|
||||||
m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
|
m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
|
||||||
|
|
||||||
// Set now so we don't get alerted on the first run
|
|
||||||
LastWatchdogThreadTick = Environment.TickCount & Int32.MaxValue;
|
|
||||||
|
|
||||||
m_watchdogTimer.Start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -199,7 +231,25 @@ namespace OpenSim.Framework.Monitoring
|
||||||
private static bool RemoveThread(int threadID)
|
private static bool RemoveThread(int threadID)
|
||||||
{
|
{
|
||||||
lock (m_threads)
|
lock (m_threads)
|
||||||
return m_threads.Remove(threadID);
|
{
|
||||||
|
ThreadWatchdogInfo twi;
|
||||||
|
if (m_threads.TryGetValue(threadID, out twi))
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId);
|
||||||
|
|
||||||
|
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)
|
public static bool AbortThread(int threadID)
|
||||||
|
@ -314,7 +364,9 @@ namespace OpenSim.Framework.Monitoring
|
||||||
if (callbackInfos == null)
|
if (callbackInfos == null)
|
||||||
callbackInfos = new List<ThreadWatchdogInfo>();
|
callbackInfos = new List<ThreadWatchdogInfo>();
|
||||||
|
|
||||||
callbackInfos.Add(threadInfo);
|
// 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,6 +380,8 @@ namespace OpenSim.Framework.Monitoring
|
||||||
if (MemoryWatchdog.Enabled)
|
if (MemoryWatchdog.Enabled)
|
||||||
MemoryWatchdog.Update();
|
MemoryWatchdog.Update();
|
||||||
|
|
||||||
|
StatsManager.RecordStats();
|
||||||
|
|
||||||
m_watchdogTimer.Start();
|
m_watchdogTimer.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,66 +26,66 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using OpenMetaverse;
|
using System.Collections.Generic;
|
||||||
using log4net;
|
|
||||||
using System.Reflection;
|
|
||||||
using OpenSim.Framework;
|
|
||||||
|
|
||||||
namespace OpenSim.Region.Framework.Scenes.Scripting
|
namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
public class NullScriptHost : IScriptHost
|
/// <summary>
|
||||||
|
/// Naive pool implementation.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Currently assumes that objects are in a useable state when returned.
|
||||||
|
/// </remarks>
|
||||||
|
public class Pool<T>
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
/// <summary>
|
||||||
|
/// Number of objects in the pool.
|
||||||
private Vector3 m_pos = new Vector3(((int)Constants.RegionSize * 0.5f), ((int)Constants.RegionSize * 0.5f), 30);
|
/// </summary>
|
||||||
|
public int Count
|
||||||
public string Name
|
|
||||||
{
|
{
|
||||||
get { return "Object"; }
|
get
|
||||||
set { }
|
{
|
||||||
|
lock (m_pool)
|
||||||
|
return m_pool.Count;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string SitName
|
private Stack<T> m_pool;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum pool size. Beyond this, any returned objects are not pooled.
|
||||||
|
/// </summary>
|
||||||
|
private int m_maxPoolSize;
|
||||||
|
|
||||||
|
private Func<T> m_createFunction;
|
||||||
|
|
||||||
|
public Pool(Func<T> createFunction, int maxSize)
|
||||||
{
|
{
|
||||||
get { return String.Empty; }
|
m_maxPoolSize = maxSize;
|
||||||
set { }
|
m_createFunction = createFunction;
|
||||||
|
m_pool = new Stack<T>(m_maxPoolSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TouchName
|
public T GetObject()
|
||||||
{
|
{
|
||||||
get { return String.Empty; }
|
lock (m_pool)
|
||||||
set { }
|
{
|
||||||
|
if (m_pool.Count > 0)
|
||||||
|
return m_pool.Pop();
|
||||||
|
else
|
||||||
|
return m_createFunction();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Description
|
public void ReturnObject(T obj)
|
||||||
{
|
{
|
||||||
get { return String.Empty; }
|
lock (m_pool)
|
||||||
set { }
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID UUID
|
|
||||||
{
|
{
|
||||||
get { return UUID.Zero; }
|
if (m_pool.Count >= m_maxPoolSize)
|
||||||
}
|
return;
|
||||||
|
else
|
||||||
public UUID OwnerID
|
m_pool.Push(obj);
|
||||||
{
|
}
|
||||||
get { return UUID.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public UUID CreatorID
|
|
||||||
{
|
|
||||||
get { return UUID.Zero; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector3 AbsolutePosition
|
|
||||||
{
|
|
||||||
get { return m_pos; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetText(string text, Vector3 color, double alpha)
|
|
||||||
{
|
|
||||||
m_log.Warn("Tried to SetText "+text+" on NullScriptHost");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -193,17 +193,6 @@ namespace OpenSim.Framework
|
||||||
public PrimitiveBaseShape()
|
public PrimitiveBaseShape()
|
||||||
{
|
{
|
||||||
PCode = (byte)PCodeEnum.Primitive;
|
PCode = (byte)PCodeEnum.Primitive;
|
||||||
ExtraParams = new byte[1];
|
|
||||||
m_textureEntry = DEFAULT_TEXTURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrimitiveBaseShape(bool noShape)
|
|
||||||
{
|
|
||||||
if (noShape)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PCode = (byte)PCodeEnum.Primitive;
|
|
||||||
ExtraParams = new byte[1];
|
|
||||||
m_textureEntry = DEFAULT_TEXTURE;
|
m_textureEntry = DEFAULT_TEXTURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +205,6 @@ namespace OpenSim.Framework
|
||||||
// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID);
|
// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID);
|
||||||
|
|
||||||
PCode = (byte)prim.PrimData.PCode;
|
PCode = (byte)prim.PrimData.PCode;
|
||||||
ExtraParams = new byte[1];
|
|
||||||
|
|
||||||
State = prim.PrimData.State;
|
State = prim.PrimData.State;
|
||||||
PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin);
|
PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin);
|
||||||
|
@ -248,7 +236,10 @@ namespace OpenSim.Framework
|
||||||
SculptTexture = prim.Sculpt.SculptTexture;
|
SculptTexture = prim.Sculpt.SculptTexture;
|
||||||
SculptType = (byte)prim.Sculpt.Type;
|
SculptType = (byte)prim.Sculpt.Type;
|
||||||
}
|
}
|
||||||
else SculptType = (byte)OpenMetaverse.SculptType.None;
|
else
|
||||||
|
{
|
||||||
|
SculptType = (byte)OpenMetaverse.SculptType.None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[XmlIgnore]
|
[XmlIgnore]
|
||||||
|
@ -340,9 +331,9 @@ namespace OpenSim.Framework
|
||||||
_scale = new Vector3(side, side, side);
|
_scale = new Vector3(side, side, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetHeigth(float heigth)
|
public void SetHeigth(float height)
|
||||||
{
|
{
|
||||||
_scale.Z = heigth;
|
_scale.Z = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetRadius(float radius)
|
public void SetRadius(float radius)
|
||||||
|
@ -631,6 +622,8 @@ namespace OpenSim.Framework
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is only used at runtime. For sculpties this holds the texture data, and for meshes
|
||||||
|
// the mesh data.
|
||||||
public byte[] SculptData
|
public byte[] SculptData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -1156,14 +1149,13 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
public void ReadSculptData(byte[] data, int pos)
|
public void ReadSculptData(byte[] data, int pos)
|
||||||
{
|
{
|
||||||
byte[] SculptTextureUUID = new byte[16];
|
UUID SculptUUID;
|
||||||
UUID SculptUUID = UUID.Zero;
|
byte SculptTypel;
|
||||||
byte SculptTypel = data[16+pos];
|
|
||||||
|
|
||||||
if (data.Length+pos >= 17)
|
if (data.Length-pos >= 17)
|
||||||
{
|
{
|
||||||
_sculptEntry = true;
|
_sculptEntry = true;
|
||||||
SculptTextureUUID = new byte[16];
|
byte[] SculptTextureUUID = new byte[16];
|
||||||
SculptTypel = data[16 + pos];
|
SculptTypel = data[16 + pos];
|
||||||
Array.Copy(data, pos, SculptTextureUUID,0, 16);
|
Array.Copy(data, pos, SculptTextureUUID,0, 16);
|
||||||
SculptUUID = new UUID(SculptTextureUUID, 0);
|
SculptUUID = new UUID(SculptTextureUUID, 0);
|
||||||
|
|
|
@ -74,16 +74,26 @@ namespace OpenSim.Framework.RegionLoader.Web
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse();
|
|
||||||
m_log.Debug("[WEBLOADER]: Downloading region information...");
|
|
||||||
StreamReader reader = new StreamReader(webResponse.GetResponseStream());
|
|
||||||
string xmlSource = String.Empty;
|
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();
|
string tempStr = reader.ReadLine();
|
||||||
while (tempStr != null)
|
while (tempStr != null)
|
||||||
{
|
{
|
||||||
xmlSource = xmlSource + tempStr;
|
xmlSource = xmlSource + tempStr;
|
||||||
tempStr = reader.ReadLine();
|
tempStr = reader.ReadLine();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_log.Debug("[WEBLOADER]: Done downloading region information from server. Total Bytes: " +
|
m_log.Debug("[WEBLOADER]: Done downloading region information from server. Total Bytes: " +
|
||||||
xmlSource.Length);
|
xmlSource.Length);
|
||||||
XmlDocument xmlDoc = new XmlDocument();
|
XmlDocument xmlDoc = new XmlDocument();
|
||||||
|
@ -107,17 +117,24 @@ namespace OpenSim.Framework.RegionLoader.Web
|
||||||
}
|
}
|
||||||
catch (WebException ex)
|
catch (WebException ex)
|
||||||
{
|
{
|
||||||
if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound)
|
using (HttpWebResponse response = (HttpWebResponse)ex.Response)
|
||||||
|
{
|
||||||
|
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
if (!allowRegionless)
|
if (!allowRegionless)
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (regionCount > 0 | allowRegionless)
|
if (regionCount > 0 | allowRegionless)
|
||||||
|
{
|
||||||
return regionInfos;
|
return regionInfos;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_log.Error("[WEBLOADER]: No region configs were available.");
|
m_log.Error("[WEBLOADER]: No region configs were available.");
|
||||||
|
|
|
@ -65,9 +65,14 @@ namespace OpenSim.Framework.Serialization
|
||||||
|
|
||||||
UserAccount account = userService.GetUserAccount(UUID.Zero, userId);
|
UserAccount account = userService.GetUserAccount(UUID.Zero, userId);
|
||||||
if (account != null)
|
if (account != null)
|
||||||
|
{
|
||||||
return MakeOspa(account.FirstName, account.LastName);
|
return MakeOspa(account.FirstName, account.LastName);
|
||||||
|
}
|
||||||
// else
|
// else
|
||||||
|
// {
|
||||||
// m_log.WarnFormat("[OSP RESOLVER]: No user account for {0}", userId);
|
// m_log.WarnFormat("[OSP RESOLVER]: No user account for {0}", userId);
|
||||||
|
// System.Console.WriteLine("[OSP RESOLVER]: No user account for {0}", userId);
|
||||||
|
// }
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -79,10 +84,13 @@ namespace OpenSim.Framework.Serialization
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static string MakeOspa(string firstName, string lastName)
|
public static string MakeOspa(string firstName, string lastName)
|
||||||
{
|
{
|
||||||
// m_log.DebugFormat("[OSP RESOLVER]: Making OSPA for {0} {1}", firstName, lastName);
|
string ospa
|
||||||
|
= OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
|
||||||
|
|
||||||
return
|
// m_log.DebugFormat("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
|
||||||
OSPA_PREFIX + OSPA_NAME_KEY + OSPA_PAIR_SEPARATOR + firstName + OSPA_NAME_VALUE_SEPARATOR + lastName;
|
// System.Console.WriteLine("[OSP RESOLVER]: Made OSPA {0} for {1} {2}", ospa, firstName, lastName);
|
||||||
|
|
||||||
|
return ospa;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
|
||||||
namespace OpenSim.Framework.Serialization.Tests
|
namespace OpenSim.Framework.Serialization.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LandDataSerializerTest
|
public class LandDataSerializerTest : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private LandData land;
|
private LandData land;
|
||||||
private LandData landWithParcelAccessList;
|
private LandData landWithParcelAccessList;
|
||||||
|
|
|
@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
|
||||||
namespace OpenSim.Framework.Serialization.Tests
|
namespace OpenSim.Framework.Serialization.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class RegionSettingsSerializerTests
|
public class RegionSettingsSerializerTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?>
|
private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?>
|
||||||
<RegionSettings>
|
<RegionSettings>
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -38,6 +37,8 @@ using log4net;
|
||||||
using log4net.Appender;
|
using log4net.Appender;
|
||||||
using log4net.Core;
|
using log4net.Core;
|
||||||
using log4net.Repository;
|
using log4net.Repository;
|
||||||
|
using OpenMetaverse;
|
||||||
|
using OpenMetaverse.StructuredData;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Console;
|
using OpenSim.Framework.Console;
|
||||||
using OpenSim.Framework.Monitoring;
|
using OpenSim.Framework.Monitoring;
|
||||||
|
@ -45,16 +46,12 @@ using OpenSim.Framework.Servers;
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
using Timer=System.Timers.Timer;
|
using Timer=System.Timers.Timer;
|
||||||
|
|
||||||
using OpenMetaverse;
|
|
||||||
using OpenMetaverse.StructuredData;
|
|
||||||
|
|
||||||
|
|
||||||
namespace OpenSim.Framework.Servers
|
namespace OpenSim.Framework.Servers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Common base for the main OpenSimServers (user, grid, inventory, region, etc)
|
/// Common base for the main OpenSimServers (user, grid, inventory, region, etc)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class BaseOpenSimServer
|
public abstract class BaseOpenSimServer : ServerBase
|
||||||
{
|
{
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
|
|
||||||
|
@ -64,27 +61,6 @@ namespace OpenSim.Framework.Servers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000);
|
private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000);
|
||||||
|
|
||||||
protected CommandConsole m_console;
|
|
||||||
protected OpenSimAppender m_consoleAppender;
|
|
||||||
protected IAppender m_logFileAppender = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Time at which this server was started
|
|
||||||
/// </summary>
|
|
||||||
protected DateTime m_startuptime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Record the initial startup directory for info purposes
|
|
||||||
/// </summary>
|
|
||||||
protected string m_startupDirectory = Environment.CurrentDirectory;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
|
|
||||||
/// </summary>
|
|
||||||
protected string m_version;
|
|
||||||
|
|
||||||
protected string m_pidFile = String.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Random uuid for private data
|
/// Random uuid for private data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -96,35 +72,13 @@ namespace OpenSim.Framework.Servers
|
||||||
get { return m_httpServer; }
|
get { return m_httpServer; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public BaseOpenSimServer() : base()
|
||||||
/// Holds the non-viewer statistics collection object for this service/server
|
|
||||||
/// </summary>
|
|
||||||
protected IStatsCollector m_stats;
|
|
||||||
|
|
||||||
public BaseOpenSimServer()
|
|
||||||
{
|
{
|
||||||
m_startuptime = DateTime.Now;
|
|
||||||
m_version = VersionInfo.Version;
|
|
||||||
|
|
||||||
// Random uuid for private data
|
// Random uuid for private data
|
||||||
m_osSecret = UUID.Random().ToString();
|
m_osSecret = UUID.Random().ToString();
|
||||||
|
|
||||||
m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
|
m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
|
||||||
m_periodicDiagnosticsTimer.Enabled = true;
|
m_periodicDiagnosticsTimer.Enabled = true;
|
||||||
|
|
||||||
// This thread will go on to become the console listening thread
|
|
||||||
Thread.CurrentThread.Name = "ConsoleThread";
|
|
||||||
|
|
||||||
ILoggerRepository repository = LogManager.GetRepository();
|
|
||||||
IAppender[] appenders = repository.GetAppenders();
|
|
||||||
|
|
||||||
foreach (IAppender appender in appenders)
|
|
||||||
{
|
|
||||||
if (appender.Name == "LogFileAppender")
|
|
||||||
{
|
|
||||||
m_logFileAppender = appender;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -132,34 +86,10 @@ namespace OpenSim.Framework.Servers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void StartupSpecific()
|
protected virtual void StartupSpecific()
|
||||||
{
|
{
|
||||||
if (m_console != null)
|
if (m_console == null)
|
||||||
{
|
return;
|
||||||
ILoggerRepository repository = LogManager.GetRepository();
|
|
||||||
IAppender[] appenders = repository.GetAppenders();
|
|
||||||
|
|
||||||
foreach (IAppender appender in appenders)
|
RegisterCommonCommands();
|
||||||
{
|
|
||||||
if (appender.Name == "Console")
|
|
||||||
{
|
|
||||||
m_consoleAppender = (OpenSimAppender)appender;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (null == m_consoleAppender)
|
|
||||||
{
|
|
||||||
Notice("No appender named Console found (see the log4net config file for this executable)!");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_consoleAppender.Console = 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));
|
|
||||||
}
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "quit",
|
m_console.Commands.AddCommand("General", false, "quit",
|
||||||
"quit",
|
"quit",
|
||||||
|
@ -168,40 +98,6 @@ namespace OpenSim.Framework.Servers
|
||||||
m_console.Commands.AddCommand("General", false, "shutdown",
|
m_console.Commands.AddCommand("General", false, "shutdown",
|
||||||
"shutdown",
|
"shutdown",
|
||||||
"Quit the application", HandleQuit);
|
"Quit the application", HandleQuit);
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "set log level",
|
|
||||||
"set log level <level>",
|
|
||||||
"Set the console logging level", HandleLogLevel);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "show info",
|
|
||||||
"show info",
|
|
||||||
"Show general information about the server", HandleShow);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "show stats",
|
|
||||||
"show stats",
|
|
||||||
"Show statistics", HandleShow);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "show threads",
|
|
||||||
"show threads",
|
|
||||||
"Show thread status", HandleShow);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "show uptime",
|
|
||||||
"show uptime",
|
|
||||||
"Show server uptime", HandleShow);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "show version",
|
|
||||||
"show version",
|
|
||||||
"Show server version", HandleShow);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "threads abort",
|
|
||||||
"threads abort <thread-id>",
|
|
||||||
"Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
|
|
||||||
|
|
||||||
m_console.Commands.AddCommand("General", false, "threads show",
|
|
||||||
"threads show",
|
|
||||||
"Show thread status. Synonym for \"show threads\"",
|
|
||||||
(string module, string[] args) => Notice(GetThreadsReport()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -226,96 +122,18 @@ namespace OpenSim.Framework.Servers
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
|
StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
|
||||||
sb.Append(GetUptimeReport());
|
sb.Append(GetUptimeReport());
|
||||||
|
sb.Append(StatsManager.SimExtraStats.Report());
|
||||||
if (m_stats != null)
|
|
||||||
{
|
|
||||||
sb.Append(m_stats.Report());
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append(Environment.NewLine);
|
sb.Append(Environment.NewLine);
|
||||||
sb.Append(GetThreadsReport());
|
sb.Append(GetThreadsReport());
|
||||||
|
|
||||||
m_log.Debug(sb);
|
m_log.Debug(sb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get a report about the registered threads in this server.
|
|
||||||
/// </summary>
|
|
||||||
protected string GetThreadsReport()
|
|
||||||
{
|
|
||||||
// This should be a constant field.
|
|
||||||
string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
|
|
||||||
|
|
||||||
sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
|
|
||||||
|
|
||||||
int timeNow = Environment.TickCount & Int32.MaxValue;
|
|
||||||
|
|
||||||
sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
|
|
||||||
sb.Append(Environment.NewLine);
|
|
||||||
|
|
||||||
foreach (Watchdog.ThreadWatchdogInfo twi in threads)
|
|
||||||
{
|
|
||||||
Thread t = twi.Thread;
|
|
||||||
|
|
||||||
sb.AppendFormat(
|
|
||||||
reportFormat,
|
|
||||||
t.ManagedThreadId,
|
|
||||||
t.Name,
|
|
||||||
timeNow - twi.LastTick,
|
|
||||||
timeNow - twi.FirstTick,
|
|
||||||
t.Priority,
|
|
||||||
t.ThreadState);
|
|
||||||
|
|
||||||
sb.Append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.Append("\n");
|
|
||||||
|
|
||||||
// For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
|
|
||||||
// zero active threads.
|
|
||||||
int totalThreads = Process.GetCurrentProcess().Threads.Count;
|
|
||||||
if (totalThreads > 0)
|
|
||||||
sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
|
|
||||||
|
|
||||||
sb.Append("Main threadpool (excluding script engine pools)\n");
|
|
||||||
sb.Append(Util.GetThreadPoolReport());
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return a report about the uptime of this server
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected string GetUptimeReport()
|
|
||||||
{
|
|
||||||
StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
|
|
||||||
sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
|
|
||||||
sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
|
|
||||||
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Performs initialisation of the scene, such as loading configuration from disk.
|
/// Performs initialisation of the scene, such as loading configuration from disk.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Startup()
|
public virtual void Startup()
|
||||||
{
|
{
|
||||||
m_log.Info("[STARTUP]: Beginning startup processing");
|
|
||||||
|
|
||||||
EnhanceVersionInformation();
|
|
||||||
|
|
||||||
m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine);
|
|
||||||
// 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(
|
|
||||||
"[STARTUP]: Operating system version: {0}, .NET platform {1}, {2}-bit\n",
|
|
||||||
Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32");
|
|
||||||
|
|
||||||
StartupSpecific();
|
StartupSpecific();
|
||||||
|
|
||||||
TimeSpan timeTaken = DateTime.Now - m_startuptime;
|
TimeSpan timeTaken = DateTime.Now - m_startuptime;
|
||||||
|
@ -343,269 +161,9 @@ namespace OpenSim.Framework.Servers
|
||||||
Shutdown();
|
Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleLogLevel(string module, string[] cmd)
|
|
||||||
{
|
|
||||||
if (null == m_consoleAppender)
|
|
||||||
{
|
|
||||||
Notice("No appender named Console found (see the log4net config file for this executable)!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd.Length > 3)
|
|
||||||
{
|
|
||||||
string rawLevel = cmd[3];
|
|
||||||
|
|
||||||
ILoggerRepository repository = LogManager.GetRepository();
|
|
||||||
Level consoleLevel = repository.LevelMap[rawLevel];
|
|
||||||
|
|
||||||
if (consoleLevel != null)
|
|
||||||
m_consoleAppender.Threshold = consoleLevel;
|
|
||||||
else
|
|
||||||
Notice(
|
|
||||||
String.Format(
|
|
||||||
"{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
|
|
||||||
rawLevel));
|
|
||||||
}
|
|
||||||
|
|
||||||
Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Show help information
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="helpArgs"></param>
|
|
||||||
protected virtual void ShowHelp(string[] helpArgs)
|
|
||||||
{
|
|
||||||
Notice("");
|
|
||||||
|
|
||||||
if (helpArgs.Length == 0)
|
|
||||||
{
|
|
||||||
Notice("set log level [level] - change the console logging level only. For example, off or debug.");
|
|
||||||
Notice("show info - show server information (e.g. startup path).");
|
|
||||||
|
|
||||||
if (m_stats != null)
|
|
||||||
Notice("show stats - show statistical information for this server");
|
|
||||||
|
|
||||||
Notice("show threads - list tracked threads");
|
|
||||||
Notice("show uptime - show server startup time and uptime.");
|
|
||||||
Notice("show version - show server version.");
|
|
||||||
Notice("");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void HandleShow(string module, string[] cmd)
|
|
||||||
{
|
|
||||||
List<string> args = new List<string>(cmd);
|
|
||||||
|
|
||||||
args.RemoveAt(0);
|
|
||||||
|
|
||||||
string[] showParams = args.ToArray();
|
|
||||||
|
|
||||||
switch (showParams[0])
|
|
||||||
{
|
|
||||||
case "info":
|
|
||||||
ShowInfo();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "stats":
|
|
||||||
if (m_stats != null)
|
|
||||||
Notice(m_stats.Report());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "threads":
|
|
||||||
Notice(GetThreadsReport());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "uptime":
|
|
||||||
Notice(GetUptimeReport());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "version":
|
|
||||||
Notice(GetVersionText());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void HandleThreadsAbort(string module, string[] cmd)
|
|
||||||
{
|
|
||||||
if (cmd.Length != 3)
|
|
||||||
{
|
|
||||||
MainConsole.Instance.Output("Usage: threads abort <thread-id>");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int threadId;
|
|
||||||
if (!int.TryParse(cmd[2], out threadId))
|
|
||||||
{
|
|
||||||
MainConsole.Instance.Output("ERROR: Thread id must be an integer");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Watchdog.AbortThread(threadId))
|
|
||||||
MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
|
|
||||||
else
|
|
||||||
MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void ShowInfo()
|
|
||||||
{
|
|
||||||
Notice(GetVersionText());
|
|
||||||
Notice("Startup directory: " + m_startupDirectory);
|
|
||||||
if (null != m_consoleAppender)
|
|
||||||
Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected string GetVersionText()
|
|
||||||
{
|
|
||||||
return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Console output is only possible if a console has been established.
|
|
||||||
/// That is something that cannot be determined within this class. So
|
|
||||||
/// all attempts to use the console MUST be verified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="msg"></param>
|
|
||||||
protected void Notice(string msg)
|
|
||||||
{
|
|
||||||
if (m_console != null)
|
|
||||||
{
|
|
||||||
m_console.Output(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Console output is only possible if a console has been established.
|
|
||||||
/// That is something that cannot be determined within this class. So
|
|
||||||
/// all attempts to use the console MUST be verified.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="format"></param>
|
|
||||||
/// <param name="components"></param>
|
|
||||||
protected void Notice(string format, params string[] components)
|
|
||||||
{
|
|
||||||
if (m_console != null)
|
|
||||||
m_console.OutputFormat(format, components);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enhance the version string with extra information if it's available.
|
|
||||||
/// </summary>
|
|
||||||
protected void EnhanceVersionInformation()
|
|
||||||
{
|
|
||||||
string buildVersion = string.Empty;
|
|
||||||
|
|
||||||
// The subversion information is deprecated and will be removed at a later date
|
|
||||||
// Add subversion revision information if available
|
|
||||||
// Try file "svn_revision" in the current directory first, then the .svn info.
|
|
||||||
// This allows to make the revision available in simulators not running from the source tree.
|
|
||||||
// FIXME: Making an assumption about the directory we're currently in - we do this all over the place
|
|
||||||
// elsewhere as well
|
|
||||||
string gitDir = "../.git/";
|
|
||||||
string gitRefPointerPath = gitDir + "HEAD";
|
|
||||||
|
|
||||||
string svnRevisionFileName = "svn_revision";
|
|
||||||
string svnFileName = ".svn/entries";
|
|
||||||
string manualVersionFileName = ".version";
|
|
||||||
string inputLine;
|
|
||||||
int strcmp;
|
|
||||||
|
|
||||||
if (File.Exists(manualVersionFileName))
|
|
||||||
{
|
|
||||||
using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
|
|
||||||
buildVersion = CommitFile.ReadLine();
|
|
||||||
|
|
||||||
m_version += buildVersion ?? "";
|
|
||||||
}
|
|
||||||
else if (File.Exists(gitRefPointerPath))
|
|
||||||
{
|
|
||||||
// m_log.DebugFormat("[OPENSIM]: Found {0}", gitRefPointerPath);
|
|
||||||
|
|
||||||
string rawPointer = "";
|
|
||||||
|
|
||||||
using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
|
|
||||||
rawPointer = pointerFile.ReadLine();
|
|
||||||
|
|
||||||
// m_log.DebugFormat("[OPENSIM]: rawPointer [{0}]", rawPointer);
|
|
||||||
|
|
||||||
Match m = Regex.Match(rawPointer, "^ref: (.+)$");
|
|
||||||
|
|
||||||
if (m.Success)
|
|
||||||
{
|
|
||||||
// m_log.DebugFormat("[OPENSIM]: Matched [{0}]", m.Groups[1].Value);
|
|
||||||
|
|
||||||
string gitRef = m.Groups[1].Value;
|
|
||||||
string gitRefPath = gitDir + gitRef;
|
|
||||||
if (File.Exists(gitRefPath))
|
|
||||||
{
|
|
||||||
// m_log.DebugFormat("[OPENSIM]: Found gitRefPath [{0}]", gitRefPath);
|
|
||||||
|
|
||||||
using (StreamReader refFile = File.OpenText(gitRefPath))
|
|
||||||
{
|
|
||||||
string gitHash = refFile.ReadLine();
|
|
||||||
m_version += gitHash.Substring(0, 7);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Remove the else logic when subversion mirror is no longer used
|
|
||||||
if (File.Exists(svnRevisionFileName))
|
|
||||||
{
|
|
||||||
StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
|
|
||||||
buildVersion = RevisionFile.ReadLine();
|
|
||||||
buildVersion.Trim();
|
|
||||||
RevisionFile.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
|
|
||||||
{
|
|
||||||
StreamReader EntriesFile = File.OpenText(svnFileName);
|
|
||||||
inputLine = EntriesFile.ReadLine();
|
|
||||||
while (inputLine != null)
|
|
||||||
{
|
|
||||||
// using the dir svn revision at the top of entries file
|
|
||||||
strcmp = String.Compare(inputLine, "dir");
|
|
||||||
if (strcmp == 0)
|
|
||||||
{
|
|
||||||
buildVersion = EntriesFile.ReadLine();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
inputLine = EntriesFile.ReadLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EntriesFile.Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void CreatePIDFile(string path)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
|
|
||||||
FileStream fs = File.Create(path);
|
|
||||||
|
|
||||||
Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
|
|
||||||
fs.Write(buf, 0, buf.Length);
|
|
||||||
fs.Close();
|
|
||||||
m_pidFile = path;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string osSecret {
|
public string osSecret {
|
||||||
// Secret uuid for the simulator
|
// Secret uuid for the simulator
|
||||||
get { return m_osSecret; }
|
get { return m_osSecret; }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string StatReport(IOSHttpRequest httpRequest)
|
public string StatReport(IOSHttpRequest httpRequest)
|
||||||
|
@ -613,26 +171,11 @@ namespace OpenSim.Framework.Servers
|
||||||
// If we catch a request for "callback", wrap the response in the value for jsonp
|
// If we catch a request for "callback", wrap the response in the value for jsonp
|
||||||
if (httpRequest.Query.ContainsKey("callback"))
|
if (httpRequest.Query.ContainsKey("callback"))
|
||||||
{
|
{
|
||||||
return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
|
return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
|
return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void RemovePIDFile()
|
|
||||||
{
|
|
||||||
if (m_pidFile != String.Empty)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
File.Delete(m_pidFile);
|
|
||||||
m_pidFile = String.Empty;
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,23 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||||
private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
|
private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the debug level.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// See MainServer.DebugLevel.
|
||||||
|
/// </value>
|
||||||
public int DebugLevel { get; set; }
|
public int DebugLevel { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Request number for diagnostic purposes.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
public int RequestNumber { get; private set; }
|
||||||
|
|
||||||
private volatile int NotSocketErrors = 0;
|
private volatile int NotSocketErrors = 0;
|
||||||
public volatile bool HTTPDRunning = false;
|
public volatile bool HTTPDRunning = false;
|
||||||
|
|
||||||
|
@ -67,7 +82,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
|
protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
|
||||||
protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
|
protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
|
||||||
protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
|
protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
|
||||||
protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
|
// protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
|
||||||
protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
|
protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
|
||||||
new Dictionary<string, PollServiceEventArgs>();
|
new Dictionary<string, PollServiceEventArgs>();
|
||||||
|
|
||||||
|
@ -245,29 +260,29 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
return new List<string>(m_pollHandlers.Keys);
|
return new List<string>(m_pollHandlers.Keys);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that the agent string is provided simply to differentiate
|
// // Note that the agent string is provided simply to differentiate
|
||||||
// the handlers - it is NOT required to be an actual agent header
|
// // the handlers - it is NOT required to be an actual agent header
|
||||||
// value.
|
// // value.
|
||||||
public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
|
// public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
|
||||||
{
|
// {
|
||||||
lock (m_agentHandlers)
|
// lock (m_agentHandlers)
|
||||||
{
|
// {
|
||||||
if (!m_agentHandlers.ContainsKey(agent))
|
// if (!m_agentHandlers.ContainsKey(agent))
|
||||||
{
|
// {
|
||||||
m_agentHandlers.Add(agent, handler);
|
// m_agentHandlers.Add(agent, handler);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
//must already have a handler for that path so return false
|
// //must already have a handler for that path so return false
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
public List<string> GetAgentHandlerKeys()
|
// public List<string> GetAgentHandlerKeys()
|
||||||
{
|
// {
|
||||||
lock (m_agentHandlers)
|
// lock (m_agentHandlers)
|
||||||
return new List<string>(m_agentHandlers.Keys);
|
// return new List<string>(m_agentHandlers.Keys);
|
||||||
}
|
// }
|
||||||
|
|
||||||
public bool AddLLSDHandler(string path, LLSDMethod handler)
|
public bool AddLLSDHandler(string path, LLSDMethod handler)
|
||||||
{
|
{
|
||||||
|
@ -296,6 +311,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
|
|
||||||
private void OnRequest(object source, RequestEventArgs args)
|
private void OnRequest(object source, RequestEventArgs args)
|
||||||
{
|
{
|
||||||
|
RequestNumber++;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
IHttpClientContext context = (IHttpClientContext)source;
|
IHttpClientContext context = (IHttpClientContext)source;
|
||||||
|
@ -405,7 +422,6 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
string requestMethod = request.HttpMethod;
|
string requestMethod = request.HttpMethod;
|
||||||
string uriString = request.RawUrl;
|
string uriString = request.RawUrl;
|
||||||
|
|
||||||
// string reqnum = "unknown";
|
|
||||||
int requestStartTick = Environment.TickCount;
|
int requestStartTick = Environment.TickCount;
|
||||||
|
|
||||||
// Will be adjusted later on.
|
// Will be adjusted later on.
|
||||||
|
@ -420,24 +436,24 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
|
// 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);
|
//m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
|
||||||
|
|
||||||
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true);
|
Culture.SetCurrentCulture();
|
||||||
|
|
||||||
// This is the REST agent interface. We require an agent to properly identify
|
// // 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
|
// // 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
|
// // 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
|
// // 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
|
// // probability event; if a request is matched it is normally expected to be
|
||||||
// handled
|
// // handled
|
||||||
IHttpAgentHandler agentHandler;
|
// IHttpAgentHandler agentHandler;
|
||||||
|
//
|
||||||
if (TryGetAgentHandler(request, response, out agentHandler))
|
// if (TryGetAgentHandler(request, response, out agentHandler))
|
||||||
{
|
// {
|
||||||
if (HandleAgentRequest(agentHandler, request, response))
|
// if (HandleAgentRequest(agentHandler, request, response))
|
||||||
{
|
// {
|
||||||
requestEndTick = Environment.TickCount;
|
// requestEndTick = Environment.TickCount;
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
//response.KeepAlive = true;
|
//response.KeepAlive = true;
|
||||||
response.SendChunked = false;
|
response.SendChunked = false;
|
||||||
|
@ -449,9 +465,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
if (TryGetStreamHandler(handlerKey, out requestHandler))
|
if (TryGetStreamHandler(handlerKey, out requestHandler))
|
||||||
{
|
{
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToStreamHandler(request, requestHandler);
|
||||||
"[BASE HTTP SERVER]: Found stream handler for {0} {1} {2} {3}",
|
|
||||||
request.HttpMethod, request.Url.PathAndQuery, requestHandler.Name, requestHandler.Description);
|
|
||||||
|
|
||||||
response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
|
response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
|
||||||
|
|
||||||
|
@ -528,11 +542,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
{
|
{
|
||||||
case null:
|
case null:
|
||||||
case "text/html":
|
case "text/html":
|
||||||
|
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToContentTypeHandler(request);
|
||||||
"[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
|
|
||||||
request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
|
|
||||||
|
|
||||||
buffer = HandleHTTPRequest(request, response);
|
buffer = HandleHTTPRequest(request, response);
|
||||||
break;
|
break;
|
||||||
|
@ -540,11 +551,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
case "application/llsd+xml":
|
case "application/llsd+xml":
|
||||||
case "application/xml+llsd":
|
case "application/xml+llsd":
|
||||||
case "application/llsd+json":
|
case "application/llsd+json":
|
||||||
|
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToContentTypeHandler(request);
|
||||||
"[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
|
|
||||||
request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
|
|
||||||
|
|
||||||
buffer = HandleLLSDRequests(request, response);
|
buffer = HandleLLSDRequests(request, response);
|
||||||
break;
|
break;
|
||||||
|
@ -563,9 +571,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
if (DoWeHaveALLSDHandler(request.RawUrl))
|
if (DoWeHaveALLSDHandler(request.RawUrl))
|
||||||
{
|
{
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToContentTypeHandler(request);
|
||||||
"[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
|
|
||||||
request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
|
|
||||||
|
|
||||||
buffer = HandleLLSDRequests(request, response);
|
buffer = HandleLLSDRequests(request, response);
|
||||||
}
|
}
|
||||||
|
@ -573,18 +579,14 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
else if (DoWeHaveAHTTPHandler(request.RawUrl))
|
else if (DoWeHaveAHTTPHandler(request.RawUrl))
|
||||||
{
|
{
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToContentTypeHandler(request);
|
||||||
"[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
|
|
||||||
request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
|
|
||||||
|
|
||||||
buffer = HandleHTTPRequest(request, response);
|
buffer = HandleHTTPRequest(request, response);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (DebugLevel >= 3)
|
if (DebugLevel >= 3)
|
||||||
m_log.DebugFormat(
|
LogIncomingToXmlRpcHandler(request);
|
||||||
"[BASE HTTP SERVER]: Assuming a generic XMLRPC request for {0} {1}",
|
|
||||||
request.HttpMethod, request.Url.PathAndQuery);
|
|
||||||
|
|
||||||
// generic login request.
|
// generic login request.
|
||||||
buffer = HandleXmlRpcRequests(request, response);
|
buffer = HandleXmlRpcRequests(request, response);
|
||||||
|
@ -643,14 +645,93 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
if (tickdiff > 3000)
|
if (tickdiff > 3000)
|
||||||
{
|
{
|
||||||
m_log.InfoFormat(
|
m_log.InfoFormat(
|
||||||
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} from {4} took {5}ms",
|
"[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
|
||||||
|
RequestNumber,
|
||||||
requestMethod,
|
requestMethod,
|
||||||
uriString,
|
uriString,
|
||||||
requestHandler != null ? requestHandler.Name : "",
|
requestHandler != null ? requestHandler.Name : "",
|
||||||
requestHandler != null ? requestHandler.Description : "",
|
requestHandler != null ? requestHandler.Description : "",
|
||||||
request.RemoteIPEndPoint.ToString(),
|
request.RemoteIPEndPoint,
|
||||||
tickdiff);
|
tickdiff);
|
||||||
}
|
}
|
||||||
|
else if (DebugLevel >= 4)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms",
|
||||||
|
RequestNumber,
|
||||||
|
Port,
|
||||||
|
tickdiff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
|
||||||
|
{
|
||||||
|
m_log.DebugFormat(
|
||||||
|
"[BASE HTTP SERVER]: 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(
|
||||||
|
"[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
|
||||||
|
RequestNumber,
|
||||||
|
Port,
|
||||||
|
(request.ContentType == null || 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(
|
||||||
|
"[BASE HTTP SERVER]: 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)
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8))
|
||||||
|
{
|
||||||
|
string output;
|
||||||
|
|
||||||
|
if (DebugLevel == 5)
|
||||||
|
{
|
||||||
|
const int sampleLength = 80;
|
||||||
|
char[] sampleChars = new char[sampleLength + 3];
|
||||||
|
reader.Read(sampleChars, 0, sampleLength);
|
||||||
|
sampleChars[80] = '.';
|
||||||
|
sampleChars[81] = '.';
|
||||||
|
sampleChars[82] = '.';
|
||||||
|
output = new string(sampleChars);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_log.DebugFormat("[BASE HTTP SERVER]: {0}", output.Replace("\n", @"\n"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -746,24 +827,24 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
|
// private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
|
||||||
{
|
// {
|
||||||
agentHandler = null;
|
// agentHandler = null;
|
||||||
|
//
|
||||||
lock (m_agentHandlers)
|
// lock (m_agentHandlers)
|
||||||
{
|
// {
|
||||||
foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
|
// foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
|
||||||
{
|
// {
|
||||||
if (handler.Match(request, response))
|
// if (handler.Match(request, response))
|
||||||
{
|
// {
|
||||||
agentHandler = handler;
|
// agentHandler = handler;
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try all the registered xmlrpc handlers when an xmlrpc request is received.
|
/// Try all the registered xmlrpc handlers when an xmlrpc request is received.
|
||||||
|
@ -1201,59 +1282,6 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
map["login"] = OSD.FromString("false");
|
map["login"] = OSD.FromString("false");
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
/// <summary>
|
|
||||||
/// A specific agent handler was provided. Such a handler is expecetd to have an
|
|
||||||
/// intimate, and highly specific relationship with the client. Consequently,
|
|
||||||
/// nothing is done here.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handler"></param>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <param name="response"></param>
|
|
||||||
|
|
||||||
private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response)
|
|
||||||
{
|
|
||||||
// In the case of REST, then handler is responsible for ALL aspects of
|
|
||||||
// the request/response handling. Nothing is done here, not even encoding.
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return handler.Handle(request, response);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
// If the handler did in fact close the stream, then this will blow
|
|
||||||
// chunks. So that that doesn't disturb anybody we throw away any
|
|
||||||
// and all exceptions raised. We've done our best to release the
|
|
||||||
// client.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_log.Warn("[HTTP-AGENT]: Error - " + e.Message);
|
|
||||||
response.SendChunked = false;
|
|
||||||
response.KeepAlive = true;
|
|
||||||
response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError;
|
|
||||||
//response.OutputStream.Close();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
response.Send();
|
|
||||||
//response.FreeContext();
|
|
||||||
}
|
|
||||||
catch (SocketException f)
|
|
||||||
{
|
|
||||||
// This has to be here to prevent a Linux/Mono crash
|
|
||||||
m_log.Warn(
|
|
||||||
String.Format("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux. ", f.Message), f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Indicate that the request has been "handled"
|
|
||||||
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
|
public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
|
||||||
{
|
{
|
||||||
|
@ -1688,21 +1716,21 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
m_pollHandlers.Remove(path);
|
m_pollHandlers.Remove(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
|
// public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
|
||||||
{
|
// {
|
||||||
lock (m_agentHandlers)
|
// lock (m_agentHandlers)
|
||||||
{
|
// {
|
||||||
IHttpAgentHandler foundHandler;
|
// IHttpAgentHandler foundHandler;
|
||||||
|
//
|
||||||
if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
|
// if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
|
||||||
{
|
// {
|
||||||
m_agentHandlers.Remove(agent);
|
// m_agentHandlers.Remove(agent);
|
||||||
return true;
|
// return true;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
return false;
|
// return false;
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void RemoveXmlRPCHandler(string method)
|
public void RemoveXmlRPCHandler(string method)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,10 +41,10 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
uint Port { get; }
|
uint Port { get; }
|
||||||
bool UseSSL { get; }
|
bool UseSSL { get; }
|
||||||
|
|
||||||
// Note that the agent string is provided simply to differentiate
|
// // Note that the agent string is provided simply to differentiate
|
||||||
// the handlers - it is NOT required to be an actual agent header
|
// // the handlers - it is NOT required to be an actual agent header
|
||||||
// value.
|
// // value.
|
||||||
bool AddAgentHandler(string agent, IHttpAgentHandler handler);
|
// bool AddAgentHandler(string agent, IHttpAgentHandler handler);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a handler for an HTTP request.
|
/// Add a handler for an HTTP request.
|
||||||
|
@ -106,13 +106,13 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
|
|
||||||
bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
|
bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
|
||||||
|
|
||||||
/// <summary>
|
// /// <summary>
|
||||||
/// Remove the agent if it is registered.
|
// /// Remove the agent if it is registered.
|
||||||
/// </summary>
|
// /// </summary>
|
||||||
/// <param name="agent"></param>
|
// /// <param name="agent"></param>
|
||||||
/// <param name="handler"></param>
|
// /// <param name="handler"></param>
|
||||||
/// <returns></returns>
|
// /// <returns></returns>
|
||||||
bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
|
// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Remove an HTTP handler
|
/// Remove an HTTP handler
|
||||||
|
|
|
@ -101,20 +101,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
using (WebResponse resp = request.GetResponse())
|
using (WebResponse resp = request.GetResponse())
|
||||||
{
|
{
|
||||||
XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
|
XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
|
||||||
Stream respStream = null;
|
|
||||||
try
|
using (Stream respStream = resp.GetResponseStream())
|
||||||
{
|
|
||||||
respStream = resp.GetResponseStream();
|
|
||||||
deserial = (TResponse)deserializer.Deserialize(respStream);
|
deserial = (TResponse)deserializer.Deserialize(respStream);
|
||||||
}
|
}
|
||||||
catch { }
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (respStream != null)
|
|
||||||
respStream.Close();
|
|
||||||
resp.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return deserial;
|
return deserial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
using log4net;
|
using log4net;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
using OpenSim.Framework.Console;
|
using OpenSim.Framework.Console;
|
||||||
|
@ -47,9 +48,12 @@ namespace OpenSim.Framework.Servers
|
||||||
/// Control the printing of certain debug messages.
|
/// Control the printing of certain debug messages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// If DebugLevel >= 1, then short warnings are logged when receiving bad input data.
|
/// 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 >= 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 >= 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.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static int DebugLevel
|
public static int DebugLevel
|
||||||
{
|
{
|
||||||
|
@ -101,17 +105,28 @@ namespace OpenSim.Framework.Servers
|
||||||
get { return new Dictionary<uint, BaseHttpServer>(m_Servers); }
|
get { return new Dictionary<uint, BaseHttpServer>(m_Servers); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void RegisterHttpConsoleCommands(ICommandConsole console)
|
public static void RegisterHttpConsoleCommands(ICommandConsole console)
|
||||||
{
|
{
|
||||||
console.Commands.AddCommand(
|
console.Commands.AddCommand(
|
||||||
"Debug", false, "debug http", "debug http [<level>]",
|
"Comms", false, "show http-handlers",
|
||||||
"Turn on inbound non-poll http request debugging.",
|
"show http-handlers",
|
||||||
"If level <= 0, then no extra logging is done.\n"
|
"Show all registered http handlers", HandleShowHttpHandlersCommand);
|
||||||
+ "If level >= 1, then short warnings are logged when receiving bad input data.\n"
|
|
||||||
+ "If level >= 2, then long warnings are logged when receiving bad input data.\n"
|
console.Commands.AddCommand(
|
||||||
+ "If level >= 3, then short notices about all incoming non-poll HTTP requests are logged.\n"
|
"Debug", false, "debug http", "debug http <in|out|all> [<level>]",
|
||||||
+ "If no level is specified then the current level is returned.",
|
"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 incoming data is logged.\n"
|
||||||
|
+ " level >= 6 then the entire incoming 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",
|
||||||
HandleDebugHttpCommand);
|
HandleDebugHttpCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,25 +134,123 @@ namespace OpenSim.Framework.Servers
|
||||||
/// Turn on some debugging values for OpenSim.
|
/// Turn on some debugging values for OpenSim.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args"></param>
|
/// <param name="args"></param>
|
||||||
private static void HandleDebugHttpCommand(string module, string[] args)
|
private static void HandleDebugHttpCommand(string module, string[] cmdparams)
|
||||||
{
|
{
|
||||||
if (args.Length == 3)
|
if (cmdparams.Length < 3)
|
||||||
{
|
{
|
||||||
int newDebug;
|
MainConsole.Instance.Output("Usage: debug http <in|out|all> 0..6");
|
||||||
if (int.TryParse(args[2], out newDebug))
|
return;
|
||||||
{
|
|
||||||
MainServer.DebugLevel = newDebug;
|
|
||||||
MainConsole.Instance.OutputFormat("Debug http level set to {0}", newDebug);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (args.Length == 2)
|
bool inReqs = false;
|
||||||
|
bool outReqs = false;
|
||||||
|
bool allReqs = false;
|
||||||
|
|
||||||
|
string subCommand = cmdparams[2];
|
||||||
|
|
||||||
|
if (subCommand.ToLower() == "in")
|
||||||
{
|
{
|
||||||
MainConsole.Instance.OutputFormat("Current debug http level is {0}", MainServer.DebugLevel);
|
inReqs = true;
|
||||||
|
}
|
||||||
|
else if (subCommand.ToLower() == "out")
|
||||||
|
{
|
||||||
|
outReqs = true;
|
||||||
|
}
|
||||||
|
else if (subCommand.ToLower() == "all")
|
||||||
|
{
|
||||||
|
allReqs = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MainConsole.Instance.Output("Usage: debug http 0..3");
|
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("* 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());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -0,0 +1,697 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) Contributors, http://opensimulator.org/
|
||||||
|
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the OpenSimulator Project nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 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.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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Console to be used for any command line output. Can be null, in which case there should be no output.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
|
||||||
|
/// </summary>
|
||||||
|
protected string m_version;
|
||||||
|
|
||||||
|
public ServerBase()
|
||||||
|
{
|
||||||
|
m_startuptime = DateTime.Now;
|
||||||
|
m_version = VersionInfo.Version;
|
||||||
|
EnhanceVersionInformation();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void CreatePIDFile(string 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details,
|
||||||
|
/// etc.).
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register common commands once m_console has been set if it is going to be set
|
||||||
|
/// </summary>
|
||||||
|
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 <level>",
|
||||||
|
"Set the console logging level for this session.", HandleSetLogLevel);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "config set",
|
||||||
|
"config set <section> <key> <value>",
|
||||||
|
"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 [<section>] [<key>]",
|
||||||
|
"Synonym for config show",
|
||||||
|
HandleConfig);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "config show",
|
||||||
|
"config show [<section>] [<key>]",
|
||||||
|
"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 <path>",
|
||||||
|
"Save current configuration to a file at the given path", HandleConfig);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "command-script",
|
||||||
|
"command-script <script>",
|
||||||
|
"Run a command script from file", HandleScript);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "show threads",
|
||||||
|
"show threads",
|
||||||
|
"Show thread status", HandleShow);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "threads abort",
|
||||||
|
"threads abort <thread-id>",
|
||||||
|
"Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "threads show",
|
||||||
|
"threads show",
|
||||||
|
"Show thread status. Synonym for \"show threads\"",
|
||||||
|
(string module, string[] args) => Notice(GetThreadsReport()));
|
||||||
|
|
||||||
|
m_console.Commands.AddCommand(
|
||||||
|
"General", false, "force gc",
|
||||||
|
"force gc",
|
||||||
|
"Manually invoke runtime garbage collection. For debugging purposes",
|
||||||
|
HandleForceGc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleForceGc(string module, string[] args)
|
||||||
|
{
|
||||||
|
Notice("Manually invoking runtime garbage collection");
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void HandleShow(string module, string[] cmd)
|
||||||
|
{
|
||||||
|
List<string> args = new List<string>(cmd);
|
||||||
|
|
||||||
|
args.RemoveAt(0);
|
||||||
|
|
||||||
|
string[] showParams = args.ToArray();
|
||||||
|
|
||||||
|
switch (showParams[0])
|
||||||
|
{
|
||||||
|
case "info":
|
||||||
|
ShowInfo();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "version":
|
||||||
|
Notice(GetVersionText());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "uptime":
|
||||||
|
Notice(GetUptimeReport());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "threads":
|
||||||
|
Notice(GetThreadsReport());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change and load configuration file data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="module"></param>
|
||||||
|
/// <param name="cmd"></param>
|
||||||
|
private void HandleConfig(string module, string[] cmd)
|
||||||
|
{
|
||||||
|
List<string> args = new List<string>(cmd);
|
||||||
|
args.RemoveAt(0);
|
||||||
|
string[] cmdparams = args.ToArray();
|
||||||
|
|
||||||
|
if (cmdparams.Length > 0)
|
||||||
|
{
|
||||||
|
string firstParam = cmdparams[0].ToLower();
|
||||||
|
|
||||||
|
switch (firstParam)
|
||||||
|
{
|
||||||
|
case "set":
|
||||||
|
if (cmdparams.Length < 4)
|
||||||
|
{
|
||||||
|
Notice("Syntax: config set <section> <key> <value>");
|
||||||
|
Notice("Example: config set ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IConfig c;
|
||||||
|
IConfigSource source = new IniConfigSource();
|
||||||
|
c = source.AddConfig(cmdparams[1]);
|
||||||
|
if (c != null)
|
||||||
|
{
|
||||||
|
string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
|
||||||
|
c.Set(cmdparams[2], _value);
|
||||||
|
Config.Merge(source);
|
||||||
|
|
||||||
|
Notice("In section [{0}], set {1} = {2}", c.Name, cmdparams[2], _value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "get":
|
||||||
|
case "show":
|
||||||
|
if (cmdparams.Length == 1)
|
||||||
|
{
|
||||||
|
foreach (IConfig config in Config.Configs)
|
||||||
|
{
|
||||||
|
Notice("[{0}]", config.Name);
|
||||||
|
string[] keys = config.GetKeys();
|
||||||
|
foreach (string key in keys)
|
||||||
|
Notice(" {0} = {1}", key, config.GetString(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (cmdparams.Length == 2 || cmdparams.Length == 3)
|
||||||
|
{
|
||||||
|
IConfig config = Config.Configs[cmdparams[1]];
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
Notice("Section \"{0}\" does not exist.",cmdparams[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (cmdparams.Length == 2)
|
||||||
|
{
|
||||||
|
Notice("[{0}]", config.Name);
|
||||||
|
foreach (string key in config.GetKeys())
|
||||||
|
Notice(" {0} = {1}", key, config.GetString(key));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Notice(
|
||||||
|
"config get {0} {1} : {2}",
|
||||||
|
cmdparams[1], cmdparams[2], config.GetString(cmdparams[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Notice("Syntax: config {0} [<section>] [<key>]", firstParam);
|
||||||
|
Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "save":
|
||||||
|
if (cmdparams.Length < 2)
|
||||||
|
{
|
||||||
|
Notice("Syntax: config save <path>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = cmdparams[1];
|
||||||
|
Notice("Saving configuration file: {0}", path);
|
||||||
|
|
||||||
|
if (Config is IniConfigSource)
|
||||||
|
{
|
||||||
|
IniConfigSource iniCon = (IniConfigSource)Config;
|
||||||
|
iniCon.Save(path);
|
||||||
|
}
|
||||||
|
else if (Config is XmlConfigSource)
|
||||||
|
{
|
||||||
|
XmlConfigSource xmlCon = (XmlConfigSource)Config;
|
||||||
|
xmlCon.Save(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleSetLogLevel(string module, string[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length != 4)
|
||||||
|
{
|
||||||
|
Notice("Usage: set log level <level>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == m_consoleAppender)
|
||||||
|
{
|
||||||
|
Notice("No appender named Console found (see the log4net config file for this executable)!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string rawLevel = cmd[3];
|
||||||
|
|
||||||
|
ILoggerRepository repository = LogManager.GetRepository();
|
||||||
|
Level consoleLevel = repository.LevelMap[rawLevel];
|
||||||
|
|
||||||
|
if (consoleLevel != null)
|
||||||
|
m_consoleAppender.Threshold = consoleLevel;
|
||||||
|
else
|
||||||
|
Notice(
|
||||||
|
"{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
|
||||||
|
rawLevel);
|
||||||
|
|
||||||
|
ShowLogLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ShowLogLevel()
|
||||||
|
{
|
||||||
|
Notice("Console log level is {0}", m_consoleAppender.Threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void HandleScript(string module, string[] parms)
|
||||||
|
{
|
||||||
|
if (parms.Length != 2)
|
||||||
|
{
|
||||||
|
Notice("Usage: command-script <path-to-script");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunCommandScript(parms[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Run an optional startup list of commands
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fileName"></param>
|
||||||
|
protected void RunCommandScript(string fileName)
|
||||||
|
{
|
||||||
|
if (m_console == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (File.Exists(fileName))
|
||||||
|
{
|
||||||
|
m_log.Info("[SERVER BASE]: Running " + fileName);
|
||||||
|
|
||||||
|
using (StreamReader readFile = File.OpenText(fileName))
|
||||||
|
{
|
||||||
|
string currentCommand;
|
||||||
|
while ((currentCommand = readFile.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
currentCommand = currentCommand.Trim();
|
||||||
|
if (!(currentCommand == ""
|
||||||
|
|| currentCommand.StartsWith(";")
|
||||||
|
|| currentCommand.StartsWith("//")
|
||||||
|
|| currentCommand.StartsWith("#")))
|
||||||
|
{
|
||||||
|
m_log.Info("[SERVER BASE]: Running '" + currentCommand + "'");
|
||||||
|
m_console.RunCommand(currentCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return a report about the uptime of this server
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string GetUptimeReport()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
|
||||||
|
sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
|
||||||
|
sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void ShowInfo()
|
||||||
|
{
|
||||||
|
Notice(GetVersionText());
|
||||||
|
Notice("Startup directory: " + m_startupDirectory);
|
||||||
|
if (null != m_consoleAppender)
|
||||||
|
Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enhance the version string with extra information if it's available.
|
||||||
|
/// </summary>
|
||||||
|
protected void EnhanceVersionInformation()
|
||||||
|
{
|
||||||
|
string buildVersion = string.Empty;
|
||||||
|
|
||||||
|
// The subversion information is deprecated and will be removed at a later date
|
||||||
|
// Add subversion revision information if available
|
||||||
|
// Try file "svn_revision" in the current directory first, then the .svn info.
|
||||||
|
// This allows to make the revision available in simulators not running from the source tree.
|
||||||
|
// FIXME: Making an assumption about the directory we're currently in - we do this all over the place
|
||||||
|
// elsewhere as well
|
||||||
|
string gitDir = "../.git/";
|
||||||
|
string gitRefPointerPath = gitDir + "HEAD";
|
||||||
|
|
||||||
|
string svnRevisionFileName = "svn_revision";
|
||||||
|
string svnFileName = ".svn/entries";
|
||||||
|
string manualVersionFileName = ".version";
|
||||||
|
string inputLine;
|
||||||
|
int strcmp;
|
||||||
|
|
||||||
|
if (File.Exists(manualVersionFileName))
|
||||||
|
{
|
||||||
|
using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
|
||||||
|
buildVersion = CommitFile.ReadLine();
|
||||||
|
|
||||||
|
m_version += buildVersion ?? "";
|
||||||
|
}
|
||||||
|
else if (File.Exists(gitRefPointerPath))
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("[SERVER BASE]: Found {0}", gitRefPointerPath);
|
||||||
|
|
||||||
|
string rawPointer = "";
|
||||||
|
|
||||||
|
using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
|
||||||
|
rawPointer = pointerFile.ReadLine();
|
||||||
|
|
||||||
|
// m_log.DebugFormat("[SERVER BASE]: rawPointer [{0}]", rawPointer);
|
||||||
|
|
||||||
|
Match m = Regex.Match(rawPointer, "^ref: (.+)$");
|
||||||
|
|
||||||
|
if (m.Success)
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("[SERVER BASE]: Matched [{0}]", m.Groups[1].Value);
|
||||||
|
|
||||||
|
string gitRef = m.Groups[1].Value;
|
||||||
|
string gitRefPath = gitDir + gitRef;
|
||||||
|
if (File.Exists(gitRefPath))
|
||||||
|
{
|
||||||
|
// m_log.DebugFormat("[SERVER BASE]: Found gitRefPath [{0}]", gitRefPath);
|
||||||
|
|
||||||
|
using (StreamReader refFile = File.OpenText(gitRefPath))
|
||||||
|
{
|
||||||
|
string gitHash = refFile.ReadLine();
|
||||||
|
m_version += gitHash.Substring(0, 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Remove the else logic when subversion mirror is no longer used
|
||||||
|
if (File.Exists(svnRevisionFileName))
|
||||||
|
{
|
||||||
|
StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
|
||||||
|
buildVersion = RevisionFile.ReadLine();
|
||||||
|
buildVersion.Trim();
|
||||||
|
RevisionFile.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
|
||||||
|
{
|
||||||
|
StreamReader EntriesFile = File.OpenText(svnFileName);
|
||||||
|
inputLine = EntriesFile.ReadLine();
|
||||||
|
while (inputLine != null)
|
||||||
|
{
|
||||||
|
// using the dir svn revision at the top of entries file
|
||||||
|
strcmp = String.Compare(inputLine, "dir");
|
||||||
|
if (strcmp == 0)
|
||||||
|
{
|
||||||
|
buildVersion = EntriesFile.ReadLine();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inputLine = EntriesFile.ReadLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EntriesFile.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected string GetVersionText()
|
||||||
|
{
|
||||||
|
return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get a report about the registered threads in this server.
|
||||||
|
/// </summary>
|
||||||
|
protected string GetThreadsReport()
|
||||||
|
{
|
||||||
|
// This should be a constant field.
|
||||||
|
string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
|
||||||
|
|
||||||
|
sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
|
||||||
|
|
||||||
|
int timeNow = Environment.TickCount & Int32.MaxValue;
|
||||||
|
|
||||||
|
sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
|
||||||
|
sb.Append(Environment.NewLine);
|
||||||
|
|
||||||
|
foreach (Watchdog.ThreadWatchdogInfo twi in threads)
|
||||||
|
{
|
||||||
|
Thread t = twi.Thread;
|
||||||
|
|
||||||
|
sb.AppendFormat(
|
||||||
|
reportFormat,
|
||||||
|
t.ManagedThreadId,
|
||||||
|
t.Name,
|
||||||
|
timeNow - twi.LastTick,
|
||||||
|
timeNow - twi.FirstTick,
|
||||||
|
t.Priority,
|
||||||
|
t.ThreadState);
|
||||||
|
|
||||||
|
sb.Append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.Append("\n");
|
||||||
|
|
||||||
|
// For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
|
||||||
|
// zero active threads.
|
||||||
|
int totalThreads = Process.GetCurrentProcess().Threads.Count;
|
||||||
|
if (totalThreads > 0)
|
||||||
|
sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
|
||||||
|
|
||||||
|
sb.Append("Main threadpool (excluding script engine pools)\n");
|
||||||
|
sb.Append(Util.GetThreadPoolReport());
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void HandleThreadsAbort(string module, string[] cmd)
|
||||||
|
{
|
||||||
|
if (cmd.Length != 3)
|
||||||
|
{
|
||||||
|
MainConsole.Instance.Output("Usage: threads abort <thread-id>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int threadId;
|
||||||
|
if (!int.TryParse(cmd[2], out threadId))
|
||||||
|
{
|
||||||
|
MainConsole.Instance.Output("ERROR: Thread id must be an integer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Watchdog.AbortThread(threadId))
|
||||||
|
MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
|
||||||
|
else
|
||||||
|
MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Console output is only possible if a console has been established.
|
||||||
|
/// That is something that cannot be determined within this class. So
|
||||||
|
/// all attempts to use the console MUST be verified.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="msg"></param>
|
||||||
|
protected void Notice(string msg)
|
||||||
|
{
|
||||||
|
if (m_console != null)
|
||||||
|
{
|
||||||
|
m_console.Output(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Console output is only possible if a console has been established.
|
||||||
|
/// That is something that cannot be determined within this class. So
|
||||||
|
/// all attempts to use the console MUST be verified.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="format"></param>
|
||||||
|
/// <param name="components"></param>
|
||||||
|
protected void Notice(string format, params object[] components)
|
||||||
|
{
|
||||||
|
if (m_console != null)
|
||||||
|
m_console.OutputFormat(format, components);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,11 +35,12 @@ using HttpServer;
|
||||||
using HttpServer.FormDecoders;
|
using HttpServer.FormDecoders;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using OpenSim.Framework.Servers.HttpServer;
|
using OpenSim.Framework.Servers.HttpServer;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Servers.Tests
|
namespace OpenSim.Framework.Servers.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class OSHttpTests
|
public class OSHttpTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
// we need an IHttpClientContext for our tests
|
// we need an IHttpClientContext for our tests
|
||||||
public class TestHttpClientContext: IHttpClientContext
|
public class TestHttpClientContext: IHttpClientContext
|
||||||
|
|
|
@ -29,11 +29,12 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Servers.Tests
|
namespace OpenSim.Framework.Servers.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class VersionInfoTests
|
public class VersionInfoTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestVersionLength()
|
public void TestVersionLength()
|
||||||
|
|
|
@ -30,7 +30,7 @@ namespace OpenSim
|
||||||
public class VersionInfo
|
public class VersionInfo
|
||||||
{
|
{
|
||||||
private const string VERSION_NUMBER = "0.7.4";
|
private const string VERSION_NUMBER = "0.7.4";
|
||||||
private const Flavour VERSION_FLAVOUR = Flavour.Dev;
|
private const Flavour VERSION_FLAVOUR = Flavour.Extended;
|
||||||
|
|
||||||
public enum Flavour
|
public enum Flavour
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,10 +35,12 @@ using OpenMetaverse;
|
||||||
namespace OpenSim.Framework
|
namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A dictionary for task inventory.
|
/// A dictionary containing task inventory items. Indexed by item UUID.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
/// This class is not thread safe. Callers must synchronize on Dictionary methods or Clone() this object before
|
/// This class is not thread safe. Callers must synchronize on Dictionary methods or Clone() this object before
|
||||||
/// iterating over it.
|
/// iterating over it.
|
||||||
|
/// </remarks>
|
||||||
public class TaskInventoryDictionary : Dictionary<UUID, TaskInventoryItem>,
|
public class TaskInventoryDictionary : Dictionary<UUID, TaskInventoryItem>,
|
||||||
ICloneable, IXmlSerializable
|
ICloneable, IXmlSerializable
|
||||||
{
|
{
|
||||||
|
|
|
@ -73,9 +73,6 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
private bool _ownerChanged = false;
|
private bool _ownerChanged = false;
|
||||||
|
|
||||||
// This used ONLY during copy. It can't be relied on at other times!
|
|
||||||
private bool _scriptRunning = true;
|
|
||||||
|
|
||||||
public UUID AssetID {
|
public UUID AssetID {
|
||||||
get {
|
get {
|
||||||
return _assetID;
|
return _assetID;
|
||||||
|
@ -353,14 +350,13 @@ namespace OpenSim.Framework
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ScriptRunning {
|
/// <summary>
|
||||||
get {
|
/// This used ONLY during copy. It can't be relied on at other times!
|
||||||
return _scriptRunning;
|
/// </summary>
|
||||||
}
|
/// <remarks>
|
||||||
set {
|
/// For true script running status, use IEntityInventory.TryGetScriptInstanceRunning() for now.
|
||||||
_scriptRunning = value;
|
/// </remarks>
|
||||||
}
|
public bool ScriptRunning { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
// See ICloneable
|
// See ICloneable
|
||||||
|
|
||||||
|
@ -388,6 +384,7 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
public TaskInventoryItem()
|
public TaskInventoryItem()
|
||||||
{
|
{
|
||||||
|
ScriptRunning = true;
|
||||||
CreationDate = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
CreationDate = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,16 +24,17 @@
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenMetaverse.StructuredData;
|
using OpenMetaverse.StructuredData;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class AgentCircuitDataTest
|
public class AgentCircuitDataTest : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private UUID AgentId;
|
private UUID AgentId;
|
||||||
private AvatarAppearance AvAppearance;
|
private AvatarAppearance AvAppearance;
|
||||||
|
|
|
@ -38,7 +38,7 @@ using Animation = OpenSim.Framework.Animation;
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class AnimationTests
|
public class AnimationTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private Animation anim1 = null;
|
private Animation anim1 = null;
|
||||||
private Animation anim2 = null;
|
private Animation anim2 = null;
|
||||||
|
|
|
@ -30,11 +30,12 @@ using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class AssetBaseTest
|
public class AssetBaseTest : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestContainsReferences()
|
public void TestContainsReferences()
|
||||||
|
|
|
@ -28,11 +28,12 @@
|
||||||
using System;
|
using System;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class CacheTests
|
public class CacheTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private Cache cache;
|
private Cache cache;
|
||||||
private UUID cacheItemUUID;
|
private UUID cacheItemUUID;
|
||||||
|
|
|
@ -26,11 +26,12 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LocationTest
|
public class LocationTest : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void locationRegionHandleRegionHandle()
|
public void locationRegionHandleRegionHandle()
|
||||||
|
|
|
@ -32,11 +32,12 @@ using OpenMetaverse.StructuredData;
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class MundaneFrameworkTests
|
public class MundaneFrameworkTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
private bool m_RegionSettingsOnSaveEventFired;
|
private bool m_RegionSettingsOnSaveEventFired;
|
||||||
private bool m_RegionLightShareDataOnSaveEventFired;
|
private bool m_RegionLightShareDataOnSaveEventFired;
|
||||||
|
@ -303,9 +304,5 @@ namespace OpenSim.Framework.Tests
|
||||||
Assert.That(Thread.CurrentThread.CurrentCulture.Name == ci.Name, "SetCurrentCulture failed to set thread culture to en-US");
|
Assert.That(Thread.CurrentThread.CurrentCulture.Name == ci.Name, "SetCurrentCulture failed to set thread culture to en-US");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,11 +31,12 @@ using NUnit.Framework;
|
||||||
using OpenMetaverse;
|
using OpenMetaverse;
|
||||||
using OpenMetaverse.StructuredData;
|
using OpenMetaverse.StructuredData;
|
||||||
using OpenSim.Framework;
|
using OpenSim.Framework;
|
||||||
|
using OpenSim.Tests.Common;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Tests
|
namespace OpenSim.Framework.Tests
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class PrimeNumberHelperTests
|
public class PrimeNumberHelperTests : OpenSimTestCase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGetPrime()
|
public void TestGetPrime()
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue