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