Merge pull request #1505 from esdrubal/tzifloatingrule
[mono.git] / mcs / class / System.Web / System.Web / HttpRequest.cs
1 //
2 // System.Web.HttpRequest.cs 
3 //
4 // 
5 // Author:
6 //      Miguel de Icaza (miguel@novell.com)
7 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
8 //      Marek Habersack <mhabersack@novell.com>
9 //
10
11 //
12 // Copyright (C) 2005-2010 Novell, Inc (http://www.novell.com)
13 // Copyright (C) 2011-2012 Xamarin, Inc (http://xamarin.com)
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34 using System.Text;
35 using System.Collections;
36 using System.Collections.Specialized;
37 using System.IO;
38 using System.Runtime.InteropServices;
39 using System.Security;
40 using System.Security.Permissions;
41 using System.Security.Principal;
42 using System.Web.Configuration;
43 using System.Web.Management;
44 using System.Web.UI;
45 using System.Web.Util;
46 using System.Globalization;
47
48 using System.Security.Authentication.ExtendedProtection;
49 using System.Web.Routing;
50
51 namespace System.Web
52 {       
53         // CAS - no InheritanceDemand here as the class is sealed
54         [AspNetHostingPermission (SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
55         public sealed partial class HttpRequest
56         {
57                 HttpWorkerRequest worker_request;
58                 HttpContext context;
59                 WebROCollection query_string_nvc;
60
61                 //
62                 //string filename;
63                 string orig_url = null;
64                 UriBuilder url_components;
65
66                 string client_target;
67
68                 //
69                 // On-demand computed values
70                 //
71                 HttpBrowserCapabilities browser_capabilities;
72                 string file_path, base_virtual_dir, root_virtual_dir, client_file_path;
73                 string content_type;
74                 int content_length = -1;
75                 Encoding encoding;
76                 string current_exe_path;
77                 string physical_path;
78                 string unescaped_path;
79                 string original_path;
80                 string path_info;
81                 string path_info_unvalidated;
82                 string raw_url;
83                 string raw_url_unvalidated;
84                 WebROCollection all_params;
85                 NameValueCollection headers;
86                 WebROCollection headers_unvalidated;
87                 Stream input_stream;
88                 InputFilterStream input_filter;
89                 Stream filter;
90                 HttpCookieCollection cookies;
91                 HttpCookieCollection cookies_unvalidated;
92                 string http_method;
93
94                 WebROCollection form;
95                 HttpFileCollection files;
96                 
97                 ServerVariablesCollection server_variables;
98                 HttpClientCertificate client_cert;
99                 
100                 string request_type;
101                 string [] accept_types;
102                 string [] user_languages;
103                 Uri cached_url;
104                 TempFileStream request_file;
105
106                 readonly static System.Net.IPAddress [] host_addresses;
107                 
108                 // Validations
109                 bool validate_cookies, validate_query_string, validate_form;
110                 bool checked_cookies, checked_query_string, checked_form;
111                 static readonly UrlMappingCollection urlMappings;
112                 readonly static char [] queryTrimChars = {'?'};
113                 bool lazyFormValidation;
114                 bool lazyQueryStringValidation;
115                 bool inputValidationEnabled;
116                 RequestContext requestContext;
117                 BufferlessInputStream bufferlessInputStream;
118                 
119                 static bool validateRequestNewMode;
120                 internal static bool ValidateRequestNewMode {
121                         get { return validateRequestNewMode; }
122                 }
123
124                 internal bool InputValidationEnabled {
125                         get { return inputValidationEnabled; }
126                 }
127                 
128                 private static char[] RequestPathInvalidCharacters {
129                         get; set;
130                 }
131
132                 private static char[] CharsFromList (string list)
133                 {
134                         // List format is very strict and enforced by the Configuration 
135                         // there must be a single char separated by commas with no trailing comma
136                         // whitespace is allowed though and should be trimmed.
137                         
138                         string [] pieces = list.Split (',');
139
140                         char [] chars = new char [pieces.Length];
141                         for (int i = 0; i < chars.Length; i++) {
142                                 string trimmed = pieces [i].Trim ();
143                                 if (trimmed.Length != 1) {
144                                         // This should have been caught by System.Web.Configuration
145                                         // and throw a configuration error. This is just here for sanity
146                                         throw new System.Configuration.ConfigurationErrorsException ();
147                                 }
148
149                                 chars [i] = trimmed [0];
150                         }
151
152                         return chars;
153                 }
154
155                 static HttpRequest ()
156                 {
157                         try {
158                                 UrlMappingsSection ums = WebConfigurationManager.GetWebApplicationSection ("system.web/urlMappings") as UrlMappingsSection;
159                                 if (ums != null && ums.IsEnabled) {
160                                         urlMappings = ums.UrlMappings;
161                                         if (urlMappings.Count == 0)
162                                                 urlMappings = null;
163                                 }
164
165                                 Version validationMode = HttpRuntime.Section.RequestValidationMode;
166
167                                 if (validationMode >= new Version (4, 0)) {
168                                         validateRequestNewMode = true;
169                                         string invalidChars = HttpRuntime.Section.RequestPathInvalidCharacters;
170                                         if (!String.IsNullOrEmpty (invalidChars))
171                                                 RequestPathInvalidCharacters = CharsFromList (invalidChars);
172                                 }
173                         } catch {
174                                 // unlikely to happen
175                         }
176                         
177                         host_addresses = GetLocalHostAddresses ();
178                 }
179                 
180                 public HttpRequest (string filename, string url, string queryString)
181                 {
182                         // warning 169: what are we supposed to do with filename?
183                         
184                         //this.filename = filename;
185
186                         orig_url = url;
187                         url_components = new UriBuilder (url);
188                         url_components.Query = queryString;
189                         
190                         query_string_nvc = new WebROCollection ();
191                         if (queryString != null)
192                                 HttpUtility.ParseQueryString (queryString, Encoding.Default, query_string_nvc);
193                         query_string_nvc.Protect ();
194                 }
195
196                 internal HttpRequest (HttpWorkerRequest worker_request, HttpContext context)
197                 {
198                         this.worker_request = worker_request;
199                         this.context = context;
200                 }
201                 
202                 internal UriBuilder UrlComponents {
203                         get {
204                                 if (url_components == null) {
205                                         string query;
206                                         byte[] queryStringRaw = worker_request.GetQueryStringRawBytes();
207                                         if(queryStringRaw != null)
208                                                 query = ContentEncoding.GetString(queryStringRaw);
209                                         else
210                                                 query = worker_request.GetQueryString();
211                                         
212                                         BuildUrlComponents (ApplyUrlMapping (worker_request.GetUriPath ()), query);
213                                 }
214                                 return url_components;
215                         }
216                 }
217
218                 void BuildUrlComponents (string path, string query)
219                 {
220                         if (url_components != null)
221                                 return;
222                         url_components = new UriBuilder ();
223                         url_components.Scheme = worker_request.GetProtocol ();
224                         url_components.Host = worker_request.GetServerName ();
225                         url_components.Port = worker_request.GetLocalPort ();
226                         url_components.Path = path;
227                         if (query != null && query.Length > 0)
228                                 url_components.Query = query.TrimStart (queryTrimChars);
229                 }
230
231                 internal string ApplyUrlMapping (string url)
232                 {
233                         if (urlMappings == null)
234                                 return url;
235
236                         string relUrl = VirtualPathUtility.ToAppRelative (url);
237                         UrlMapping um = null;
238                         
239                         foreach (UrlMapping u in urlMappings) {
240                                 if (u == null)
241                                         continue;
242                                 if (String.Compare (relUrl, u.Url, StringComparison.Ordinal) == 0) {
243                                         um = u;
244                                         break;
245                                 }
246                         }
247
248                         if (um == null)
249                                 return url;
250
251                         string rawUrl = VirtualPathUtility.ToAbsolute (um.MappedUrl.Trim ());
252                         Uri newUrl = new Uri ("http://host.com" + rawUrl);
253
254                         if (url_components != null) {
255                                 url_components.Path = newUrl.AbsolutePath;
256                                 url_components.Query = newUrl.Query.TrimStart (queryTrimChars);
257                                 query_string_nvc = new WebROCollection ();
258                                 HttpUtility.ParseQueryString (newUrl.Query, Encoding.Default, query_string_nvc);
259                                 query_string_nvc.Protect ();
260                         } else
261                                 BuildUrlComponents (newUrl.AbsolutePath, newUrl.Query);
262
263                         return url_components.Path;
264                 }
265
266                 string [] SplitHeader (int header_index)
267                 {
268                         string [] result = null;
269                         string header = worker_request.GetKnownRequestHeader (header_index);
270                         if (header != null && header != "" && header.Trim () != "") {
271                                 result = header.Split (',');
272                                 for (int i = result.Length - 1; i >= 0; i--)
273                                         result [i] = result [i].Trim ();
274                         }
275                         return result;
276                 }
277
278                 public string [] AcceptTypes {
279                         get {
280                                 if (worker_request == null)
281                                         return null;
282
283                                 if (accept_types == null)
284                                         accept_types = SplitHeader (HttpWorkerRequest.HeaderAccept);
285
286                                 return accept_types;
287                         }
288                 }
289
290                 public WindowsIdentity LogonUserIdentity {
291                         get { throw new NotImplementedException (); }
292                 }
293                 
294                 string anonymous_id;
295                 public string AnonymousID {
296                         get {
297                                 return anonymous_id;
298                         }
299                         internal set {
300                                 anonymous_id = value;
301                         }
302                 }
303
304                 public string ApplicationPath {
305                         get {
306                                 if (worker_request == null)
307                                         return null;
308                                 return worker_request.GetAppPath ();
309                         }
310                 }
311
312                 public HttpBrowserCapabilities Browser {
313                         get {
314                                 if (browser_capabilities == null)
315                                         browser_capabilities = HttpCapabilitiesBase.BrowserCapabilitiesProvider.GetBrowserCapabilities (this);
316
317                                 return browser_capabilities;
318                         }
319
320                         set {
321                                 browser_capabilities = value;
322                         }
323                 }
324
325                 internal bool BrowserMightHaveSpecialWriter {
326                         get {
327                                 return (browser_capabilities != null 
328                                         || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
329                         }
330                 }
331
332                 internal bool BrowserMightHaveAdapters {
333                         get {
334                                 return (browser_capabilities != null 
335                                         || HttpApplicationFactory.AppBrowsersFiles.Length > 0);
336                         }
337                 }
338
339                 public HttpClientCertificate ClientCertificate {
340                         get {
341                                 if (client_cert == null)
342                                         client_cert = new HttpClientCertificate (worker_request);
343                                 return client_cert;
344                         }
345                 }
346
347                 static internal string GetParameter (string header, string attr)
348                 {
349                         int ap = header.IndexOf (attr);
350                         if (ap == -1)
351                                 return null;
352
353                         ap += attr.Length;
354                         if (ap >= header.Length)
355                                 return null;
356                         
357                         char ending = header [ap];
358                         if (ending != '"')
359                                 ending = ' ';
360                         
361                         int end = header.IndexOf (ending, ap+1);
362                         if (end == -1)
363                                 return (ending == '"') ? null : header.Substring (ap);
364
365                         return header.Substring (ap+1, end-ap-1);
366                 }
367
368                 public Encoding ContentEncoding {
369                         get {
370                                 if (encoding == null){
371                                         if (worker_request == null)
372                                                 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
373                                         
374                                         string content_type = ContentType;
375                                         string parameter = GetParameter (content_type, "; charset=");
376                                         if (parameter == null) {
377                                                 encoding = WebEncoding.RequestEncoding;
378                                         } else {
379                                                 try {
380                                                         // Do what the #1 web server does
381                                                         encoding = Encoding.GetEncoding (parameter);
382                                                 } catch {
383                                                         encoding = WebEncoding.RequestEncoding;
384                                                 }
385                                         }
386                                 }
387                                 return encoding;
388                         }
389
390                         set {
391                                 encoding = value;
392                         }
393                 }
394
395                 public int ContentLength {
396                         get {
397                                 if (content_length == -1){
398                                         if (worker_request == null)
399                                                 return 0;
400
401                                         string cl = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentLength);
402
403                                         if (cl != null) {
404                                                 try {
405                                                         content_length = Int32.Parse (cl);
406                                                 } catch { }
407                                         }
408                                 }
409
410                                 // content_length will still be < 0, but we know we gotta read from the client
411                                 if (content_length < 0)
412                                         return 0;
413
414                                 return content_length;
415                         }
416                 }
417
418                 public string ContentType {
419                         get {
420                                 if (content_type == null){
421                                         if (worker_request != null)
422                                                 content_type = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderContentType);
423
424                                         if (content_type == null)
425                                                 content_type = String.Empty;
426                                 }
427                                 
428                                 return content_type;
429                         }
430
431                         set {
432                                 content_type = value;
433                         }
434                 }
435
436                 internal HttpCookieCollection CookiesNoValidation {
437                         get {
438                                 if (cookies_unvalidated == null) {
439                                         if (worker_request == null) {
440                                                 cookies_unvalidated = new HttpCookieCollection ();
441                                         } else {
442                                                 string cookie_hv = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderCookie);
443                                                 cookies_unvalidated = new HttpCookieCollection (cookie_hv);
444                                         }
445                                 }
446
447                                 return cookies_unvalidated;
448                         }
449                 }
450
451                 public HttpCookieCollection Cookies {
452                         get {
453                                 if (cookies == null) {
454                                         cookies = CookiesNoValidation;
455                                 }
456
457                                 bool needValidation = validate_cookies;
458                                 needValidation |= validateRequestNewMode;
459                                 if (needValidation && !checked_cookies) {
460                                         // Setting this before calling the validator prevents
461                                         // possible endless recursion
462                                         checked_cookies = true;
463                                         ValidateCookieCollection (cookies);
464                                 }
465
466                                 return cookies;
467                         }
468
469                 }
470
471                 public string CurrentExecutionFilePath {
472                         get {
473                                 if (current_exe_path != null)
474                                         return current_exe_path;
475
476                                 return FilePath;
477                         }
478                 }
479                 public string CurrentExecutionFilePathExtension {
480                         get { return global::System.IO.Path.GetExtension (CurrentExecutionFilePath); }
481                 }
482                 public string AppRelativeCurrentExecutionFilePath {
483                         get {
484                                 return VirtualPathUtility.ToAppRelative (CurrentExecutionFilePath);
485                         }
486                 }
487
488                 public string FilePath {
489                         get {
490                                 if (worker_request == null)
491                                         return "/"; // required for 2.0
492
493                                 if (file_path == null)
494                                         file_path = UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
495
496                                 return file_path;
497                         }
498                 }
499
500                 internal string ClientFilePath {
501                         get {
502                                 if (client_file_path == null) {
503                                         if (worker_request == null)
504                                                 return "/";
505                                         
506                                         return UrlUtils.Canonic (ApplyUrlMapping (worker_request.GetFilePath ()));
507                                 }
508                                 
509                                 return client_file_path;
510                         }
511
512                         set {
513                                 if (value == null || value.Length == 0)
514                                         client_file_path = null;
515                                 else
516                                         client_file_path = value;
517                         }
518                 }
519                 
520                 internal string BaseVirtualDir {
521                         get {
522                                 if (base_virtual_dir == null){
523                                         base_virtual_dir = FilePath;
524                                         if (UrlUtils.HasSessionId (base_virtual_dir))
525                                                 base_virtual_dir = UrlUtils.RemoveSessionId (VirtualPathUtility.GetDirectory (base_virtual_dir), base_virtual_dir);
526                                         
527                                         int p = base_virtual_dir.LastIndexOf ('/');
528                                         if (p != -1) {
529                                                 if (p == 0)
530                                                         p = 1;
531                                                 base_virtual_dir = base_virtual_dir.Substring (0, p);
532                                         } else
533                                                 base_virtual_dir = "/";
534                                 }
535                                 return base_virtual_dir;
536                         }
537                 }
538                 
539                 public HttpFileCollection Files {
540                         get {
541                                 if (files == null) {
542                                         files = new HttpFileCollection ();
543                                         if ((worker_request != null) && IsContentType ("multipart/form-data", true)) {
544                                                 form = new WebROCollection ();
545                                                 LoadMultiPart ();
546                                                 form.Protect ();
547                                         }
548                                 }
549                                 return files;
550                         }
551                 }
552
553                 public Stream Filter {
554                         get {
555                                 if (filter != null)
556                                         return filter;
557
558                                 if (input_filter == null)
559                                         input_filter = new InputFilterStream ();
560
561                                 return input_filter;
562                         }
563
564                         set {
565                                 // This checks that get_ was called before.
566                                 if (input_filter == null)
567                                         throw new HttpException ("Invalid filter");
568
569                                 filter = value;
570                         }
571                 }
572
573                 // GetSubStream returns a 'copy' of the InputStream with Position set to 0.
574                 static Stream GetSubStream (Stream stream)
575                 {
576                         if (stream is IntPtrStream)
577                                 return new IntPtrStream (stream);
578
579                         if (stream is MemoryStream) {
580                                 MemoryStream other = (MemoryStream) stream;
581                                 return new MemoryStream (other.GetBuffer (), 0, (int) other.Length, false, true);
582                         }
583
584                         if (stream is TempFileStream) {
585                                 ((TempFileStream) stream).SavePosition ();
586                                 return stream;
587                         }
588
589                         throw new NotSupportedException ("The stream is " + stream.GetType ());
590                 }
591
592                 static void EndSubStream (Stream stream)
593                 {
594                         if (stream is TempFileStream) {
595                                 ((TempFileStream) stream).RestorePosition ();
596                         }
597                 }
598
599                 //
600                 // Loads the data on the form for multipart/form-data
601                 //
602                 void LoadMultiPart ()
603                 {
604                         string boundary = GetParameter (ContentType, "; boundary=");
605                         if (boundary == null)
606                                 return;
607
608                         Stream input = GetSubStream (InputStream);
609                         HttpMultipart multi_part = new HttpMultipart (input, boundary, ContentEncoding);
610
611                         HttpMultipart.Element e;
612                         while ((e = multi_part.ReadNextElement ()) != null) {
613                                 if (e.Filename == null){
614                                         byte [] copy = new byte [e.Length];
615                                 
616                                         input.Position = e.Start;
617                                         input.Read (copy, 0, (int) e.Length);
618
619                                         form.Add (e.Name, ContentEncoding.GetString (copy));
620                                 } else {
621                                         //
622                                         // We use a substream, as in 2.x we will support large uploads streamed to disk,
623                                         //
624                                         HttpPostedFile sub = new HttpPostedFile (e.Filename, e.ContentType, input, e.Start, e.Length);
625                                         files.AddFile (e.Name, sub);
626                                 }
627                         }
628                         EndSubStream (input);
629                 }
630
631                 //
632                 // Adds the key/value to the form, and sets the argumets to empty
633                 //
634                 void AddRawKeyValue (StringBuilder key, StringBuilder value)
635                 {
636                         string decodedKey = HttpUtility.UrlDecode (key.ToString (), ContentEncoding);
637                         form.Add (decodedKey,
638                                   HttpUtility.UrlDecode (value.ToString (), ContentEncoding));
639
640                         key.Length = 0;
641                         value.Length = 0;
642                 }
643
644                 //
645                 // Loads the form data from on a application/x-www-form-urlencoded post
646                 // 
647                 void LoadWwwForm ()
648                 {
649                         using (Stream input = GetSubStream (InputStream)) {
650                                 using (StreamReader s = new StreamReader (input, ContentEncoding)) {
651                                         StringBuilder key = new StringBuilder ();
652                                         StringBuilder value = new StringBuilder ();
653                                         int c;
654
655                                         while ((c = s.Read ()) != -1){
656                                                 if (c == '='){
657                                                         value.Length = 0;
658                                                         while ((c = s.Read ()) != -1){
659                                                                 if (c == '&'){
660                                                                         AddRawKeyValue (key, value);
661                                                                         break;
662                                                                 } else
663                                                                         value.Append ((char) c);
664                                                         }
665                                                         if (c == -1){
666                                                                 AddRawKeyValue (key, value);
667                                                                 return;
668                                                         }
669                                                 } else if (c == '&')
670                                                         AddRawKeyValue (key, value);
671                                                 else
672                                                         key.Append ((char) c);
673                                         }
674                                         if (c == -1)
675                                                 AddRawKeyValue (key, value);
676
677                                         EndSubStream (input);
678                                 }
679                         }
680                 }
681
682                 bool IsContentType (string ct, bool starts_with)
683                 {
684                         if (starts_with)
685                                 return StrUtils.StartsWith (ContentType, ct, true);
686
687                         return String.Compare (ContentType, ct, true, Helpers.InvariantCulture) == 0;
688                 }
689
690                 internal WebROCollection FormUnvalidated {
691                         get {
692                                 if (form == null){
693                                         form = new WebROCollection ();
694                                         files = new HttpFileCollection ();
695
696                                         if (IsContentType ("multipart/form-data", true))
697                                                 LoadMultiPart ();
698                                         else if (
699                                                 IsContentType ("application/x-www-form-urlencoded", true))
700                                                 LoadWwwForm ();
701
702                                         form.Protect ();
703                                 }
704
705                                 return form;
706                         }
707                 }
708                 
709                 public NameValueCollection Form {
710                         get {
711                                 NameValueCollection form = FormUnvalidated;
712                                 if (validateRequestNewMode && !checked_form) {
713                                         if (!lazyFormValidation) {
714                                                 // Setting this before calling the validator prevents
715                                                 // possible endless recursion
716                                                 checked_form = true;
717                                                 ValidateNameValueCollection ("Form", form, RequestValidationSource.Form);
718                                         }
719                                 } else
720                                         if (validate_form && !checked_form){
721                                                 checked_form = true;
722                                                 ValidateNameValueCollection ("Form", form);
723                                         }
724                                 
725                                 return form;
726                         }
727                 }
728
729                 internal NameValueCollection HeadersNoValidation {
730                         get {
731                                 if (headers_unvalidated == null) {
732                                         headers_unvalidated = new HeadersCollection (this);
733                                 }
734                         
735                                 return headers_unvalidated;
736                         }
737                 }
738
739                 public NameValueCollection Headers {
740                         get {
741                                 if (headers == null) {
742                                         headers = HeadersNoValidation;
743                                         if (validateRequestNewMode) {
744                                                 RequestValidator validator = RequestValidator.Current;
745                                                 int validationFailureIndex;
746
747                                                 foreach (string hkey in headers.AllKeys) {
748                                                         string value = headers [hkey];
749                                                         
750                                                         if (!validator.IsValidRequestString (HttpContext.Current, value, RequestValidationSource.Headers, hkey, out validationFailureIndex))
751                                                                 ThrowValidationException ("Headers", hkey, value);
752                                                 }
753                                         }
754                                 }
755                                 
756                                 return headers;
757                         }
758                 }
759
760                 public string HttpMethod {
761                         get {
762                                 if (http_method == null){
763                                         if (worker_request != null)
764                                                 http_method = worker_request.GetHttpVerbName ();
765                                         else
766                                                 http_method = "GET";
767                                 }
768                                 return http_method;
769                         }
770                 }
771
772                 void DoFilter (byte [] buffer)
773                 {
774                         if (input_filter == null || filter == null)
775                                 return;
776
777                         if (buffer.Length < 1024)
778                                 buffer = new byte [1024];
779
780                         // Replace the input with the filtered input
781                         input_filter.BaseStream = input_stream;
782                         MemoryStream ms = new MemoryStream ();
783                         while (true) {
784                                 int n = filter.Read (buffer, 0, buffer.Length);
785                                 if (n <= 0)
786                                         break;
787                                 ms.Write (buffer, 0, n);
788                         }
789                         // From now on input_stream has the filtered input
790                         input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
791                 }
792
793                 const int INPUT_BUFFER_SIZE = 32*1024;
794
795                 TempFileStream GetTempStream ()
796                 {
797                         string tempdir = AppDomain.CurrentDomain.SetupInformation.DynamicBase;
798                         TempFileStream f = null;
799                         string path;
800                         Random rnd = new Random ();
801                         int num;
802                         do {
803                                 num = rnd.Next ();
804                                 num++;
805                                 path = System.IO.Path.Combine (tempdir, "tmp" + num.ToString("x") + ".req");
806
807                                 try {
808                                         f = new TempFileStream (path);
809                                 } catch (SecurityException) {
810                                         // avoid an endless loop
811                                         throw;
812                                 } catch { }
813                         } while (f == null);
814
815                         return f;
816                 }
817
818                 void MakeInputStream ()
819                 {
820                         if (input_stream != null)
821                                 return;
822
823                         if (worker_request == null) {
824                                 input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
825                                 DoFilter (new byte [1024]);
826                                 return;
827                         }
828
829                         //
830                         // Use an unmanaged memory block as this might be a large
831                         // upload
832                         //
833                         int content_length = ContentLength;
834                         int content_length_kb = content_length / 1024;
835                         HttpRuntimeSection config = HttpRuntime.Section;
836                         if (content_length_kb > config.MaxRequestLength)
837                                 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
838
839                         int total = 0;
840                         byte [] buffer;
841                         buffer = worker_request.GetPreloadedEntityBody ();
842                         // we check the instance field 'content_length' here, not the local var.
843                         if (this.content_length <= 0 || worker_request.IsEntireEntityBodyIsPreloaded ()) {
844                                 if (buffer == null || content_length == 0) {
845                                         input_stream = new MemoryStream (new byte [0], 0, 0, false, true);
846                                 } else {
847                                         input_stream = new MemoryStream (buffer, 0, buffer.Length, false, true);
848                                 }
849                                 DoFilter (new byte [1024]);
850                                 return;
851                         }
852
853                         if (buffer != null)
854                                 total = buffer.Length;
855
856                         if (content_length > 0 && content_length_kb >= config.RequestLengthDiskThreshold) {
857                                 // Writes the request to disk
858                                 total = Math.Min (content_length, total);
859                                 request_file = GetTempStream ();
860                                 Stream output = request_file;
861                                 if (total > 0)
862                                         output.Write (buffer, 0, total);
863
864                                 if (total < content_length) {
865                                         buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
866                                         do {
867                                                 int n;
868                                                 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
869                                                 n = worker_request.ReadEntityBody (buffer, min);
870                                                 if (n <= 0)
871                                                         break;
872                                                 output.Write (buffer, 0, n);
873                                                 total += n;
874                                         } while (total < content_length);
875                                 }
876
877                                 request_file.SetReadOnly ();
878                                 input_stream = request_file;
879                         } else if (content_length > 0) {
880                                 // Buffers the request in an IntPtrStream
881                                 total = Math.Min (content_length, total);
882                                 IntPtr content = Marshal.AllocHGlobal (content_length);
883                                 if (content == (IntPtr) 0)
884                                         throw HttpException.NewWithCode (
885                                                 String.Format ("Not enough memory to allocate {0} bytes.", content_length),
886                                                 WebEventCodes.WebErrorOtherError);
887
888                                 if (total > 0)
889                                         Marshal.Copy (buffer, 0, content, total);
890
891                                 if (total < content_length) {
892                                         buffer = new byte [Math.Min (content_length, INPUT_BUFFER_SIZE)];
893                                         do {
894                                                 int n;
895                                                 int min = Math.Min (content_length - total, INPUT_BUFFER_SIZE);
896                                                 n = worker_request.ReadEntityBody (buffer, min);
897                                                 if (n <= 0)
898                                                         break;
899                                                 Marshal.Copy (buffer, 0, (IntPtr) ((long)content + total), n);
900                                                 total += n;
901                                         } while (total < content_length);
902                                 }
903
904                                 input_stream = new IntPtrStream (content, total);
905                         } else {
906                                 // Buffers the request in a MemoryStream or writes to disk if threshold exceeded
907                                 MemoryStream ms = new MemoryStream ();
908                                 Stream output = ms;
909                                 if (total > 0)
910                                         ms.Write (buffer, 0, total);
911
912                                 buffer = new byte [INPUT_BUFFER_SIZE];
913                                 long maxlength = config.MaxRequestLength * 1024L;
914                                 long disk_th = config.RequestLengthDiskThreshold * 1024L;
915                                 int n;
916                                 while (true) {
917                                         n = worker_request.ReadEntityBody (buffer, INPUT_BUFFER_SIZE);
918                                         if (n <= 0)
919                                                 break;
920                                         total += n;
921                                         if (total < 0 || total > maxlength)
922                                                 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
923
924                                         if (ms != null && total > disk_th) {
925                                                 // Swith to on-disk file.
926                                                 request_file = GetTempStream ();
927                                                 ms.WriteTo (request_file);
928                                                 ms = null;
929                                                 output = request_file;
930                                         }
931                                         output.Write (buffer, 0, n);
932                                 }
933
934                                 if (ms != null) {
935                                         input_stream = new MemoryStream (ms.GetBuffer (), 0, (int) ms.Length, false, true);
936                                 } else {
937                                         request_file.SetReadOnly ();
938                                         input_stream = request_file;
939                                 }
940                         }
941                         DoFilter (buffer);
942
943                         if (total < content_length)
944                                 throw HttpException.NewWithCode (411, "The request body is incomplete.", WebEventCodes.WebErrorOtherError);
945                 }
946
947                 internal void ReleaseResources ()
948                 {
949                         Stream stream;
950                         if (input_stream != null){
951                                 stream = input_stream;
952                                 input_stream = null;
953                                 try {
954                                         stream.Close ();
955                                 } catch {}
956                         }
957
958                         if (request_file != null) {
959                                 stream = request_file;
960                                 request_file = null;
961                                 try {
962                                         stream.Close ();
963                                 } catch {}
964                         }
965                 }
966                 public RequestContext RequestContext {
967                         get {
968                                 if (requestContext == null)
969                                         requestContext = new RequestContext (new HttpContextWrapper (this.context ?? HttpContext.Current), new RouteData ());
970
971                                 return requestContext;
972                         }
973                         
974                         internal set { requestContext = value; }        
975                 }
976
977                 public ChannelBinding HttpChannelBinding {
978                         get {
979                                 throw new PlatformNotSupportedException ("This property is not supported.");
980                         }
981                 }
982
983                 public Stream GetBufferlessInputStream ()
984                 {
985                         if (bufferlessInputStream == null) {
986                                 if (input_stream != null)
987                                         throw new HttpException ("Input stream has already been created");
988
989                                 // we don't need to hook up the filter here, because the raw stream should be returned
990                                 bufferlessInputStream = new BufferlessInputStream (this);
991                         }
992
993                         return bufferlessInputStream;
994                 }
995
996                 //
997                 // Stream that returns the data as it is read, without buffering
998                 //
999                 class BufferlessInputStream : Stream {
1000                         HttpRequest request;
1001
1002                         // cached, the request content-length
1003                         int content_length;
1004
1005                         // buffer that holds preloaded data
1006                         byte [] preloadedBuffer;
1007
1008                         // indicates if we already served the whole preloaded buffer
1009                         bool preloaded_served;
1010
1011                         // indicates if we already checked the request content-length against httpRuntime limit
1012                         bool checked_maxRequestLength;
1013
1014                         // our stream position
1015                         long position;
1016
1017                         //
1018                         // @request: the containing request that created us, used to find out content length
1019                         public BufferlessInputStream (HttpRequest request)
1020                         {
1021                                 this.request = request;
1022                                 content_length = request.ContentLength;
1023                         }
1024
1025                         public override bool CanRead {
1026                                 get { return true; }
1027                         }
1028
1029                         public override bool CanSeek {
1030                                 get { return false; }
1031                         }
1032
1033                         public override bool CanWrite {
1034                                 get { return false; }
1035                         }
1036
1037                         public override long Length {
1038                                 get {
1039                                         return content_length;
1040                                 }
1041                         }
1042
1043                         public override long Position {
1044                                 get {
1045                                         return position;
1046                                 }
1047                                 set {
1048                                         throw new NotSupportedException ("This is a readonly stream");
1049                                 }
1050                         }
1051
1052                         public override void Flush ()
1053                         {
1054                         }
1055
1056                         public override int Read (byte [] buffer, int offset, int count)
1057                         {
1058                                 if (buffer == null)
1059                                         throw new ArgumentNullException ("buffer");
1060
1061                                 if (offset < 0 || count < 0)
1062                                         throw new ArgumentOutOfRangeException ("offset or count less than zero.");
1063
1064                                 if (buffer.Length - offset < count )
1065                                         throw new ArgumentException ("offset+count",
1066                                                                      "The size of the buffer is less than offset + count.");
1067
1068                                 if (count == 0 || request.worker_request == null)
1069                                         return 0;
1070
1071                                 if (!checked_maxRequestLength) {
1072                                         int content_length_kb = content_length / 1024;
1073                                         HttpRuntimeSection config = HttpRuntime.Section;
1074                                         if (content_length_kb > config.MaxRequestLength)
1075                                                 throw HttpException.NewWithCode (400, "Upload size exceeds httpRuntime limit.", WebEventCodes.RuntimeErrorPostTooLarge);
1076                                         else
1077                                                 checked_maxRequestLength = true;
1078                                 }
1079
1080                                 // Serve the bytes we might have preloaded already.
1081                                 if (!preloaded_served) {
1082                                         if (preloadedBuffer == null)
1083                                                 preloadedBuffer = request.worker_request.GetPreloadedEntityBody ();
1084
1085                                         if (preloadedBuffer != null) {
1086                                                 long bytes_left = preloadedBuffer.Length-position;
1087                                                 int n = (int) Math.Min (count, bytes_left);
1088                                                 Array.Copy (preloadedBuffer, position, buffer, offset, n);
1089                                                 position += n;
1090
1091                                                 if (n == bytes_left)
1092                                                         preloaded_served = true;
1093
1094                                                 return n;
1095                                         }
1096                                         else
1097                                                 preloaded_served = true;
1098                                 }
1099
1100                                 // serve bytes from worker request if available
1101                                 if (position < content_length) {
1102                                         long bytes_left = content_length-position;
1103                                         int n = count;
1104
1105                                         if (bytes_left < count)
1106                                                 n = (int) bytes_left;
1107
1108                                         int bytes_read = request.worker_request.ReadEntityBody (buffer, offset, n);
1109                                         position += bytes_read;
1110                                         return bytes_read;
1111                                 }
1112
1113                                 return 0;
1114                         }
1115
1116                         public override long Seek (long offset, SeekOrigin origin)
1117                         {
1118                                 throw new NotSupportedException ("Can not seek on the HttpRequest.BufferlessInputStream");
1119                         }
1120
1121                         public override void SetLength (long value)
1122                         {
1123                                 throw new NotSupportedException ("Can not set length on the HttpRequest.BufferlessInputStream");
1124                         }
1125
1126                         public override void Write (byte [] buffer, int offset, int count)
1127                         {
1128                                 throw new NotSupportedException ("Can not write on the HttpRequest.BufferlessInputStream");
1129                         }
1130
1131                         //
1132                         // TODO: explicitly support the async methods if there is a convenient way of doing it
1133                         //
1134                 }
1135                 public Stream InputStream {
1136                         get {
1137                                 if (input_stream == null)
1138                                         MakeInputStream ();
1139
1140                                 return input_stream;
1141                         }
1142                 }
1143
1144                 public bool IsAuthenticated {
1145                         get {
1146                                 if (context.User == null || context.User.Identity == null)
1147                                         return false;
1148                                 return context.User.Identity.IsAuthenticated;
1149                         }
1150                 }
1151
1152                 public bool IsSecureConnection {
1153                         get {
1154                                 if (worker_request == null)
1155                                         return false;
1156                                 return worker_request.IsSecure ();
1157                         }
1158                 }
1159
1160                 public string this [string key] {
1161                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1162                         get {
1163                                 // "The QueryString, Form, Cookies, or ServerVariables collection member
1164                                 // specified in the key parameter."
1165                                 string val = QueryString [key];
1166                                 if (val == null)
1167                                         val = Form [key];
1168                                 if (val == null) {
1169                                         HttpCookie cookie = Cookies [key];
1170                                         if (cookie != null)
1171                                                 val = cookie.Value;
1172                                 }
1173                                 if (val == null)
1174                                         val = ServerVariables [key];
1175
1176                                 return val;
1177                         }
1178                 }
1179
1180                 public NameValueCollection Params {
1181                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1182                         get {
1183                                 if (all_params == null)
1184                                         all_params = new HttpParamsCollection (QueryString, Form, ServerVariables, Cookies);
1185
1186                                 return all_params;
1187                         }
1188                 }
1189
1190                 internal string PathNoValidation {
1191                         get {
1192                                 if (original_path == null) {
1193                                         if (url_components != null)
1194                                                 // use only if it's already been instantiated, so that we can't go into endless
1195                                                 // recursion in some scenarios
1196                                                 original_path = UrlComponents.Path;
1197                                         else
1198                                                 original_path = ApplyUrlMapping (worker_request.GetUriPath ());
1199                                 }
1200
1201                                 return original_path;
1202                         }
1203                 }
1204                 
1205                 public string Path {
1206                         get {
1207                                 if (unescaped_path == null) {
1208                                         unescaped_path = PathNoValidation;
1209                                         if (validateRequestNewMode) {
1210                                                 RequestValidator validator = RequestValidator.Current;
1211                                                 int validationFailureIndex;
1212                                                 
1213                                                 if (!validator.IsValidRequestString (HttpContext.Current, unescaped_path, RequestValidationSource.Path, null, out validationFailureIndex))
1214                                                         ThrowValidationException ("Path", "Path", unescaped_path);
1215                                         }
1216                                 }
1217                                 
1218                                 return unescaped_path;
1219                         }
1220                 }
1221
1222                 internal string PathInfoNoValidation {
1223                         get {
1224                                 if (path_info_unvalidated == null) {
1225                                         if (worker_request == null)
1226                                                 return String.Empty;
1227
1228                                         path_info_unvalidated = worker_request.GetPathInfo () ?? String.Empty;
1229                                 }
1230
1231                                 return path_info_unvalidated;
1232                         }
1233                 }
1234
1235                 public string PathInfo {
1236                         get {
1237                                 if (path_info == null) {
1238                                         path_info = PathInfoNoValidation;
1239                                         if (validateRequestNewMode) {
1240                                                 RequestValidator validator = RequestValidator.Current;
1241                                                 int validationFailureIndex;
1242                                                 
1243                                                 if (!validator.IsValidRequestString (HttpContext.Current, path_info, RequestValidationSource.PathInfo, null, out validationFailureIndex))
1244                                                         ThrowValidationException ("PathInfo", "PathInfo", path_info);
1245                                         }
1246                                 }
1247
1248                                 return path_info;
1249                         }
1250                 }
1251
1252                 public string PhysicalApplicationPath {
1253                         get {
1254                                 if (worker_request == null)
1255                                         throw new ArgumentNullException (); // like 2.0, 1.x throws TypeInitializationException
1256
1257                                 string path = HttpRuntime.AppDomainAppPath;
1258                                 if (SecurityManager.SecurityEnabled) {
1259                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, path).Demand ();
1260                                 }
1261                                 return path;
1262                         }
1263                 }
1264
1265                 public string PhysicalPath {
1266                         get {
1267                                 if (worker_request == null)
1268                                         return String.Empty; // don't check security with an empty string!
1269
1270                                 if (physical_path == null) {
1271                                         // Don't call HttpRequest.MapPath here, as that one *trims* the input
1272                                         physical_path = worker_request.MapPath (FilePath);
1273                                 }
1274
1275                                 if (SecurityManager.SecurityEnabled) {
1276                                         new FileIOPermission (FileIOPermissionAccess.PathDiscovery, physical_path).Demand ();
1277                                 }
1278                                 return physical_path;
1279                         }
1280                 }
1281
1282                 internal string RootVirtualDir {
1283                         get {
1284                                 if (root_virtual_dir == null){
1285                                         string fp = FilePath;
1286                                         int p = fp.LastIndexOf ('/');
1287
1288                                         if (p < 1)
1289                                                 root_virtual_dir = "/";
1290                                         else
1291                                                 root_virtual_dir = fp.Substring (0, p);
1292                                 }
1293                                 return root_virtual_dir;
1294                         }
1295                 }
1296
1297                 internal WebROCollection QueryStringUnvalidated {
1298                         get {
1299                                 if (query_string_nvc == null) {
1300                                         query_string_nvc = new WebROCollection ();
1301                                         string q = UrlComponents.Query;
1302                                         if (q != null) {
1303                                                 if (q.Length != 0)
1304                                                         q = q.Remove(0, 1);
1305                                         
1306                                                 HttpUtility.ParseQueryString (q, ContentEncoding, query_string_nvc);
1307                                         }
1308                                         
1309                                         query_string_nvc.Protect();
1310                                 }
1311
1312                                 return query_string_nvc;
1313                         }
1314                 }
1315                 
1316                 public NameValueCollection QueryString {
1317                         get {
1318                                 NameValueCollection query_string_nvc = QueryStringUnvalidated;
1319                                 if (validateRequestNewMode && !checked_query_string) {
1320                                         if (!lazyQueryStringValidation) {
1321                                                 // Setting this before calling the validator prevents
1322                                                 // possible endless recursion
1323                                                 checked_query_string = true;
1324                                                 ValidateNameValueCollection ("QueryString", query_string_nvc, RequestValidationSource.QueryString);
1325                                         }
1326                                 } else
1327                                         if (validate_query_string && !checked_query_string) {
1328                                                 // Setting this before calling the validator prevents
1329                                                 // possible endless recursion
1330                                                 checked_query_string = true;
1331                                                 ValidateNameValueCollection ("QueryString", query_string_nvc);
1332                                         }
1333                                 
1334                                 return query_string_nvc;
1335                         }
1336                 }
1337
1338                 internal string RawUrlUnvalidated {
1339                         get {
1340                                 if (raw_url_unvalidated == null) {
1341                                         if (worker_request != null)
1342                                                 raw_url_unvalidated = worker_request.GetRawUrl ();
1343                                         else
1344                                                 raw_url_unvalidated = UrlComponents.Path + UrlComponents.Query;
1345                                         
1346                                         if (raw_url_unvalidated == null)
1347                                                 raw_url_unvalidated = String.Empty;
1348                                 }
1349
1350                                 return raw_url_unvalidated;
1351                         }
1352                 }
1353
1354                 public string RawUrl {
1355                         get {
1356                                 if (raw_url == null) {
1357                                         raw_url = RawUrlUnvalidated;
1358                                         if (validateRequestNewMode) {
1359                                                 RequestValidator validator = RequestValidator.Current;
1360                                                 int validationFailureIndex;
1361
1362                                                 if (!validator.IsValidRequestString (HttpContext.Current, raw_url, RequestValidationSource.RawUrl, null, out validationFailureIndex))
1363                                                         ThrowValidationException ("RawUrl", "RawUrl", raw_url);
1364                                         }
1365                                 }
1366                                 
1367                                 return raw_url;
1368                         }
1369                 }
1370
1371                 //
1372                 // "GET" or "SET"
1373                 //
1374                 public string RequestType {
1375                         get {
1376                                 if (request_type == null){
1377                                         if (worker_request != null) {
1378                                                 request_type = worker_request.GetHttpVerbName ();
1379                                                 http_method = request_type;
1380                                         } else {
1381                                                 request_type = "GET";
1382                                         }
1383                                 }
1384                                 return request_type;
1385                         }
1386
1387                         set {
1388                                 request_type = value;
1389                         }
1390                 }
1391
1392                 public NameValueCollection ServerVariables {
1393                         [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Low)]
1394                         get {
1395                                 if (server_variables == null)
1396                                         server_variables = new ServerVariablesCollection (this);
1397
1398                                 return server_variables;
1399                         }
1400                 }
1401
1402                 public int TotalBytes {
1403                         get {
1404                                 Stream ins = InputStream;
1405                                 return (int) ins.Length;
1406                         }
1407                 }
1408
1409                 public UnvalidatedRequestValues Unvalidated { 
1410                         get {
1411                                 var vals = new UnvalidatedRequestValues ();
1412                                 
1413                                 vals.Cookies = CookiesNoValidation;
1414                                 vals.Files = Files;
1415                                 vals.Form = FormUnvalidated;
1416                                 vals.Headers = HeadersNoValidation;
1417                                 vals.Path = PathNoValidation;
1418                                 vals.PathInfo = PathInfoNoValidation;
1419                                 vals.QueryString = QueryStringUnvalidated;
1420                                 vals.RawUrl = RawUrlUnvalidated;
1421                                 vals.Url = Url;
1422                                 
1423                                 return vals;
1424                         }
1425                 }
1426
1427                 public Uri Url {
1428                         get {
1429                                 if (cached_url == null) {
1430                                         if (orig_url == null)
1431                                                 cached_url = UrlComponents.Uri;
1432                                         else
1433                                                 cached_url = new Uri (orig_url);
1434                                 }
1435
1436                                 return cached_url;                      
1437                         }
1438                 }
1439
1440                 public Uri UrlReferrer {
1441                         get {
1442                                 if (worker_request == null)
1443                                         return null;
1444
1445                                 string hr = worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderReferer);
1446                                 if (hr == null)
1447                                         return null;
1448
1449                                 Uri uri = null;
1450                                 try {
1451                                         uri = new Uri (hr);
1452                                 } catch (UriFormatException) {}
1453                                 return uri;
1454                         }
1455                 }
1456
1457                 public string UserAgent {
1458                         get {
1459                                 if (worker_request == null)
1460                                         return null;
1461
1462                                 return worker_request.GetKnownRequestHeader (HttpWorkerRequest.HeaderUserAgent);
1463                         }
1464                 }
1465
1466                 public string UserHostAddress {
1467                         get {
1468                                 if (worker_request == null)
1469                                         return null;
1470
1471                                 return worker_request.GetRemoteAddress ();
1472                         }
1473                 }
1474
1475                 public string UserHostName {
1476                         get {
1477                                 if (worker_request == null)
1478                                         return null;
1479
1480                                 return worker_request.GetRemoteName ();
1481                         }
1482                 }
1483
1484                 public string [] UserLanguages {
1485                         get {
1486                                 if (worker_request == null)
1487                                         return null;
1488
1489                                 if (user_languages == null)
1490                                         user_languages = SplitHeader (HttpWorkerRequest.HeaderAcceptLanguage);
1491
1492                                 return user_languages;
1493                         }
1494                 }
1495
1496                 public byte [] BinaryRead (int count)
1497                 {
1498                         if (count < 0)
1499                                 throw new ArgumentException ("count is < 0");
1500
1501                         Stream s = InputStream;
1502                         byte [] ret = new byte [count];
1503                         if (s.Read (ret, 0, count) != count)
1504                                 throw new ArgumentException (
1505                                         String.Format ("count {0} exceeds length of available input {1}",
1506                                                 count, s.Length - s.Position));
1507                         return ret;
1508                 }
1509
1510                 public int [] MapImageCoordinates (string imageFieldName)
1511                 {
1512                         string method = HttpMethod;
1513                         NameValueCollection coll = null;
1514                         if (method == "HEAD" || method == "GET")
1515                                 coll = QueryString;
1516                         else if (method == "POST")
1517                                 coll = Form;
1518
1519                         if (coll == null)
1520                                 return null;
1521
1522                         string x = coll [imageFieldName + ".x"];
1523                         if (x == null || x == "")
1524                                 return null;
1525
1526                         string y = coll [imageFieldName + ".y"];
1527                         if (y == null || y == "")
1528                                 return null;
1529
1530                         int [] result = new int [2];
1531                         try {
1532                                 result [0] = Int32.Parse (x);
1533                                 result [1] = Int32.Parse (y);
1534                         } catch {
1535                                 return null;
1536                         }
1537
1538                         return result;
1539                 }
1540
1541                 public string MapPath (string virtualPath)
1542                 {
1543                         if (worker_request == null)
1544                                 return null;
1545
1546                         return MapPath (virtualPath, BaseVirtualDir, true);
1547                 }
1548
1549                 public string MapPath (string virtualPath, string baseVirtualDir, bool allowCrossAppMapping)
1550                 {
1551                         if (worker_request == null)
1552                                 throw HttpException.NewWithCode ("No HttpWorkerRequest", WebEventCodes.RuntimeErrorRequestAbort);
1553
1554                         if (virtualPath == null)
1555                                 virtualPath = "~";
1556                         else {
1557                                 virtualPath = virtualPath.Trim ();
1558                                 if (virtualPath.Length == 0)
1559                                         virtualPath = "~";
1560                         }
1561
1562                         if (!VirtualPathUtility.IsValidVirtualPath (virtualPath))
1563                                 throw HttpException.NewWithCode (String.Format ("'{0}' is not a valid virtual path.", virtualPath), WebEventCodes.RuntimeErrorRequestAbort);
1564
1565                         string appVirtualPath = HttpRuntime.AppDomainAppVirtualPath;
1566                         if (!VirtualPathUtility.IsRooted (virtualPath)) {
1567                                 if (StrUtils.IsNullOrEmpty (baseVirtualDir))
1568                                         baseVirtualDir = appVirtualPath;
1569                                 virtualPath = VirtualPathUtility.Combine (VirtualPathUtility.AppendTrailingSlash (baseVirtualDir), virtualPath);
1570                                 if (!VirtualPathUtility.IsAbsolute (virtualPath))
1571                                         virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1572                         } else if (!VirtualPathUtility.IsAbsolute (virtualPath))
1573                                 virtualPath = VirtualPathUtility.ToAbsolute (virtualPath, false);
1574
1575                         bool isAppVirtualPath = String.Compare (virtualPath, appVirtualPath, RuntimeHelpers.StringComparison) == 0;
1576                         appVirtualPath = VirtualPathUtility.AppendTrailingSlash (appVirtualPath);
1577                         if (!allowCrossAppMapping){
1578                                 if (!StrUtils.StartsWith (virtualPath, appVirtualPath, true))
1579                                         throw new ArgumentException ("MapPath: Mapping across applications not allowed");
1580                                 if (appVirtualPath.Length > 1 && virtualPath.Length > 1 && virtualPath [0] != '/')
1581                                         throw HttpException.NewWithCode ("MapPath: Mapping across applications not allowed", WebEventCodes.RuntimeErrorRequestAbort);
1582                         }
1583                         
1584                         if (!isAppVirtualPath && !virtualPath.StartsWith (appVirtualPath, RuntimeHelpers.StringComparison))
1585                                 throw new InvalidOperationException (String.Format ("Failed to map path '{0}'", virtualPath));
1586                         string path = worker_request.MapPath (virtualPath);
1587                         if (virtualPath [virtualPath.Length - 1] != '/' && path [path.Length - 1] == System.IO.Path.DirectorySeparatorChar)
1588                                 path = path.TrimEnd (System.IO.Path.DirectorySeparatorChar);
1589                         return path;
1590                 }
1591
1592                 public void SaveAs (string filename, bool includeHeaders)
1593                 {
1594                         Stream output = new FileStream (filename, FileMode.Create);
1595                         if (includeHeaders) {
1596                                 StringBuilder sb = new StringBuilder ();
1597                                 string version = String.Empty;
1598                                 string path = "/";
1599                                 if (worker_request != null) {
1600                                         version = worker_request.GetHttpVersion ();
1601                                         path = UrlComponents.Path;
1602                                 }
1603                                 string qs = UrlComponents.Query;
1604
1605                                 sb.AppendFormat ("{0} {1}{2} {3}\r\n", HttpMethod, path, qs, version);
1606                                 NameValueCollection coll = Headers;
1607                                 foreach (string k in coll.AllKeys) {
1608                                         sb.Append (k);
1609                                         sb.Append (':');
1610                                         sb.Append (coll [k]);
1611                                         sb.Append ("\r\n");
1612                                 }
1613                                 sb.Append ("\r\n");
1614                                 // latin1
1615                                 byte [] bytes = Encoding.GetEncoding (28591).GetBytes (sb.ToString ());
1616                                 output.Write (bytes, 0, bytes.Length);
1617                         }
1618
1619                         // More than 1 call to SaveAs works fine on MS, so we "copy" the stream
1620                         // to keep InputStream in its state.
1621                         Stream input = GetSubStream (InputStream);
1622                         try {
1623                                 long len = input.Length;
1624                                 int buf_size = (int) Math.Min ((len < 0 ? 0 : len), 8192);
1625                                 byte [] data = new byte [buf_size];
1626                                 int count = 0;
1627                                 while (len > 0 && (count = input.Read (data, 0, buf_size)) > 0) {
1628                                         output.Write (data, 0, count);
1629                                         len -= count;
1630                                 }
1631                         } finally {
1632                                 output.Flush ();
1633                                 output.Close ();
1634                                 EndSubStream (input);
1635                         }
1636                 }
1637
1638                 public void ValidateInput ()
1639                 {
1640                         validate_cookies = true;
1641                         validate_query_string = true;
1642                         validate_form = true;
1643                         inputValidationEnabled = true;
1644                 }
1645                 internal void Validate ()
1646                 {
1647                         var cfg = HttpRuntime.Section;
1648                         string query = UrlComponents.Query;
1649                         
1650                         if (query != null && query.Length > cfg.MaxQueryStringLength)
1651                                 throw new HttpException (400, "The length of the query string for this request exceeds the configured maxQueryStringLength value.");
1652                         
1653                         string path = PathNoValidation;
1654                         if (path != null) {
1655                                 if (path.Length > cfg.MaxUrlLength)
1656                                         throw new HttpException (400, "The length of the URL for this request exceeds the configured maxUrlLength value.");
1657                                 
1658                                 char[] invalidChars = RequestPathInvalidCharacters;
1659                                 if (invalidChars != null) {
1660                                         int idx = path.IndexOfAny (invalidChars);
1661                                         if (idx != -1)
1662                                                 throw HttpException.NewWithCode (
1663                                                         String.Format ("A potentially dangerous Request.Path value was detected from the client ({0}).", path [idx]),
1664                                                         WebEventCodes.RuntimeErrorValidationFailure
1665                                                 );
1666                                 }
1667                         }
1668
1669                         if (validateRequestNewMode)
1670                                 ValidateInput ();
1671                 }
1672 #region internal routines
1673                 internal string ClientTarget {
1674                         get {
1675                                 return client_target;
1676                         }
1677
1678                         set {
1679                                 client_target = value;
1680                         }
1681                 }
1682                 
1683                 public bool IsLocal {
1684                         get {
1685                                 string address = worker_request.GetRemoteAddress ();
1686
1687                                 if (StrUtils.IsNullOrEmpty (address))
1688                                         return false;
1689
1690                                 if (address == "127.0.0.1")
1691                                         return true;
1692
1693                                 System.Net.IPAddress remoteAddr = System.Net.IPAddress.Parse (address);
1694                                 if (System.Net.IPAddress.IsLoopback (remoteAddr))
1695                                         return true;
1696
1697                                 for (int i = 0; i < host_addresses.Length; i++)
1698                                         if (remoteAddr.Equals (host_addresses [i]))
1699                                                 return true;
1700
1701                                 return false;
1702                         }
1703                 }
1704
1705                 internal void SetFilePath (string path)
1706                 {
1707                         file_path = path;
1708                         physical_path = null;
1709                         original_path = null;
1710                 }
1711
1712                 internal void SetCurrentExePath (string path)
1713                 {
1714                         cached_url = null;
1715                         current_exe_path = path;
1716                         UrlComponents.Path = path + PathInfo;
1717                         // recreated on demand
1718                         root_virtual_dir = null;
1719                         base_virtual_dir = null;
1720                         physical_path = null;
1721                         unescaped_path = null;
1722                         original_path = null;
1723                 }
1724
1725                 internal void SetPathInfo (string pi)
1726                 {
1727                         cached_url = null;
1728                         path_info = pi;
1729                         original_path = null;
1730
1731                         string path = UrlComponents.Path;
1732                         UrlComponents.Path = path + PathInfo;
1733                 }
1734                 internal void SetFormCollection (WebROCollection coll, bool lazyValidation)
1735                 {
1736                         if (coll == null)
1737                                 return;
1738                         form = coll;
1739                         lazyFormValidation = lazyValidation;
1740                 }
1741
1742                 internal void SetQueryStringCollection (WebROCollection coll, bool lazyValidation)
1743                 {
1744                         if (coll == null)
1745                                 return;
1746                         query_string_nvc = coll;
1747                         lazyQueryStringValidation = lazyValidation;
1748                 }
1749                 // Headers is ReadOnly, so we need this hack for cookie-less sessions.
1750                 internal void SetHeader (string name, string value)
1751                 {
1752                         WebROCollection h = (WebROCollection) Headers;
1753                         h.Unprotect ();
1754                         h [name] = value;
1755                         h.Protect ();
1756                 }
1757
1758                 // Notice: there is nothing raw about this querystring.
1759                 internal string QueryStringRaw {
1760                         get {
1761                                 UriBuilder urlComponents = UrlComponents;
1762
1763                                 if (urlComponents == null) {
1764                                         string ret = worker_request.GetQueryString ();
1765
1766                                         if (ret == null || ret.Length == 0)
1767                                                 return String.Empty;
1768
1769                                         if (ret [0] == '?')
1770                                                 return ret;
1771
1772                                         return "?" + ret;
1773                                 }
1774                                 
1775                                 return UrlComponents.Query;
1776                         }
1777
1778                         set {
1779                                 UrlComponents.Query = value;
1780                                 cached_url = null;
1781                                 query_string_nvc = null;
1782                         }
1783                 }
1784
1785                 // Internal, dont know what it does, so flagged as public so we can see it.
1786                 internal void SetForm (WebROCollection coll)
1787                 {
1788                         form = coll;
1789                 }
1790
1791                 internal HttpWorkerRequest WorkerRequest {
1792                         get {
1793                                 return worker_request;
1794                         }
1795                 }
1796
1797                 internal HttpContext Context {
1798                         get { return context; }
1799                         set { context = value; }
1800                 }
1801
1802                 static void ValidateNameValueCollection (string name, NameValueCollection coll)
1803                 {
1804                         if (coll == null)
1805                                 return;
1806                 
1807                         foreach (string key in coll.Keys) {
1808                                 string val = coll [key];
1809                                 if (val != null && val.Length > 0 && IsInvalidString (val))
1810                                         ThrowValidationException (name, key, val);
1811                         }
1812                 }
1813                 static void ValidateNameValueCollection (string name, NameValueCollection coll, RequestValidationSource source)
1814                 {
1815                         if (coll == null)
1816                                 return;
1817
1818                         RequestValidator validator = RequestValidator.Current;
1819                         int validationFailureIndex;
1820                         HttpContext context = HttpContext.Current;
1821
1822                         foreach (string key in coll.Keys) {
1823                                 string val = coll [key];
1824                                 if (val != null && val.Length > 0 && !validator.IsValidRequestString (context, val, source, key, out validationFailureIndex))
1825                                         ThrowValidationException (name, key, val);
1826                         }
1827                 }
1828
1829                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1830                 public void InsertEntityBody ()
1831                 {
1832                         throw new PlatformNotSupportedException ("This method is not supported.");
1833                 }
1834
1835                 [AspNetHostingPermission (SecurityAction.Demand, Level = AspNetHostingPermissionLevel.High)]
1836                 public void InsertEntityBody (byte[] buffer, int offset, int count)
1837                 {
1838                         throw new PlatformNotSupportedException ("This method is not supported.");
1839                 }
1840                 static void ValidateCookieCollection (HttpCookieCollection cookies)
1841                 {
1842                         if (cookies == null)
1843                                 return;
1844                 
1845                         int size = cookies.Count;
1846                         HttpCookie cookie;
1847                         RequestValidator validator = RequestValidator.Current;
1848                         int validationFailureIndex;
1849                         HttpContext context = HttpContext.Current;
1850                         bool invalid;
1851                         
1852                         for (int i = 0 ; i < size ; i++) {
1853                                 cookie = cookies[i];
1854                                 if (cookie == null)
1855                                         continue;
1856                                 
1857                                 string value = cookie.Value;
1858                                 string name = cookie.Name;
1859
1860                                 if (!String.IsNullOrEmpty (value)) {
1861                                         if (validateRequestNewMode)
1862                                                 invalid = !validator.IsValidRequestString (context, value, RequestValidationSource.Cookies, name, out validationFailureIndex);
1863                                         else
1864                                                 invalid = IsInvalidString (value);
1865
1866                                         if (invalid)
1867                                                 ThrowValidationException ("Cookies", name, value);
1868                                 }
1869                         }
1870                 }
1871
1872                 static void ThrowValidationException (string name, string key, string value)
1873                 {
1874                         string v = "\"" + value + "\"";
1875                         if (v.Length > 20)
1876                                 v = v.Substring (0, 16) + "...\"";
1877                 
1878                         string msg = String.Format ("A potentially dangerous Request.{0} value was " +
1879                                                     "detected from the client ({1}={2}).", name, key, v);
1880                 
1881                         throw new HttpRequestValidationException (msg);
1882                 }
1883                 internal static void ValidateString (string key, string value, RequestValidationSource source)
1884                 {
1885                         if (String.IsNullOrEmpty (value))
1886                                 return;
1887 #pragma warning disable 219
1888                         int ignore;
1889 #pragma warning restore 219
1890                         if (IsInvalidString (value, out ignore))
1891                                 ThrowValidationException (source.ToString (), key, value);
1892                 }
1893                 internal static bool IsInvalidString (string val)
1894                 {
1895 #pragma warning disable 219
1896                         int validationFailureIndex;
1897 #pragma warning restore 219
1898                         return IsInvalidString (val, out validationFailureIndex);
1899                 }
1900
1901                 internal static bool IsInvalidString (string val, out int validationFailureIndex)
1902                 {
1903                         validationFailureIndex = 0;
1904
1905                         int len = val.Length;
1906                         if (len < 2)
1907                                 return false;
1908
1909                         char current = val [0];
1910                         for (int idx = 1; idx < len; idx++) {
1911                                 char next = val [idx];
1912                                 // See http://secunia.com/advisories/14325
1913                                 if (current == '<' || current == '\xff1c') {
1914                                         if (next == '!' || next < ' '
1915                                             || (next >= 'a' && next <= 'z')
1916                                             || (next >= 'A' && next <= 'Z')) {
1917                                                 validationFailureIndex = idx - 1;
1918                                                 return true;
1919                                         }
1920                                 } else if (current == '&' && next == '#') {
1921                                         validationFailureIndex = idx - 1;
1922                                         return true;
1923                                 }
1924
1925                                 current = next;
1926                         }
1927
1928                         return false;
1929                 }
1930                 
1931                 static System.Net.IPAddress [] GetLocalHostAddresses ()
1932                 {
1933                         try {
1934                                 string hostName = System.Net.Dns.GetHostName ();
1935                                 System.Net.IPAddress [] ipaddr = System.Net.Dns.GetHostAddresses (hostName);
1936                                 return ipaddr;
1937                         } catch {
1938                                 return new System.Net.IPAddress[0];
1939                         }
1940                 }
1941         }
1942 #endregion
1943
1944 #region Helper classes
1945
1946         //
1947         // Stream-based multipart handling.
1948         //
1949         // In this incarnation deals with an HttpInputStream as we are now using
1950         // IntPtr-based streams instead of byte [].   In the future, we will also
1951         // send uploads above a certain threshold into the disk (to implement
1952         // limit-less HttpInputFiles). 
1953         //
1954         
1955         class HttpMultipart {
1956
1957                 public class Element {
1958                         public string ContentType;
1959                         public string Name;
1960                         public string Filename;
1961                         public long Start;
1962                         public long Length;
1963                         
1964                         public override string ToString ()
1965                         {
1966                                 return "ContentType " + ContentType + ", Name " + Name + ", Filename " + Filename + ", Start " +
1967                                         Start.ToString () + ", Length " + Length.ToString ();
1968                         }
1969                 }
1970                 
1971                 Stream data;
1972                 string boundary;
1973                 byte [] boundary_bytes;
1974                 byte [] buffer;
1975                 bool at_eof;
1976                 Encoding encoding;
1977                 StringBuilder sb;
1978                 
1979                 const byte HYPHEN = (byte) '-', LF = (byte) '\n', CR = (byte) '\r';
1980                 
1981                 // See RFC 2046 
1982                 // In the case of multipart entities, in which one or more different
1983                 // sets of data are combined in a single body, a "multipart" media type
1984                 // field must appear in the entity's header.  The body must then contain
1985                 // one or more body parts, each preceded by a boundary delimiter line,
1986                 // and the last one followed by a closing boundary delimiter line.
1987                 // After its boundary delimiter line, each body part then consists of a
1988                 // header area, a blank line, and a body area.  Thus a body part is
1989                 // similar to an RFC 822 message in syntax, but different in meaning.
1990                 
1991                 public HttpMultipart (Stream data, string b, Encoding encoding)
1992                 {
1993                         this.data = data;
1994                         boundary = b;
1995                         boundary_bytes = encoding.GetBytes (b);
1996                         buffer = new byte [boundary_bytes.Length + 2]; // CRLF or '--'
1997                         this.encoding = encoding;
1998                         sb = new StringBuilder ();
1999                 }
2000
2001                 string ReadLine ()
2002                 {
2003                         // CRLF or LF are ok as line endings.
2004                         bool got_cr = false;
2005                         int b = 0;
2006                         sb.Length = 0;
2007                         while (true) {
2008                                 b = data.ReadByte ();
2009                                 if (b == -1) {
2010                                         return null;
2011                                 }
2012
2013                                 if (b == LF) {
2014                                         break;
2015                                 }
2016                                 got_cr = (b == CR);
2017                                 sb.Append ((char) b);
2018                         }
2019
2020                         if (got_cr)
2021                                 sb.Length--;
2022
2023                         return sb.ToString ();
2024
2025                 }
2026
2027                 static string GetContentDispositionAttribute (string l, string name)
2028                 {
2029                         int idx = l.IndexOf (name + "=\"");
2030                         if (idx < 0)
2031                                 return null;
2032                         int begin = idx + name.Length + "=\"".Length;
2033                         int end = l.IndexOf ('"', begin);
2034                         if (end < 0)
2035                                 return null;
2036                         if (begin == end)
2037                                 return "";
2038                         return l.Substring (begin, end - begin);
2039                 }
2040
2041                 string GetContentDispositionAttributeWithEncoding (string l, string name)
2042                 {
2043                         int idx = l.IndexOf (name + "=\"");
2044                         if (idx < 0)
2045                                 return null;
2046                         int begin = idx + name.Length + "=\"".Length;
2047                         int end = l.IndexOf ('"', begin);
2048                         if (end < 0)
2049                                 return null;
2050                         if (begin == end)
2051                                 return "";
2052
2053                         string temp = l.Substring (begin, end - begin);
2054                         byte [] source = new byte [temp.Length];
2055                         for (int i = temp.Length - 1; i >= 0; i--)
2056                                 source [i] = (byte) temp [i];
2057
2058                         return encoding.GetString (source);
2059                 }
2060
2061                 bool ReadBoundary ()
2062                 {
2063                         try {
2064                                 string line = ReadLine ();
2065                                 while (line == "")
2066                                         line = ReadLine ();
2067                                 if (line [0] != '-' || line [1] != '-')
2068                                         return false;
2069
2070                                 if (!StrUtils.EndsWith (line, boundary, false))
2071                                         return true;
2072                         } catch {
2073                         }
2074
2075                         return false;
2076                 }
2077
2078                 string ReadHeaders ()
2079                 {
2080                         string s = ReadLine ();
2081                         if (s == "")
2082                                 return null;
2083
2084                         return s;
2085                 }
2086
2087                 bool CompareBytes (byte [] orig, byte [] other)
2088                 {
2089                         for (int i = orig.Length - 1; i >= 0; i--)
2090                                 if (orig [i] != other [i])
2091                                         return false;
2092
2093                         return true;
2094                 }
2095
2096                 long MoveToNextBoundary ()
2097                 {
2098                         long retval = 0;
2099                         bool got_cr = false;
2100
2101                         int state = 0;
2102                         int c = data.ReadByte ();
2103                         while (true) {
2104                                 if (c == -1)
2105                                         return -1;
2106
2107                                 if (state == 0 && c == LF) {
2108                                         retval = data.Position - 1;
2109                                         if (got_cr)
2110                                                 retval--;
2111                                         state = 1;
2112                                         c = data.ReadByte ();
2113                                 } else if (state == 0) {
2114                                         got_cr = (c == CR);
2115                                         c = data.ReadByte ();
2116                                 } else if (state == 1 && c == '-') {
2117                                         c = data.ReadByte ();
2118                                         if (c == -1)
2119                                                 return -1;
2120
2121                                         if (c != '-') {
2122                                                 state = 0;
2123                                                 got_cr = false;
2124                                                 continue; // no ReadByte() here
2125                                         }
2126
2127                                         int nread = data.Read (buffer, 0, buffer.Length);
2128                                         int bl = buffer.Length;
2129                                         if (nread != bl)
2130                                                 return -1;
2131
2132                                         if (!CompareBytes (boundary_bytes, buffer)) {
2133                                                 state = 0;
2134                                                 data.Position = retval + 2;
2135                                                 if (got_cr) {
2136                                                         data.Position++;
2137                                                         got_cr = false;
2138                                                 }
2139                                                 c = data.ReadByte ();
2140                                                 continue;
2141                                         }
2142
2143                                         if (buffer [bl - 2] == '-' && buffer [bl - 1] == '-') {
2144                                                 at_eof = true;
2145                                         } else if (buffer [bl - 2] != CR || buffer [bl - 1] != LF) {
2146                                                 state = 0;
2147                                                 data.Position = retval + 2;
2148                                                 if (got_cr) {
2149                                                         data.Position++;
2150                                                         got_cr = false;
2151                                                 }
2152                                                 c = data.ReadByte ();
2153                                                 continue;
2154                                         }
2155                                         data.Position = retval + 2;
2156                                         if (got_cr)
2157                                                 data.Position++;
2158                                         break;
2159                                 } else {
2160                                         // state == 1
2161                                         state = 0; // no ReadByte() here
2162                                 }
2163                         }
2164
2165                         return retval;
2166                 }
2167
2168                 public Element ReadNextElement ()
2169                 {
2170                         if (at_eof || ReadBoundary ())
2171                                 return null;
2172
2173                         Element elem = new Element ();
2174                         string header;
2175                         while ((header = ReadHeaders ()) != null) {
2176                                 if (StrUtils.StartsWith (header, "Content-Disposition:", true)) {
2177                                         elem.Name = GetContentDispositionAttribute (header, "name");
2178                                         elem.Filename = StripPath (GetContentDispositionAttributeWithEncoding (header, "filename"));
2179                                 } else if (StrUtils.StartsWith (header, "Content-Type:", true)) {
2180                                         elem.ContentType = header.Substring ("Content-Type:".Length).Trim ();
2181                                 }
2182                         }
2183
2184                         long start = data.Position;
2185                         elem.Start = start;
2186                         long pos = MoveToNextBoundary ();
2187                         if (pos == -1)
2188                                 return null;
2189
2190                         elem.Length = pos - start;
2191                         return elem;
2192                 }
2193
2194                 static string StripPath (string path)
2195                 {
2196                         if (path == null || path.Length == 0)
2197                                 return path;
2198                         
2199                         if (path.IndexOf (":\\") != 1 && !path.StartsWith ("\\\\"))
2200                                 return path;
2201                         return path.Substring (path.LastIndexOf ('\\') + 1);
2202                 }
2203         }
2204 #endregion
2205 }
2206