uriparser.pp 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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] = '/' then
  165. LastValidPos := i
  166. else if s[i] in [':', '@'] then
  167. break;
  168. if (LastValidPos > 0) and
  169. (Length(S)>LastValidPos) and
  170. (S[LastValidPos+1]<>'/') then
  171. begin
  172. Result.Path := Unescape(Copy(s, LastValidPos, Length(s)));
  173. s := Copy(s, 1, LastValidPos - 1);
  174. end;
  175. // Extract the port number
  176. for i := Length(s) downto 1 do
  177. if s[i] = ':' then
  178. begin
  179. Result.Port := StrToInt(Copy(s, i + 1, Length(s)));
  180. s := Copy(s, 1, i - 1);
  181. break;
  182. end else if s[i] in ['@', '/'] then
  183. break;
  184. // Extract the hostname
  185. if ((Length(s) > 2) and (s[1] = '/') and (s[2] = '/')) or
  186. ((Length(s) > 1) and (s[1] <> '/')) then
  187. begin
  188. if s[1] <> '/' then
  189. s := '//' + s;
  190. for i := Length(s) downto 1 do
  191. if s[i] in ['@', '/'] then
  192. begin
  193. Result.Host := Copy(s, i + 1, Length(s));
  194. s := Copy(s, 3, i - 3);
  195. break;
  196. end;
  197. // Extract username and password
  198. if Length(s) > 0 then
  199. begin
  200. i := Pos(':', s);
  201. if i = 0 then
  202. Result.Username := s
  203. else
  204. begin
  205. Result.Username := Copy(s, 1, i - 1);
  206. Result.Password := Copy(s, i + 1, Length(s));
  207. end;
  208. end;
  209. end;
  210. end;
  211. end.