root / trunk / hammock / src / net35 / 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("&"); |
370 |
break; |
371 |
case '"': |
372 |
output.Append("""); |
373 |
break; |
374 |
case '<': |
375 |
output.Append("<"); |
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("&"); |
1125 |
break; |
1126 |
case '>': |
1127 |
output.Append(">"); |
1128 |
break; |
1129 |
case '<': |
1130 |
output.Append("<"); |
1131 |
break; |
1132 |
case '"': |
1133 |
output.Append("""); |
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 |
} |