aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs')
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RequestData.cs371
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;
34using System.Text.RegularExpressions; 34using System.Text.RegularExpressions;
35using System.Collections.Generic; 35using System.Collections.Generic;
36using System.Collections.Specialized; 36using System.Collections.Specialized;
37using OpenSim.Framework;
37using OpenSim.Framework.Servers; 38using OpenSim.Framework.Servers;
38using libsecondlife; 39using libsecondlife;
39using System.Xml; 40using 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()