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