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