diff options
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs | 371 |
1 files changed, 249 insertions, 122 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs index a885b25..6e42b6c 100644 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs +++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs | |||
@@ -34,6 +34,7 @@ using System.Security.Cryptography; | |||
34 | using System.Text.RegularExpressions; | 34 | using System.Text.RegularExpressions; |
35 | using System.Collections.Generic; | 35 | using System.Collections.Generic; |
36 | using System.Collections.Specialized; | 36 | using System.Collections.Specialized; |
37 | using OpenSim.Framework; | ||
37 | using OpenSim.Framework.Servers; | 38 | using OpenSim.Framework.Servers; |
38 | using libsecondlife; | 39 | using libsecondlife; |
39 | using System.Xml; | 40 | using System.Xml; |
@@ -50,7 +51,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
50 | /// This structure is created on entry to the Handler | 51 | /// This structure is created on entry to the Handler |
51 | /// method and is disposed of upon return. It is part of | 52 | /// method and is disposed of upon return. It is part of |
52 | /// the plug-in infrastructure, rather than the functionally | 53 | /// the plug-in infrastructure, rather than the functionally |
53 | /// specifici REST handler, and fundamental changes to | 54 | /// specific REST handler, and fundamental changes to |
54 | /// this should be reflected in the Rest HandlerVersion. The | 55 | /// this should be reflected in the Rest HandlerVersion. The |
55 | /// object is instantiated, and may be extended by, any | 56 | /// object is instantiated, and may be extended by, any |
56 | /// given handler. See the inventory handler for an example | 57 | /// given handler. See the inventory handler for an example |
@@ -71,11 +72,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
71 | 72 | ||
72 | internal OSHttpRequest request = null; | 73 | internal OSHttpRequest request = null; |
73 | internal OSHttpResponse response = null; | 74 | internal OSHttpResponse response = null; |
75 | internal string qprefix = null; | ||
74 | 76 | ||
75 | // Request lifetime values | 77 | // Request lifetime values |
76 | 78 | ||
77 | internal NameValueCollection headers = null; | ||
78 | internal List<string> removed_headers = null; | ||
79 | internal byte[] buffer = null; | 79 | internal byte[] buffer = null; |
80 | internal string body = null; | 80 | internal string body = null; |
81 | internal string html = null; | 81 | internal string html = null; |
@@ -96,11 +96,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
96 | internal string hostname = "localhost"; | 96 | internal string hostname = "localhost"; |
97 | internal int port = 80; | 97 | internal int port = 80; |
98 | internal string prefix = Rest.UrlPathSeparator; | 98 | internal string prefix = Rest.UrlPathSeparator; |
99 | internal bool keepAlive = false; | ||
100 | internal bool chunked = false; | ||
99 | 101 | ||
100 | // Authentication related state | 102 | // Authentication related state |
101 | 103 | ||
102 | internal bool authenticated = false; | 104 | internal bool authenticated = false; |
103 | internal string scheme = Rest.AS_DIGEST; | 105 | // internal string scheme = Rest.AS_DIGEST; |
106 | // internal string scheme = Rest.AS_BASIC; | ||
107 | internal string scheme = null; | ||
104 | internal string realm = Rest.Realm; | 108 | internal string realm = Rest.Realm; |
105 | internal string domain = null; | 109 | internal string domain = null; |
106 | internal string nonce = null; | 110 | internal string nonce = null; |
@@ -148,13 +152,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
148 | private static Regex basicParms = new Regex("^\\s*(?:\\w+)\\s+(?<pval>\\S+)\\s*", | 152 | private static Regex basicParms = new Regex("^\\s*(?:\\w+)\\s+(?<pval>\\S+)\\s*", |
149 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | 153 | RegexOptions.Compiled | RegexOptions.IgnoreCase); |
150 | 154 | ||
151 | private static Regex digestParm1 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*\"(?<pval>\\S+)\"", | 155 | private static Regex digestParm1 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*\"(?<pval>[^\"]+)\"", |
152 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | 156 | RegexOptions.Compiled | RegexOptions.IgnoreCase); |
153 | 157 | ||
154 | private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)", | 158 | private static Regex digestParm2 = new Regex("\\s*(?<parm>\\w+)\\s*=\\s*(?<pval>[^\\p{P}\\s]+)", |
155 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | 159 | RegexOptions.Compiled | RegexOptions.IgnoreCase); |
156 | 160 | ||
157 | private static Regex reuserPass = new Regex("\\s*(?<user>\\w+)\\s*:\\s*(?<pass>\\S*)", | 161 | private static Regex reuserPass = new Regex("\\s*(?<user>[^:]+)\\s*:\\s*(?<pass>\\S*)", |
158 | RegexOptions.Compiled | RegexOptions.IgnoreCase); | 162 | RegexOptions.Compiled | RegexOptions.IgnoreCase); |
159 | 163 | ||
160 | // For efficiency, we create static instances of these objects | 164 | // For efficiency, we create static instances of these objects |
@@ -165,11 +169,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
165 | 169 | ||
166 | // Constructor | 170 | // Constructor |
167 | 171 | ||
168 | internal RequestData(OSHttpRequest p_request, OSHttpResponse p_response, string qprefix) | 172 | internal RequestData(OSHttpRequest p_request, OSHttpResponse p_response, string p_qprefix) |
169 | { | 173 | { |
170 | 174 | ||
171 | request = p_request; | 175 | request = p_request; |
172 | response = p_response; | 176 | response = p_response; |
177 | qprefix = p_qprefix; | ||
173 | 178 | ||
174 | sbuilder.Length = 0; | 179 | sbuilder.Length = 0; |
175 | 180 | ||
@@ -182,7 +187,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
182 | method = request.HttpMethod.ToLower(); | 187 | method = request.HttpMethod.ToLower(); |
183 | initUrl(); | 188 | initUrl(); |
184 | 189 | ||
185 | initParameters(qprefix.Length); | 190 | initParameters(p_qprefix.Length); |
186 | 191 | ||
187 | } | 192 | } |
188 | 193 | ||
@@ -254,11 +259,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
254 | } | 259 | } |
255 | 260 | ||
256 | // If we want a specific authentication mechanism, make sure | 261 | // If we want a specific authentication mechanism, make sure |
257 | // we get it. | 262 | // we get it. null indicates we don't care. non-null indicates |
263 | // a specific scheme requirement. | ||
258 | 264 | ||
259 | if (scheme != null && scheme.ToLower() != reqscheme) | 265 | if (scheme != null && scheme.ToLower() != reqscheme) |
260 | { | 266 | { |
261 | Rest.Log.DebugFormat("{0} Challenge reason: Required scheme not accepted", MsgId); | 267 | Rest.Log.DebugFormat("{0} Challenge reason: Requested scheme not acceptable", MsgId); |
262 | DoChallenge(); | 268 | DoChallenge(); |
263 | } | 269 | } |
264 | 270 | ||
@@ -268,15 +274,15 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
268 | 274 | ||
269 | switch (reqscheme) | 275 | switch (reqscheme) |
270 | { | 276 | { |
271 | case "digest" : | 277 | case "digest" : |
272 | Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId); | 278 | Rest.Log.DebugFormat("{0} Digest authentication offered", MsgId); |
273 | DoDigest(authdata); | 279 | DoDigest(authdata); |
274 | break; | 280 | break; |
275 | 281 | ||
276 | case "basic" : | 282 | case "basic" : |
277 | Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId); | 283 | Rest.Log.DebugFormat("{0} Basic authentication offered", MsgId); |
278 | DoBasic(authdata); | 284 | DoBasic(authdata); |
279 | break; | 285 | break; |
280 | } | 286 | } |
281 | 287 | ||
282 | // If the current header is invalid, then a challenge is still needed. | 288 | // If the current header is invalid, then a challenge is still needed. |
@@ -406,10 +412,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
406 | authparms.TryGetValue("uri", out authPrefix); | 412 | authparms.TryGetValue("uri", out authPrefix); |
407 | 413 | ||
408 | // There MUST be a nonce string present. We're not preserving any server | 414 | // There MUST be a nonce string present. We're not preserving any server |
409 | // side state and we can;t validate the MD5 unless the lcient returns it | 415 | // side state and we can't validate the MD5 unless the client returns it |
410 | // to us, as it should. | 416 | // to us, as it should. |
411 | 417 | ||
412 | if (!authparms.TryGetValue("nonce", out nonce)) | 418 | if (!authparms.TryGetValue("nonce", out nonce) || nonce == null) |
413 | { | 419 | { |
414 | Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId); | 420 | Rest.Log.WarnFormat("{0} Authentication failed: nonce missing", MsgId); |
415 | break; | 421 | break; |
@@ -457,26 +463,28 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
457 | 463 | ||
458 | cnonce = authparms["cnonce"]; | 464 | cnonce = authparms["cnonce"]; |
459 | 465 | ||
460 | if (!authparms.ContainsKey("nc")) | 466 | if (!authparms.TryGetValue("nc", out nck) || nck == null) |
461 | { | 467 | { |
462 | Rest.Log.WarnFormat("{0} Authentication failed: cnonce counter missing", MsgId); | 468 | Rest.Log.WarnFormat("{0} Authentication failed: cnonce counter missing", MsgId); |
463 | break; | 469 | break; |
464 | } | 470 | } |
465 | 471 | ||
466 | nck = authparms["nc"]; | 472 | Rest.Log.DebugFormat("{0} Comparing nonce indices", MsgId); |
467 | 473 | ||
468 | if (cntable.TryGetValue(cnonce, out ncl)) | 474 | if (cntable.TryGetValue(nonce, out ncl)) |
469 | { | 475 | { |
470 | if (Rest.Hex2Int(ncl) <= Rest.Hex2Int(nck)) | 476 | Rest.Log.DebugFormat("{0} nonce values: Verify that request({1}) > Reference({2})", MsgId, nck, ncl); |
477 | |||
478 | if (Rest.Hex2Int(ncl) >= Rest.Hex2Int(nck)) | ||
471 | { | 479 | { |
472 | Rest.Log.WarnFormat("{0} Authentication failed: bad cnonce counter", MsgId); | 480 | Rest.Log.WarnFormat("{0} Authentication failed: bad cnonce counter", MsgId); |
473 | break; | 481 | break; |
474 | } | 482 | } |
475 | cntable[cnonce] = nck; | 483 | cntable[nonce] = nck; |
476 | } | 484 | } |
477 | else | 485 | else |
478 | { | 486 | { |
479 | lock (cntable) cntable.Add(cnonce, nck); | 487 | lock (cntable) cntable.Add(nonce, nck); |
480 | } | 488 | } |
481 | 489 | ||
482 | } | 490 | } |
@@ -519,6 +527,22 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
519 | 527 | ||
520 | sbuilder.Length = 0; | 528 | sbuilder.Length = 0; |
521 | 529 | ||
530 | if (scheme == null || scheme == Rest.AS_BASIC) | ||
531 | { | ||
532 | |||
533 | sbuilder.Append(Rest.AS_BASIC); | ||
534 | |||
535 | if (realm != null) | ||
536 | { | ||
537 | sbuilder.Append(" realm=\""); | ||
538 | sbuilder.Append(realm); | ||
539 | sbuilder.Append("\""); | ||
540 | } | ||
541 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); | ||
542 | } | ||
543 | |||
544 | sbuilder.Length = 0; | ||
545 | |||
522 | if (scheme == null || scheme == Rest.AS_DIGEST) | 546 | if (scheme == null || scheme == Rest.AS_DIGEST) |
523 | { | 547 | { |
524 | 548 | ||
@@ -583,57 +607,135 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
583 | sbuilder.Append(Rest.CS_COMMA); | 607 | sbuilder.Append(Rest.CS_COMMA); |
584 | } | 608 | } |
585 | 609 | ||
586 | if (Rest.Domains.Count != 0) | 610 | // We don;t know the userid that will be used |
587 | { | 611 | // so we cannot make any authentication domain |
588 | sbuilder.Append("domain="); | 612 | // assumptions. So the prefix will determine |
589 | sbuilder.Append(Rest.CS_DQUOTE); | 613 | // this. |
590 | foreach (string dom in Rest.Domains.Values) | ||
591 | { | ||
592 | sbuilder.Append(dom); | ||
593 | sbuilder.Append(Rest.CS_SPACE); | ||
594 | } | ||
595 | if (sbuilder[sbuilder.Length-1] == Rest.C_SPACE) | ||
596 | { | ||
597 | sbuilder.Length = sbuilder.Length-1; | ||
598 | } | ||
599 | sbuilder.Append(Rest.CS_DQUOTE); | ||
600 | sbuilder.Append(Rest.CS_COMMA); | ||
601 | } | ||
602 | 614 | ||
603 | if (sbuilder[sbuilder.Length-1] == Rest.C_COMMA) | 615 | sbuilder.Append("domain="); |
604 | { | 616 | sbuilder.Append(Rest.CS_DQUOTE); |
605 | sbuilder.Length = sbuilder.Length-1; | 617 | sbuilder.Append(qprefix); |
606 | } | 618 | sbuilder.Append(Rest.CS_DQUOTE); |
607 | 619 | ||
608 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); | 620 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); |
609 | 621 | ||
610 | } | 622 | } |
611 | 623 | ||
612 | if (scheme == null || scheme == Rest.AS_BASIC) | 624 | } |
613 | { | ||
614 | 625 | ||
615 | sbuilder.Append(Rest.AS_BASIC); | 626 | /// <summary> |
627 | /// This method provides validation in support of the BASIC | ||
628 | /// authentication method. This is not normaly expected to be | ||
629 | /// used, but is included for completeness (and because I tried | ||
630 | /// it first). | ||
631 | /// </summary> | ||
616 | 632 | ||
617 | if (realm != null) | 633 | private bool Validate(string user, string pass) |
618 | { | 634 | { |
619 | sbuilder.Append(" realm=\""); | 635 | |
620 | sbuilder.Append(realm); | 636 | Rest.Log.DebugFormat("{0} Simple User Validation", MsgId); |
621 | sbuilder.Append("\""); | 637 | |
622 | } | 638 | // Both values are required |
623 | AddHeader(Rest.HttpHeaderWWWAuthenticate,sbuilder.ToString()); | 639 | |
624 | } | 640 | if (user == null || pass == null) |
641 | return false; | ||
642 | |||
643 | // Eliminate any leading or trailing spaces | ||
644 | user = user.Trim(); | ||
645 | |||
646 | return vetPassword(user, pass); | ||
625 | 647 | ||
626 | } | 648 | } |
627 | 649 | ||
628 | private bool Validate(string user, string pass) | 650 | /// <summary> |
651 | /// This mechanism is used by the digest authetnication mechanism | ||
652 | /// to return the user's password. In fact, because the OpenSim | ||
653 | /// user's passwords are already hashed, and the HTTP mechanism | ||
654 | /// does not supply an open password, the hashed passwords cannot | ||
655 | /// be used unless the cliemt has used the same salting mechanism | ||
656 | /// to has the password before using it in the authentication | ||
657 | /// algorithn. This is not inconceivable... | ||
658 | /// </summary> | ||
659 | |||
660 | private string getPassword(string user) | ||
629 | { | 661 | { |
630 | Rest.Log.DebugFormat("{0} Validating {1}:{2}", MsgId, user, pass); | 662 | |
631 | return user == "awebb" && pass == getPassword(user); | 663 | int x; |
664 | string first; | ||
665 | string last; | ||
666 | |||
667 | // Distinguish the parts, if necessary | ||
668 | |||
669 | if ((x=user.IndexOf(Rest.C_SPACE)) != -1) | ||
670 | { | ||
671 | first = user.Substring(0,x); | ||
672 | last = user.Substring(x+1); | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | first = user; | ||
677 | last = String.Empty; | ||
678 | } | ||
679 | |||
680 | UserProfileData udata = Rest.UserServices.GetUserProfile(first, last); | ||
681 | |||
682 | // If we don;t recognize the user id, perhaps it is god? | ||
683 | |||
684 | if (udata == null) | ||
685 | { | ||
686 | Rest.Log.DebugFormat("{0} Administrator", MsgId); | ||
687 | return Rest.GodKey; | ||
688 | } | ||
689 | else | ||
690 | { | ||
691 | Rest.Log.DebugFormat("{0} Normal User {1}", MsgId, user); | ||
692 | return udata.PasswordHash; | ||
693 | } | ||
694 | |||
632 | } | 695 | } |
633 | 696 | ||
634 | private string getPassword(string user) | 697 | /// <summary> |
698 | /// This is used by the BASIC authentication scheme to calculate | ||
699 | /// the double hash used by OpenSim to encode user's passwords. | ||
700 | /// It returns true, if the supplied password is actually correct. | ||
701 | /// If the specified user-id is not recognized, but the password | ||
702 | /// matches the God password, then this is accepted as an admin | ||
703 | /// session. | ||
704 | /// </summary> | ||
705 | |||
706 | private bool vetPassword(string user, string pass) | ||
635 | { | 707 | { |
636 | return Rest.GodKey; | 708 | |
709 | int x; | ||
710 | string HA1; | ||
711 | string first; | ||
712 | string last; | ||
713 | |||
714 | // Distinguish the parts, if necessary | ||
715 | |||
716 | if ((x=user.IndexOf(Rest.C_SPACE)) != -1) | ||
717 | { | ||
718 | first = user.Substring(0,x); | ||
719 | last = user.Substring(x+1); | ||
720 | } | ||
721 | else | ||
722 | { | ||
723 | first = user; | ||
724 | last = String.Empty; | ||
725 | } | ||
726 | |||
727 | UserProfileData udata = Rest.UserServices.GetUserProfile(first, last); | ||
728 | |||
729 | // If we don;t recognize the user id, perhaps it is god? | ||
730 | |||
731 | if (udata == null) | ||
732 | return pass == Rest.GodKey; | ||
733 | |||
734 | HA1 = HashToString(pass); | ||
735 | HA1 = HashToString(String.Format("{0}:{1}",HA1,udata.PasswordSalt)); | ||
736 | |||
737 | return (0 == sc.Compare(HA1, udata.PasswordHash)); | ||
738 | |||
637 | } | 739 | } |
638 | 740 | ||
639 | // Validate the request-digest | 741 | // Validate the request-digest |
@@ -773,11 +875,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
773 | if (reset) | 875 | if (reset) |
774 | { | 876 | { |
775 | buffer = null; | 877 | buffer = null; |
776 | body = null; | 878 | SendHtml(message); |
879 | body = html; | ||
777 | } | 880 | } |
778 | 881 | ||
779 | if (Rest.DEBUG) | 882 | if (Rest.DEBUG) |
780 | { | 883 | { |
884 | Rest.Log.DebugFormat("{0} Request Failure State Dump", MsgId); | ||
781 | Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme); | 885 | Rest.Log.DebugFormat("{0} Scheme = {1}", MsgId, scheme); |
782 | Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm); | 886 | Rest.Log.DebugFormat("{0} Realm = {1}", MsgId, realm); |
783 | Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain); | 887 | Rest.Log.DebugFormat("{0} Domain = {1}", MsgId, domain); |
@@ -828,10 +932,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
828 | { | 932 | { |
829 | 933 | ||
830 | Rest.Log.DebugFormat("{0} Generating Response", MsgId); | 934 | Rest.Log.DebugFormat("{0} Generating Response", MsgId); |
831 | 935 | Rest.Log.DebugFormat("{0} Method is {1}", MsgId, method); | |
832 | // Process any arbitrary headers collected | ||
833 | |||
834 | BuildHeaders(); | ||
835 | 936 | ||
836 | // A Head request can NOT have a body! | 937 | // A Head request can NOT have a body! |
837 | if (method != Rest.HEAD) | 938 | if (method != Rest.HEAD) |
@@ -839,6 +940,12 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
839 | 940 | ||
840 | Rest.Log.DebugFormat("{0} Response is not abbreviated", MsgId); | 941 | Rest.Log.DebugFormat("{0} Response is not abbreviated", MsgId); |
841 | 942 | ||
943 | // If the writer is non-null then we know that an XML | ||
944 | // data component exists. Flush and close the writer and | ||
945 | // then convert the result to the expected buffer format | ||
946 | // unless the request has already been failed for some | ||
947 | // reason. | ||
948 | |||
842 | if (writer != null) | 949 | if (writer != null) |
843 | { | 950 | { |
844 | Rest.Log.DebugFormat("{0} XML Response handler extension ENTRY", MsgId); | 951 | Rest.Log.DebugFormat("{0} XML Response handler extension ENTRY", MsgId); |
@@ -869,41 +976,57 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
869 | } | 976 | } |
870 | } | 977 | } |
871 | 978 | ||
979 | // OK, if the buffer contains something, regardless of how | ||
980 | // it got there, set various response headers accordingly. | ||
981 | |||
872 | if (buffer != null) | 982 | if (buffer != null) |
873 | { | 983 | { |
874 | Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId); | 984 | Rest.Log.DebugFormat("{0} Buffer-based entity", MsgId); |
875 | if (response.Headers.Get("Content-Encoding") == null) | ||
876 | response.ContentEncoding = encoding; | ||
877 | response.ContentLength64 = buffer.Length; | 985 | response.ContentLength64 = buffer.Length; |
878 | response.SendChunked = false; | ||
879 | response.KeepAlive = false; | ||
880 | } | 986 | } |
987 | else | ||
988 | { | ||
989 | response.ContentLength64 = 0; | ||
990 | } | ||
991 | |||
992 | if (response.Headers.Get("Content-Encoding") == null) | ||
993 | response.ContentEncoding = encoding; | ||
994 | |||
995 | response.SendChunked = chunked; | ||
996 | response.KeepAlive = keepAlive; | ||
881 | 997 | ||
882 | } | 998 | } |
883 | 999 | ||
884 | // Set the status code & description. If nothing | 1000 | // Set the status code & description. If nothing has been stored, |
885 | // has been stored, we consider that a success | 1001 | // we consider that a success. |
886 | 1002 | ||
887 | if (statusCode == 0) | 1003 | if (statusCode == 0) |
888 | { | 1004 | { |
889 | Complete(); | 1005 | Complete(); |
890 | } | 1006 | } |
891 | 1007 | ||
1008 | // Set the response code in the actual carrier | ||
1009 | |||
892 | response.StatusCode = statusCode; | 1010 | response.StatusCode = statusCode; |
893 | 1011 | ||
894 | if (response.StatusCode == (int)OSHttpStatusCode.RedirectMovedTemporarily || | 1012 | // For a redirect we need to set the relocation header accordingly |
895 | response.StatusCode == (int)OSHttpStatusCode.RedirectMovedPermanently) | 1013 | |
1014 | if (response.StatusCode == (int) Rest.HttpStatusCodeTemporaryRedirect || | ||
1015 | response.StatusCode == (int) Rest.HttpStatusCodePermanentRedirect) | ||
896 | { | 1016 | { |
1017 | Rest.Log.DebugFormat("{0} Re-direct location is {1}", MsgId, redirectLocation); | ||
897 | response.RedirectLocation = redirectLocation; | 1018 | response.RedirectLocation = redirectLocation; |
898 | } | 1019 | } |
899 | 1020 | ||
1021 | // And include the status description if provided. | ||
1022 | |||
900 | if (statusDescription != null) | 1023 | if (statusDescription != null) |
901 | { | 1024 | { |
1025 | Rest.Log.DebugFormat("{0} Status description is {1}", MsgId, statusDescription); | ||
902 | response.StatusDescription = statusDescription; | 1026 | response.StatusDescription = statusDescription; |
903 | } | 1027 | } |
904 | 1028 | ||
905 | // Finally we send back our response, consuming | 1029 | // Finally we send back our response. |
906 | // any exceptions that doing so might produce. | ||
907 | 1030 | ||
908 | // We've left the setting of handled' until the | 1031 | // We've left the setting of handled' until the |
909 | // last minute because the header settings included | 1032 | // last minute because the header settings included |
@@ -913,6 +1036,14 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
913 | 1036 | ||
914 | handled = true; | 1037 | handled = true; |
915 | 1038 | ||
1039 | DumpHeaders(); | ||
1040 | |||
1041 | // if (request.InputStream != null) | ||
1042 | // { | ||
1043 | // Rest.Log.DebugFormat("{0} Closing input stream", MsgId); | ||
1044 | // request.InputStream.Close(); | ||
1045 | // } | ||
1046 | |||
916 | if (buffer != null && buffer.Length != 0) | 1047 | if (buffer != null && buffer.Length != 0) |
917 | { | 1048 | { |
918 | Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>", | 1049 | Rest.Log.DebugFormat("{0} Entity buffer, length = {1} : <{2}>", |
@@ -920,12 +1051,10 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
920 | response.OutputStream.Write(buffer, 0, buffer.Length); | 1051 | response.OutputStream.Write(buffer, 0, buffer.Length); |
921 | } | 1052 | } |
922 | 1053 | ||
923 | response.OutputStream.Close(); | 1054 | // Closing the outputstream should complete the transmission process |
924 | 1055 | ||
925 | if (request.InputStream != null) | 1056 | Rest.Log.DebugFormat("{0} Closing output stream", MsgId); |
926 | { | 1057 | response.OutputStream.Close(); |
927 | request.InputStream.Close(); | ||
928 | } | ||
929 | 1058 | ||
930 | } | 1059 | } |
931 | 1060 | ||
@@ -935,19 +1064,23 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
935 | 1064 | ||
936 | } | 1065 | } |
937 | 1066 | ||
938 | // Add a header to the table. If the header | 1067 | // Add a header to the table. We need to allow |
939 | // already exists, it is replaced. | 1068 | // multiple instances of many of the headers. |
1069 | // If the | ||
940 | 1070 | ||
941 | internal void AddHeader(string hdr, string data) | 1071 | internal void AddHeader(string hdr, string data) |
942 | { | 1072 | { |
943 | 1073 | if (Rest.DEBUG) | |
944 | if (headers == null) | ||
945 | { | 1074 | { |
946 | headers = new NameValueCollection(); | 1075 | Rest.Log.DebugFormat("{0} Adding header: <{1}: {2}>", |
1076 | MsgId, hdr, data); | ||
1077 | if (response.Headers.Get(hdr) != null) | ||
1078 | { | ||
1079 | Rest.Log.DebugFormat("{0} Multipe {1} headers will be generated>", | ||
1080 | MsgId, hdr); | ||
1081 | } | ||
947 | } | 1082 | } |
948 | 1083 | response.Headers.Add(hdr, data); | |
949 | headers[hdr] = data; | ||
950 | |||
951 | } | 1084 | } |
952 | 1085 | ||
953 | // Keep explicit track of any headers which | 1086 | // Keep explicit track of any headers which |
@@ -955,43 +1088,30 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
955 | 1088 | ||
956 | internal void RemoveHeader(string hdr) | 1089 | internal void RemoveHeader(string hdr) |
957 | { | 1090 | { |
958 | 1091 | if (Rest.DEBUG) | |
959 | if (removed_headers == null) | ||
960 | { | ||
961 | removed_headers = new List<string>(); | ||
962 | } | ||
963 | |||
964 | removed_headers.Add(hdr); | ||
965 | |||
966 | if (headers != null) | ||
967 | { | 1092 | { |
968 | headers.Remove(hdr); | 1093 | Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, hdr); |
1094 | if (response.Headers.Get(hdr) == null) | ||
1095 | { | ||
1096 | Rest.Log.DebugFormat("{0} No such header existed", | ||
1097 | MsgId, hdr); | ||
1098 | } | ||
969 | } | 1099 | } |
970 | 1100 | response.Headers.Remove(hdr); | |
971 | } | 1101 | } |
972 | 1102 | ||
973 | // Should it prove necessary, we could always | 1103 | /// <summary> |
974 | // restore the header collection from a cloned | 1104 | /// Dump headers that will be generated in the response |
975 | // copy, but for now we'll assume that that is | 1105 | /// </summary> |
976 | // not necessary. | ||
977 | 1106 | ||
978 | private void BuildHeaders() | 1107 | internal void DumpHeaders() |
979 | { | 1108 | { |
980 | if (removed_headers != null) | 1109 | if (Rest.DEBUG) |
981 | { | ||
982 | foreach (string h in removed_headers) | ||
983 | { | ||
984 | Rest.Log.DebugFormat("{0} Removing header: <{1}>", MsgId, h); | ||
985 | response.Headers.Remove(h); | ||
986 | } | ||
987 | } | ||
988 | if (headers!= null) | ||
989 | { | 1110 | { |
990 | for (int i = 0; i < headers.Count; i++) | 1111 | for (int i=0;i<response.Headers.Count;i++) |
991 | { | 1112 | { |
992 | Rest.Log.DebugFormat("{0} Adding header: <{1}: {2}>", | 1113 | Rest.Log.DebugFormat("{0} Header[{1}] : {2}", MsgId, i, |
993 | MsgId, headers.GetKey(i), headers.Get(i)); | 1114 | response.Headers.Get(i)); |
994 | response.Headers.Add(headers.GetKey(i), headers.Get(i)); | ||
995 | } | 1115 | } |
996 | } | 1116 | } |
997 | } | 1117 | } |
@@ -1019,7 +1139,6 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1019 | path = uri.AbsolutePath; | 1139 | path = uri.AbsolutePath; |
1020 | if (path.EndsWith(Rest.UrlPathSeparator)) | 1140 | if (path.EndsWith(Rest.UrlPathSeparator)) |
1021 | path = path.Substring(0,path.Length-1); | 1141 | path = path.Substring(0,path.Length-1); |
1022 | path = Uri.UnescapeDataString(path); | ||
1023 | } | 1142 | } |
1024 | 1143 | ||
1025 | // If we succeeded in getting a path, perform any | 1144 | // If we succeeded in getting a path, perform any |
@@ -1040,6 +1159,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1040 | pathNodes = EmptyPath; | 1159 | pathNodes = EmptyPath; |
1041 | } | 1160 | } |
1042 | 1161 | ||
1162 | // Elimiate any %-escaped values. This is left until here | ||
1163 | // so that escaped "+' are not mistakenly replaced. | ||
1164 | |||
1165 | path = Uri.UnescapeDataString(path); | ||
1166 | |||
1043 | // Request server context info | 1167 | // Request server context info |
1044 | 1168 | ||
1045 | hostname = uri.Host; | 1169 | hostname = uri.Host; |
@@ -1149,14 +1273,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
1149 | 1273 | ||
1150 | internal void initXmlReader() | 1274 | internal void initXmlReader() |
1151 | { | 1275 | { |
1276 | |||
1152 | XmlReaderSettings settings = new XmlReaderSettings(); | 1277 | XmlReaderSettings settings = new XmlReaderSettings(); |
1278 | |||
1153 | settings.ConformanceLevel = ConformanceLevel.Fragment; | 1279 | settings.ConformanceLevel = ConformanceLevel.Fragment; |
1154 | settings.IgnoreComments = true; | 1280 | settings.IgnoreComments = true; |
1155 | settings.IgnoreWhitespace = true; | 1281 | settings.IgnoreWhitespace = true; |
1156 | settings.IgnoreProcessingInstructions = true; | 1282 | settings.IgnoreProcessingInstructions = true; |
1157 | settings.ValidationType = ValidationType.None; | 1283 | settings.ValidationType = ValidationType.None; |
1158 | // reader = XmlReader.Create(new StringReader(entity),settings); | 1284 | |
1159 | reader = XmlReader.Create(request.InputStream,settings); | 1285 | reader = XmlReader.Create(request.InputStream,settings); |
1286 | |||
1160 | } | 1287 | } |
1161 | 1288 | ||
1162 | private void Flush() | 1289 | private void Flush() |