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