HttpUtility.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766
  1. //
  2. // System.Web.HttpUtility
  3. //
  4. // Authors:
  5. // Patrik Torstensson ([email protected])
  6. // Wictor Wilén (decode/encode functions) ([email protected])
  7. // Tim Coleman ([email protected])
  8. // Gonzalo Paniagua Javier ([email protected])
  9. //
  10. // Copyright (C) 2005-2010 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. using System;
  32. using System.Collections;
  33. using System.Collections.Generic;
  34. using System.Collections.Specialized;
  35. using System.Globalization;
  36. using System.IO;
  37. using System.Security.Permissions;
  38. using System.Text;
  39. namespace RestSharp.Contrib
  40. {
  41. //#if !MONOTOUCH
  42. // // CAS - no InheritanceDemand here as the class is sealed
  43. // [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  44. //#endif
  45. public sealed class HttpUtility
  46. {
  47. sealed class HttpQSCollection : NameValueCollection
  48. {
  49. public override string ToString()
  50. {
  51. int count = Count;
  52. if (count == 0)
  53. return "";
  54. StringBuilder sb = new StringBuilder();
  55. string[] keys = AllKeys;
  56. for (int i = 0; i < count; i++)
  57. {
  58. sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]);
  59. }
  60. if (sb.Length > 0)
  61. sb.Length--;
  62. return sb.ToString();
  63. }
  64. }
  65. #region Constructors
  66. public HttpUtility()
  67. {
  68. }
  69. #endregion // Constructors
  70. #region Methods
  71. public static void HtmlAttributeEncode(string s, TextWriter output)
  72. {
  73. if (output == null)
  74. {
  75. #if NET_4_0
  76. throw new ArgumentNullException ("output");
  77. #else
  78. throw new NullReferenceException(".NET emulation");
  79. #endif
  80. }
  81. #if NET_4_0
  82. HttpEncoder.Current.HtmlAttributeEncode (s, output);
  83. #else
  84. output.Write(HttpEncoder.HtmlAttributeEncode(s));
  85. #endif
  86. }
  87. public static string HtmlAttributeEncode(string s)
  88. {
  89. #if NET_4_0
  90. if (s == null)
  91. return null;
  92. using (var sw = new StringWriter ()) {
  93. HttpEncoder.Current.HtmlAttributeEncode (s, sw);
  94. return sw.ToString ();
  95. }
  96. #else
  97. return HttpEncoder.HtmlAttributeEncode(s);
  98. #endif
  99. }
  100. public static string UrlDecode(string str)
  101. {
  102. return UrlDecode(str, Encoding.UTF8);
  103. }
  104. static char[] GetChars(MemoryStream b, Encoding e)
  105. {
  106. return e.GetChars(b.GetBuffer(), 0, (int)b.Length);
  107. }
  108. static void WriteCharBytes(IList buf, char ch, Encoding e)
  109. {
  110. if (ch > 255)
  111. {
  112. foreach (byte b in e.GetBytes(new char[] { ch }))
  113. buf.Add(b);
  114. }
  115. else
  116. buf.Add((byte)ch);
  117. }
  118. public static string UrlDecode(string s, Encoding e)
  119. {
  120. if (null == s)
  121. return null;
  122. if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1)
  123. return s;
  124. if (e == null)
  125. e = Encoding.UTF8;
  126. long len = s.Length;
  127. var bytes = new List<byte>();
  128. int xchar;
  129. char ch;
  130. for (int i = 0; i < len; i++)
  131. {
  132. ch = s[i];
  133. if (ch == '%' && i + 2 < len && s[i + 1] != '%')
  134. {
  135. if (s[i + 1] == 'u' && i + 5 < len)
  136. {
  137. // unicode hex sequence
  138. xchar = GetChar(s, i + 2, 4);
  139. if (xchar != -1)
  140. {
  141. WriteCharBytes(bytes, (char)xchar, e);
  142. i += 5;
  143. }
  144. else
  145. WriteCharBytes(bytes, '%', e);
  146. }
  147. else if ((xchar = GetChar(s, i + 1, 2)) != -1)
  148. {
  149. WriteCharBytes(bytes, (char)xchar, e);
  150. i += 2;
  151. }
  152. else
  153. {
  154. WriteCharBytes(bytes, '%', e);
  155. }
  156. continue;
  157. }
  158. if (ch == '+')
  159. WriteCharBytes(bytes, ' ', e);
  160. else
  161. WriteCharBytes(bytes, ch, e);
  162. }
  163. byte[] buf = bytes.ToArray();
  164. bytes = null;
  165. return e.GetString(buf);
  166. }
  167. public static string UrlDecode(byte[] bytes, Encoding e)
  168. {
  169. if (bytes == null)
  170. return null;
  171. return UrlDecode(bytes, 0, bytes.Length, e);
  172. }
  173. static int GetInt(byte b)
  174. {
  175. char c = (char)b;
  176. if (c >= '0' && c <= '9')
  177. return c - '0';
  178. if (c >= 'a' && c <= 'f')
  179. return c - 'a' + 10;
  180. if (c >= 'A' && c <= 'F')
  181. return c - 'A' + 10;
  182. return -1;
  183. }
  184. static int GetChar(byte[] bytes, int offset, int length)
  185. {
  186. int value = 0;
  187. int end = length + offset;
  188. for (int i = offset; i < end; i++)
  189. {
  190. int current = GetInt(bytes[i]);
  191. if (current == -1)
  192. return -1;
  193. value = (value << 4) + current;
  194. }
  195. return value;
  196. }
  197. static int GetChar(string str, int offset, int length)
  198. {
  199. int val = 0;
  200. int end = length + offset;
  201. for (int i = offset; i < end; i++)
  202. {
  203. char c = str[i];
  204. if (c > 127)
  205. return -1;
  206. int current = GetInt((byte)c);
  207. if (current == -1)
  208. return -1;
  209. val = (val << 4) + current;
  210. }
  211. return val;
  212. }
  213. public static string UrlDecode(byte[] bytes, int offset, int count, Encoding e)
  214. {
  215. if (bytes == null)
  216. return null;
  217. if (count == 0)
  218. return String.Empty;
  219. if (bytes == null)
  220. throw new ArgumentNullException("bytes");
  221. if (offset < 0 || offset > bytes.Length)
  222. throw new ArgumentOutOfRangeException("offset");
  223. if (count < 0 || offset + count > bytes.Length)
  224. throw new ArgumentOutOfRangeException("count");
  225. StringBuilder output = new StringBuilder();
  226. MemoryStream acc = new MemoryStream();
  227. int end = count + offset;
  228. int xchar;
  229. for (int i = offset; i < end; i++)
  230. {
  231. if (bytes[i] == '%' && i + 2 < count && bytes[i + 1] != '%')
  232. {
  233. if (bytes[i + 1] == (byte)'u' && i + 5 < end)
  234. {
  235. if (acc.Length > 0)
  236. {
  237. output.Append(GetChars(acc, e));
  238. acc.SetLength(0);
  239. }
  240. xchar = GetChar(bytes, i + 2, 4);
  241. if (xchar != -1)
  242. {
  243. output.Append((char)xchar);
  244. i += 5;
  245. continue;
  246. }
  247. }
  248. else if ((xchar = GetChar(bytes, i + 1, 2)) != -1)
  249. {
  250. acc.WriteByte((byte)xchar);
  251. i += 2;
  252. continue;
  253. }
  254. }
  255. if (acc.Length > 0)
  256. {
  257. output.Append(GetChars(acc, e));
  258. acc.SetLength(0);
  259. }
  260. if (bytes[i] == '+')
  261. {
  262. output.Append(' ');
  263. }
  264. else
  265. {
  266. output.Append((char)bytes[i]);
  267. }
  268. }
  269. if (acc.Length > 0)
  270. {
  271. output.Append(GetChars(acc, e));
  272. }
  273. acc = null;
  274. return output.ToString();
  275. }
  276. public static byte[] UrlDecodeToBytes(byte[] bytes)
  277. {
  278. if (bytes == null)
  279. return null;
  280. return UrlDecodeToBytes(bytes, 0, bytes.Length);
  281. }
  282. public static byte[] UrlDecodeToBytes(string str)
  283. {
  284. return UrlDecodeToBytes(str, Encoding.UTF8);
  285. }
  286. public static byte[] UrlDecodeToBytes(string str, Encoding e)
  287. {
  288. if (str == null)
  289. return null;
  290. if (e == null)
  291. throw new ArgumentNullException("e");
  292. return UrlDecodeToBytes(e.GetBytes(str));
  293. }
  294. public static byte[] UrlDecodeToBytes(byte[] bytes, int offset, int count)
  295. {
  296. if (bytes == null)
  297. return null;
  298. if (count == 0)
  299. return new byte[0];
  300. int len = bytes.Length;
  301. if (offset < 0 || offset >= len)
  302. throw new ArgumentOutOfRangeException("offset");
  303. if (count < 0 || offset > len - count)
  304. throw new ArgumentOutOfRangeException("count");
  305. MemoryStream result = new MemoryStream();
  306. int end = offset + count;
  307. for (int i = offset; i < end; i++)
  308. {
  309. char c = (char)bytes[i];
  310. if (c == '+')
  311. {
  312. c = ' ';
  313. }
  314. else if (c == '%' && i < end - 2)
  315. {
  316. int xchar = GetChar(bytes, i + 1, 2);
  317. if (xchar != -1)
  318. {
  319. c = (char)xchar;
  320. i += 2;
  321. }
  322. }
  323. result.WriteByte((byte)c);
  324. }
  325. return result.ToArray();
  326. }
  327. public static string UrlEncode(string str)
  328. {
  329. return UrlEncode(str, Encoding.UTF8);
  330. }
  331. public static string UrlEncode(string s, Encoding Enc)
  332. {
  333. if (s == null)
  334. return null;
  335. if (s == String.Empty)
  336. return String.Empty;
  337. bool needEncode = false;
  338. int len = s.Length;
  339. for (int i = 0; i < len; i++)
  340. {
  341. char c = s[i];
  342. if ((c < '0') || (c < 'A' && c > '9') || (c > 'Z' && c < 'a') || (c > 'z'))
  343. {
  344. if (HttpEncoder.NotEncoded(c))
  345. continue;
  346. needEncode = true;
  347. break;
  348. }
  349. }
  350. if (!needEncode)
  351. return s;
  352. // avoided GetByteCount call
  353. byte[] bytes = new byte[Enc.GetMaxByteCount(s.Length)];
  354. int realLen = Enc.GetBytes(s, 0, s.Length, bytes, 0);
  355. return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, realLen));
  356. }
  357. public static string UrlEncode(byte[] bytes)
  358. {
  359. if (bytes == null)
  360. return null;
  361. if (bytes.Length == 0)
  362. return String.Empty;
  363. return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, 0, bytes.Length));
  364. }
  365. public static string UrlEncode(byte[] bytes, int offset, int count)
  366. {
  367. if (bytes == null)
  368. return null;
  369. if (bytes.Length == 0)
  370. return String.Empty;
  371. return Encoding.ASCII.GetString(UrlEncodeToBytes(bytes, offset, count));
  372. }
  373. public static byte[] UrlEncodeToBytes(string str)
  374. {
  375. return UrlEncodeToBytes(str, Encoding.UTF8);
  376. }
  377. public static byte[] UrlEncodeToBytes(string str, Encoding e)
  378. {
  379. if (str == null)
  380. return null;
  381. if (str.Length == 0)
  382. return new byte[0];
  383. byte[] bytes = e.GetBytes(str);
  384. return UrlEncodeToBytes(bytes, 0, bytes.Length);
  385. }
  386. public static byte[] UrlEncodeToBytes(byte[] bytes)
  387. {
  388. if (bytes == null)
  389. return null;
  390. if (bytes.Length == 0)
  391. return new byte[0];
  392. return UrlEncodeToBytes(bytes, 0, bytes.Length);
  393. }
  394. public static byte[] UrlEncodeToBytes(byte[] bytes, int offset, int count)
  395. {
  396. if (bytes == null)
  397. return null;
  398. #if NET_4_0
  399. return HttpEncoder.Current.UrlEncode (bytes, offset, count);
  400. #else
  401. return HttpEncoder.UrlEncodeToBytes(bytes, offset, count);
  402. #endif
  403. }
  404. public static string UrlEncodeUnicode(string str)
  405. {
  406. if (str == null)
  407. return null;
  408. return Encoding.ASCII.GetString(UrlEncodeUnicodeToBytes(str));
  409. }
  410. public static byte[] UrlEncodeUnicodeToBytes(string str)
  411. {
  412. if (str == null)
  413. return null;
  414. if (str.Length == 0)
  415. return new byte[0];
  416. MemoryStream result = new MemoryStream(str.Length);
  417. foreach (char c in str)
  418. {
  419. HttpEncoder.UrlEncodeChar(c, result, true);
  420. }
  421. return result.ToArray();
  422. }
  423. /// <summary>
  424. /// Decodes an HTML-encoded string and returns the decoded string.
  425. /// </summary>
  426. /// <param name="s">The HTML string to decode. </param>
  427. /// <returns>The decoded text.</returns>
  428. public static string HtmlDecode(string s)
  429. {
  430. #if NET_4_0
  431. if (s == null)
  432. return null;
  433. using (var sw = new StringWriter ()) {
  434. HttpEncoder.Current.HtmlDecode (s, sw);
  435. return sw.ToString ();
  436. }
  437. #else
  438. return HttpEncoder.HtmlDecode(s);
  439. #endif
  440. }
  441. /// <summary>
  442. /// Decodes an HTML-encoded string and sends the resulting output to a TextWriter output stream.
  443. /// </summary>
  444. /// <param name="s">The HTML string to decode</param>
  445. /// <param name="output">The TextWriter output stream containing the decoded string. </param>
  446. public static void HtmlDecode(string s, TextWriter output)
  447. {
  448. if (output == null)
  449. {
  450. #if NET_4_0
  451. throw new ArgumentNullException ("output");
  452. #else
  453. throw new NullReferenceException(".NET emulation");
  454. #endif
  455. }
  456. if (!String.IsNullOrEmpty(s))
  457. {
  458. #if NET_4_0
  459. HttpEncoder.Current.HtmlDecode (s, output);
  460. #else
  461. output.Write(HttpEncoder.HtmlDecode(s));
  462. #endif
  463. }
  464. }
  465. public static string HtmlEncode(string s)
  466. {
  467. #if NET_4_0
  468. if (s == null)
  469. return null;
  470. using (var sw = new StringWriter ()) {
  471. HttpEncoder.Current.HtmlEncode (s, sw);
  472. return sw.ToString ();
  473. }
  474. #else
  475. return HttpEncoder.HtmlEncode(s);
  476. #endif
  477. }
  478. /// <summary>
  479. /// HTML-encodes a string and sends the resulting output to a TextWriter output stream.
  480. /// </summary>
  481. /// <param name="s">The string to encode. </param>
  482. /// <param name="output">The TextWriter output stream containing the encoded string. </param>
  483. public static void HtmlEncode(string s, TextWriter output)
  484. {
  485. if (output == null)
  486. {
  487. #if NET_4_0
  488. throw new ArgumentNullException ("output");
  489. #else
  490. throw new NullReferenceException(".NET emulation");
  491. #endif
  492. }
  493. if (!String.IsNullOrEmpty(s))
  494. {
  495. #if NET_4_0
  496. HttpEncoder.Current.HtmlEncode (s, output);
  497. #else
  498. output.Write(HttpEncoder.HtmlEncode(s));
  499. #endif
  500. }
  501. }
  502. #if NET_4_0
  503. public static string HtmlEncode (object value)
  504. {
  505. if (value == null)
  506. return null;
  507. IHtmlString htmlString = value as IHtmlString;
  508. if (htmlString != null)
  509. return htmlString.ToHtmlString ();
  510. return HtmlEncode (value.ToString ());
  511. }
  512. public static string JavaScriptStringEncode (string value)
  513. {
  514. return JavaScriptStringEncode (value, false);
  515. }
  516. public static string JavaScriptStringEncode (string value, bool addDoubleQuotes)
  517. {
  518. if (String.IsNullOrEmpty (value))
  519. return addDoubleQuotes ? "\"\"" : String.Empty;
  520. int len = value.Length;
  521. bool needEncode = false;
  522. char c;
  523. for (int i = 0; i < len; i++) {
  524. c = value [i];
  525. if (c >= 0 && c <= 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92) {
  526. needEncode = true;
  527. break;
  528. }
  529. }
  530. if (!needEncode)
  531. return addDoubleQuotes ? "\"" + value + "\"" : value;
  532. var sb = new StringBuilder ();
  533. if (addDoubleQuotes)
  534. sb.Append ('"');
  535. for (int i = 0; i < len; i++) {
  536. c = value [i];
  537. if (c >= 0 && c <= 7 || c == 11 || c >= 14 && c <= 31 || c == 39 || c == 60 || c == 62)
  538. sb.AppendFormat ("\\u{0:x4}", (int)c);
  539. else switch ((int)c) {
  540. case 8:
  541. sb.Append ("\\b");
  542. break;
  543. case 9:
  544. sb.Append ("\\t");
  545. break;
  546. case 10:
  547. sb.Append ("\\n");
  548. break;
  549. case 12:
  550. sb.Append ("\\f");
  551. break;
  552. case 13:
  553. sb.Append ("\\r");
  554. break;
  555. case 34:
  556. sb.Append ("\\\"");
  557. break;
  558. case 92:
  559. sb.Append ("\\\\");
  560. break;
  561. default:
  562. sb.Append (c);
  563. break;
  564. }
  565. }
  566. if (addDoubleQuotes)
  567. sb.Append ('"');
  568. return sb.ToString ();
  569. }
  570. #endif
  571. public static string UrlPathEncode(string s)
  572. {
  573. #if NET_4_0
  574. return HttpEncoder.Current.UrlPathEncode (s);
  575. #else
  576. return HttpEncoder.UrlPathEncode(s);
  577. #endif
  578. }
  579. public static NameValueCollection ParseQueryString(string query)
  580. {
  581. return ParseQueryString(query, Encoding.UTF8);
  582. }
  583. public static NameValueCollection ParseQueryString(string query, Encoding encoding)
  584. {
  585. if (query == null)
  586. throw new ArgumentNullException("query");
  587. if (encoding == null)
  588. throw new ArgumentNullException("encoding");
  589. if (query.Length == 0 || (query.Length == 1 && query[0] == '?'))
  590. return new NameValueCollection();
  591. if (query[0] == '?')
  592. query = query.Substring(1);
  593. NameValueCollection result = new HttpQSCollection();
  594. ParseQueryString(query, encoding, result);
  595. return result;
  596. }
  597. internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result)
  598. {
  599. if (query.Length == 0)
  600. return;
  601. string decoded = HtmlDecode(query);
  602. int decodedLength = decoded.Length;
  603. int namePos = 0;
  604. bool first = true;
  605. while (namePos <= decodedLength)
  606. {
  607. int valuePos = -1, valueEnd = -1;
  608. for (int q = namePos; q < decodedLength; q++)
  609. {
  610. if (valuePos == -1 && decoded[q] == '=')
  611. {
  612. valuePos = q + 1;
  613. }
  614. else if (decoded[q] == '&')
  615. {
  616. valueEnd = q;
  617. break;
  618. }
  619. }
  620. if (first)
  621. {
  622. first = false;
  623. if (decoded[namePos] == '?')
  624. namePos++;
  625. }
  626. string name, value;
  627. if (valuePos == -1)
  628. {
  629. name = null;
  630. valuePos = namePos;
  631. }
  632. else
  633. {
  634. name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding);
  635. }
  636. if (valueEnd < 0)
  637. {
  638. namePos = -1;
  639. valueEnd = decoded.Length;
  640. }
  641. else
  642. {
  643. namePos = valueEnd + 1;
  644. }
  645. value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding);
  646. result.Add(name, value);
  647. if (namePos == -1)
  648. break;
  649. }
  650. }
  651. #endregion // Methods
  652. }
  653. }