Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (41.5 kB)

1
using System.Globalization;
2
using System.IO;
3
using System.Text;
4
using System.Collections.Generic;
5

    
6
#if SILVERLIGHT
7
using Hammock.Silverlight.Compat;
8
#endif
9

    
10
namespace System.Web
11
{
12
    public sealed class HttpUtility
13
    {
14
        #region Fields
15

    
16
        private static Dictionary<string, string> entities;
17
        private static readonly object lock_ = new object();
18

    
19
        #endregion // Fields
20

    
21
        private static Dictionary<string, string> Entities
22
        {
23
            get
24
            {
25
                lock (lock_)
26
                {
27
                    if (entities == null)
28
                    {
29
                        InitEntities();
30
                    }
31

    
32
                    return entities;
33
                }
34
            }
35
        }
36

    
37
        #region Constructors
38

    
39
        private static void InitEntities()
40
        {
41
            // Build the hash table of HTML entity references.  This list comes
42
            // from the HTML 4.01 W3C recommendation.
43
            entities = new Dictionary<string, string>
44
                           {
45
                               {"nbsp", "\u00A0"},
46
                               {"iexcl", "\u00A1"},
47
                               {"cent", "\u00A2"},
48
                               {"pound", "\u00A3"},
49
                               {"curren", "\u00A4"},
50
                               {"yen", "\u00A5"},
51
                               {"brvbar", "\u00A6"},
52
                               {"sect", "\u00A7"},
53
                               {"uml", "\u00A8"},
54
                               {"copy", "\u00A9"},
55
                               {"ordf", "\u00AA"},
56
                               {"laquo", "\u00AB"},
57
                               {"not", "\u00AC"},
58
                               {"shy", "\u00AD"},
59
                               {"reg", "\u00AE"},
60
                               {"macr", "\u00AF"},
61
                               {"deg", "\u00B0"},
62
                               {"plusmn", "\u00B1"},
63
                               {"sup2", "\u00B2"},
64
                               {"sup3", "\u00B3"},
65
                               {"acute", "\u00B4"},
66
                               {"micro", "\u00B5"},
67
                               {"para", "\u00B6"},
68
                               {"middot", "\u00B7"},
69
                               {"cedil", "\u00B8"},
70
                               {"sup1", "\u00B9"},
71
                               {"ordm", "\u00BA"},
72
                               {"raquo", "\u00BB"},
73
                               {"frac14", "\u00BC"},
74
                               {"frac12", "\u00BD"},
75
                               {"frac34", "\u00BE"},
76
                               {"iquest", "\u00BF"},
77
                               {"Agrave", "\u00C0"},
78
                               {"Aacute", "\u00C1"},
79
                               {"Acirc", "\u00C2"},
80
                               {"Atilde", "\u00C3"},
81
                               {"Auml", "\u00C4"},
82
                               {"Aring", "\u00C5"},
83
                               {"AElig", "\u00C6"},
84
                               {"Ccedil", "\u00C7"},
85
                               {"Egrave", "\u00C8"},
86
                               {"Eacute", "\u00C9"},
87
                               {"Ecirc", "\u00CA"},
88
                               {"Euml", "\u00CB"},
89
                               {"Igrave", "\u00CC"},
90
                               {"Iacute", "\u00CD"},
91
                               {"Icirc", "\u00CE"},
92
                               {"Iuml", "\u00CF"},
93
                               {"ETH", "\u00D0"},
94
                               {"Ntilde", "\u00D1"},
95
                               {"Ograve", "\u00D2"},
96
                               {"Oacute", "\u00D3"},
97
                               {"Ocirc", "\u00D4"},
98
                               {"Otilde", "\u00D5"},
99
                               {"Ouml", "\u00D6"},
100
                               {"times", "\u00D7"},
101
                               {"Oslash", "\u00D8"},
102
                               {"Ugrave", "\u00D9"},
103
                               {"Uacute", "\u00DA"},
104
                               {"Ucirc", "\u00DB"},
105
                               {"Uuml", "\u00DC"},
106
                               {"Yacute", "\u00DD"},
107
                               {"THORN", "\u00DE"},
108
                               {"szlig", "\u00DF"},
109
                               {"agrave", "\u00E0"},
110
                               {"aacute", "\u00E1"},
111
                               {"acirc", "\u00E2"},
112
                               {"atilde", "\u00E3"},
113
                               {"auml", "\u00E4"},
114
                               {"aring", "\u00E5"},
115
                               {"aelig", "\u00E6"},
116
                               {"ccedil", "\u00E7"},
117
                               {"egrave", "\u00E8"},
118
                               {"eacute", "\u00E9"},
119
                               {"ecirc", "\u00EA"},
120
                               {"euml", "\u00EB"},
121
                               {"igrave", "\u00EC"},
122
                               {"iacute", "\u00ED"},
123
                               {"icirc", "\u00EE"},
124
                               {"iuml", "\u00EF"},
125
                               {"eth", "\u00F0"},
126
                               {"ntilde", "\u00F1"},
127
                               {"ograve", "\u00F2"},
128
                               {"oacute", "\u00F3"},
129
                               {"ocirc", "\u00F4"},
130
                               {"otilde", "\u00F5"},
131
                               {"ouml", "\u00F6"},
132
                               {"divide", "\u00F7"},
133
                               {"oslash", "\u00F8"},
134
                               {"ugrave", "\u00F9"},
135
                               {"uacute", "\u00FA"},
136
                               {"ucirc", "\u00FB"},
137
                               {"uuml", "\u00FC"},
138
                               {"yacute", "\u00FD"},
139
                               {"thorn", "\u00FE"},
140
                               {"yuml", "\u00FF"},
141
                               {"fnof", "\u0192"},
142
                               {"Alpha", "\u0391"},
143
                               {"Beta", "\u0392"},
144
                               {"Gamma", "\u0393"},
145
                               {"Delta", "\u0394"},
146
                               {"Epsilon", "\u0395"},
147
                               {"Zeta", "\u0396"},
148
                               {"Eta", "\u0397"},
149
                               {"Theta", "\u0398"},
150
                               {"Iota", "\u0399"},
151
                               {"Kappa", "\u039A"},
152
                               {"Lambda", "\u039B"},
153
                               {"Mu", "\u039C"},
154
                               {"Nu", "\u039D"},
155
                               {"Xi", "\u039E"},
156
                               {"Omicron", "\u039F"},
157
                               {"Pi", "\u03A0"},
158
                               {"Rho", "\u03A1"},
159
                               {"Sigma", "\u03A3"},
160
                               {"Tau", "\u03A4"},
161
                               {"Upsilon", "\u03A5"},
162
                               {"Phi", "\u03A6"},
163
                               {"Chi", "\u03A7"},
164
                               {"Psi", "\u03A8"},
165
                               {"Omega", "\u03A9"},
166
                               {"alpha", "\u03B1"},
167
                               {"beta", "\u03B2"},
168
                               {"gamma", "\u03B3"},
169
                               {"delta", "\u03B4"},
170
                               {"epsilon", "\u03B5"},
171
                               {"zeta", "\u03B6"},
172
                               {"eta", "\u03B7"},
173
                               {"theta", "\u03B8"},
174
                               {"iota", "\u03B9"},
175
                               {"kappa", "\u03BA"},
176
                               {"lambda", "\u03BB"},
177
                               {"mu", "\u03BC"},
178
                               {"nu", "\u03BD"},
179
                               {"xi", "\u03BE"},
180
                               {"omicron", "\u03BF"},
181
                               {"pi", "\u03C0"},
182
                               {"rho", "\u03C1"},
183
                               {"sigmaf", "\u03C2"},
184
                               {"sigma", "\u03C3"},
185
                               {"tau", "\u03C4"},
186
                               {"upsilon", "\u03C5"},
187
                               {"phi", "\u03C6"},
188
                               {"chi", "\u03C7"},
189
                               {"psi", "\u03C8"},
190
                               {"omega", "\u03C9"},
191
                               {"thetasym", "\u03D1"},
192
                               {"upsih", "\u03D2"},
193
                               {"piv", "\u03D6"},
194
                               {"bull", "\u2022"},
195
                               {"hellip", "\u2026"},
196
                               {"prime", "\u2032"},
197
                               {"Prime", "\u2033"},
198
                               {"oline", "\u203E"},
199
                               {"frasl", "\u2044"},
200
                               {"weierp", "\u2118"},
201
                               {"image", "\u2111"},
202
                               {"real", "\u211C"},
203
                               {"trade", "\u2122"},
204
                               {"alefsym", "\u2135"},
205
                               {"larr", "\u2190"},
206
                               {"uarr", "\u2191"},
207
                               {"rarr", "\u2192"},
208
                               {"darr", "\u2193"},
209
                               {"harr", "\u2194"},
210
                               {"crarr", "\u21B5"},
211
                               {"lArr", "\u21D0"},
212
                               {"uArr", "\u21D1"},
213
                               {"rArr", "\u21D2"},
214
                               {"dArr", "\u21D3"},
215
                               {"hArr", "\u21D4"},
216
                               {"forall", "\u2200"},
217
                               {"part", "\u2202"},
218
                               {"exist", "\u2203"},
219
                               {"empty", "\u2205"},
220
                               {"nabla", "\u2207"},
221
                               {"isin", "\u2208"},
222
                               {"notin", "\u2209"},
223
                               {"ni", "\u220B"},
224
                               {"prod", "\u220F"},
225
                               {"sum", "\u2211"},
226
                               {"minus", "\u2212"},
227
                               {"lowast", "\u2217"},
228
                               {"radic", "\u221A"},
229
                               {"prop", "\u221D"},
230
                               {"infin", "\u221E"},
231
                               {"ang", "\u2220"},
232
                               {"and", "\u2227"},
233
                               {"or", "\u2228"},
234
                               {"cap", "\u2229"},
235
                               {"cup", "\u222A"},
236
                               {"int", "\u222B"},
237
                               {"there4", "\u2234"},
238
                               {"sim", "\u223C"},
239
                               {"cong", "\u2245"},
240
                               {"asymp", "\u2248"},
241
                               {"ne", "\u2260"},
242
                               {"equiv", "\u2261"},
243
                               {"le", "\u2264"},
244
                               {"ge", "\u2265"},
245
                               {"sub", "\u2282"},
246
                               {"sup", "\u2283"},
247
                               {"nsub", "\u2284"},
248
                               {"sube", "\u2286"},
249
                               {"supe", "\u2287"},
250
                               {"oplus", "\u2295"},
251
                               {"otimes", "\u2297"},
252
                               {"perp", "\u22A5"},
253
                               {"sdot", "\u22C5"},
254
                               {"lceil", "\u2308"},
255
                               {"rceil", "\u2309"},
256
                               {"lfloor", "\u230A"},
257
                               {"rfloor", "\u230B"},
258
                               {"lang", "\u2329"},
259
                               {"rang", "\u232A"},
260
                               {"loz", "\u25CA"},
261
                               {"spades", "\u2660"},
262
                               {"clubs", "\u2663"},
263
                               {"hearts", "\u2665"},
264
                               {"diams", "\u2666"},
265
                               {"quot", "\u0022"},
266
                               {"amp", "\u0026"},
267
                               {"lt", "\u003C"},
268
                               {"gt", "\u003E"},
269
                               {"OElig", "\u0152"},
270
                               {"oelig", "\u0153"},
271
                               {"Scaron", "\u0160"},
272
                               {"scaron", "\u0161"},
273
                               {"Yuml", "\u0178"},
274
                               {"circ", "\u02C6"},
275
                               {"tilde", "\u02DC"},
276
                               {"ensp", "\u2002"},
277
                               {"emsp", "\u2003"},
278
                               {"thinsp", "\u2009"},
279
                               {"zwnj", "\u200C"},
280
                               {"zwj", "\u200D"},
281
                               {"lrm", "\u200E"},
282
                               {"rlm", "\u200F"},
283
                               {"ndash", "\u2013"},
284
                               {"mdash", "\u2014"},
285
                               {"lsquo", "\u2018"},
286
                               {"rsquo", "\u2019"},
287
                               {"sbquo", "\u201A"},
288
                               {"ldquo", "\u201C"},
289
                               {"rdquo", "\u201D"},
290
                               {"bdquo", "\u201E"},
291
                               {"dagger", "\u2020"},
292
                               {"Dagger", "\u2021"},
293
                               {"permil", "\u2030"},
294
                               {"lsaquo", "\u2039"},
295
                               {"rsaquo", "\u203A"},
296
                               {"euro", "\u20AC"}
297
                           };
298
        }
299

    
300
        #endregion // Constructors
301

    
302
        #region Methods
303

    
304
        private static readonly char[] hexChars = "0123456789abcdef".ToCharArray();
305

    
306
        public static void HtmlAttributeEncode(string s, TextWriter output)
307
        {
308
            output.Write(HtmlAttributeEncode(s));
309
        }
310

    
311
        public static string HtmlAttributeEncode(string s)
312
        {
313
            if (null == s)
314
            {
315
                return null;
316
            }
317

    
318
            var needEncode = false;
319
            for (var i = 0; i < s.Length; i++)
320
            {
321
                if (s[i] == '&' || s[i] == '"' || s[i] == '<')
322
                {
323
                    needEncode = true;
324
                    break;
325
                }
326
            }
327

    
328
            if (!needEncode)
329
            {
330
                return s;
331
            }
332

    
333
            var output = new StringBuilder();
334
            var len = s.Length;
335
            for (var i = 0; i < len; i++)
336
            {
337
                switch (s[i])
338
                {
339
                    case '&':
340
                        output.Append("&amp;");
341
                        break;
342
                    case '"':
343
                        output.Append("&quot;");
344
                        break;
345
                    case '<':
346
                        output.Append("&lt;");
347
                        break;
348
                    default:
349
                        output.Append(s[i]);
350
                        break;
351
                }
352
            }
353

    
354
            return output.ToString();
355
        }
356

    
357
        public static string UrlDecode(string str)
358
        {
359
            return UrlDecode(str, Encoding.UTF8);
360
        }
361

    
362
        private static char[] GetChars(MemoryStream b, Encoding e)
363
        {
364
            return e.GetChars(b.GetBuffer(), 0, (int) b.Length);
365
        }
366

    
367
        public static string UrlDecode(string s, Encoding e)
368
        {
369
            if (null == s)
370
            {
371
                return null;
372
            }
373

    
374
            if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
375
            {
376
                return s;
377
            }
378

    
379
            if (e == null)
380
            {
381
                e = Encoding.UTF8;
382
            }
383

    
384
            var output = new StringBuilder();
385
            long len = s.Length;
386
            var bytes = new MemoryStream();
387

    
388
            for (var i = 0; i < len; i++)
389
            {
390
                if (s[i] == '%' && i + 2 < len && s[i + 1] != '%')
391
                {
392
                    int xchar;
393
                    if (s[i + 1] == 'u' && i + 5 < len)
394
                    {
395
                        if (bytes.Length > 0)
396
                        {
397
                            output.Append(GetChars(bytes, e));
398
                            bytes.SetLength(0);
399
                        }
400

    
401
                        xchar = GetChar(s, i + 2, 4);
402
                        if (xchar != -1)
403
                        {
404
                            output.Append((char) xchar);
405
                            i += 5;
406
                        }
407
                        else
408
                        {
409
                            output.Append('%');
410
                        }
411
                    }
412
                    else if ((xchar = GetChar(s, i + 1, 2)) != -1)
413
                    {
414
                        bytes.WriteByte((byte) xchar);
415
                        i += 2;
416
                    }
417
                    else
418
                    {
419
                        output.Append('%');
420
                    }
421
                    continue;
422
                }
423

    
424
                if (bytes.Length > 0)
425
                {
426
                    output.Append(GetChars(bytes, e));
427
                    bytes.SetLength(0);
428
                }
429

    
430
                if (s[i] == '+')
431
                {
432
                    output.Append(' ');
433
                }
434
                else
435
                {
436
                    output.Append(s[i]);
437
                }
438
            }
439

    
440
            if (bytes.Length > 0)
441
            {
442
                output.Append(GetChars(bytes, e));
443
            }
444

    
445
            return output.ToString();
446
        }
447

    
448
        public static string UrlDecode(byte[] bytes, Encoding e)
449
        {
450
            return bytes == null ? null : UrlDecode(bytes, 0, bytes.Length, e);
451
        }
452

    
453
        private static int GetInt(byte b)
454
        {
455
            var c = (char) b;
456
            if (c >= '0' && c <= '9')
457
            {
458
                return c - '0';
459
            }
460

    
461
            if (c >= 'a' && c <= 'f')
462
            {
463
                return c - 'a' + 10;
464
            }
465

    
466
            if (c >= 'A' && c <= 'F')
467
            {
468
                return c - 'A' + 10;
469
            }
470

    
471
            return -1;
472
        }
473

    
474
        private static int GetChar(byte[] bytes, int offset, int length)
475
        {
476
            var value = 0;
477
            var end = length + offset;
478
            for (var i = offset; i < end; i++)
479
            {
480
                var current = GetInt(bytes[i]);
481
                if (current == -1)
482
                {
483
                    return -1;
484
                }
485
                value = (value << 4) + current;
486
            }
487

    
488
            return value;
489
        }
490

    
491
        private static int GetChar(string str, int offset, int length)
492
        {
493
            var val = 0;
494
            var end = length + offset;
495
            for (var i = offset; i < end; i++)
496
            {
497
                var c = str[i];
498
                if (c > 127)
499
                {
500
                    return -1;
501
                }
502

    
503
                var current = GetInt((byte) c);
504
                if (current == -1)
505
                {
506
                    return -1;
507
                }
508
                val = (val << 4) + current;
509
            }
510

    
511
            return val;
512
        }
513

    
514
        public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
515
        {
516
            if (bytes == null)
517
            {
518
                return null;
519
            }
520
            if (count == 0)
521
            {
522
                return String.Empty;
523
            }
524

    
525
            if (bytes == null)
526
            {
527
                throw new ArgumentNullException("bytes");
528
            }
529

    
530
            if (offset < 0 || offset > bytes.Length)
531
            {
532
                throw new ArgumentOutOfRangeException("offset");
533
            }
534

    
535
            if (count < 0 || offset + count > bytes.Length)
536
            {
537
                throw new ArgumentOutOfRangeException("count");
538
            }
539

    
540
            var output = new StringBuilder();
541
            var acc = new MemoryStream();
542

    
543
            var end = count + offset;
544
            for (var i = offset; i < end; i++)
545
            {
546
                if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
547
                {
548
                    int xchar;
549
                    if (bytes[i + 1] == (byte) 'u' && i + 5 < end)
550
                    {
551
                        if (acc.Length > 0)
552
                        {
553
                            output.Append(GetChars(acc, e));
554
                            acc.SetLength(0);
555
                        }
556
                        xchar = GetChar(bytes, i + 2, 4);
557
                        if (xchar != -1)
558
                        {
559
                            output.Append((char) xchar);
560
                            i += 5;
561
                            continue;
562
                        }
563
                    }
564
                    else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
565
                    {
566
                        acc.WriteByte((byte) xchar);
567
                        i += 2;
568
                        continue;
569
                    }
570
                }
571

    
572
                if (acc.Length > 0)
573
                {
574
                    output.Append(GetChars(acc, e));
575
                    acc.SetLength(0);
576
                }
577

    
578
                if (bytes[i] == '+')
579
                {
580
                    output.Append(' ');
581
                }
582
                else
583
                {
584
                    output.Append((char) bytes[i]);
585
                }
586
            }
587

    
588
            if (acc.Length > 0)
589
            {
590
                output.Append(GetChars(acc, e));
591
            }
592

    
593
            return output.ToString();
594
        }
595

    
596
        public static byte[] UrlDecodeToBytes(byte[] bytes)
597
        {
598
            return bytes == null ? null : UrlDecodeToBytes(bytes, 0, bytes.Length);
599
        }
600

    
601
        public static byte[] UrlDecodeToBytes(string str)
602
        {
603
            return UrlDecodeToBytes(str, Encoding.UTF8);
604
        }
605

    
606
        public static byte[] UrlDecodeToBytes(string str, Encoding e)
607
        {
608
            if (str == null)
609
            {
610
                return null;
611
            }
612

    
613
            if (e == null)
614
            {
615
                throw new ArgumentNullException("e");
616
            }
617

    
618
            return UrlDecodeToBytes(e.GetBytes(str));
619
        }
620

    
621
        public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count)
622
        {
623
            if (bytes == null)
624
            {
625
                return null;
626
            }
627
            if (count == 0)
628
            {
629
                return new byte[0];
630
            }
631

    
632
            var len = bytes.Length;
633
            if (offset < 0 || offset >= len)
634
            {
635
                throw new ArgumentOutOfRangeException("offset");
636
            }
637

    
638
            if (count < 0 || offset > len - count)
639
            {
640
                throw new ArgumentOutOfRangeException("count");
641
            }
642

    
643
            var result = new MemoryStream();
644
            var end = offset + count;
645
            for (var i = offset; i < end; i++)
646
            {
647
                var c = (char) bytes[i];
648
                if (c == '+')
649
                {
650
                    c = ' ';
651
                }
652
                else if (c == '%' && i < end - 2)
653
                {
654
                    var xchar = GetChar(bytes, i + 1, 2);
655
                    if (xchar != -1)
656
                    {
657
                        c = (char) xchar;
658
                        i += 2;
659
                    }
660
                }
661
                result.WriteByte((byte) c);
662
            }
663

    
664
            return result.ToArray();
665
        }
666

    
667
        public static string UrlEncode(string str)
668
        {
669
            return UrlEncode(str, Encoding.UTF8);
670
        }
671

    
672
        public static string UrlEncode(string s, Encoding Enc)
673
        {
674
            if (s == null)
675
            {
676
                return null;
677
            }
678

    
679
            if (s == "")
680
            {
681
                return "";
682
            }
683

    
684
            var needEncode = false;
685
            var len = s.Length;
686
            for (var i = 0; i < len; i++)
687
            {
688
                var c = s[i];
689
                if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z'))
690
                {
691
                    if (NotEncoded(c))
692
                    {
693
                        continue;
694
                    }
695

    
696
                    needEncode = true;
697
                    break;
698
                }
699
            }
700

    
701
            if (!needEncode)
702
            {
703
                return s;
704
            }
705

    
706
            // avoided GetByteCount call
707
            var bytes = new byte[Enc.GetMaxByteCount(s.Length)];
708
            var realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0);
709
            return Encoding.UTF8.GetString(UrlEncodeToBytes(bytes, 0, realLen), 0, realLen);
710
        }
711

    
712
        public static string UrlEncode(byte[] bytes)
713
        {
714
            if (bytes == null)
715
            {
716
                return null;
717
            }
718

    
719
            if (bytes.Length == 0)
720
            {
721
                return "";
722
            }
723

    
724
            return Encoding.UTF8.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length), 0, bytes.Length);
725
        }
726

    
727
        public static string UrlEncode(byte[] bytes, int offset, int count)
728
        {
729
            if (bytes == null)
730
            {
731
                return null;
732
            }
733

    
734
            if (bytes.Length == 0)
735
            {
736
                return "";
737
            }
738

    
739
            return Encoding.UTF8.GetString(UrlEncodeToBytes(bytes, offset, count), offset, count);
740
        }
741

    
742
        public static byte[] UrlEncodeToBytes(string str)
743
        {
744
            return UrlEncodeToBytes(str, Encoding.UTF8);
745
        }
746

    
747
        public static byte[] UrlEncodeToBytes(string str, Encoding e)
748
        {
749
            if (str == null)
750
            {
751
                return null;
752
            }
753

    
754
            if (str == "")
755
            {
756
                return new byte[0];
757
            }
758

    
759
            var bytes = e.GetBytes(str);
760
            return UrlEncodeToBytes(bytes, 0, bytes.Length);
761
        }
762

    
763
        public static byte[] UrlEncodeToBytes(byte[] bytes)
764
        {
765
            if (bytes == null)
766
            {
767
                return null;
768
            }
769

    
770
            if (bytes.Length == 0)
771
            {
772
                return new byte[0];
773
            }
774

    
775
            return UrlEncodeToBytes(bytes, 0, bytes.Length);
776
        }
777

    
778
        private static bool NotEncoded(char c)
779
        {
780
            return (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || c == '.' || c == '_');
781
        }
782

    
783
        private static void UrlEncodeChar(char c, Stream result, bool isUnicode)
784
        {
785
            if (c > 255)
786
            {
787
                //FIXME: what happens when there is an internal error?
788
                //if (!isUnicode)
789
                //	throw new ArgumentOutOfRangeException ("c", c, "c must be less than 256");
790
                int i = c;
791

    
792
                result.WriteByte((byte) '%');
793
                result.WriteByte((byte) 'u');
794
                var idx = i >> 12;
795
                result.WriteByte((byte) hexChars[idx]);
796
                idx = (i >> 8) & 0x0F;
797
                result.WriteByte((byte) hexChars[idx]);
798
                idx = (i >> 4) & 0x0F;
799
                result.WriteByte((byte) hexChars[idx]);
800
                idx = i & 0x0F;
801
                result.WriteByte((byte) hexChars[idx]);
802
                return;
803
            }
804

    
805
            if (c > ' ' && NotEncoded(c))
806
            {
807
                result.WriteByte((byte) c);
808
                return;
809
            }
810
            if (c == ' ')
811
            {
812
                result.WriteByte((byte) '+');
813
                return;
814
            }
815
            if ((c < '0') ||
816
                (c < 'A' && c > '9') ||
817
                (c > 'Z' && c < 'a') ||
818
                (c > 'z'))
819
            {
820
                if (isUnicode && c > 127)
821
                {
822
                    result.WriteByte((byte) '%');
823
                    result.WriteByte((byte) 'u');
824
                    result.WriteByte((byte) '0');
825
                    result.WriteByte((byte) '0');
826
                }
827
                else
828
                {
829
                    result.WriteByte((byte) '%');
830
                }
831

    
832
                var idx = (c) >> 4;
833
                result.WriteByte((byte) hexChars[idx]);
834
                idx = (c) & 0x0F;
835
                result.WriteByte((byte) hexChars[idx]);
836
            }
837
            else
838
            {
839
                result.WriteByte((byte) c);
840
            }
841
        }
842

    
843
        public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
844
        {
845
            if (bytes == null)
846
            {
847
                return null;
848
            }
849

    
850
            var len = bytes.Length;
851
            if (len == 0)
852
            {
853
                return new byte[0];
854
            }
855

    
856
            if (offset < 0 || offset >= len)
857
            {
858
                throw new ArgumentOutOfRangeException("offset");
859
            }
860

    
861
            if (count < 0 || count > len - offset)
862
            {
863
                throw new ArgumentOutOfRangeException("count");
864
            }
865

    
866
            var result = new MemoryStream(count);
867
            var end = offset + count;
868
            for (var i = offset; i < end; i++)
869
            {
870
                UrlEncodeChar((char) bytes[i], result, false);
871
            }
872

    
873
            return result.ToArray();
874
        }
875

    
876
        public static string UrlEncodeUnicode(string str)
877
        {
878
            if (str == null)
879
            {
880
                return null;
881
            }
882

    
883
            var bytes = UrlEncodeUnicodeToBytes(str);
884
            return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
885
        }
886

    
887
        public static byte[] UrlEncodeUnicodeToBytes(string str)
888
        {
889
            if (str == null)
890
            {
891
                return null;
892
            }
893

    
894
            if (str == "")
895
            {
896
                return new byte[0];
897
            }
898

    
899
            var result = new MemoryStream(str.Length);
900
            foreach (var c in str)
901
            {
902
                UrlEncodeChar(c, result, true);
903
            }
904
            return result.ToArray();
905
        }
906

    
907
        /// <summary>
908
        /// Decodes an HTML-encoded string and returns the decoded string.
909
        /// </summary>
910
        /// <param name="s">The HTML string to decode. </param>
911
        /// <returns>The decoded text.</returns>
912
        public static string HtmlDecode(string s)
913
        {
914
            if (s == null)
915
            {
916
                throw new ArgumentNullException("s");
917
            }
918

    
919
            if (s.IndexOf('&') == -1)
920
            {
921
                return s;
922
            }
923

    
924
            var entity = new StringBuilder();
925
            var output = new StringBuilder();
926
            var len = s.Length;
927
            // 0 -> nothing,
928
            // 1 -> right after '&'
929
            // 2 -> between '&' and ';' but no '#'
930
            // 3 -> '#' found after '&' and getting numbers
931
            var state = 0;
932
            var number = 0;
933
            var have_trailing_digits = false;
934

    
935
            for (var i = 0; i < len; i++)
936
            {
937
                var c = s[i];
938
                if (state == 0)
939
                {
940
                    if (c == '&')
941
                    {
942
                        entity.Append(c);
943
                        state = 1;
944
                    }
945
                    else
946
                    {
947
                        output.Append(c);
948
                    }
949
                    continue;
950
                }
951

    
952
                if (c == '&')
953
                {
954
                    state = 1;
955
                    if (have_trailing_digits)
956
                    {
957
                        entity.Append(number.ToString(CultureInfo.InvariantCulture));
958
                        have_trailing_digits = false;
959
                    }
960

    
961
                    output.Append(entity.ToString());
962
                    entity.Length = 0;
963
                    entity.Append('&');
964
                    continue;
965
                }
966

    
967
                if (state == 1)
968
                {
969
                    if (c == ';')
970
                    {
971
                        state = 0;
972
                        output.Append(entity.ToString());
973
                        output.Append(c);
974
                        entity.Length = 0;
975
                    }
976
                    else
977
                    {
978
                        number = 0;
979
                        state = c != '#' ? 2 : 3;
980
                        entity.Append(c);
981
                    }
982
                }
983
                else if (state == 2)
984
                {
985
                    entity.Append(c);
986
                    if (c == ';')
987
                    {
988
                        var key = entity.ToString();
989
                        if (key.Length > 1 && Entities.ContainsKey(key.Substring(1, key.Length - 2)))
990
                        {
991
                            key = Entities[key.Substring(1, key.Length - 2)].ToString();
992
                        }
993

    
994
                        output.Append(key);
995
                        state = 0;
996
                        entity.Length = 0;
997
                    }
998
                }
999
                else if (state == 3)
1000
                {
1001
                    if (c == ';')
1002
                    {
1003
                        if (number > 65535)
1004
                        {
1005
                            output.Append("&#");
1006
                            output.Append(number.ToString(CultureInfo.InvariantCulture));
1007
                            output.Append(";");
1008
                        }
1009
                        else
1010
                        {
1011
                            output.Append((char) number);
1012
                        }
1013
                        state = 0;
1014
                        entity.Length = 0;
1015
                        have_trailing_digits = false;
1016
                    }
1017
                    else if (Char.IsDigit(c))
1018
                    {
1019
                        number = number*10 + (c - '0');
1020
                        have_trailing_digits = true;
1021
                    }
1022
                    else
1023
                    {
1024
                        state = 2;
1025
                        if (have_trailing_digits)
1026
                        {
1027
                            entity.Append(number.ToString(CultureInfo.InvariantCulture));
1028
                            have_trailing_digits = false;
1029
                        }
1030
                        entity.Append(c);
1031
                    }
1032
                }
1033
            }
1034

    
1035
            if (entity.Length > 0)
1036
            {
1037
                output.Append(entity.ToString());
1038
            }
1039
            else if (have_trailing_digits)
1040
            {
1041
                output.Append(number.ToString(CultureInfo.InvariantCulture));
1042
            }
1043
            return output.ToString();
1044
        }
1045

    
1046
        /// <summary>
1047
        /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
1048
        /// </summary>
1049
        /// <param name="s">The HTML string to decode</param>
1050
        /// <param name="output">The TextWriter output stream containing the decoded string. </param>
1051
        public static void HtmlDecode(string s, TextWriter output)
1052
        {
1053
            if (s != null)
1054
            {
1055
                output.Write(HtmlDecode(s));
1056
            }
1057
        }
1058

    
1059
        /// <summary>
1060
        /// HTML-encodes a string and returns the encoded string.
1061
        /// </summary>
1062
        /// <param name="s">The text string to encode. </param>
1063
        /// <returns>The HTML-encoded text.</returns>
1064
        public static string HtmlEncode(string s)
1065
        {
1066
            if (s == null)
1067
            {
1068
                return null;
1069
            }
1070

    
1071
            var needEncode = false;
1072
            for (var i = 0; i < s.Length; i++)
1073
            {
1074
                var c = s[i];
1075
                if (c == '&' || c == '"' || c == '<' || c == '>' || c > 159)
1076
                {
1077
                    needEncode = true;
1078
                    break;
1079
                }
1080
            }
1081

    
1082
            if (!needEncode)
1083
            {
1084
                return s;
1085
            }
1086

    
1087
            var output = new StringBuilder();
1088

    
1089
            var len = s.Length;
1090
            for (var i = 0; i < len; i++)
1091
            {
1092
                switch (s[i])
1093
                {
1094
                    case '&':
1095
                        output.Append("&amp;");
1096
                        break;
1097
                    case '>':
1098
                        output.Append("&gt;");
1099
                        break;
1100
                    case '<':
1101
                        output.Append("&lt;");
1102
                        break;
1103
                    case '"':
1104
                        output.Append("&quot;");
1105
                        break;
1106
                    default:
1107
                        // MS starts encoding with &# from 160 and stops at 255.
1108
                        // We don't do that. One reason is the 65308/65310 unicode
1109
                        // characters that look like '<' and '>'.
1110
#if TARGET_JVM
1111
					if (s [i] > 159 && s [i] < 256) {
1112
#else
1113
                        if (s[i] > 159)
1114
                        {
1115
#endif
1116
                            output.Append("&#");
1117
                            output.Append(((int) s[i]).ToString(CultureInfo.InvariantCulture));
1118
                            output.Append(";");
1119
                        }
1120
                        else
1121
                        {
1122
                            output.Append(s[i]);
1123
                        }
1124
                        break;
1125
                }
1126
            }
1127
            return output.ToString();
1128
        }
1129

    
1130
        /// <summary>
1131
        /// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
1132
        /// </summary>
1133
        /// <param name="s">The string to encode. </param>
1134
        /// <param name="output">The TextWriter output stream containing the encoded string. </param>
1135
        public static void HtmlEncode(string s, TextWriter output)
1136
        {
1137
            if (s != null)
1138
            {
1139
                output.Write(HtmlEncode(s));
1140
            }
1141
        }
1142

    
1143
        public static string UrlPathEncode(string s)
1144
        {
1145
            if (string.IsNullOrEmpty(s))
1146
                return s;
1147

    
1148
            var result = new MemoryStream();
1149
            var length = s.Length;
1150
            for (var i = 0; i < length; i++)
1151
            {
1152
                UrlPathEncodeChar(s[i], result);
1153
            }
1154

    
1155
            var bytes = result.ToArray();
1156
            return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
1157
        }
1158

    
1159
        private static void UrlPathEncodeChar(char c, Stream result)
1160
        {
1161
            if (c < 33 || c > 126)
1162
            {
1163
                var bIn = Encoding.UTF8.GetBytes(c.ToString());
1164
                for (var i = 0; i < bIn.Length; i++)
1165
                {
1166
                    result.WriteByte((byte) '%');
1167
                    var idx = bIn[i] >> 4;
1168
                    result.WriteByte((byte) hexChars[idx]);
1169
                    idx = bIn[i] & 0x0F;
1170
                    result.WriteByte((byte) hexChars[idx]);
1171
                }
1172
            }
1173
            else if (c == ' ')
1174
            {
1175
                result.WriteByte((byte) '%');
1176
                result.WriteByte((byte) '2');
1177
                result.WriteByte((byte) '0');
1178
            }
1179
            else
1180
                result.WriteByte((byte) c);
1181
        }
1182

    
1183
        public static NameValueCollection ParseQueryString(string query)
1184
        {
1185
            return ParseQueryString(query, Encoding.UTF8);
1186
        }
1187

    
1188
        public static NameValueCollection ParseQueryString(string query, Encoding encoding)
1189
        {
1190
            if (query == null)
1191
                throw new ArgumentNullException("query");
1192
            if (encoding == null)
1193
                throw new ArgumentNullException("encoding");
1194
            if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
1195
                return new NameValueCollection();
1196
            if (query[0] == '?')
1197
                query = query.Substring(1);
1198

    
1199
            var result = new NameValueCollection();
1200
            ParseQueryString(query, encoding, result);
1201
            return result;
1202
        }
1203

    
1204
        internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
1205
        {
1206
            if (query.Length == 0)
1207
            {
1208
                return;
1209
            }
1210

    
1211
            var decoded = HtmlDecode(query);
1212
            var decodedLength = decoded.Length;
1213
            var namePos = 0;
1214
            var first = true;
1215
            while (namePos <= decodedLength)
1216
            {
1217
                int valuePos = -1, valueEnd = -1;
1218
                for (var q = namePos; q < decodedLength; q++)
1219
                {
1220
                    if (valuePos == -1 && decoded[q] == '=')
1221
                    {
1222
                        valuePos = q + 1;
1223
                    }
1224
                    else if (decoded[q] == '&')
1225
                    {
1226
                        valueEnd = q;
1227
                        break;
1228
                    }
1229
                }
1230

    
1231
                if (first)
1232
                {
1233
                    first = false;
1234
                    if (decoded[namePos] == '?')
1235
                    {
1236
                        namePos++;
1237
                    }
1238
                }
1239

    
1240
                string name;
1241
                if (valuePos == -1)
1242
                {
1243
                    name = null;
1244
                    valuePos = namePos;
1245
                }
1246
                else
1247
                {
1248
                    name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
1249
                }
1250
                if (valueEnd < 0)
1251
                {
1252
                    namePos = -1;
1253
                    valueEnd = decoded.Length;
1254
                }
1255
                else
1256
                {
1257
                    namePos = valueEnd + 1;
1258
                }
1259
                var value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
1260

    
1261
                result.Add(name, value);
1262
                if (namePos == -1)
1263
                {
1264
                    break;
1265
                }
1266
            }
1267
        }
1268

    
1269
        #endregion // Methods
1270
    }
1271
}