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