uriparser.pp 5.8 KB

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