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