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