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