PageRenderTime 59ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/System/System/Uri.cs

https://bitbucket.org/cosi2/dotnetanywhere-wb
C# | 2543 lines | 2186 code | 167 blank | 190 comment | 459 complexity | 02dff9ea78601b7faea675060a1ee586 MD5 | raw file
  1. using System.ComponentModel;
  2. using System.IO;
  3. using System.Net;
  4. //using System.Runtime.Serialization;
  5. using System.Text;
  6. using System.Collections;
  7. using System.Collections.Generic;
  8. using System.Globalization;
  9. //
  10. // Disable warnings on Obsolete methods being used
  11. //
  12. #pragma warning disable 612
  13. namespace System
  14. {
  15. //[Serializable]
  16. // [TypeConverter(typeof(UriTypeConverter))]
  17. public class Uri //: ISerializable
  18. {
  19. // NOTES:
  20. // o scheme excludes the scheme delimiter
  21. // o port is -1 to indicate no port is defined
  22. // o path is empty or starts with / when scheme delimiter == "://"
  23. // o query is empty or starts with ? char, escaped.
  24. // o fragment is empty or starts with # char, unescaped.
  25. // o all class variables are in escaped format when they are escapable,
  26. // except cachedToString.
  27. // o UNC is supported, as starts with "\\" for windows,
  28. // or "//" with unix.
  29. private bool isUnixFilePath;
  30. private string source;
  31. private string scheme = String.Empty;
  32. private string host = String.Empty;
  33. private int port = -1;
  34. private string path = String.Empty;
  35. private string query = String.Empty;
  36. private string fragment = String.Empty;
  37. private string userinfo;
  38. private bool isUnc;
  39. private bool isOpaquePart;
  40. private bool isAbsoluteUri = true;
  41. private long scope_id;
  42. private List<string> segments;
  43. private bool userEscaped;
  44. private string cachedAbsoluteUri;
  45. private string cachedToString;
  46. private string cachedLocalPath;
  47. private int cachedHashCode;
  48. private static readonly string hexUpperChars = "0123456789ABCDEF";
  49. private static readonly string[] Empty = new string[0];
  50. private static bool isWin32 = (Path.DirectorySeparatorChar == '\\');
  51. // Fields
  52. public static readonly string SchemeDelimiter = "://";
  53. public static readonly string UriSchemeFile = "file";
  54. public static readonly string UriSchemeFtp = "ftp";
  55. public static readonly string UriSchemeGopher = "gopher";
  56. public static readonly string UriSchemeHttp = "http";
  57. public static readonly string UriSchemeHttps = "https";
  58. public static readonly string UriSchemeMailto = "mailto";
  59. public static readonly string UriSchemeNews = "news";
  60. public static readonly string UriSchemeNntp = "nntp";
  61. public static readonly string UriSchemeNetPipe = "net.pipe";
  62. public static readonly string UriSchemeNetTcp = "net.tcp";
  63. // Constructors
  64. #if MOONLIGHT
  65. public Uri (string uriString) : this (uriString, UriKind.Absolute)
  66. {
  67. }
  68. #else
  69. public Uri(string uriString)
  70. : this(uriString, false)
  71. {
  72. }
  73. #endif
  74. //protected Uri(SerializationInfo serializationInfo, StreamingContext streamingContext)
  75. //{
  76. // string uri = serializationInfo.GetString("AbsoluteUri");
  77. // if (uri.Length > 0)
  78. // {
  79. // source = uri;
  80. // ParseUri(UriKind.Absolute);
  81. // }
  82. // else
  83. // {
  84. // uri = serializationInfo.GetString("RelativeUri");
  85. // if (uri.Length > 0)
  86. // {
  87. // source = uri;
  88. // ParseUri(UriKind.Relative);
  89. // }
  90. // else
  91. // {
  92. // throw new ArgumentException("Uri string was null or empty.");
  93. // }
  94. // }
  95. //}
  96. public Uri(string uriString, UriKind uriKind)
  97. {
  98. source = uriString;
  99. ParseUri(uriKind);
  100. switch (uriKind)
  101. {
  102. case UriKind.Absolute:
  103. if (!IsAbsoluteUri)
  104. throw new UriFormatException("Invalid URI: The format of the URI could not be "
  105. + "determined.");
  106. break;
  107. case UriKind.Relative:
  108. if (IsAbsoluteUri)
  109. throw new UriFormatException("Invalid URI: The format of the URI could not be "
  110. + "determined because the parameter 'uriString' represents an absolute URI.");
  111. break;
  112. case UriKind.RelativeOrAbsolute:
  113. break;
  114. default:
  115. string msg = String.Format("Invalid UriKind value '{0}'.", uriKind);
  116. throw new ArgumentException(msg);
  117. }
  118. }
  119. //
  120. // An exception-less constructor, returns success
  121. // condition on the out parameter `success'.
  122. //
  123. Uri(string uriString, UriKind uriKind, out bool success)
  124. {
  125. if (uriString == null)
  126. {
  127. success = false;
  128. return;
  129. }
  130. if (uriKind != UriKind.RelativeOrAbsolute &&
  131. uriKind != UriKind.Absolute &&
  132. uriKind != UriKind.Relative)
  133. {
  134. string msg = String.Format("Invalid UriKind value '{0}'.", uriKind);
  135. throw new ArgumentException(msg);
  136. }
  137. source = uriString;
  138. if (ParseNoExceptions(uriKind, uriString) != null)
  139. success = false;
  140. else
  141. {
  142. success = true;
  143. switch (uriKind)
  144. {
  145. case UriKind.Absolute:
  146. if (!IsAbsoluteUri)
  147. success = false;
  148. break;
  149. case UriKind.Relative:
  150. if (IsAbsoluteUri)
  151. success = false;
  152. break;
  153. case UriKind.RelativeOrAbsolute:
  154. break;
  155. default:
  156. success = false;
  157. break;
  158. }
  159. }
  160. }
  161. public Uri(Uri baseUri, Uri relativeUri)
  162. {
  163. Merge(baseUri, relativeUri == null ? String.Empty : relativeUri.OriginalString);
  164. // FIXME: this should call UriParser.Resolve
  165. }
  166. // note: doc says that dontEscape is always false but tests show otherwise
  167. //[Obsolete]
  168. public Uri(string uriString, bool dontEscape)
  169. {
  170. userEscaped = dontEscape;
  171. source = uriString;
  172. ParseUri(UriKind.Absolute);
  173. if (!isAbsoluteUri)
  174. throw new UriFormatException("Invalid URI: The format of the URI could not be "
  175. + "determined: " + uriString);
  176. }
  177. public Uri(Uri baseUri, string relativeUri)
  178. {
  179. Merge(baseUri, relativeUri);
  180. // FIXME: this should call UriParser.Resolve
  181. }
  182. [Obsolete("dontEscape is always false")]
  183. public Uri(Uri baseUri, string relativeUri, bool dontEscape)
  184. {
  185. userEscaped = dontEscape;
  186. Merge(baseUri, relativeUri);
  187. }
  188. private void Merge(Uri baseUri, string relativeUri)
  189. {
  190. if (baseUri == null)
  191. throw new ArgumentNullException("baseUri");
  192. if (!baseUri.IsAbsoluteUri)
  193. throw new ArgumentOutOfRangeException("baseUri");
  194. if (relativeUri == null)
  195. relativeUri = String.Empty;
  196. // See RFC 2396 Par 5.2 and Appendix C
  197. // Check Windows UNC (for // it is scheme/host separator)
  198. if (relativeUri.Length >= 2 && relativeUri[0] == '\\' && relativeUri[1] == '\\')
  199. {
  200. source = relativeUri;
  201. ParseUri(UriKind.Absolute);
  202. return;
  203. }
  204. int pos = relativeUri.IndexOf(':');
  205. if (pos != -1)
  206. {
  207. int pos2 = relativeUri.IndexOfAny(new char[] { '/', '\\', '?' });
  208. // pos2 < 0 ... e.g. mailto
  209. // pos2 > pos ... to block ':' in query part
  210. if (pos2 > pos || pos2 < 0)
  211. {
  212. // in some cases, it is equivanent to new Uri (relativeUri, dontEscape):
  213. // 1) when the URI scheme in the
  214. // relative path is different from that
  215. // of the baseUri, or
  216. // 2) the URI scheme is non-standard
  217. // ones (non-standard URIs are always
  218. // treated as absolute here), or
  219. // 3) the relative URI path is absolute.
  220. if (String.CompareOrdinal(baseUri.Scheme, 0, relativeUri, 0, pos) != 0 ||
  221. !IsPredefinedScheme(baseUri.Scheme) ||
  222. (relativeUri.Length > pos + 1 && relativeUri[pos + 1] == '/'))
  223. {
  224. Uri tmp = null;
  225. if (Uri.TryCreate(relativeUri, UriKind.Absolute, out tmp))
  226. {
  227. source = relativeUri;
  228. ParseUri(UriKind.Absolute);
  229. return;
  230. }
  231. else if (pos == 1)
  232. {
  233. // special case as this looks like a windows path
  234. string msg = ParseAsWindowsAbsoluteFilePath(relativeUri);
  235. if (msg != null)
  236. throw new UriFormatException(msg);
  237. }
  238. // otherwise continue with 'full' relativeUri
  239. }
  240. else
  241. relativeUri = relativeUri.Substring(pos + 1);
  242. }
  243. }
  244. this.scheme = baseUri.scheme;
  245. this.host = baseUri.host;
  246. this.port = baseUri.port;
  247. this.userinfo = baseUri.userinfo;
  248. this.isUnc = baseUri.isUnc;
  249. this.isUnixFilePath = baseUri.isUnixFilePath;
  250. this.isOpaquePart = baseUri.isOpaquePart;
  251. if (relativeUri.Length == 0)
  252. {
  253. this.path = baseUri.path;
  254. this.query = baseUri.query;
  255. this.fragment = baseUri.fragment;
  256. return;
  257. }
  258. // 8 fragment
  259. // Note that in relative constructor, file URI cannot handle '#' as a filename character, but just regarded as a fragment identifier.
  260. string original_fragment = String.Empty;
  261. pos = relativeUri.IndexOf('#');
  262. if (pos != -1)
  263. {
  264. original_fragment = relativeUri.Substring(pos);
  265. if (userEscaped)
  266. fragment = original_fragment;
  267. else
  268. fragment = "#" + EscapeString(relativeUri.Substring(pos + 1));
  269. relativeUri = pos == 0 ? String.Empty : relativeUri.Substring(0, pos);
  270. }
  271. bool consider_query = false;
  272. // 6 query
  273. pos = relativeUri.IndexOf('?');
  274. if (pos != -1)
  275. {
  276. query = relativeUri.Substring(pos);
  277. if (!userEscaped)
  278. query = EscapeString(query);
  279. #if !NET_4_0 && !MOONLIGHT && !MOBILE
  280. consider_query = query.Length > 0;
  281. #endif
  282. relativeUri = pos == 0 ? String.Empty : relativeUri.Substring(0, pos);
  283. }
  284. else if (relativeUri.Length == 0)
  285. {
  286. // if there is no relative path then we keep the Query and Fragment from the absolute
  287. query = baseUri.query;
  288. }
  289. if (relativeUri.Length > 0 && relativeUri[0] == '/')
  290. {
  291. if (relativeUri.Length > 1 && relativeUri[1] == '/')
  292. {
  293. source = scheme + ':' + relativeUri;
  294. ParseUri(UriKind.Absolute);
  295. return;
  296. }
  297. else
  298. {
  299. path = relativeUri;
  300. if (!userEscaped)
  301. path = EscapeString(path);
  302. return;
  303. }
  304. }
  305. // par 5.2 step 6 a)
  306. path = baseUri.path;
  307. if ((relativeUri.Length > 0) || consider_query)
  308. {
  309. pos = path.LastIndexOf('/');
  310. if (pos >= 0)
  311. path = path.Substring(0, pos + 1);
  312. }
  313. if (relativeUri.Length == 0)
  314. {
  315. // when merging URI the OriginalString is not quite original
  316. source = GetLeftPart(UriPartial.Authority) + query + original_fragment;
  317. return;
  318. }
  319. // 6 b)
  320. path += relativeUri;
  321. // 6 c)
  322. int startIndex = 0;
  323. while (true)
  324. {
  325. pos = path.IndexOf("./", startIndex);
  326. if (pos == -1)
  327. break;
  328. if (pos == 0)
  329. path = path.Remove(0, 2);
  330. else if (path[pos - 1] != '.')
  331. path = path.Remove(pos, 2);
  332. else
  333. startIndex = pos + 1;
  334. }
  335. // 6 d)
  336. if (path.Length > 1 &&
  337. path[path.Length - 1] == '.' &&
  338. path[path.Length - 2] == '/')
  339. path = path.Remove(path.Length - 1, 1);
  340. // 6 e)
  341. startIndex = 0;
  342. while (true)
  343. {
  344. pos = path.IndexOf("/../", startIndex);
  345. if (pos == -1)
  346. break;
  347. if (pos == 0)
  348. {
  349. startIndex = 3;
  350. continue;
  351. }
  352. int pos2 = path.LastIndexOf('/', pos - 1);
  353. if (pos2 == -1)
  354. {
  355. startIndex = pos + 1;
  356. }
  357. else
  358. {
  359. if (path.Substring(pos2 + 1, pos - pos2 - 1) != "..")
  360. path = path.Remove(pos2 + 1, pos - pos2 + 3);
  361. else
  362. startIndex = pos + 1;
  363. }
  364. }
  365. // 6 f)
  366. if (path.Length > 3 && path.EndsWith("/.."))
  367. {
  368. pos = path.LastIndexOf('/', path.Length - 4);
  369. if (pos != -1)
  370. if (path.Substring(pos + 1, path.Length - pos - 4) != "..")
  371. path = path.Remove(pos + 1, path.Length - pos - 1);
  372. }
  373. // 6 g)
  374. while (path.StartsWith("/../"/*, StringComparison.Ordinal*/))
  375. path = path.Substring(3);
  376. if (!userEscaped)
  377. path = EscapeString(path);
  378. // when merging URI the OriginalString is not quite original
  379. source = GetLeftPart(UriPartial.Authority) + path + query + original_fragment;
  380. }
  381. // Properties
  382. public string AbsolutePath
  383. {
  384. get
  385. {
  386. EnsureAbsoluteUri();
  387. if (scheme == "mailto" || scheme == "file")
  388. // faster (mailto) and special (file) cases
  389. return path;
  390. if (path.Length == 0)
  391. {
  392. string start = scheme + SchemeDelimiter;
  393. if (path.StartsWith(start/*, StringComparison.Ordinal*/))
  394. return "/";
  395. else
  396. return String.Empty;
  397. }
  398. return path;
  399. }
  400. }
  401. public string AbsoluteUri
  402. {
  403. get
  404. {
  405. EnsureAbsoluteUri();
  406. if (cachedAbsoluteUri == null)
  407. {
  408. cachedAbsoluteUri = GetLeftPart(UriPartial.Path);
  409. if (query.Length > 0)
  410. cachedAbsoluteUri += query;
  411. if (fragment.Length > 0)
  412. cachedAbsoluteUri += fragment;
  413. }
  414. return cachedAbsoluteUri;
  415. }
  416. }
  417. public string Authority
  418. {
  419. get
  420. {
  421. EnsureAbsoluteUri();
  422. return (GetDefaultPort(Scheme) == port)
  423. ? host : host + ":" + port;
  424. }
  425. }
  426. public string Fragment
  427. {
  428. get
  429. {
  430. EnsureAbsoluteUri();
  431. return fragment;
  432. }
  433. }
  434. public string Host
  435. {
  436. get
  437. {
  438. EnsureAbsoluteUri();
  439. return host;
  440. }
  441. }
  442. public UriHostNameType HostNameType
  443. {
  444. get
  445. {
  446. EnsureAbsoluteUri();
  447. UriHostNameType ret = CheckHostName(Host);
  448. if (ret != UriHostNameType.Unknown)
  449. return ret;
  450. if (scheme == "mailto")
  451. return UriHostNameType.Basic;
  452. return (IsFile) ? UriHostNameType.Basic : ret;
  453. }
  454. }
  455. public bool IsDefaultPort
  456. {
  457. get
  458. {
  459. EnsureAbsoluteUri();
  460. return GetDefaultPort(Scheme) == port;
  461. }
  462. }
  463. public bool IsFile
  464. {
  465. get
  466. {
  467. EnsureAbsoluteUri();
  468. return (Scheme == UriSchemeFile);
  469. }
  470. }
  471. public bool IsLoopback
  472. {
  473. get
  474. {
  475. EnsureAbsoluteUri();
  476. if (Host.Length == 0)
  477. {
  478. return IsFile;
  479. }
  480. if (host == "loopback" || host == "localhost")
  481. return true;
  482. IPAddress result;
  483. if (IPAddress.TryParse(host, out result))
  484. if (IPAddress.Loopback.Equals(result))
  485. return true;
  486. /*IPv6Address result6;
  487. if (IPv6Address.TryParse(host, out result6))
  488. {
  489. if (IPv6Address.IsLoopback(result6))
  490. return true;
  491. }*/
  492. return false;
  493. }
  494. }
  495. public bool IsUnc
  496. {
  497. // rule: This should be true only if
  498. // - uri string starts from "\\", or
  499. // - uri string starts from "//" (Samba way)
  500. get
  501. {
  502. EnsureAbsoluteUri();
  503. return isUnc;
  504. }
  505. }
  506. private bool IsLocalIdenticalToAbsolutePath()
  507. {
  508. if (IsFile)
  509. return false;
  510. if ((scheme == Uri.UriSchemeNews) || (scheme == Uri.UriSchemeNntp) || (scheme == Uri.UriSchemeFtp))
  511. return false;
  512. return IsWellFormedOriginalString();
  513. }
  514. public string LocalPath
  515. {
  516. get
  517. {
  518. EnsureAbsoluteUri();
  519. if (cachedLocalPath != null)
  520. return cachedLocalPath;
  521. if (IsLocalIdenticalToAbsolutePath())
  522. {
  523. cachedLocalPath = Unescape(AbsolutePath);
  524. return cachedLocalPath;
  525. }
  526. if (!IsUnc)
  527. {
  528. string p = Unescape(path);
  529. bool windows = (path.Length > 3 && path[1] == ':' &&
  530. (path[2] == '\\' || path[2] == '/'));
  531. if (windows)
  532. cachedLocalPath = p.Replace('/', '\\');
  533. else
  534. cachedLocalPath = p;
  535. }
  536. else
  537. {
  538. // support *nix and W32 styles
  539. if (path.Length > 1 && path[1] == ':')
  540. cachedLocalPath = Unescape(path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
  541. // LAMESPEC: ok, now we cannot determine
  542. // if such URI like "file://foo/bar" is
  543. // Windows UNC or unix file path, so
  544. // they should be handled differently.
  545. else if (System.IO.Path.DirectorySeparatorChar == '\\')
  546. {
  547. string h = host;
  548. if (path.Length > 0)
  549. {
  550. if ((path.Length > 1) || (path[0] != '/'))
  551. {
  552. h += path.Replace('/', '\\');
  553. }
  554. }
  555. cachedLocalPath = "\\\\" + Unescape(h);
  556. }
  557. else
  558. cachedLocalPath = Unescape(path);
  559. }
  560. if (cachedLocalPath.Length == 0)
  561. cachedLocalPath = Path.DirectorySeparatorChar.ToString();
  562. return cachedLocalPath;
  563. }
  564. }
  565. public string PathAndQuery
  566. {
  567. get
  568. {
  569. EnsureAbsoluteUri();
  570. return path + Query;
  571. }
  572. }
  573. public int Port
  574. {
  575. get
  576. {
  577. EnsureAbsoluteUri();
  578. return port;
  579. }
  580. }
  581. public string Query
  582. {
  583. get
  584. {
  585. EnsureAbsoluteUri();
  586. return query;
  587. }
  588. }
  589. public string Scheme
  590. {
  591. get
  592. {
  593. EnsureAbsoluteUri();
  594. return scheme;
  595. }
  596. }
  597. public string[] Segments
  598. {
  599. get
  600. {
  601. EnsureAbsoluteUri();
  602. // return a (pre-allocated) empty array
  603. if (path.Length == 0)
  604. return Empty;
  605. // do not return the original array (since items can be changed)
  606. if (segments != null)
  607. return segments.ToArray();
  608. List<string> list = new List<string>();
  609. StringBuilder current = new StringBuilder();
  610. for (int i = 0; i < path.Length; i++)
  611. {
  612. switch (path[i])
  613. {
  614. case '/':
  615. case '\\':
  616. current.Append(path[i]);
  617. list.Add(current.ToString());
  618. current.Length = 0;
  619. break;
  620. case '%':
  621. if ((i < path.Length - 2) && (path[i + 1] == '5' && path[i + 2] == 'C'))
  622. {
  623. current.Append("%5C");
  624. list.Add(current.ToString());
  625. current.Length = 0;
  626. i += 2;
  627. }
  628. else
  629. {
  630. current.Append('%');
  631. }
  632. break;
  633. default:
  634. current.Append(path[i]);
  635. break;
  636. }
  637. }
  638. if (current.Length > 0)
  639. list.Add(current.ToString());
  640. if (IsFile && (list.Count > 0))
  641. {
  642. string first = list[0];
  643. if ((first.Length > 1) && (first[1] == ':'))
  644. {
  645. list.Insert(0, "/");
  646. }
  647. }
  648. segments = list;
  649. return segments.ToArray();
  650. }
  651. }
  652. public bool UserEscaped
  653. {
  654. get { return userEscaped; }
  655. }
  656. public string UserInfo
  657. {
  658. get
  659. {
  660. EnsureAbsoluteUri();
  661. return userinfo == null ? String.Empty : userinfo;
  662. }
  663. }
  664. public string DnsSafeHost
  665. {
  666. get
  667. {
  668. EnsureAbsoluteUri();
  669. string host = Host;
  670. /*if (HostNameType == UriHostNameType.IPv6)
  671. {
  672. host = Host.Substring(1, Host.Length - 2);
  673. if (scope_id != 0)
  674. host += "%" + scope_id.ToString();
  675. }*/
  676. return Unescape(host);
  677. }
  678. }
  679. #if NET_2_0
  680. public
  681. #else
  682. internal
  683. #endif
  684. bool IsAbsoluteUri
  685. {
  686. get { return isAbsoluteUri; }
  687. }
  688. // LAMESPEC: source field is supplied in such case that this
  689. // property makes sense. For such case that source field is
  690. // not supplied (i.e. .ctor(Uri, string), this property
  691. // makes no sense. To avoid silly regression it just returns
  692. // ToString() value now. See bug #78374.
  693. public string OriginalString
  694. {
  695. get { return source != null ? source : ToString(); }
  696. }
  697. // Methods
  698. public static UriHostNameType CheckHostName(string name)
  699. {
  700. if (name == null || name.Length == 0)
  701. return UriHostNameType.Unknown;
  702. if (IsIPv4Address(name))
  703. return UriHostNameType.IPv4;
  704. if (IsDomainAddress(name))
  705. return UriHostNameType.Dns;
  706. //IPv6Address addr;
  707. //if (IPv6Address.TryParse(name, out addr))
  708. // return UriHostNameType.IPv6;
  709. return UriHostNameType.Unknown;
  710. }
  711. internal static bool IsIPv4Address(string name)
  712. {
  713. string[] captures = name.Split(new char[] { '.' });
  714. if (captures.Length != 4)
  715. return false;
  716. for (int i = 0; i < 4; i++)
  717. {
  718. int length;
  719. length = captures[i].Length;
  720. if (length == 0)
  721. return false;
  722. uint number;
  723. if (!UInt32.TryParse(captures[i], out number))
  724. return false;
  725. if (number > 255)
  726. return false;
  727. }
  728. return true;
  729. }
  730. internal static bool IsDomainAddress(string name)
  731. {
  732. int len = name.Length;
  733. int count = 0;
  734. for (int i = 0; i < len; i++)
  735. {
  736. char c = name[i];
  737. if (count == 0)
  738. {
  739. if (!Char.IsLetterOrDigit(c))
  740. return false;
  741. }
  742. else if (c == '.')
  743. {
  744. // www..host.com is bad
  745. if (i + 1 < len && name[i + 1] == '.')
  746. return false;
  747. count = 0;
  748. }
  749. else if (!Char.IsLetterOrDigit(c) && c != '-' && c != '_')
  750. {
  751. return false;
  752. }
  753. if (++count == 64)
  754. return false;
  755. }
  756. return true;
  757. }
  758. #if !NET_2_1
  759. // [Obsolete("This method does nothing, it has been obsoleted")]
  760. protected virtual void Canonicalize()
  761. {
  762. //
  763. // This is flagged in the Microsoft documentation as used
  764. // internally, no longer in use, and Obsolete.
  765. //
  766. }
  767. // [MonoTODO("Find out what this should do")]
  768. //[Obsolete]
  769. protected virtual void CheckSecurity()
  770. {
  771. }
  772. #endif // NET_2_1
  773. // defined in RFC3986 as = ALPHA *( ALPHA / DIGIT / "+" / "-" / ".")
  774. public static bool CheckSchemeName(string schemeName)
  775. {
  776. if (schemeName == null || schemeName.Length == 0)
  777. return false;
  778. if (!IsAlpha(schemeName[0]))
  779. return false;
  780. int len = schemeName.Length;
  781. for (int i = 1; i < len; i++)
  782. {
  783. char c = schemeName[i];
  784. if (!Char.IsDigit(c) && !IsAlpha(c) && c != '.' && c != '+' && c != '-')
  785. return false;
  786. }
  787. return true;
  788. }
  789. private static bool IsAlpha(char c)
  790. {
  791. // as defined in rfc2234
  792. // %x41-5A / %x61-7A (A-Z / a-z)
  793. int i = (int)c;
  794. return (((i >= 0x41) && (i <= 0x5A)) || ((i >= 0x61) && (i <= 0x7A)));
  795. }
  796. public override bool Equals(object comparant)
  797. {
  798. if (comparant == null)
  799. return false;
  800. Uri uri = comparant as Uri;
  801. if ((object)uri == null)
  802. {
  803. string s = comparant as String;
  804. if (s == null)
  805. return false;
  806. uri = new Uri(s);
  807. }
  808. return InternalEquals(uri);
  809. }
  810. // Assumes: uri != null
  811. bool InternalEquals(Uri uri)
  812. {
  813. if (this.isAbsoluteUri != uri.isAbsoluteUri)
  814. return false;
  815. if (!this.isAbsoluteUri)
  816. return this.source == uri.source;
  817. CultureInfo inv = CultureInfo.InvariantCulture;
  818. return this.scheme.ToLower(inv) == uri.scheme.ToLower(inv)
  819. && this.host.ToLower(inv) == uri.host.ToLower(inv)
  820. && this.port == uri.port
  821. && this.query == uri.query
  822. && this.path == uri.path;
  823. }
  824. public static bool operator ==(Uri u1, Uri u2)
  825. {
  826. return object.Equals(u1, u2);
  827. }
  828. public static bool operator !=(Uri u1, Uri u2)
  829. {
  830. return !(u1 == u2);
  831. }
  832. public override int GetHashCode()
  833. {
  834. if (cachedHashCode == 0)
  835. {
  836. CultureInfo inv = CultureInfo.InvariantCulture;
  837. if (isAbsoluteUri)
  838. {
  839. cachedHashCode = scheme.ToLower(inv).GetHashCode()
  840. ^ host.ToLower(inv).GetHashCode()
  841. ^ port
  842. ^ query.GetHashCode()
  843. ^ path.GetHashCode();
  844. }
  845. else
  846. {
  847. cachedHashCode = source.GetHashCode();
  848. }
  849. }
  850. return cachedHashCode;
  851. }
  852. public string GetLeftPart(UriPartial part)
  853. {
  854. EnsureAbsoluteUri();
  855. int defaultPort;
  856. switch (part)
  857. {
  858. case UriPartial.Scheme:
  859. return scheme + GetOpaqueWiseSchemeDelimiter();
  860. case UriPartial.Authority:
  861. if ((scheme == Uri.UriSchemeMailto) || (scheme == Uri.UriSchemeNews))
  862. return String.Empty;
  863. StringBuilder s = new StringBuilder();
  864. s.Append(scheme);
  865. s.Append(GetOpaqueWiseSchemeDelimiter());
  866. if (path.Length > 1 && path[1] == ':' && (Uri.UriSchemeFile == scheme))
  867. s.Append('/'); // win32 file
  868. if (userinfo != null)
  869. s.Append(userinfo).Append('@');
  870. s.Append(host);
  871. defaultPort = GetDefaultPort(scheme);
  872. if ((port != -1) && (port != defaultPort))
  873. s.Append(':').Append(port);
  874. return s.ToString();
  875. case UriPartial.Path:
  876. StringBuilder sb = new StringBuilder();
  877. sb.Append(scheme);
  878. sb.Append(GetOpaqueWiseSchemeDelimiter());
  879. if (path.Length > 1 && path[1] == ':' && (Uri.UriSchemeFile == scheme))
  880. sb.Append('/'); // win32 file
  881. if (userinfo != null)
  882. sb.Append(userinfo).Append('@');
  883. sb.Append(host);
  884. defaultPort = GetDefaultPort(scheme);
  885. if ((port != -1) && (port != defaultPort))
  886. sb.Append(':').Append(port);
  887. if (path.Length > 0)
  888. {
  889. if (scheme == "mailto" || scheme == "news")
  890. sb.Append(path);
  891. else
  892. sb.Append(Reduce(path, CompactEscaped(scheme)));
  893. }
  894. return sb.ToString();
  895. }
  896. return null;
  897. }
  898. public static int FromHex(char digit)
  899. {
  900. if ('0' <= digit && digit <= '9')
  901. {
  902. return (int)(digit - '0');
  903. }
  904. if ('a' <= digit && digit <= 'f')
  905. return (int)(digit - 'a' + 10);
  906. if ('A' <= digit && digit <= 'F')
  907. return (int)(digit - 'A' + 10);
  908. throw new ArgumentException("digit");
  909. }
  910. public static string HexEscape(char character)
  911. {
  912. if (character > 255)
  913. {
  914. throw new ArgumentOutOfRangeException("character");
  915. }
  916. return "%" + hexUpperChars[((character & 0xf0) >> 4)]
  917. + hexUpperChars[((character & 0x0f))];
  918. }
  919. public static char HexUnescape(string pattern, ref int index)
  920. {
  921. if (pattern == null)
  922. throw new ArgumentException("pattern");
  923. if (index < 0 || index >= pattern.Length)
  924. throw new ArgumentOutOfRangeException("index");
  925. if (!IsHexEncoding(pattern, index))
  926. return pattern[index++];
  927. index++;
  928. int msb = FromHex(pattern[index++]);
  929. int lsb = FromHex(pattern[index++]);
  930. return (char)((msb << 4) | lsb);
  931. }
  932. public static bool IsHexDigit(char digit)
  933. {
  934. return (('0' <= digit && digit <= '9') ||
  935. ('a' <= digit && digit <= 'f') ||
  936. ('A' <= digit && digit <= 'F'));
  937. }
  938. public static bool IsHexEncoding(string pattern, int index)
  939. {
  940. if ((index + 3) > pattern.Length)
  941. return false;
  942. return ((pattern[index++] == '%') &&
  943. IsHexDigit(pattern[index++]) &&
  944. IsHexDigit(pattern[index]));
  945. }
  946. //
  947. // Implemented by copying most of the MakeRelative code
  948. //
  949. public Uri MakeRelativeUri(Uri uri)
  950. {
  951. #if NET_4_0 || MOONLIGHT || MOBILE
  952. if (uri == null)
  953. throw new ArgumentNullException ("uri");
  954. #endif
  955. if (Host != uri.Host || Scheme != uri.Scheme)
  956. return uri;
  957. string result = String.Empty;
  958. if (this.path != uri.path)
  959. {
  960. string[] segments = this.Segments;
  961. string[] segments2 = uri.Segments;
  962. int k = 0;
  963. int max = Math.Min(segments.Length, segments2.Length);
  964. for (; k < max; k++)
  965. if (segments[k] != segments2[k])
  966. break;
  967. for (int i = k + 1; i < segments.Length; i++)
  968. result += "../";
  969. for (int i = k; i < segments2.Length; i++)
  970. result += segments2[i];
  971. }
  972. uri.AppendQueryAndFragment(ref result);
  973. return new Uri(result, UriKind.Relative);
  974. }
  975. [Obsolete("Use MakeRelativeUri(Uri uri) instead.")]
  976. public string MakeRelative(Uri toUri)
  977. {
  978. if ((this.Scheme != toUri.Scheme) ||
  979. (this.Authority != toUri.Authority))
  980. return toUri.ToString();
  981. string result = String.Empty;
  982. if (this.path != toUri.path)
  983. {
  984. string[] segments = this.Segments;
  985. string[] segments2 = toUri.Segments;
  986. int k = 0;
  987. int max = Math.Min(segments.Length, segments2.Length);
  988. for (; k < max; k++)
  989. if (segments[k] != segments2[k])
  990. break;
  991. for (int i = k + 1; i < segments.Length; i++)
  992. result += "../";
  993. for (int i = k; i < segments2.Length; i++)
  994. result += segments2[i];
  995. }
  996. // Important: MakeRelative does not append fragment or query.
  997. return result;
  998. }
  999. void AppendQueryAndFragment(ref string result)
  1000. {
  1001. if (query.Length > 0)
  1002. {
  1003. string q = query[0] == '?' ? '?' + Unescape(query.Substring(1), true, false) : Unescape(query, false);
  1004. result += q;
  1005. }
  1006. if (fragment.Length > 0)
  1007. result += Unescape(fragment, true, false);
  1008. }
  1009. public override string ToString()
  1010. {
  1011. if (cachedToString != null)
  1012. return cachedToString;
  1013. if (isAbsoluteUri)
  1014. {
  1015. cachedToString = Unescape(GetLeftPart(UriPartial.Path), true);
  1016. AppendQueryAndFragment(ref cachedToString);
  1017. }
  1018. else
  1019. {
  1020. // Everything is contained in path in this case.
  1021. cachedToString = path;
  1022. }
  1023. return cachedToString;
  1024. }
  1025. //protected void GetObjectData(SerializationInfo info, StreamingContext context)
  1026. //{
  1027. // if (this.isAbsoluteUri)
  1028. // {
  1029. // info.AddValue("AbsoluteUri", this.AbsoluteUri);
  1030. // }
  1031. // else
  1032. // {
  1033. // info.AddValue("AbsoluteUri", String.Empty);
  1034. // info.AddValue("RelativeUri", this.OriginalString);
  1035. // }
  1036. //}
  1037. //void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
  1038. //{
  1039. // GetObjectData(info, context);
  1040. //}
  1041. // Internal Methods
  1042. //[Obsolete]
  1043. protected virtual void Escape()
  1044. {
  1045. path = EscapeString(path);
  1046. }
  1047. #if MOONLIGHT
  1048. static string EscapeString (string str)
  1049. #else
  1050. //[Obsolete]
  1051. protected static string EscapeString(string str)
  1052. #endif
  1053. {
  1054. return EscapeString(str, Uri.EscapeCommonHexBrackets);
  1055. }
  1056. private const string EscapeCommon = "<>%\"{}|\\^`";
  1057. private const string EscapeReserved = ";/?:@&=+$,";
  1058. private const string EscapeFragment = "#";
  1059. private const string EscapeBrackets = "[]";
  1060. private const string EscapeNews = EscapeCommon + EscapeBrackets + "?";
  1061. private const string EscapeCommonHex = EscapeCommon + EscapeFragment;
  1062. private const string EscapeCommonBrackets = EscapeCommon + EscapeBrackets;
  1063. internal const string EscapeCommonHexBrackets = EscapeCommon + EscapeFragment + EscapeBrackets;
  1064. internal const string EscapeCommonHexBracketsQuery = EscapeCommonHexBrackets + "?";
  1065. internal static string EscapeString(string str, string escape)
  1066. {
  1067. return EscapeString(str, escape, true);
  1068. }
  1069. internal static string EscapeString(string str, string escape, bool nonAsciiEscape)
  1070. {
  1071. if (String.IsNullOrEmpty(str))
  1072. return String.Empty;
  1073. StringBuilder s = new StringBuilder();
  1074. int len = str.Length;
  1075. for (int i = 0; i < len; i++)
  1076. {
  1077. // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | ","
  1078. // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
  1079. // control = <US-ASCII coded characters 00-1F and 7F hexadecimal>
  1080. // space = <US-ASCII coded character 20 hexadecimal>
  1081. // delims = "<" | ">" | "#" | "%" | <">
  1082. // unwise = "{" | "}" | "|" | "\" | "^" | "[" | "]" | "`"
  1083. // check for escape code already placed in str,
  1084. // i.e. for encoding that follows the pattern
  1085. // "%hexhex" in a string, where "hex" is a digit from 0-9
  1086. // or a letter from A-F (case-insensitive).
  1087. if (IsHexEncoding(str, i))
  1088. {
  1089. // if ,yes , copy it as is
  1090. s.Append(str.Substring(i, 3));
  1091. i += 2;
  1092. continue;
  1093. }
  1094. char c = str[i];
  1095. bool outside_limited_ascii = ((c <= 0x20) || (c >= 0x7f));
  1096. bool needs_escape = (escape.IndexOf(c) != -1);
  1097. if (nonAsciiEscape && outside_limited_ascii)
  1098. {
  1099. byte[] data = Encoding.UTF8.GetBytes(new char[] { c });
  1100. int length = data.Length;
  1101. for (int j = 0; j < length; j++)
  1102. {
  1103. c = (char)data[j];
  1104. if (needs_escape || nonAsciiEscape)
  1105. s.Append(HexEscape(c));
  1106. else
  1107. s.Append(c);
  1108. }
  1109. }
  1110. else if (needs_escape)
  1111. {
  1112. s.Append(HexEscape(c));
  1113. }
  1114. else
  1115. {
  1116. s.Append(c);
  1117. }
  1118. }
  1119. return s.ToString();
  1120. }
  1121. // On .NET 1.x, this method is called from .ctor(). When overriden, we
  1122. // can avoid the "absolute uri" constraints of the .ctor() by
  1123. // overriding with custom code.
  1124. [Obsolete("The method has been deprecated. It is not used by the system.")]
  1125. protected virtual void Parse()
  1126. {
  1127. }
  1128. private void ParseUri(UriKind kind)
  1129. {
  1130. Parse(kind, source);
  1131. if (userEscaped)
  1132. return;
  1133. // non-ascii characters are not escaped for the host name
  1134. host = EscapeString(host, EscapeCommonHex, false);
  1135. if (host.Length > 1 && host[0] != '[' && host[host.Length - 1] != ']')
  1136. {
  1137. // host name present (but not an IPv6 address)
  1138. host = host.ToLower(CultureInfo.InvariantCulture);
  1139. }
  1140. if (isAbsoluteUri && (path.Length > 0))
  1141. path = EscapeString(path);
  1142. }
  1143. #if MOONLIGHT
  1144. string Unescape (string str)
  1145. #else
  1146. //[Obsolete]
  1147. protected virtual string Unescape(string str)
  1148. #endif
  1149. {
  1150. return Unescape(str, false, false);
  1151. }
  1152. internal static string Unescape(string str, bool excludeSpecial)
  1153. {
  1154. return Unescape(str, excludeSpecial, excludeSpecial);
  1155. }
  1156. internal static string Unescape(string str, bool excludeSpecial, bool excludeBackslash)
  1157. {
  1158. if (String.IsNullOrEmpty(str))
  1159. return String.Empty;
  1160. StringBuilder s = new StringBuilder();
  1161. int len = str.Length;
  1162. for (int i = 0; i < len; i++)
  1163. {
  1164. char c = str[i];
  1165. if (c == '%')
  1166. {
  1167. char surrogate;
  1168. char x = HexUnescapeMultiByte(str, ref i, out surrogate);
  1169. if (excludeSpecial && x == '#')
  1170. s.Append("%23");
  1171. else if (excludeSpecial && x == '%')
  1172. s.Append("%25");
  1173. else if (excludeSpecial && x == '?')
  1174. s.Append("%3F");
  1175. else if (excludeBackslash && x == '\\')
  1176. s.Append("%5C");
  1177. else
  1178. {
  1179. s.Append(x);
  1180. if (surrogate != char.MinValue)
  1181. s.Append(surrogate);
  1182. }
  1183. i--;
  1184. }
  1185. else
  1186. s.Append(c);
  1187. }
  1188. return s.ToString();
  1189. }
  1190. // Private Methods
  1191. private void ParseAsWindowsUNC(string uriString)
  1192. {
  1193. scheme = UriSchemeFile;
  1194. port = -1;
  1195. fragment = String.Empty;
  1196. query = String.Empty;
  1197. isUnc = true;
  1198. uriString = uriString.TrimStart(new char[] { '\\' });
  1199. int pos = uriString.IndexOf('\\');
  1200. if (pos > 0)
  1201. {
  1202. path = uriString.Substring(pos);
  1203. host = uriString.Substring(0, pos);
  1204. }
  1205. else
  1206. { // "\\\\server"
  1207. host = uriString;
  1208. path = String.Empty;
  1209. }
  1210. path = path.Replace("\\", "/");
  1211. }
  1212. //
  1213. // Returns null on success, string with error on failure
  1214. //
  1215. private string ParseAsWindowsAbsoluteFilePath(string uriString)
  1216. {
  1217. if (uriString.Length > 2 && uriString[2] != '\\' && uriString[2] != '/')
  1218. return "Relative file path is not allowed.";
  1219. scheme = UriSchemeFile;
  1220. host = String.Empty;
  1221. port = -1;
  1222. path = uriString.Replace("\\", "/");
  1223. fragment = String.Empty;
  1224. query = String.Empty;
  1225. return null;
  1226. }
  1227. private void ParseAsUnixAbsoluteFilePath(string uriString)
  1228. {
  1229. isUnixFilePath = true;
  1230. scheme = UriSchemeFile;
  1231. port = -1;
  1232. fragment = String.Empty;
  1233. query = String.Empty;
  1234. host = String.Empty;
  1235. path = null;
  1236. if (uriString.Length >= 2 && uriString[0] == '/' && uriString[1] == '/')
  1237. {
  1238. uriString = uriString.TrimStart(new char[] { '/' });
  1239. // Now we don't regard //foo/bar as "foo" host.
  1240. /*
  1241. int pos = uriString.IndexOf ('/');
  1242. if (pos > 0) {
  1243. path = '/' + uriString.Substring (pos + 1);
  1244. host = uriString.Substring (0, pos);
  1245. } else { // "///server"
  1246. host = uriString;
  1247. path = String.Empty;
  1248. }
  1249. */
  1250. path = '/' + uriString;
  1251. }
  1252. if (path == null)
  1253. path = uriString;
  1254. }
  1255. //
  1256. // This parse method will throw exceptions on failure
  1257. //
  1258. private void Parse(UriKind kind, string uriString)
  1259. {
  1260. if (uriString == null)
  1261. throw new ArgumentNullException("uriString");
  1262. string s = ParseNoExceptions(kind, uriString);
  1263. if (s != null)
  1264. throw new UriFormatException(s);
  1265. }
  1266. private bool SupportsQuery()
  1267. {
  1268. return ((scheme != Uri.UriSchemeNntp) && (scheme != Uri.UriSchemeFtp) && (scheme != Uri.UriSchemeFile));
  1269. }
  1270. //
  1271. // This parse method will not throw exceptions on failure
  1272. //
  1273. // Returns null on success, or a description of the error in the parsing
  1274. //
  1275. private string ParseNoExceptions(UriKind kind, string uriString)
  1276. {
  1277. //
  1278. // From RFC 2396 :
  1279. //
  1280. // ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
  1281. // 12 3 4 5 6 7 8 9
  1282. //
  1283. uriString = uriString.Trim();
  1284. int len = uriString.Length;
  1285. if (len == 0)
  1286. {
  1287. if (kind == UriKind.Relative || kind == UriKind.RelativeOrAbsolute)
  1288. {
  1289. isAbsoluteUri = false;
  1290. return null;
  1291. }
  1292. }
  1293. if (len <= 1 && (kind == UriKind.Absolute))
  1294. return "Absolute URI is too short";
  1295. int pos = 0;
  1296. // 1, 2
  1297. // Identify Windows path, unix path, or standard URI.
  1298. if (uriString[0] == '/' && Path.DirectorySeparatorChar == '/')
  1299. {
  1300. //Unix Path
  1301. ParseAsUnixAbsoluteFilePath(uriString);
  1302. #if MOONLIGHT
  1303. isAbsoluteUri = false;
  1304. #else
  1305. if (kind == UriKind.Relative)
  1306. isAbsoluteUri = false;
  1307. #endif
  1308. return null;
  1309. }
  1310. else if (uriString.Length >= 2 && uriString[0] == '\\' && uriString[1] == '\\')
  1311. {
  1312. //Windows UNC
  1313. ParseAsWindowsUNC(uriString);
  1314. return null;
  1315. }
  1316. pos = uriString.IndexOf(':');
  1317. if (pos == 0)
  1318. {
  1319. if (kind == UriKind.Absolute)
  1320. return "Invalid URI: The format of the URI could not be determined.";
  1321. isAbsoluteUri = false;
  1322. path = uriString;
  1323. return null;
  1324. }
  1325. else if (pos < 0)
  1326. {
  1327. /* Relative path */
  1328. isAbsoluteUri = false;
  1329. path = uriString;
  1330. return null;
  1331. }
  1332. else if (pos == 1)
  1333. {
  1334. if (!IsAlpha(uriString[0]))
  1335. {
  1336. if (kind == UriKind.Absolute)
  1337. return "Invalid URI: The URI scheme is not valid.";
  1338. isAbsoluteUri = false;
  1339. path = uriString;
  1340. return null;
  1341. }
  1342. // This means 'a:' == windows full path.
  1343. string msg = ParseAsWindowsAbsoluteFilePath(uriString);
  1344. if (msg != null)
  1345. return msg;
  1346. return null;
  1347. }
  1348. // scheme
  1349. scheme = uriString.Substring(0, pos).ToLower(CultureInfo.InvariantCulture);
  1350. // Check scheme name characters as specified in RFC2396.
  1351. // Note: different checks in 1.x and 2.0
  1352. if (!CheckSchemeName(scheme))
  1353. {
  1354. if (kind == UriKind.Absolute)
  1355. return "Invalid URI: The URI scheme is not valid.";
  1356. isAbsoluteUri = false;
  1357. path = uriString;
  1358. return null;
  1359. }
  1360. // from here we're practically working on uriString.Substring(startpos,endpos-startpos)
  1361. int startpos = pos + 1;
  1362. int endpos = uriString.Length;
  1363. // 8 fragment
  1364. pos = uriString.IndexOf('#', startpos);
  1365. if (!IsUnc && pos != -1)
  1366. {
  1367. if (userEscaped)
  1368. fragment = uriString.Substring(pos);
  1369. else
  1370. fragment = "#" + EscapeString(uriString.Substring(pos + 1));
  1371. endpos = pos;
  1372. }
  1373. // special case: there is no query part for 'news'
  1374. if (scheme == Uri.UriSchemeNews)
  1375. {
  1376. pos = scheme.Length + 1;
  1377. path = EscapeString(uriString.Substring(pos, endpos - pos), EscapeNews);
  1378. return null;
  1379. }
  1380. // special case: there is no query part for 'nntp', 'file' and 'ftp' but there is an host, port, user...
  1381. if (SupportsQuery())
  1382. {
  1383. // 6 query
  1384. pos = uriString.IndexOf('?', startpos, endpos - startpos);
  1385. if (pos != -1)
  1386. {
  1387. query = uriString.Substring(pos, endpos - pos);
  1388. endpos = pos;
  1389. if (!userEscaped)
  1390. query = EscapeString(query);
  1391. }
  1392. }
  1393. // 3
  1394. if (IsPredefinedScheme(scheme) && scheme != UriSchemeMailto && (
  1395. (endpos - startpos < 2) ||
  1396. (endpos - startpos >= 2 && uriString[startpos] == '/' && uriString[startpos + 1] != '/')))
  1397. return "Invalid URI: The Authority/Host could not be parsed.";
  1398. bool startsWithSlashSlash = endpos - startpos >= 2 && uriString[startpos] == '/' && uriString[startpos + 1] == '/';
  1399. bool unixAbsPath = scheme == UriSchemeFile && startsWithSlashSlash && (endpos - startpos == 2 || uriString[startpos + 2] == '/');
  1400. bool windowsFilePath = false;
  1401. if (startsWithSlashSlash)
  1402. {
  1403. if (kind == UriKind.Relative)
  1404. return "Absolute URI when we expected a relative one";
  1405. if (scheme != UriSchemeMailto)
  1406. startpos += 2;
  1407. if (scheme == UriSchemeFile)
  1408. {
  1409. int num_leading_slash = 2;
  1410. for (int i = startpos; i < endpos; i++)
  1411. {
  1412. if (uriString[i] != '/')
  1413. break;
  1414. num_leading_slash++;
  1415. }
  1416. if (num_leading_slash >= 4)
  1417. {
  1418. unixAbsPath = false;
  1419. while (startpos < endpos && uriString[startpos] == '/')
  1420. {
  1421. startpos++;
  1422. }
  1423. }
  1424. else if (num_leading_slash >= 3)
  1425. {
  1426. startpos += 1;
  1427. }
  1428. }
  1429. if (endpos - startpos > 1 && uriString[startpos + 1] == ':')
  1430. {
  1431. unixAbsPath = false;
  1432. windowsFilePath = true;
  1433. }
  1434. }
  1435. else if (!IsPredefinedScheme(scheme))
  1436. {
  1437. path = uriString.Substring(startpos, endpos - startpos);
  1438. isOpaquePart = true;
  1439. return null;
  1440. }
  1441. // 5 path
  1442. if (unixAbsPath)
  1443. {
  1444. pos = -1;
  1445. }
  1446. else
  1447. {
  1448. pos = uriString.IndexOf('/', startpos, endpos - startpos);
  1449. if (pos == -1 && windowsFilePath)
  1450. pos = uriString.IndexOf('\\', startpos, endpos - startpos);
  1451. }
  1452. if (pos != -1)
  1453. {
  1454. path = uriString.Substring(pos, endpos - pos);
  1455. if (!SupportsQuery())
  1456. {
  1457. if (scheme != Uri.UriSchemeNntp)
  1458. path = path.Replace('\\', '/');
  1459. path = EscapeString(path, EscapeNews);
  1460. }
  1461. endpos = pos;
  1462. }
  1463. else
  1464. {
  1465. if (scheme != Uri.UriSchemeMailto)
  1466. path = "/";
  1467. }
  1468. // 4.a user info
  1469. if (unixAbsPath)
  1470. pos = -1;
  1471. else
  1472. pos = uriString.IndexOf('@', startpos, endpos - startpos);
  1473. if (pos != -1)
  1474. {
  1475. // supplying username / password on a file URI is not supported
  1476. if (scheme == UriSchemeFile)
  1477. return "Invalid host";
  1478. userinfo = uriString.Substring(startpos, pos - startpos);
  1479. startpos = pos + 1;
  1480. }
  1481. // 4.b port
  1482. bool valid_port = true;
  1483. port = -1;
  1484. if (unixAbsPath)
  1485. pos = -1;
  1486. else
  1487. pos = uriString.LastIndexOf(':', endpos - 1, endpos - startpos);
  1488. if (pos != -1 && pos != endpos - 1)
  1489. {
  1490. string portStr = uriString.Substring(pos + 1, endpos - (pos + 1));
  1491. if (portStr.Length > 0 && portStr[portStr.Length - 1] != ']')
  1492. {
  1493. if (!Int32.TryParse(portStr, NumberStyles.None, CultureInfo.InvariantCulture, out port) ||
  1494. port < 0 || port > UInt16.MaxValue)
  1495. valid_port = false; // delay reporting
  1496. else
  1497. endpos = pos;
  1498. }
  1499. else
  1500. {
  1501. if (port == -1)
  1502. {
  1503. port = GetDefaultPort(scheme);
  1504. }
  1505. }
  1506. }
  1507. else if (!IsFile)
  1508. {
  1509. // if no port is specified by a colon ':' is present then we must ignore it
  1510. // since it would be part of the host name and, as such, would be invalid
  1511. if (pos == endpos - 1)
  1512. endpos--;
  1513. if (port == -1)
  1514. port = GetDefaultPort(scheme);
  1515. }
  1516. // 4 authority
  1517. uriString = uriString.Substring(startpos, endpos - startpos);
  1518. host = uriString;
  1519. if (unixAbsPath)
  1520. {
  1521. path = Reduce('/' + uriString, true);
  1522. host = String.Empty;
  1523. }
  1524. else if (host.Length == 2 && host[1] == ':')
  1525. {
  1526. if (scheme != UriSchemeFile)
  1527. {
  1528. host = host[0].ToString();
  1529. }
  1530. else
  1531. {
  1532. // windows filepath
  1533. path = host + path;
  1534. host = String.Empty;
  1535. }
  1536. }
  1537. else if (isUnixFilePath)
  1538. {
  1539. uriString = "//" + uriString;
  1540. host = String.Empty;
  1541. }
  1542. else if (scheme == UriSchemeFile)
  1543. {
  1544. // under Windows all file:// URI are considered UNC, which is not the case other MacOS (e.g. Silverlight)
  1545. isUnc = isWin32;
  1546. }
  1547. else if (host.Length == 0 &&
  1548. (scheme == UriSchemeHttp || scheme == UriSchemeGopher || scheme == UriSchemeNntp ||
  1549. scheme == UriSchemeHttps || scheme == UriSchemeFtp))
  1550. {
  1551. return "Invalid URI: The Authority/Host could not be parsed.";
  1552. }
  1553. if (host.Length > 0)
  1554. {
  1555. switch (CheckHostName(host))
  1556. {
  1557. case UriHostNameType.Unknown:
  1558. if ((host[0] == ':') || (host[0] == '@'))
  1559. return "Invalid URI: The hostname could not be parsed.";
  1560. if (host.IndexOf(':') != -1)
  1561. return "Invalid URI: Invalid port specified.";
  1562. if (Parser is DefaultUriParser || Parser == null)
  1563. return "Invalid URI: The hostname could not be parsed.";
  1564. break;
  1565. case UriHostNameType.IPv6:
  1566. throw new NotImplementedException();
  1567. //IPv6Address ipv6addr;
  1568. //if (IPv6Address.TryParse(host, out ipv6addr))
  1569. //{
  1570. // host = "[" + ipv6addr.ToString(true) + "]";
  1571. // scope_id = ipv6addr.ScopeId;
  1572. //}
  1573. break;
  1574. }
  1575. }
  1576. // delayed reporting (to throw the expected exception in the right order)
  1577. if (!valid_port)
  1578. return "Invalid URI: Invalid port number";
  1579. UriFormatException ex = null;
  1580. if (Parser != null)
  1581. Parser.InitializeAndValidate(this, out ex);
  1582. if (ex != null)
  1583. return ex.Message;
  1584. if ((scheme != Uri.UriSchemeMailto) && (scheme != Uri.UriSchemeFile))
  1585. {
  1586. path = Reduce(path, CompactEscaped(scheme));
  1587. }
  1588. return null;
  1589. }
  1590. private static bool CompactEscaped(string scheme)
  1591. {
  1592. if (scheme == null || scheme.Length < 4)
  1593. return false;
  1594. char first = scheme[0];
  1595. if (first == 'h')
  1596. {
  1597. return scheme == "http" || scheme == "https";
  1598. }
  1599. else if (first == 'f' && scheme == "file")
  1600. {
  1601. return true;
  1602. }
  1603. else if (first == 'n')
  1604. return scheme == "net.pipe" || scheme == "net.tcp";
  1605. return false;
  1606. }
  1607. // replace '\', %5C ('\') and %2f ('/') into '/'
  1608. // replace %2e ('.') into '.'
  1609. private static string NormalizePath(string path)
  1610. {
  1611. StringBuilder res = new StringBuilder();
  1612. for (int i = 0; i < path.Length; i++)
  1613. {
  1614. char c = path[i];
  1615. switch (c)
  1616. {
  1617. case '\\':
  1618. c = '/';
  1619. break;
  1620. case '%':
  1621. if (i < path.Length - 2)
  1622. {
  1623. char c1 = path[i + 1];
  1624. char c2 = Char.ToUpper(path[i + 2]);
  1625. if ((c1 == '2') && (c2 == 'E'))
  1626. {
  1627. c = '.';
  1628. i += 2;
  1629. }
  1630. else if (((c1 == '2') && (c2 == 'F')) || ((c1 == '5') && (c2 == 'C')))
  1631. {
  1632. c = '/';
  1633. i += 2;
  1634. }
  1635. }
  1636. break;
  1637. }
  1638. res.Append(c);
  1639. }
  1640. return res.ToString();
  1641. }
  1642. // This is called "compacting" in the MSDN documentation
  1643. private static string Reduce(string path, bool compact_escaped)
  1644. {
  1645. // quick out, allocation-free, for a common case
  1646. if (path == "/")
  1647. return path;
  1648. if (compact_escaped && (path.IndexOf('%') != -1))
  1649. {
  1650. // replace '\', %2f, %5c with '/' and replace %2e with '.'
  1651. // other escaped values seems to survive this step
  1652. path = NormalizePath(path);
  1653. }
  1654. else
  1655. {
  1656. // (always) replace '\' with '/'
  1657. path = path.Replace('\\', '/');
  1658. }
  1659. List<string> result = new List<string>();
  1660. bool begin = true;
  1661. for (int startpos = 0; startpos < path.Length; )
  1662. {
  1663. int endpos = path.IndexOf('/', startpos);
  1664. if (endpos == -1)
  1665. endpos = path.Length;
  1666. string current = path.Substring(startpos, endpos - startpos);
  1667. startpos = endpos + 1;
  1668. if ((begin && current.Length == 0) || current == ".")
  1669. {
  1670. begin = false;
  1671. continue;
  1672. }
  1673. begin = false;
  1674. if (current == "..")
  1675. {
  1676. int resultCount = result.Count;
  1677. // in 2.0 profile, skip leading ".." parts
  1678. if (resultCount == 0)
  1679. {
  1680. continue;
  1681. }
  1682. result.RemoveAt(resultCount - 1);
  1683. continue;
  1684. }
  1685. result.Add(current);
  1686. }
  1687. if (result.Count == 0)
  1688. return "/";
  1689. StringBuilder res = new StringBuilder();
  1690. if (path[0] == '/')
  1691. res.Append('/');
  1692. bool first = true;
  1693. foreach (string part in result)
  1694. {
  1695. if (first)
  1696. {
  1697. first = false;
  1698. }
  1699. else
  1700. {
  1701. res.Append('/');
  1702. }
  1703. res.Append(part);
  1704. }
  1705. if (path[path.Length - 1] == '/')
  1706. res.Append('/');
  1707. return res.ToString();
  1708. }
  1709. // A variant of HexUnescape() which can decode multi-byte escaped
  1710. // sequences such as (e.g.) %E3%81%8B into a single character
  1711. private static char HexUnescapeMultiByte(string pattern, ref int index, out char surrogate)
  1712. {
  1713. surrogate = char.MinValue;
  1714. if (pattern == null)
  1715. throw new ArgumentException("pattern");
  1716. if (index < 0 || index >= pattern.Length)
  1717. throw new ArgumentOutOfRangeException("index");
  1718. if (!IsHexEncoding(pattern, index))
  1719. return pattern[index++];
  1720. int orig_index = index++;
  1721. int msb = FromHex(pattern[index++]);
  1722. int lsb = FromHex(pattern[index++]);
  1723. // We might be dealing with a multi-byte character:
  1724. // The number of ones at the top-end of the first byte will tell us
  1725. // how many bytes will make up this character.
  1726. int msb_copy = msb;
  1727. int num_bytes = 0;
  1728. while ((msb_copy & 0x8) == 0x8)
  1729. {
  1730. num_bytes++;
  1731. msb_copy <<= 1;
  1732. }
  1733. // We might be dealing with a single-byte character:
  1734. // If there was only 0 or 1 leading ones then we're not dealing
  1735. // with a multi-byte character.
  1736. if (num_bytes <= 1)
  1737. return (char)((msb << 4) | lsb);
  1738. // Now that we know how many bytes *should* follow, we'll check them
  1739. // to ensure we are dealing with a valid multi-byte character.
  1740. byte[] chars = new byte[num_bytes];
  1741. bool all_invalid = false;
  1742. chars[0] = (byte)((msb << 4) | lsb);
  1743. for (int i = 1; i < num_bytes; i++)
  1744. {
  1745. if (!IsHexEncoding(pattern, index++))
  1746. {
  1747. all_invalid = true;
  1748. break;
  1749. }
  1750. // All following bytes must be in the form 10xxxxxx
  1751. int cur_msb = FromHex(pattern[index++]);
  1752. if ((cur_msb & 0xc) != 0x8)
  1753. {
  1754. all_invalid = true;
  1755. break;
  1756. }
  1757. int cur_lsb = FromHex(pattern[index++]);
  1758. chars[i] = (byte)((cur_msb << 4) | cur_lsb);
  1759. }
  1760. // If what looked like a multi-byte character is invalid, then we'll
  1761. // just return the first byte as a single byte character.
  1762. if (all_invalid)
  1763. {
  1764. index = orig_index + 3;
  1765. return (char)chars[0];
  1766. }
  1767. // Otherwise, we're dealing with a valid multi-byte character.
  1768. // We need to ignore the leading ones from the first byte:
  1769. byte mask = (byte)0xFF;
  1770. mask >>= (num_bytes + 1);
  1771. int result = chars[0] & mask;
  1772. // The result will now be built up from the following bytes.
  1773. for (int i = 1; i < num_bytes; i++)
  1774. {
  1775. // Ignore upper two bits
  1776. result <<= 6;
  1777. result |= (chars[i] & 0x3F);
  1778. }
  1779. if (result <= 0xFFFF)
  1780. {
  1781. return (char)result;
  1782. }
  1783. else
  1784. {
  1785. // We need to handle this as a UTF16 surrogate (i.e. return
  1786. // two characters)
  1787. result -= 0x10000;
  1788. surrogate = (char)((result & 0x3FF) | 0xDC00);
  1789. return (char)((result >> 10) | 0xD800);
  1790. }
  1791. }
  1792. private struct UriScheme
  1793. {
  1794. public string scheme;
  1795. public string delimiter;
  1796. public int defaultPort;
  1797. public UriScheme(string s, string d, int p)
  1798. {
  1799. scheme = s;
  1800. delimiter = d;
  1801. defaultPort = p;
  1802. }
  1803. };
  1804. static UriScheme[] schemes = new UriScheme[] {
  1805. new UriScheme (UriSchemeHttp, SchemeDelimiter, 80),
  1806. new UriScheme (UriSchemeHttps, SchemeDelimiter, 443),
  1807. new UriScheme (UriSchemeFtp, SchemeDelimiter, 21),
  1808. new UriScheme (UriSchemeFile, SchemeDelimiter, -1),
  1809. new UriScheme (UriSchemeMailto, ":", 25),
  1810. new UriScheme (UriSchemeNews, ":", 119),
  1811. new UriScheme (UriSchemeNntp, SchemeDelimiter, 119),
  1812. new UriScheme (UriSchemeGopher, SchemeDelimiter, 70),
  1813. };
  1814. internal static string GetSchemeDelimiter(string scheme)
  1815. {
  1816. for (int i = 0; i < schemes.Length; i++)
  1817. if (schemes[i].scheme == scheme)
  1818. return schemes[i].delimiter;
  1819. return Uri.SchemeDelimiter;
  1820. }
  1821. internal static int GetDefaultPort(string scheme)
  1822. {
  1823. UriParser parser = UriParser.GetParser(scheme);
  1824. if (parser == null)
  1825. return -1;
  1826. return parser.DefaultPort;
  1827. }
  1828. private string GetOpaqueWiseSchemeDelimiter()
  1829. {
  1830. if (isOpaquePart)
  1831. return ":";
  1832. else
  1833. return GetSchemeDelimiter(scheme);
  1834. }
  1835. //[Obsolete]
  1836. protected virtual bool IsBadFileSystemCharacter(char ch)
  1837. {
  1838. // It does not always overlap with InvalidPathChars.
  1839. int chInt = (int)ch;
  1840. if (chInt < 32 || (chInt < 64 && chInt > 57))
  1841. return true;
  1842. switch (chInt)
  1843. {
  1844. case 0:
  1845. case 34: // "
  1846. case 38: // &
  1847. case 42: // *
  1848. case 44: // ,
  1849. case 47: // /
  1850. case 92: // \
  1851. case 94: // ^
  1852. case 124: // |
  1853. return true;
  1854. }
  1855. return false;
  1856. }
  1857. //[Obsolete]
  1858. protected static bool IsExcludedCharacter(char ch)
  1859. {
  1860. if (ch <= 32 || ch >= 127)
  1861. return true;
  1862. if (ch == '"' || ch == '#' || ch == '%' || ch == '<' ||
  1863. ch == '>' || ch == '[' || ch == '\\' || ch == ']' ||
  1864. ch == '^' || ch == '`' || ch == '{' || ch == '|' ||
  1865. ch == '}')
  1866. return true;
  1867. return false;
  1868. }
  1869. internal static bool MaybeUri(string s)
  1870. {
  1871. int p = s.IndexOf(':');
  1872. if (p == -1)
  1873. return false;
  1874. if (p >= 10)
  1875. return false;
  1876. return IsPredefinedScheme(s.Substring(0, p));
  1877. }
  1878. //
  1879. // Using a simple block of if's is twice as slow as the compiler generated
  1880. // switch statement. But using this tuned code is faster than the
  1881. // compiler generated code, with a million loops on x86-64:
  1882. //
  1883. // With "http": .10 vs .51 (first check)
  1884. // with "https": .16 vs .51 (second check)
  1885. // with "foo": .22 vs .31 (never found)
  1886. // with "mailto": .12 vs .51 (last check)
  1887. //
  1888. //
  1889. private static bool IsPredefinedScheme(string scheme)
  1890. {
  1891. if (scheme == null && scheme.Length < 3)
  1892. return false;
  1893. char c = scheme[0];
  1894. if (c == 'h')
  1895. return (scheme == "http" || scheme == "https");
  1896. if (c == 'f')
  1897. return (scheme == "file" || scheme == "ftp");
  1898. if (c == 'n')
  1899. {
  1900. c = scheme[1];
  1901. if (c == 'e')
  1902. return (scheme == "news" || scheme == "net.pipe" || scheme == "net.tcp");
  1903. if (scheme == "nntp")
  1904. return true;
  1905. return false;
  1906. }
  1907. if ((c == 'g' && scheme == "gopher") || (c == 'm' && scheme == "mailto"))
  1908. return true;
  1909. return false;
  1910. }
  1911. //[Obsolete]
  1912. protected virtual bool IsReservedCharacter(char ch)
  1913. {
  1914. if (ch == '$' || ch == '&' || ch == '+' || ch == ',' ||
  1915. ch == '/' || ch == ':' || ch == ';' || ch == '=' ||
  1916. ch == '@')
  1917. return true;
  1918. return false;
  1919. }
  1920. //[NonSerialized]
  1921. private UriParser parser;
  1922. private UriParser Parser
  1923. {
  1924. get
  1925. {
  1926. if (parser == null)
  1927. {
  1928. parser = UriParser.GetParser(Scheme);
  1929. // no specific parser ? then use a default one
  1930. if (parser == null)
  1931. parser = new DefaultUriParser("*");
  1932. }
  1933. return parser;
  1934. }
  1935. set { parser = value; }
  1936. }
  1937. public string GetComponents(UriComponents components, UriFormat format)
  1938. {
  1939. return Parser.GetComponents(this, components, format);
  1940. }
  1941. public bool IsBaseOf(Uri uri)
  1942. {
  1943. #if NET_4_0 || MOONLIGHT || MOBILE
  1944. if (uri == null)
  1945. throw new ArgumentNullException ("uri");
  1946. #endif
  1947. return Parser.IsBaseOf(this, uri);
  1948. }
  1949. public bool IsWellFormedOriginalString()
  1950. {
  1951. // funny, but it does not use the Parser's IsWellFormedOriginalString().
  1952. // Also, it seems we need to *not* escape hex.
  1953. return EscapeString(OriginalString, EscapeCommonBrackets) == OriginalString;
  1954. }
  1955. // static methods
  1956. private const int MaxUriLength = 32766;
  1957. public static int Compare(Uri uri1, Uri uri2, UriComponents partsToCompare, UriFormat compareFormat, StringComparison comparisonType)
  1958. {
  1959. if ((comparisonType < StringComparison.CurrentCulture) || (comparisonType > StringComparison.OrdinalIgnoreCase))
  1960. {
  1961. string msg = String.Format("Invalid StringComparison value '{0}'", comparisonType);
  1962. throw new ArgumentException("comparisonType", msg);
  1963. }
  1964. if ((uri1 == null) && (uri2 == null))
  1965. return 0;
  1966. string s1 = uri1.GetComponents(partsToCompare, compareFormat);
  1967. string s2 = uri2.GetComponents(partsToCompare, compareFormat);
  1968. return String.Compare(s1, s2, comparisonType);
  1969. }
  1970. //
  1971. // The rules for EscapeDataString
  1972. //
  1973. static bool NeedToEscapeDataChar(char b)
  1974. {
  1975. return !((b >= 'A' && b <= 'Z') ||
  1976. (b >= 'a' && b <= 'z') ||
  1977. (b >= '0' && b <= '9') ||
  1978. b == '_' || b == '~' || b == '!' || b == '\'' ||
  1979. b == '(' || b == ')' || b == '*' || b == '-' || b == '.');
  1980. }
  1981. public static string EscapeDataString(string stringToEscape)
  1982. {
  1983. if (stringToEscape == null)
  1984. throw new ArgumentNullException("stringToEscape");
  1985. if (stringToEscape.Length > MaxUriLength)
  1986. {
  1987. string msg = String.Format("Uri is longer than the maximum {0} characters.");
  1988. throw new UriFormatException(msg);
  1989. }
  1990. bool escape = false;
  1991. foreach (char c in stringToEscape)
  1992. {
  1993. if (NeedToEscapeDataChar(c))
  1994. {
  1995. escape = true;
  1996. break;
  1997. }
  1998. }
  1999. if (!escape)
  2000. {
  2001. return stringToEscape;
  2002. }
  2003. StringBuilder sb = new StringBuilder();
  2004. byte[] bytes = Encoding.UTF8.GetBytes(stringToEscape);
  2005. foreach (byte b in bytes)
  2006. {
  2007. if (NeedToEscapeDataChar((char)b))
  2008. sb.Append(HexEscape((char)b));
  2009. else
  2010. sb.Append((char)b);
  2011. }
  2012. return sb.ToString();
  2013. }
  2014. //
  2015. // The rules for EscapeUriString
  2016. //
  2017. static bool NeedToEscapeUriChar(char b)
  2018. {
  2019. return !((b >= 'A' && b <= 'Z') ||
  2020. (b >= 'a' && b <= 'z') ||
  2021. (b >= '&' && b <= ';') ||
  2022. b == '!' || b == '#' || b == '$' || b == '=' ||
  2023. b == '?' || b == '@' || b == '_' || b == '~');
  2024. }
  2025. public static string EscapeUriString(string stringToEscape)
  2026. {
  2027. if (stringToEscape == null)
  2028. throw new ArgumentNullException("stringToEscape");
  2029. if (stringToEscape.Length > MaxUriLength)
  2030. {
  2031. string msg = String.Format("Uri is longer than the maximum {0} characters.");
  2032. throw new UriFormatException(msg);
  2033. }
  2034. bool escape = false;
  2035. foreach (char c in stringToEscape)
  2036. {
  2037. if (NeedToEscapeUriChar(c))
  2038. {
  2039. escape = true;
  2040. break;
  2041. }
  2042. }
  2043. if (!escape)
  2044. return stringToEscape;
  2045. StringBuilder sb = new StringBuilder();
  2046. byte[] bytes = Encoding.UTF8.GetBytes(stringToEscape);
  2047. foreach (byte b in bytes)
  2048. {
  2049. if (NeedToEscapeUriChar((char)b))
  2050. sb.Append(HexEscape((char)b));
  2051. else
  2052. sb.Append((char)b);
  2053. }
  2054. return sb.ToString();
  2055. }
  2056. public static bool IsWellFormedUriString(string uriString, UriKind uriKind)
  2057. {
  2058. if (uriString == null)
  2059. return false;
  2060. Uri uri;
  2061. if (Uri.TryCreate(uriString, uriKind, out uri))
  2062. return uri.IsWellFormedOriginalString();
  2063. return false;
  2064. }
  2065. public static bool TryCreate(string uriString, UriKind uriKind, out Uri result)
  2066. {
  2067. bool success;
  2068. Uri r = new Uri(uriString, uriKind, out success);
  2069. if (success)
  2070. {
  2071. result = r;
  2072. return true;
  2073. }
  2074. result = null;
  2075. return false;
  2076. }
  2077. // [MonoTODO ("rework code to avoid exception catching")]
  2078. public static bool TryCreate(Uri baseUri, string relativeUri, out Uri result)
  2079. {
  2080. result = null;
  2081. if (relativeUri == null)
  2082. return false;
  2083. try
  2084. {
  2085. Uri relative = new Uri(relativeUri, UriKind.RelativeOrAbsolute);
  2086. if ((baseUri != null) && baseUri.IsAbsoluteUri)
  2087. {
  2088. // FIXME: this should call UriParser.Resolve
  2089. result = new Uri(baseUri, relative);
  2090. }
  2091. else if (relative.IsAbsoluteUri)
  2092. {
  2093. // special case - see unit tests
  2094. result = relative;
  2095. }
  2096. return (result != null);
  2097. }
  2098. catch (UriFormatException)
  2099. {
  2100. return false;
  2101. }
  2102. }
  2103. //[MonoTODO ("rework code to avoid exception catching")]
  2104. public static bool TryCreate(Uri baseUri, Uri relativeUri, out Uri result)
  2105. {
  2106. result = null;
  2107. if ((baseUri == null) || !baseUri.IsAbsoluteUri)
  2108. return false;
  2109. #if NET_4_0 || MOONLIGHT || MOBILE
  2110. if (relativeUri == null)
  2111. return false;
  2112. #endif
  2113. try
  2114. {
  2115. // FIXME: this should call UriParser.Resolve
  2116. result = new Uri(baseUri, relativeUri.OriginalString);
  2117. return true;
  2118. }
  2119. catch (UriFormatException)
  2120. {
  2121. return false;
  2122. }
  2123. }
  2124. public static string UnescapeDataString(string stringToUnescape)
  2125. {
  2126. return UnescapeDataString(stringToUnescape, false);
  2127. }
  2128. internal static string UnescapeDataString(string stringToUnescape, bool safe)
  2129. {
  2130. if (stringToUnescape == null)
  2131. throw new ArgumentNullException("stringToUnescape");
  2132. if (stringToUnescape.IndexOf('%') == -1 && stringToUnescape.IndexOf('+') == -1)
  2133. return stringToUnescape;
  2134. StringBuilder output = new StringBuilder();
  2135. long len = stringToUnescape.Length;
  2136. MemoryStream bytes = new MemoryStream();
  2137. int xchar;
  2138. for (int i = 0; i < len; i++)
  2139. {
  2140. if (stringToUnescape[i] == '%' && i + 2 < len && stringToUnescape[i + 1] != '%')
  2141. {
  2142. if (stringToUnescape[i + 1] == 'u' && i + 5 < len)
  2143. {
  2144. if (bytes.Length > 0)
  2145. {
  2146. output.Append(GetChars(bytes, Encoding.UTF8));
  2147. bytes.SetLength(0);
  2148. }
  2149. xchar = GetChar(stringToUnescape, i + 2, 4, safe);
  2150. if (xchar != -1)
  2151. {
  2152. output.Append((char)xchar);
  2153. i += 5;
  2154. }
  2155. else
  2156. {
  2157. output.Append('%');
  2158. }
  2159. }
  2160. else if ((xchar = GetChar(stringToUnescape, i + 1, 2, safe)) != -1)
  2161. {
  2162. bytes.WriteByte((byte)xchar);
  2163. i += 2;
  2164. }
  2165. else
  2166. {
  2167. output.Append('%');
  2168. }
  2169. continue;
  2170. }
  2171. if (bytes.Length > 0)
  2172. {
  2173. output.Append(GetChars(bytes, Encoding.UTF8));
  2174. bytes.SetLength(0);
  2175. }
  2176. output.Append(stringToUnescape[i]);
  2177. }
  2178. if (bytes.Length > 0)
  2179. {
  2180. output.Append(GetChars(bytes, Encoding.UTF8));
  2181. }
  2182. bytes = null;
  2183. return output.ToString();
  2184. }
  2185. private static int GetInt(byte b)
  2186. {
  2187. char c = (char)b;
  2188. if (c >= '0' && c <= '9')
  2189. return c - '0';
  2190. if (c >= 'a' && c <= 'f')
  2191. return c - 'a' + 10;
  2192. if (c >= 'A' && c <= 'F')
  2193. return c - 'A' + 10;
  2194. return -1;
  2195. }
  2196. private static int GetChar(string str, int offset, int length, bool safe)
  2197. {
  2198. int val = 0;
  2199. int end = length + offset;
  2200. for (int i = offset; i < end; i++)
  2201. {
  2202. char c = str[i];
  2203. if (c > 127)
  2204. return -1;
  2205. int current = GetInt((byte)c);
  2206. if (current == -1)
  2207. return -1;
  2208. val = (val << 4) + current;
  2209. }
  2210. if (!safe)
  2211. return val;
  2212. switch ((char)val)
  2213. {
  2214. case '%':
  2215. case '#':
  2216. case '?':
  2217. case '/':
  2218. case '\\':
  2219. case '@':
  2220. case '&': // not documented
  2221. return -1;
  2222. default:
  2223. return val;
  2224. }
  2225. }
  2226. private static char[] GetChars(System.IO.MemoryStream b, Encoding e)
  2227. {
  2228. return e.GetChars(b.GetBuffer(), 0, (int)b.Length);
  2229. }
  2230. private void EnsureAbsoluteUri()
  2231. {
  2232. if (!IsAbsoluteUri)
  2233. throw new InvalidOperationException("This operation is not supported for a relative URI.");
  2234. }
  2235. }
  2236. }