Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net40 / Hammock.ClientProfile / Mono / HttpUtility.cs
1 // 
2 // System.Web.HttpUtility
3 //
4 // Authors:
5 //   Patrik Torstensson (Patrik.Torstensson@labs2.com)
6 //   Wictor WilĂ©n (decode/encode functions) (wictor@ibizkit.se)
7 //   Tim Coleman (tim@timcoleman.com)
8 //   Gonzalo Paniagua Javier (gonzalo@ximian.com)
9 //
10 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32
33 using System.Collections;
34 using System.Collections.Specialized;
35 using System.Globalization;
36 using System.IO;
37 using System.Text;
38
39 namespace System.Compat.Web
40 {
41     public sealed class HttpUtility
42     {
43         #region Fields
44
45         private static Hashtable entities;
46         private static readonly object lock_ = new object();
47
48         #endregion // Fields
49
50         private static Hashtable Entities
51         {
52             get
53             {
54                 lock (lock_)
55                 {
56                     if (entities == null)
57                     {
58                         InitEntities();
59                     }
60
61                     return entities;
62                 }
63             }
64         }
65
66         #region Constructors
67
68         private static void InitEntities()
69         {
70             // Build the hash table of HTML entity references.  This list comes
71             // from the HTML 4.01 W3C recommendation.
72             entities = new Hashtable
73                            {
74                                {"nbsp", '\u00A0'},
75                                {"iexcl", '\u00A1'},
76                                {"cent", '\u00A2'},
77                                {"pound", '\u00A3'},
78                                {"curren", '\u00A4'},
79                                {"yen", '\u00A5'},
80                                {"brvbar", '\u00A6'},
81                                {"sect", '\u00A7'},
82                                {"uml", '\u00A8'},
83                                {"copy", '\u00A9'},
84                                {"ordf", '\u00AA'},
85                                {"laquo", '\u00AB'},
86                                {"not", '\u00AC'},
87                                {"shy", '\u00AD'},
88                                {"reg", '\u00AE'},
89                                {"macr", '\u00AF'},
90                                {"deg", '\u00B0'},
91                                {"plusmn", '\u00B1'},
92                                {"sup2", '\u00B2'},
93                                {"sup3", '\u00B3'},
94                                {"acute", '\u00B4'},
95                                {"micro", '\u00B5'},
96                                {"para", '\u00B6'},
97                                {"middot", '\u00B7'},
98                                {"cedil", '\u00B8'},
99                                {"sup1", '\u00B9'},
100                                {"ordm", '\u00BA'},
101                                {"raquo", '\u00BB'},
102                                {"frac14", '\u00BC'},
103                                {"frac12", '\u00BD'},
104                                {"frac34", '\u00BE'},
105                                {"iquest", '\u00BF'},
106                                {"Agrave", '\u00C0'},
107                                {"Aacute", '\u00C1'},
108                                {"Acirc", '\u00C2'},
109                                {"Atilde", '\u00C3'},
110                                {"Auml", '\u00C4'},
111                                {"Aring", '\u00C5'},
112                                {"AElig", '\u00C6'},
113                                {"Ccedil", '\u00C7'},
114                                {"Egrave", '\u00C8'},
115                                {"Eacute", '\u00C9'},
116                                {"Ecirc", '\u00CA'},
117                                {"Euml", '\u00CB'},
118                                {"Igrave", '\u00CC'},
119                                {"Iacute", '\u00CD'},
120                                {"Icirc", '\u00CE'},
121                                {"Iuml", '\u00CF'},
122                                {"ETH", '\u00D0'},
123                                {"Ntilde", '\u00D1'},
124                                {"Ograve", '\u00D2'},
125                                {"Oacute", '\u00D3'},
126                                {"Ocirc", '\u00D4'},
127                                {"Otilde", '\u00D5'},
128                                {"Ouml", '\u00D6'},
129                                {"times", '\u00D7'},
130                                {"Oslash", '\u00D8'},
131                                {"Ugrave", '\u00D9'},
132                                {"Uacute", '\u00DA'},
133                                {"Ucirc", '\u00DB'},
134                                {"Uuml", '\u00DC'},
135                                {"Yacute", '\u00DD'},
136                                {"THORN", '\u00DE'},
137                                {"szlig", '\u00DF'},
138                                {"agrave", '\u00E0'},
139                                {"aacute", '\u00E1'},
140                                {"acirc", '\u00E2'},
141                                {"atilde", '\u00E3'},
142                                {"auml", '\u00E4'},
143                                {"aring", '\u00E5'},
144                                {"aelig", '\u00E6'},
145                                {"ccedil", '\u00E7'},
146                                {"egrave", '\u00E8'},
147                                {"eacute", '\u00E9'},
148                                {"ecirc", '\u00EA'},
149                                {"euml", '\u00EB'},
150                                {"igrave", '\u00EC'},
151                                {"iacute", '\u00ED'},
152                                {"icirc", '\u00EE'},
153                                {"iuml", '\u00EF'},
154                                {"eth", '\u00F0'},
155                                {"ntilde", '\u00F1'},
156                                {"ograve", '\u00F2'},
157                                {"oacute", '\u00F3'},
158                                {"ocirc", '\u00F4'},
159                                {"otilde", '\u00F5'},
160                                {"ouml", '\u00F6'},
161                                {"divide", '\u00F7'},
162                                {"oslash", '\u00F8'},
163                                {"ugrave", '\u00F9'},
164                                {"uacute", '\u00FA'},
165                                {"ucirc", '\u00FB'},
166                                {"uuml", '\u00FC'},
167                                {"yacute", '\u00FD'},
168                                {"thorn", '\u00FE'},
169                                {"yuml", '\u00FF'},
170                                {"fnof", '\u0192'},
171                                {"Alpha", '\u0391'},
172                                {"Beta", '\u0392'},
173                                {"Gamma", '\u0393'},
174                                {"Delta", '\u0394'},
175                                {"Epsilon", '\u0395'},
176                                {"Zeta", '\u0396'},
177                                {"Eta", '\u0397'},
178                                {"Theta", '\u0398'},
179                                {"Iota", '\u0399'},
180                                {"Kappa", '\u039A'},
181                                {"Lambda", '\u039B'},
182                                {"Mu", '\u039C'},
183                                {"Nu", '\u039D'},
184                                {"Xi", '\u039E'},
185                                {"Omicron", '\u039F'},
186                                {"Pi", '\u03A0'},
187                                {"Rho", '\u03A1'},
188                                {"Sigma", '\u03A3'},
189                                {"Tau", '\u03A4'},
190                                {"Upsilon", '\u03A5'},
191                                {"Phi", '\u03A6'},
192                                {"Chi", '\u03A7'},
193                                {"Psi", '\u03A8'},
194                                {"Omega", '\u03A9'},
195                                {"alpha", '\u03B1'},
196                                {"beta", '\u03B2'},
197                                {"gamma", '\u03B3'},
198                                {"delta", '\u03B4'},
199                                {"epsilon", '\u03B5'},
200                                {"zeta", '\u03B6'},
201                                {"eta", '\u03B7'},
202                                {"theta", '\u03B8'},
203                                {"iota", '\u03B9'},
204                                {"kappa", '\u03BA'},
205                                {"lambda", '\u03BB'},
206                                {"mu", '\u03BC'},
207                                {"nu", '\u03BD'},
208                                {"xi", '\u03BE'},
209                                {"omicron", '\u03BF'},
210                                {"pi", '\u03C0'},
211                                {"rho", '\u03C1'},
212                                {"sigmaf", '\u03C2'},
213                                {"sigma", '\u03C3'},
214                                {"tau", '\u03C4'},
215                                {"upsilon", '\u03C5'},
216                                {"phi", '\u03C6'},
217                                {"chi", '\u03C7'},
218                                {"psi", '\u03C8'},
219                                {"omega", '\u03C9'},
220                                {"thetasym", '\u03D1'},
221                                {"upsih", '\u03D2'},
222                                {"piv", '\u03D6'},
223                                {"bull", '\u2022'},
224                                {"hellip", '\u2026'},
225                                {"prime", '\u2032'},
226                                {"Prime", '\u2033'},
227                                {"oline", '\u203E'},
228                                {"frasl", '\u2044'},
229                                {"weierp", '\u2118'},
230                                {"image", '\u2111'},
231                                {"real", '\u211C'},
232                                {"trade", '\u2122'},
233                                {"alefsym", '\u2135'},
234                                {"larr", '\u2190'},
235                                {"uarr", '\u2191'},
236                                {"rarr", '\u2192'},
237                                {"darr", '\u2193'},
238                                {"harr", '\u2194'},
239                                {"crarr", '\u21B5'},
240                                {"lArr", '\u21D0'},
241                                {"uArr", '\u21D1'},
242                                {"rArr", '\u21D2'},
243                                {"dArr", '\u21D3'},
244                                {"hArr", '\u21D4'},
245                                {"forall", '\u2200'},
246                                {"part", '\u2202'},
247                                {"exist", '\u2203'},
248                                {"empty", '\u2205'},
249                                {"nabla", '\u2207'},
250                                {"isin", '\u2208'},
251                                {"notin", '\u2209'},
252                                {"ni", '\u220B'},
253                                {"prod", '\u220F'},
254                                {"sum", '\u2211'},
255                                {"minus", '\u2212'},
256                                {"lowast", '\u2217'},
257                                {"radic", '\u221A'},
258                                {"prop", '\u221D'},
259                                {"infin", '\u221E'},
260                                {"ang", '\u2220'},
261                                {"and", '\u2227'},
262                                {"or", '\u2228'},
263                                {"cap", '\u2229'},
264                                {"cup", '\u222A'},
265                                {"int", '\u222B'},
266                                {"there4", '\u2234'},
267                                {"sim", '\u223C'},
268                                {"cong", '\u2245'},
269                                {"asymp", '\u2248'},
270                                {"ne", '\u2260'},
271                                {"equiv", '\u2261'},
272                                {"le", '\u2264'},
273                                {"ge", '\u2265'},
274                                {"sub", '\u2282'},
275                                {"sup", '\u2283'},
276                                {"nsub", '\u2284'},
277                                {"sube", '\u2286'},
278                                {"supe", '\u2287'},
279                                {"oplus", '\u2295'},
280                                {"otimes", '\u2297'},
281                                {"perp", '\u22A5'},
282                                {"sdot", '\u22C5'},
283                                {"lceil", '\u2308'},
284                                {"rceil", '\u2309'},
285                                {"lfloor", '\u230A'},
286                                {"rfloor", '\u230B'},
287                                {"lang", '\u2329'},
288                                {"rang", '\u232A'},
289                                {"loz", '\u25CA'},
290                                {"spades", '\u2660'},
291                                {"clubs", '\u2663'},
292                                {"hearts", '\u2665'},
293                                {"diams", '\u2666'},
294                                {"quot", '\u0022'},
295                                {"amp", '\u0026'},
296                                {"lt", '\u003C'},
297                                {"gt", '\u003E'},
298                                {"OElig", '\u0152'},
299                                {"oelig", '\u0153'},
300                                {"Scaron", '\u0160'},
301                                {"scaron", '\u0161'},
302                                {"Yuml", '\u0178'},
303                                {"circ", '\u02C6'},
304                                {"tilde", '\u02DC'},
305                                {"ensp", '\u2002'},
306                                {"emsp", '\u2003'},
307                                {"thinsp", '\u2009'},
308                                {"zwnj", '\u200C'},
309                                {"zwj", '\u200D'},
310                                {"lrm", '\u200E'},
311                                {"rlm", '\u200F'},
312                                {"ndash", '\u2013'},
313                                {"mdash", '\u2014'},
314                                {"lsquo", '\u2018'},
315                                {"rsquo", '\u2019'},
316                                {"sbquo", '\u201A'},
317                                {"ldquo", '\u201C'},
318                                {"rdquo", '\u201D'},
319                                {"bdquo", '\u201E'},
320                                {"dagger", '\u2020'},
321                                {"Dagger", '\u2021'},
322                                {"permil", '\u2030'},
323                                {"lsaquo", '\u2039'},
324                                {"rsaquo", '\u203A'},
325                                {"euro", '\u20AC'}
326                            };
327         }
328
329         #endregion // Constructors
330
331         #region Methods
332
333         private static readonly char[] hexChars = "0123456789abcdef".ToCharArray();
334
335         public static void HtmlAttributeEncode(string s, TextWriter output)
336         {
337             output.Write(HtmlAttributeEncode(s));
338         }
339
340         public static string HtmlAttributeEncode(string s)
341         {
342             if (null == s)
343             {
344                 return null;
345             }
346
347             var needEncode = false;
348             for (var i = 0; i < s.Length; i++)
349             {
350                 if (s[i] == '&' || s[i] == '"' || s[i] == '<')
351                 {
352                     needEncode = true;
353                     break;
354                 }
355             }
356
357             if (!needEncode)
358             {
359                 return s;
360             }
361
362             var output = new StringBuilder();
363             var len = s.Length;
364             for (var i = 0; i < len; i++)
365             {
366                 switch (s[i])
367                 {
368                     case '&':
369                         output.Append("&amp;");
370                         break;
371                     case '"':
372                         output.Append("&quot;");
373                         break;
374                     case '<':
375                         output.Append("&lt;");
376                         break;
377                     default:
378                         output.Append(s[i]);
379                         break;
380                 }
381             }
382
383             return output.ToString();
384         }
385
386         public static string UrlDecode(string str)
387         {
388             return UrlDecode(str, Encoding.UTF8);
389         }
390
391         private static char[] GetChars(MemoryStream b, Encoding e)
392         {
393             return e.GetChars(b.GetBuffer(), 0, (int) b.Length);
394         }
395
396         public static string UrlDecode(string s, Encoding e)
397         {
398             if (null == s)
399             {
400                 return null;
401             }
402
403             if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
404             {
405                 return s;
406             }
407
408             if (e == null)
409             {
410                 e = Encoding.UTF8;
411             }
412
413             var output = new StringBuilder();
414             long len = s.Length;
415             var bytes = new MemoryStream();
416
417             for (var i = 0; i < len; i++)
418             {
419                 if (s[i] == '%' && i + 2 < len && s[i + 1] != '%')
420                 {
421                     int xchar;
422                     if (s[i + 1] == 'u' && i + 5 < len)
423                     {
424                         if (bytes.Length > 0)
425                         {
426                             output.Append(GetChars(bytes, e));
427                             bytes.SetLength(0);
428                         }
429
430                         xchar = GetChar(s, i + 2, 4);
431                         if (xchar != -1)
432                         {
433                             output.Append((char) xchar);
434                             i += 5;
435                         }
436                         else
437                         {
438                             output.Append('%');
439                         }
440                     }
441                     else if ((xchar = GetChar(s, i + 1, 2)) != -1)
442                     {
443                         bytes.WriteByte((byte) xchar);
444                         i += 2;
445                     }
446                     else
447                     {
448                         output.Append('%');
449                     }
450                     continue;
451                 }
452
453                 if (bytes.Length > 0)
454                 {
455                     output.Append(GetChars(bytes, e));
456                     bytes.SetLength(0);
457                 }
458
459                 if (s[i] == '+')
460                 {
461                     output.Append(' ');
462                 }
463                 else
464                 {
465                     output.Append(s[i]);
466                 }
467             }
468
469             if (bytes.Length > 0)
470             {
471                 output.Append(GetChars(bytes, e));
472             }
473
474             return output.ToString();
475         }
476
477         public static string UrlDecode(byte[] bytes, Encoding e)
478         {
479             return bytes == null ? null : UrlDecode(bytes, 0, bytes.Length, e);
480         }
481
482         private static int GetInt(byte b)
483         {
484             var c = (char) b;
485             if (c >= '0' && c <= '9')
486             {
487                 return c - '0';
488             }
489
490             if (c >= 'a' && c <= 'f')
491             {
492                 return c - 'a' + 10;
493             }
494
495             if (c >= 'A' && c <= 'F')
496             {
497                 return c - 'A' + 10;
498             }
499
500             return -1;
501         }
502
503         private static int GetChar(byte[] bytes, int offset, int length)
504         {
505             var value = 0;
506             var end = length + offset;
507             for (var i = offset; i < end; i++)
508             {
509                 var current = GetInt(bytes[i]);
510                 if (current == -1)
511                 {
512                     return -1;
513                 }
514                 value = (value << 4) + current;
515             }
516
517             return value;
518         }
519
520         private static int GetChar(string str, int offset, int length)
521         {
522             var val = 0;
523             var end = length + offset;
524             for (var i = offset; i < end; i++)
525             {
526                 var c = str[i];
527                 if (c > 127)
528                 {
529                     return -1;
530                 }
531
532                 var current = GetInt((byte) c);
533                 if (current == -1)
534                 {
535                     return -1;
536                 }
537                 val = (val << 4) + current;
538             }
539
540             return val;
541         }
542
543         public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
544         {
545             if (bytes == null)
546             {
547                 return null;
548             }
549             if (count == 0)
550             {
551                 return String.Empty;
552             }
553
554             if (bytes == null)
555             {
556                 throw new ArgumentNullException("bytes");
557             }
558
559             if (offset < 0 || offset > bytes.Length)
560             {
561                 throw new ArgumentOutOfRangeException("offset");
562             }
563
564             if (count < 0 || offset + count > bytes.Length)
565             {
566                 throw new ArgumentOutOfRangeException("count");
567             }
568
569             var output = new StringBuilder();
570             var acc = new MemoryStream();
571
572             var end = count + offset;
573             for (var i = offset; i < end; i++)
574             {
575                 if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
576                 {
577                     int xchar;
578                     if (bytes[i + 1] == (byte) 'u' && i + 5 < end)
579                     {
580                         if (acc.Length > 0)
581                         {
582                             output.Append(GetChars(acc, e));
583                             acc.SetLength(0);
584                         }
585                         xchar = GetChar(bytes, i + 2, 4);
586                         if (xchar != -1)
587                         {
588                             output.Append((char) xchar);
589                             i += 5;
590                             continue;
591                         }
592                     }
593                     else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
594                     {
595                         acc.WriteByte((byte) xchar);
596                         i += 2;
597                         continue;
598                     }
599                 }
600
601                 if (acc.Length > 0)
602                 {
603                     output.Append(GetChars(acc, e));
604                     acc.SetLength(0);
605                 }
606
607                 if (bytes[i] == '+')
608                 {
609                     output.Append(' ');
610                 }
611                 else
612                 {
613                     output.Append((char) bytes[i]);
614                 }
615             }
616
617             if (acc.Length > 0)
618             {
619                 output.Append(GetChars(acc, e));
620             }
621
622             return output.ToString();
623         }
624
625         public static byte[] UrlDecodeToBytes(byte[] bytes)
626         {
627             return bytes == null ? null : UrlDecodeToBytes(bytes, 0, bytes.Length);
628         }
629
630         public static byte[] UrlDecodeToBytes(string str)
631         {
632             return UrlDecodeToBytes(str, Encoding.UTF8);
633         }
634
635         public static byte[] UrlDecodeToBytes(string str, Encoding e)
636         {
637             if (str == null)
638             {
639                 return null;
640             }
641
642             if (e == null)
643             {
644                 throw new ArgumentNullException("e");
645             }
646
647             return UrlDecodeToBytes(e.GetBytes(str));
648         }
649
650         public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count)
651         {
652             if (bytes == null)
653             {
654                 return null;
655             }
656             if (count == 0)
657             {
658                 return new byte[0];
659             }
660
661             var len = bytes.Length;
662             if (offset < 0 || offset >= len)
663             {
664                 throw new ArgumentOutOfRangeException("offset");
665             }
666
667             if (count < 0 || offset > len - count)
668             {
669                 throw new ArgumentOutOfRangeException("count");
670             }
671
672             var result = new MemoryStream();
673             var end = offset + count;
674             for (var i = offset; i < end; i++)
675             {
676                 var c = (char) bytes[i];
677                 if (c == '+')
678                 {
679                     c = ' ';
680                 }
681                 else if (c == '%' && i < end - 2)
682                 {
683                     var xchar = GetChar(bytes, i + 1, 2);
684                     if (xchar != -1)
685                     {
686                         c = (char) xchar;
687                         i += 2;
688                     }
689                 }
690                 result.WriteByte((byte) c);
691             }
692
693             return result.ToArray();
694         }
695
696         public static string UrlEncode(string str)
697         {
698             return UrlEncode(str, Encoding.UTF8);
699         }
700
701         public static string UrlEncode(string s, Encoding Enc)
702         {
703             if (s == null)
704             {
705                 return null;
706             }
707
708             if (s == "")
709             {
710                 return "";
711             }
712
713             var needEncode = false;
714             var len = s.Length;
715             for (var i = 0; i < len; i++)
716             {
717                 var c = s[i];
718                 if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z'))
719                 {
720                     if (NotEncoded(c))
721                     {
722                         continue;
723                     }
724
725                     needEncode = true;
726                     break;
727                 }
728             }
729
730             if (!needEncode)
731             {
732                 return s;
733             }
734
735             // avoided GetByteCount call
736             var bytes = new byte[Enc.GetMaxByteCount(s.Length)];
737             var realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0);
738             return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, realLen), 0, realLen);
739         }
740
741         public static string UrlEncode(byte[] bytes)
742         {
743             if (bytes == null)
744             {
745                 return null;
746             }
747
748             if (bytes.Length == 0)
749             {
750                 return "";
751             }
752
753             return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length), 0, bytes.Length);
754         }
755
756         public static string UrlEncode(byte[] bytes, int offset, int count)
757         {
758             if (bytes == null)
759             {
760                 return null;
761             }
762
763             if (bytes.Length == 0)
764             {
765                 return "";
766             }
767
768             return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count), offset, count);
769         }
770
771         public static byte[] UrlEncodeToBytes(string str)
772         {
773             return UrlEncodeToBytes(str, Encoding.UTF8);
774         }
775
776         public static byte[] UrlEncodeToBytes(string str, Encoding e)
777         {
778             if (str == null)
779             {
780                 return null;
781             }
782
783             if (str == "")
784             {
785                 return new byte[0];
786             }
787
788             var bytes = e.GetBytes(str);
789             return UrlEncodeToBytes(bytes, 0, bytes.Length);
790         }
791
792         public static byte[] UrlEncodeToBytes(byte[] bytes)
793         {
794             if (bytes == null)
795             {
796                 return null;
797             }
798
799             if (bytes.Length == 0)
800             {
801                 return new byte[0];
802             }
803
804             return UrlEncodeToBytes(bytes, 0, bytes.Length);
805         }
806
807         private static bool NotEncoded(char c)
808         {
809             return (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_');
810         }
811
812         private static void UrlEncodeChar(char c, Stream result, bool isUnicode)
813         {
814             if (c > 255)
815             {
816                 //FIXME: what happens when there is an internal error?
817                 //if (!isUnicode)
818                 //      throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256");
819                 int i = c;
820
821                 result.WriteByte((byte) '%');
822                 result.WriteByte((byte) 'u');
823                 var idx = i >> 12;
824                 result.WriteByte((byte) hexChars[idx]);
825                 idx = (i >> 8) & 0x0F;
826                 result.WriteByte((byte) hexChars[idx]);
827                 idx = (i >> 4) & 0x0F;
828                 result.WriteByte((byte) hexChars[idx]);
829                 idx = i & 0x0F;
830                 result.WriteByte((byte) hexChars[idx]);
831                 return;
832             }
833
834             if (c > ' ' && NotEncoded(c))
835             {
836                 result.WriteByte((byte) c);
837                 return;
838             }
839             if (c == ' ')
840             {
841                 result.WriteByte((byte) '+');
842                 return;
843             }
844             if ((c < '0') ||
845                 (c < 'A' && c > '9') ||
846                 (c > 'Z' && c < 'a') ||
847                 (c > 'z'))
848             {
849                 if (isUnicode && c > 127)
850                 {
851                     result.WriteByte((byte) '%');
852                     result.WriteByte((byte) 'u');
853                     result.WriteByte((byte) '0');
854                     result.WriteByte((byte) '0');
855                 }
856                 else
857                 {
858                     result.WriteByte((byte) '%');
859                 }
860
861                 var idx = (c) >> 4;
862                 result.WriteByte((byte) hexChars[idx]);
863                 idx = (c) & 0x0F;
864                 result.WriteByte((byte) hexChars[idx]);
865             }
866             else
867             {
868                 result.WriteByte((byte) c);
869             }
870         }
871
872         public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
873         {
874             if (bytes == null)
875             {
876                 return null;
877             }
878
879             var len = bytes.Length;
880             if (len == 0)
881             {
882                 return new byte[0];
883             }
884
885             if (offset < 0 || offset >= len)
886             {
887                 throw new ArgumentOutOfRangeException("offset");
888             }
889
890             if (count < 0 || count > len - offset)
891             {
892                 throw new ArgumentOutOfRangeException("count");
893             }
894
895             var result = new MemoryStream(count);
896             var end = offset + count;
897             for (var i = offset; i < end; i++)
898             {
899                 UrlEncodeChar((char) bytes[i], result, false);
900             }
901
902             return result.ToArray();
903         }
904
905         public static string UrlEncodeUnicode(string str)
906         {
907             if (str == null)
908             {
909                 return null;
910             }
911
912             var bytes = UrlEncodeUnicodeToBytes(str);
913             return Encoding.ASCII.GetString(bytes, 0, bytes.Length);
914         }
915
916         public static byte[] UrlEncodeUnicodeToBytes(string str)
917         {
918             if (str == null)
919             {
920                 return null;
921             }
922
923             if (str == "")
924             {
925                 return new byte[0];
926             }
927
928             var result = new MemoryStream(str.Length);
929             foreach (var c in str)
930             {
931                 UrlEncodeChar(c, result, true);
932             }
933             return result.ToArray();
934         }
935
936         /// <summary>
937         /// Decodes an HTML-encoded string and returns the decoded string.
938         /// </summary>
939         /// <param name="s">The HTML string to decode. </param>
940         /// <returns>The decoded text.</returns>
941         public static string HtmlDecode(string s)
942         {
943             if (s == null)
944             {
945                 throw new ArgumentNullException("s");
946             }
947
948             if (s.IndexOf('&') == -1)
949             {
950                 return s;
951             }
952
953             var entity = new StringBuilder();
954             var output = new StringBuilder();
955             var len = s.Length;
956             // 0 -> nothing,
957             // 1 -> right after '&'
958             // 2 -> between '&' and ';' but no '#'
959             // 3 -> '#' found after '&' and getting numbers
960             var state = 0;
961             var number = 0;
962             var have_trailing_digits = false;
963
964             for (var i = 0; i < len; i++)
965             {
966                 var c = s[i];
967                 if (state == 0)
968                 {
969                     if (c == '&')
970                     {
971                         entity.Append(c);
972                         state = 1;
973                     }
974                     else
975                     {
976                         output.Append(c);
977                     }
978                     continue;
979                 }
980
981                 if (c == '&')
982                 {
983                     state = 1;
984                     if (have_trailing_digits)
985                     {
986                         entity.Append(number.ToString(CultureInfo.InvariantCulture));
987                         have_trailing_digits = false;
988                     }
989
990                     output.Append(entity.ToString());
991                     entity.Length = 0;
992                     entity.Append('&');
993                     continue;
994                 }
995
996                 if (state == 1)
997                 {
998                     if (c == ';')
999                     {
1000                         state = 0;
1001                         output.Append(entity.ToString());
1002                         output.Append(c);
1003                         entity.Length = 0;
1004                     }
1005                     else
1006                     {
1007                         number = 0;
1008                         state = c != '#' ? 2 : 3;
1009                         entity.Append(c);
1010                     }
1011                 }
1012                 else if (state == 2)
1013                 {
1014                     entity.Append(c);
1015                     if (c == ';')
1016                     {
1017                         var key = entity.ToString();
1018                         if (key.Length > 1 && Entities.ContainsKey(key.Substring(1, key.Length - 2)))
1019                         {
1020                             key = Entities[key.Substring(1, key.Length - 2)].ToString();
1021                         }
1022
1023                         output.Append(key);
1024                         state = 0;
1025                         entity.Length = 0;
1026                     }
1027                 }
1028                 else if (state == 3)
1029                 {
1030                     if (c == ';')
1031                     {
1032                         if (number > 65535)
1033                         {
1034                             output.Append("&#");
1035                             output.Append(number.ToString(CultureInfo.InvariantCulture));
1036                             output.Append(";");
1037                         }
1038                         else
1039                         {
1040                             output.Append((char) number);
1041                         }
1042                         state = 0;
1043                         entity.Length = 0;
1044                         have_trailing_digits = false;
1045                     }
1046                     else if (Char.IsDigit(c))
1047                     {
1048                         number = number*10 + (c - '0');
1049                         have_trailing_digits = true;
1050                     }
1051                     else
1052                     {
1053                         state = 2;
1054                         if (have_trailing_digits)
1055                         {
1056                             entity.Append(number.ToString(CultureInfo.InvariantCulture));
1057                             have_trailing_digits = false;
1058                         }
1059                         entity.Append(c);
1060                     }
1061                 }
1062             }
1063
1064             if (entity.Length > 0)
1065             {
1066                 output.Append(entity.ToString());
1067             }
1068             else if (have_trailing_digits)
1069             {
1070                 output.Append(number.ToString(CultureInfo.InvariantCulture));
1071             }
1072             return output.ToString();
1073         }
1074
1075         /// <summary>
1076         /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
1077         /// </summary>
1078         /// <param name="s">The HTML string to decode</param>
1079         /// <param name="output">The TextWriter output stream containing the decoded string. </param>
1080         public static void HtmlDecode(string s, TextWriter output)
1081         {
1082             if (s != null)
1083             {
1084                 output.Write(HtmlDecode(s));
1085             }
1086         }
1087
1088         /// <summary>
1089         /// HTML-encodes a string and returns the encoded string.
1090         /// </summary>
1091         /// <param name="s">The text string to encode. </param>
1092         /// <returns>The HTML-encoded text.</returns>
1093         public static string HtmlEncode(string s)
1094         {
1095             if (s == null)
1096             {
1097                 return null;
1098             }
1099
1100             var needEncode = false;
1101             for (var i = 0; i < s.Length; i++)
1102             {
1103                 var c = s[i];
1104                 if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159)
1105                 {
1106                     needEncode = true;
1107                     break;
1108                 }
1109             }
1110
1111             if (!needEncode)
1112             {
1113                 return s;
1114             }
1115
1116             var output = new StringBuilder();
1117
1118             var len = s.Length;
1119             for (var i = 0; i < len; i++)
1120             {
1121                 switch (s[i])
1122                 {
1123                     case '&':
1124                         output.Append("&amp;");
1125                         break;
1126                     case '>':
1127                         output.Append("&gt;");
1128                         break;
1129                     case '<':
1130                         output.Append("&lt;");
1131                         break;
1132                     case '"':
1133                         output.Append("&quot;");
1134                         break;
1135                     default:
1136                         // MS starts encoding with &# from 160 and stops at 255.
1137                         // We don't do that. One reason is the 65308/65310 unicode
1138                         // characters that look like '<' and '>'.
1139 #if TARGET_JVM
1140                                         if (s [i] > 159 && s [i] < 256) {
1141 #else
1142                         if (s[i] > 159)
1143                         {
1144 #endif
1145                             output.Append("&#");
1146                             output.Append(((int) s[i]).ToString(CultureInfo.InvariantCulture));
1147                             output.Append(";");
1148                         }
1149                         else
1150                         {
1151                             output.Append(s[i]);
1152                         }
1153                         break;
1154                 }
1155             }
1156             return output.ToString();
1157         }
1158
1159         /// <summary>
1160         /// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
1161         /// </summary>
1162         /// <param name="s">The string to encode. </param>
1163         /// <param name="output">The TextWriter output stream containing the encoded string. </param>
1164         public static void HtmlEncode(string s, TextWriter output)
1165         {
1166             if (s != null)
1167             {
1168                 output.Write(HtmlEncode(s));
1169             }
1170         }
1171
1172         public static string UrlPathEncode(string s)
1173         {
1174             if (string.IsNullOrEmpty(s))
1175                 return s;
1176
1177             var result = new MemoryStream();
1178             var length = s.Length;
1179             for (var i = 0; i < length; i++)
1180             {
1181                 UrlPathEncodeChar(s[i], result);
1182             }
1183
1184             var bytes = result.ToArray();
1185             return Encoding.ASCII.GetString(bytes, 0, bytes.Length);
1186         }
1187
1188         private static void UrlPathEncodeChar(char c, Stream result)
1189         {
1190             if (c < 33 || c > 126)
1191             {
1192                 var bIn = Encoding.UTF8.GetBytes(c.ToString());
1193                 for (var i = 0; i < bIn.Length; i++)
1194                 {
1195                     result.WriteByte((byte) '%');
1196                     var idx = bIn[i] >> 4;
1197                     result.WriteByte((byte) hexChars[idx]);
1198                     idx = bIn[i] & 0x0F;
1199                     result.WriteByte((byte) hexChars[idx]);
1200                 }
1201             }
1202             else if (c == ' ')
1203             {
1204                 result.WriteByte((byte) '%');
1205                 result.WriteByte((byte) '2');
1206                 result.WriteByte((byte) '0');
1207             }
1208             else
1209                 result.WriteByte((byte) c);
1210         }
1211
1212         public static NameValueCollection ParseQueryString(string query)
1213         {
1214             return ParseQueryString(query, Encoding.UTF8);
1215         }
1216
1217         public static NameValueCollection ParseQueryString(string query, Encoding encoding)
1218         {
1219             if (query == null)
1220                 throw new ArgumentNullException("query");
1221             if (encoding == null)
1222                 throw new ArgumentNullException("encoding");
1223             if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
1224                 return new NameValueCollection();
1225             if (query[0] == '?')
1226                 query = query.Substring(1);
1227
1228             var result = new NameValueCollection();
1229             ParseQueryString(query, encoding, result);
1230             return result;
1231         }
1232
1233         internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
1234         {
1235             if (query.Length == 0)
1236             {
1237                 return;
1238             }
1239
1240             var decoded = HtmlDecode(query);
1241             var decodedLength = decoded.Length;
1242             var namePos = 0;
1243             var first = true;
1244             while (namePos <= decodedLength)
1245             {
1246                 int valuePos = -1, valueEnd = -1;
1247                 for (var q = namePos; q < decodedLength; q++)
1248                 {
1249                     if (valuePos == -1 && decoded[q] == '=')
1250                     {
1251                         valuePos = q + 1;
1252                     }
1253                     else if (decoded[q] == '&')
1254                     {
1255                         valueEnd = q;
1256                         break;
1257                     }
1258                 }
1259
1260                 if (first)
1261                 {
1262                     first = false;
1263                     if (decoded[namePos] == '?')
1264                     {
1265                         namePos++;
1266                     }
1267                 }
1268
1269                 string name;
1270                 if (valuePos == -1)
1271                 {
1272                     name = null;
1273                     valuePos = namePos;
1274                 }
1275                 else
1276                 {
1277                     name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
1278                 }
1279                 if (valueEnd < 0)
1280                 {
1281                     namePos = -1;
1282                     valueEnd = decoded.Length;
1283                 }
1284                 else
1285                 {
1286                     namePos = valueEnd + 1;
1287                 }
1288                 var value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
1289
1290                 result.Add(name, value);
1291                 if (namePos == -1)
1292                 {
1293                     break;
1294                 }
1295             }
1296         }
1297
1298         #endregion // Methods
1299     }
1300 }