Rest.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. *
  27. */
  28. using System;
  29. using System.Collections.Generic;
  30. using System.Reflection;
  31. using System.Text;
  32. using log4net;
  33. using Nini.Config;
  34. using OpenSim.Framework;
  35. using OpenSim.Framework.Communications;
  36. using OpenSim.Services.Interfaces;
  37. using IUserService = OpenSim.Framework.Communications.IUserService;
  38. namespace OpenSim.ApplicationPlugins.Rest.Inventory
  39. {
  40. public class Rest
  41. {
  42. internal static readonly ILog Log =
  43. LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  44. internal static bool DEBUG = Log.IsDebugEnabled;
  45. /// <summary>
  46. /// Supported authentication schemes
  47. /// </summary>
  48. public const string AS_BASIC = "Basic"; // simple user/password verification
  49. public const string AS_DIGEST = "Digest"; // password safe authentication
  50. /// Supported Digest algorithms
  51. public const string Digest_MD5 = "MD5"; // assumed default if omitted
  52. public const string Digest_MD5Sess = "MD5-sess"; // session-span - not good for REST?
  53. public const string Qop_Auth = "auth"; // authentication only
  54. public const string Qop_Int = "auth-int"; // TODO
  55. /// <summary>
  56. /// These values have a single value for the whole
  57. /// domain and lifetime of the plugin handler. We
  58. /// make them static for ease of reference within
  59. /// the assembly. These are initialized by the
  60. /// RestHandler class during start-up.
  61. /// </summary>
  62. internal static IRestHandler Plugin = null;
  63. internal static OpenSimBase main = null;
  64. internal static string Prefix = null;
  65. internal static IConfig Config = null;
  66. internal static string GodKey = null;
  67. internal static bool Authenticate = true;
  68. internal static bool Secure = true;
  69. internal static bool ExtendedEscape = true;
  70. internal static bool DumpAsset = false;
  71. internal static bool Fill = true;
  72. internal static bool FlushEnabled = true;
  73. internal static string Realm = "OpenSim REST";
  74. internal static string Scheme = AS_BASIC;
  75. internal static int DumpLineSize = 32; // Should be a multiple of 16 or (possibly) 4
  76. /// <summary>
  77. /// These are all dependent upon the Comms manager
  78. /// being initialized. So they have to be properties
  79. /// because the comms manager is now a module and is
  80. /// not guaranteed to be there when the rest handler
  81. /// initializes.
  82. /// </summary>
  83. internal static CommunicationsManager Comms
  84. {
  85. get { return main.CommunicationsManager; }
  86. }
  87. internal static IInventoryService InventoryServices
  88. {
  89. get { return main.SceneManager.CurrentOrFirstScene.InventoryService; }
  90. }
  91. internal static IUserService UserServices
  92. {
  93. get { return Comms.UserService; }
  94. }
  95. internal static IAvatarService AvatarServices
  96. {
  97. get { return Comms.AvatarService; }
  98. }
  99. internal static IAssetService AssetServices
  100. {
  101. get { return main.SceneManager.CurrentOrFirstScene.AssetService; }
  102. }
  103. /// <summary>
  104. /// HTTP requires that status information be generated for PUT
  105. /// and POST opertaions. This is in support of that. The
  106. /// operation verb gets substituted into the first string,
  107. /// and the completion code is inserted into the tail. The
  108. /// strings are put here to encourage consistency.
  109. /// </summary>
  110. internal static string statusHead = "<html><body><title>{0} status</title><break>";
  111. internal static string statusTail = "</body></html>";
  112. internal static Dictionary<int,string> HttpStatusDesc;
  113. static Rest()
  114. {
  115. HttpStatusDesc = new Dictionary<int,string>();
  116. if (HttpStatusCodeArray.Length != HttpStatusDescArray.Length)
  117. {
  118. Log.ErrorFormat("{0} HTTP Status Code and Description arrays do not match");
  119. throw new Exception("HTTP Status array discrepancy");
  120. }
  121. // Repackage the data into something more tractable. The sparse
  122. // nature of HTTP return codes makes an array a bad choice.
  123. for (int i=0; i<HttpStatusCodeArray.Length; i++)
  124. {
  125. HttpStatusDesc.Add(HttpStatusCodeArray[i], HttpStatusDescArray[i]);
  126. }
  127. }
  128. internal static int CreationDate
  129. {
  130. get { return (int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds; }
  131. }
  132. internal static string MsgId
  133. {
  134. get { return Plugin.MsgId; }
  135. }
  136. internal static string RequestId
  137. {
  138. get { return Plugin.RequestId; }
  139. }
  140. internal static Encoding Encoding = Util.UTF8;
  141. /// <summary>
  142. /// Version control for REST implementation. This
  143. /// refers to the overall infrastructure represented
  144. /// by the following classes
  145. /// RequestData
  146. /// RequestInventoryPlugin
  147. /// Rest
  148. /// It does no describe implementation classes such as
  149. /// RestInventoryServices, which may morph much more
  150. /// often. Such classes ARE dependent upon this however
  151. /// and should check it in their Initialize method.
  152. /// </summary>
  153. public static readonly float Version = 1.0F;
  154. public const string Name = "REST 1.0";
  155. /// <summary>
  156. /// Currently defined HTTP methods.
  157. /// Only GET and HEAD are required to be
  158. /// supported by all servers. See Respond
  159. /// to see how these are handled.
  160. /// </summary>
  161. // REST AGENT 1.0 interpretations
  162. public const string GET = "get"; // information retrieval - server state unchanged
  163. public const string HEAD = "head"; // same as get except only the headers are returned.
  164. public const string POST = "post"; // Replace the URI designated resource with the entity.
  165. public const string PUT = "put"; // Add the entity to the context represented by the URI
  166. public const string DELETE = "delete"; // Remove the URI designated resource from the server.
  167. public const string OPTIONS = "options"; //
  168. public const string TRACE = "trace"; //
  169. public const string CONNECT = "connect"; //
  170. // Define this in one place...
  171. public const string UrlPathSeparator = "/";
  172. public const string UrlMethodSeparator = ":";
  173. // Redirection qualifications
  174. public const bool PERMANENT = false;
  175. public const bool TEMPORARY = true;
  176. // Constant arrays used by String.Split
  177. public static readonly char C_SPACE = ' ';
  178. public static readonly char C_SLASH = '/';
  179. public static readonly char C_PATHSEP = '/';
  180. public static readonly char C_COLON = ':';
  181. public static readonly char C_PLUS = '+';
  182. public static readonly char C_PERIOD = '.';
  183. public static readonly char C_COMMA = ',';
  184. public static readonly char C_DQUOTE = '"';
  185. public static readonly string CS_SPACE = " ";
  186. public static readonly string CS_SLASH = "/";
  187. public static readonly string CS_PATHSEP = "/";
  188. public static readonly string CS_COLON = ":";
  189. public static readonly string CS_PLUS = "+";
  190. public static readonly string CS_PERIOD = ".";
  191. public static readonly string CS_COMMA = ",";
  192. public static readonly string CS_DQUOTE = "\"";
  193. public static readonly char[] CA_SPACE = { C_SPACE };
  194. public static readonly char[] CA_SLASH = { C_SLASH };
  195. public static readonly char[] CA_PATHSEP = { C_PATHSEP };
  196. public static readonly char[] CA_COLON = { C_COLON };
  197. public static readonly char[] CA_PERIOD = { C_PERIOD };
  198. public static readonly char[] CA_PLUS = { C_PLUS };
  199. public static readonly char[] CA_COMMA = { C_COMMA };
  200. public static readonly char[] CA_DQUOTE = { C_DQUOTE };
  201. // HTTP Code Values (in value order)
  202. public const int HttpStatusCodeContinue = 100;
  203. public const int HttpStatusCodeSwitchingProtocols = 101;
  204. public const int HttpStatusCodeOK = 200;
  205. public const int HttpStatusCodeCreated = 201;
  206. public const int HttpStatusCodeAccepted = 202;
  207. public const int HttpStatusCodeNonAuthoritative = 203;
  208. public const int HttpStatusCodeNoContent = 204;
  209. public const int HttpStatusCodeResetContent = 205;
  210. public const int HttpStatusCodePartialContent = 206;
  211. public const int HttpStatusCodeMultipleChoices = 300;
  212. public const int HttpStatusCodePermanentRedirect = 301;
  213. public const int HttpStatusCodeFound = 302;
  214. public const int HttpStatusCodeSeeOther = 303;
  215. public const int HttpStatusCodeNotModified = 304;
  216. public const int HttpStatusCodeUseProxy = 305;
  217. public const int HttpStatusCodeReserved306 = 306;
  218. public const int HttpStatusCodeTemporaryRedirect = 307;
  219. public const int HttpStatusCodeBadRequest = 400;
  220. public const int HttpStatusCodeNotAuthorized = 401;
  221. public const int HttpStatusCodePaymentRequired = 402;
  222. public const int HttpStatusCodeForbidden = 403;
  223. public const int HttpStatusCodeNotFound = 404;
  224. public const int HttpStatusCodeMethodNotAllowed = 405;
  225. public const int HttpStatusCodeNotAcceptable = 406;
  226. public const int HttpStatusCodeProxyAuthenticate = 407;
  227. public const int HttpStatusCodeTimeOut = 408;
  228. public const int HttpStatusCodeConflict = 409;
  229. public const int HttpStatusCodeGone = 410;
  230. public const int HttpStatusCodeLengthRequired = 411;
  231. public const int HttpStatusCodePreconditionFailed = 412;
  232. public const int HttpStatusCodeEntityTooLarge = 413;
  233. public const int HttpStatusCodeUriTooLarge = 414;
  234. public const int HttpStatusCodeUnsupportedMedia = 415;
  235. public const int HttpStatusCodeRangeNotSatsified = 416;
  236. public const int HttpStatusCodeExpectationFailed = 417;
  237. public const int HttpStatusCodeServerError = 500;
  238. public const int HttpStatusCodeNotImplemented = 501;
  239. public const int HttpStatusCodeBadGateway = 502;
  240. public const int HttpStatusCodeServiceUnavailable = 503;
  241. public const int HttpStatusCodeGatewayTimeout = 504;
  242. public const int HttpStatusCodeHttpVersionError = 505;
  243. public static readonly int[] HttpStatusCodeArray = {
  244. HttpStatusCodeContinue,
  245. HttpStatusCodeSwitchingProtocols,
  246. HttpStatusCodeOK,
  247. HttpStatusCodeCreated,
  248. HttpStatusCodeAccepted,
  249. HttpStatusCodeNonAuthoritative,
  250. HttpStatusCodeNoContent,
  251. HttpStatusCodeResetContent,
  252. HttpStatusCodePartialContent,
  253. HttpStatusCodeMultipleChoices,
  254. HttpStatusCodePermanentRedirect,
  255. HttpStatusCodeFound,
  256. HttpStatusCodeSeeOther,
  257. HttpStatusCodeNotModified,
  258. HttpStatusCodeUseProxy,
  259. HttpStatusCodeReserved306,
  260. HttpStatusCodeTemporaryRedirect,
  261. HttpStatusCodeBadRequest,
  262. HttpStatusCodeNotAuthorized,
  263. HttpStatusCodePaymentRequired,
  264. HttpStatusCodeForbidden,
  265. HttpStatusCodeNotFound,
  266. HttpStatusCodeMethodNotAllowed,
  267. HttpStatusCodeNotAcceptable,
  268. HttpStatusCodeProxyAuthenticate,
  269. HttpStatusCodeTimeOut,
  270. HttpStatusCodeConflict,
  271. HttpStatusCodeGone,
  272. HttpStatusCodeLengthRequired,
  273. HttpStatusCodePreconditionFailed,
  274. HttpStatusCodeEntityTooLarge,
  275. HttpStatusCodeUriTooLarge,
  276. HttpStatusCodeUnsupportedMedia,
  277. HttpStatusCodeRangeNotSatsified,
  278. HttpStatusCodeExpectationFailed,
  279. HttpStatusCodeServerError,
  280. HttpStatusCodeNotImplemented,
  281. HttpStatusCodeBadGateway,
  282. HttpStatusCodeServiceUnavailable,
  283. HttpStatusCodeGatewayTimeout,
  284. HttpStatusCodeHttpVersionError
  285. };
  286. // HTTP Status Descriptions (in status code order)
  287. // This array must be kept strictly consistent with respect
  288. // to the status code array above.
  289. public static readonly string[] HttpStatusDescArray = {
  290. "Continue Request",
  291. "Switching Protocols",
  292. "OK",
  293. "CREATED",
  294. "ACCEPTED",
  295. "NON-AUTHORITATIVE INFORMATION",
  296. "NO CONTENT",
  297. "RESET CONTENT",
  298. "PARTIAL CONTENT",
  299. "MULTIPLE CHOICES",
  300. "PERMANENT REDIRECT",
  301. "FOUND",
  302. "SEE OTHER",
  303. "NOT MODIFIED",
  304. "USE PROXY",
  305. "RESERVED CODE 306",
  306. "TEMPORARY REDIRECT",
  307. "BAD REQUEST",
  308. "NOT AUTHORIZED",
  309. "PAYMENT REQUIRED",
  310. "FORBIDDEN",
  311. "NOT FOUND",
  312. "METHOD NOT ALLOWED",
  313. "NOT ACCEPTABLE",
  314. "PROXY AUTHENTICATION REQUIRED",
  315. "TIMEOUT",
  316. "CONFLICT",
  317. "GONE",
  318. "LENGTH REQUIRED",
  319. "PRECONDITION FAILED",
  320. "ENTITY TOO LARGE",
  321. "URI TOO LARGE",
  322. "UNSUPPORTED MEDIA",
  323. "RANGE NOT SATISFIED",
  324. "EXPECTATION FAILED",
  325. "SERVER ERROR",
  326. "NOT IMPLEMENTED",
  327. "BAD GATEWAY",
  328. "SERVICE UNAVAILABLE",
  329. "GATEWAY TIMEOUT",
  330. "HTTP VERSION NOT SUPPORTED"
  331. };
  332. // HTTP Headers
  333. public const string HttpHeaderAccept = "Accept";
  334. public const string HttpHeaderAcceptCharset = "Accept-Charset";
  335. public const string HttpHeaderAcceptEncoding = "Accept-Encoding";
  336. public const string HttpHeaderAcceptLanguage = "Accept-Language";
  337. public const string HttpHeaderAcceptRanges = "Accept-Ranges";
  338. public const string HttpHeaderAge = "Age";
  339. public const string HttpHeaderAllow = "Allow";
  340. public const string HttpHeaderAuthorization = "Authorization";
  341. public const string HttpHeaderCacheControl = "Cache-Control";
  342. public const string HttpHeaderConnection = "Connection";
  343. public const string HttpHeaderContentEncoding = "Content-Encoding";
  344. public const string HttpHeaderContentLanguage = "Content-Language";
  345. public const string HttpHeaderContentLength = "Content-Length";
  346. public const string HttpHeaderContentLocation = "Content-Location";
  347. public const string HttpHeaderContentMD5 = "Content-MD5";
  348. public const string HttpHeaderContentRange = "Content-Range";
  349. public const string HttpHeaderContentType = "Content-Type";
  350. public const string HttpHeaderDate = "Date";
  351. public const string HttpHeaderETag = "ETag";
  352. public const string HttpHeaderExpect = "Expect";
  353. public const string HttpHeaderExpires = "Expires";
  354. public const string HttpHeaderFrom = "From";
  355. public const string HttpHeaderHost = "Host";
  356. public const string HttpHeaderIfMatch = "If-Match";
  357. public const string HttpHeaderIfModifiedSince = "If-Modified-Since";
  358. public const string HttpHeaderIfNoneMatch = "If-None-Match";
  359. public const string HttpHeaderIfRange = "If-Range";
  360. public const string HttpHeaderIfUnmodifiedSince = "If-Unmodified-Since";
  361. public const string HttpHeaderLastModified = "Last-Modified";
  362. public const string HttpHeaderLocation = "Location";
  363. public const string HttpHeaderMaxForwards = "Max-Forwards";
  364. public const string HttpHeaderPragma = "Pragma";
  365. public const string HttpHeaderProxyAuthenticate = "Proxy-Authenticate";
  366. public const string HttpHeaderProxyAuthorization = "Proxy-Authorization";
  367. public const string HttpHeaderRange = "Range";
  368. public const string HttpHeaderReferer = "Referer";
  369. public const string HttpHeaderRetryAfter = "Retry-After";
  370. public const string HttpHeaderServer = "Server";
  371. public const string HttpHeaderTE = "TE";
  372. public const string HttpHeaderTrailer = "Trailer";
  373. public const string HttpHeaderTransferEncoding = "Transfer-Encoding";
  374. public const string HttpHeaderUpgrade = "Upgrade";
  375. public const string HttpHeaderUserAgent = "User-Agent";
  376. public const string HttpHeaderVary = "Vary";
  377. public const string HttpHeaderVia = "Via";
  378. public const string HttpHeaderWarning = "Warning";
  379. public const string HttpHeaderWWWAuthenticate = "WWW-Authenticate";
  380. /// Utility routines
  381. public static string StringToBase64(string str)
  382. {
  383. try
  384. {
  385. byte[] encData_byte = new byte[str.Length];
  386. encData_byte = Util.UTF8.GetBytes(str);
  387. return Convert.ToBase64String(encData_byte);
  388. }
  389. catch
  390. {
  391. return String.Empty;
  392. }
  393. }
  394. public static string Base64ToString(string str)
  395. {
  396. try
  397. {
  398. return Util.Base64ToString(str);
  399. }
  400. catch
  401. {
  402. return String.Empty;
  403. }
  404. }
  405. private const string hvals = "0123456789abcdef";
  406. public static int Hex2Int(string hex)
  407. {
  408. int val = 0;
  409. int sum = 0;
  410. string tmp = null;
  411. if (hex != null)
  412. {
  413. tmp = hex.ToLower();
  414. for (int i = 0; i < tmp.Length; i++)
  415. {
  416. val = hvals.IndexOf(tmp[i]);
  417. if (val == -1)
  418. break;
  419. sum *= 16;
  420. sum += val;
  421. }
  422. }
  423. return sum;
  424. }
  425. // Nonce management
  426. public static string NonceGenerator()
  427. {
  428. return StringToBase64(CreationDate + Guid.NewGuid().ToString());
  429. }
  430. // Dump the specified data stream
  431. public static void Dump(byte[] data)
  432. {
  433. char[] buffer = new char[DumpLineSize];
  434. int cc = 0;
  435. for (int i = 0; i < data.Length; i++)
  436. {
  437. if (i % DumpLineSize == 0) Console.Write("\n{0}: ",i.ToString("d8"));
  438. if (i % 4 == 0) Console.Write(" ");
  439. Console.Write("{0}",data[i].ToString("x2"));
  440. if (data[i] < 127 && data[i] > 31)
  441. buffer[i % DumpLineSize] = (char) data[i];
  442. else
  443. buffer[i % DumpLineSize] = '.';
  444. cc++;
  445. if (i != 0 && (i + 1) % DumpLineSize == 0)
  446. {
  447. Console.Write(" |"+(new String(buffer))+"|");
  448. cc = 0;
  449. }
  450. }
  451. // Finish off any incomplete line
  452. if (cc != 0)
  453. {
  454. for (int i = cc ; i < DumpLineSize; i++)
  455. {
  456. if (i % 4 == 0) Console.Write(" ");
  457. Console.Write(" ");
  458. buffer[i % DumpLineSize] = ' ';
  459. }
  460. Console.WriteLine(" |"+(new String(buffer))+"|");
  461. }
  462. else
  463. {
  464. Console.Write("\n");
  465. }
  466. }
  467. }
  468. // Local exception type
  469. public class RestException : Exception
  470. {
  471. internal int statusCode;
  472. internal string statusDesc;
  473. internal string httpmethod;
  474. internal string httppath;
  475. public RestException(string msg) : base(msg)
  476. {
  477. }
  478. }
  479. }