Statistics
| Branch: | Revision:

root / trunk / hammock / src / net40 / Hammock / Mono / HttpUtility.cs @ 0eea575a

History | View | Annotate | Download (42.8 kB)

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
}