uriparser.pp 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2003 by the Free Pascal development team
  4. Original author: Sebastian Guenther
  5. Unit to parse complete URI in its parts or to reassemble an URI
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$MODE objfpc}
  13. {$H+}
  14. unit URIParser;
  15. interface
  16. type
  17. TURI = record
  18. Protocol: String;
  19. Username: String;
  20. Password: String;
  21. Host: String;
  22. Port: Word;
  23. Path: String;
  24. Document: String;
  25. Params: String;
  26. Bookmark: String;
  27. end;
  28. function EncodeURI(const URI: TURI): String;
  29. function ParseURI(const URI: String): TURI;
  30. function ParseURI(const URI, DefaultProtocol: String; DefaultPort: Word): TURI;
  31. implementation
  32. uses SysUtils;
  33. const
  34. HexTable: array[0..15] of Char = '0123456789abcdef';
  35. function EncodeURI(const URI: TURI): String;
  36. function Escape(const s: String): String;
  37. var
  38. i: Integer;
  39. begin
  40. SetLength(Result, 0);
  41. for i := 1 to Length(s) do
  42. if not (s[i] in ['0'..'9', 'A'..'Z', 'a'..'z', ',', '-', '.', '_',
  43. '/', '\']) then
  44. Result := Result + '%' + HexTable[Ord(s[i]) shr 4] +
  45. HexTable[Ord(s[i]) and $f]
  46. else
  47. Result := Result + s[i];
  48. end;
  49. begin
  50. SetLength(Result, 0);
  51. if Length(URI.Protocol) > 0 then
  52. Result := LowerCase(URI.Protocol) + ':';
  53. if Length(URI.Host) > 0 then
  54. begin
  55. Result := Result + '//';
  56. if Length(URI.Username) > 0 then
  57. begin
  58. Result := Result + URI.Username;
  59. if Length(URI.Password) > 0 then
  60. Result := Result + ':' + URI.Password;
  61. Result := Result + '@';
  62. end;
  63. Result := Result + URI.Host;
  64. end;
  65. if URI.Port <> 0 then
  66. Result := Result + ':' + IntToStr(URI.Port);
  67. Result := Result + Escape(URI.Path);
  68. if Length(URI.Document) > 0 then
  69. begin
  70. if (Length(Result) = 0) or (Result[Length(Result)] <> '/') then
  71. Result := Result + '/';
  72. Result := Result + Escape(URI.Document);
  73. end;
  74. if Length(URI.Params) > 0 then
  75. Result := Result + '?' + URI.Params;
  76. if Length(URI.Bookmark) > 0 then
  77. Result := Result + '#' + Escape(URI.Bookmark);
  78. end;
  79. function ParseURI(const URI: String): TURI;
  80. begin
  81. Result := ParseURI(URI, '', 0);
  82. end;
  83. function ParseURI(const URI, DefaultProtocol: String; DefaultPort: Word): TURI;
  84. function Unescape(const s: String): String;
  85. function HexValue(c: Char): Integer;
  86. begin
  87. if (c >= '0') and (c <= '9') then
  88. Result := Ord(c) - Ord('0')
  89. else if (c >= 'A') and (c <= 'F') then
  90. Result := Ord(c) - Ord('A') + 10
  91. else if (c >= 'a') and (c <= 'f') then
  92. Result := Ord(c) - Ord('a') + 10
  93. else
  94. Result := 0;
  95. end;
  96. var
  97. i, RealLength: Integer;
  98. begin
  99. SetLength(Result, Length(s));
  100. i := 1;
  101. RealLength := 0;
  102. while i <= Length(s) do
  103. begin
  104. Inc(RealLength);
  105. if s[i] = '%' then
  106. begin
  107. Result[RealLength] := Chr(HexValue(s[i + 1]) shl 4 or HexValue(s[i + 2]));
  108. Inc(i, 3);
  109. end else
  110. begin
  111. Result[RealLength] := s[i];
  112. Inc(i);
  113. end;
  114. end;
  115. SetLength(Result, RealLength);
  116. end;
  117. var
  118. s: String;
  119. i, LastValidPos: Integer;
  120. begin
  121. Result.Protocol := LowerCase(DefaultProtocol);
  122. Result.Port := DefaultPort;
  123. s := URI;
  124. // Extract the protocol
  125. for i := 1 to Length(s) do
  126. if s[i] = ':' then
  127. begin
  128. Result.Protocol := Copy(s, 1, i - 1);
  129. s := Copy(s, i + 1, Length(s));
  130. break;
  131. end else if not (s[i] in ['0'..'9', 'A'..'Z', 'a'..'z']) then
  132. break;
  133. // Extract the bookmark name
  134. for i := Length(s) downto 1 do
  135. if s[i] = '#' then
  136. begin
  137. Result.Bookmark := Unescape(Copy(s, i + 1, Length(s)));
  138. s := Copy(s, 1, i - 1);
  139. break;
  140. end else if s[i] = '/' then
  141. break;
  142. // Extract the params
  143. for i := Length(s) downto 1 do
  144. if s[i] = '?' then
  145. begin
  146. Result.Params := Copy(s, i + 1, Length(s));
  147. s := Copy(s, 1, i - 1);
  148. break;
  149. end else if s[i] = '/' then
  150. break;
  151. // Extract the document name
  152. for i := Length(s) downto 1 do
  153. if s[i] = '/' then
  154. begin
  155. Result.Document := Unescape(Copy(s, i + 1, Length(s)));
  156. s := Copy(s, 1, i - 1);
  157. break;
  158. end else if s[i] = ':' then
  159. break;
  160. // Extract the path
  161. LastValidPos := 0;
  162. for i := Length(s) downto 1 do
  163. if (s[i] = '/')
  164. and ((I>1) and (S[i-1]<>'/'))
  165. and ((I<Length(S)) and (S[I+1]<>'/')) then
  166. LastValidPos := i
  167. else if s[i] in [':', '@'] then
  168. break;
  169. if (LastValidPos > 0) and
  170. (Length(S)>LastValidPos) and
  171. (S[LastValidPos+1]<>'/') then
  172. begin
  173. Result.Path := Unescape(Copy(s, LastValidPos, Length(s)));
  174. s := Copy(s, 1, LastValidPos - 1);
  175. end;
  176. // Extract the port number
  177. for i := Length(s) downto 1 do
  178. if s[i] = ':' then
  179. begin
  180. Result.Port := StrToInt(Copy(s, i + 1, Length(s)));
  181. s := Copy(s, 1, i - 1);
  182. break;
  183. end else if s[i] in ['@', '/'] then
  184. break;
  185. // Extract the hostname
  186. if ((Length(s) > 2) and (s[1] = '/') and (s[2] = '/')) or
  187. ((Length(s) > 1) and (s[1] <> '/')) then
  188. begin
  189. if s[1] <> '/' then
  190. s := '//' + s;
  191. for i := Length(s) downto 1 do
  192. if s[i] in ['@', '/'] then
  193. begin
  194. Result.Host := Copy(s, i + 1, Length(s));
  195. s := Copy(s, 3, i - 3);
  196. break;
  197. end;
  198. // Extract username and password
  199. if Length(s) > 0 then
  200. begin
  201. i := Pos(':', s);
  202. if i = 0 then
  203. Result.Username := s
  204. else
  205. begin
  206. Result.Username := Copy(s, 1, i - 1);
  207. Result.Password := Copy(s, i + 1, Length(s));
  208. end;
  209. end;
  210. end;
  211. end;
  212. end.