IdURI.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10413: IdURI.pas
  11. {
  12. { Rev 1.1 29/11/2002 10:15:48 AM SGrobety Version: 1.1
  13. { Changed URL encoding
  14. }
  15. {
  16. { Rev 1.0 2002.11.12 10:59:20 PM czhower
  17. }
  18. unit IdURI;
  19. {Details of implementation
  20. -------------------------
  21. 2002-Apr-14 Peter Mee
  22. - Fixed reset. Now resets FParams as well - wasn't before.
  23. 2001-Nov Doychin Bondzhev
  24. - Fixes in URLEncode. There is difference when encoding Path+Doc and Params
  25. 2001-Oct-17 Peter Mee
  26. - Minor speed improvement - removed use of NormalizePath in SetURI.
  27. - Fixed bug that was cutting off the first two chars of the host when a
  28. username / password present.
  29. - Fixed bug that prevented username and password being updated.
  30. - Fixed bug that was leaving the bookmark in the document when no ? or =
  31. parameters existed.
  32. 2001-Feb-18 Doychin Bondzhev
  33. - Added UserName and Password to support URI's like
  34. http://username:password@hostname:port/path/document#bookmark
  35. }
  36. interface
  37. Uses
  38. IdException;
  39. type
  40. TIdURIOptionalFields = (ofAuthInfo, ofBookmark);
  41. TIdURIOptionalFieldsSet = set of TIdURIOptionalFields;
  42. TIdURI = class
  43. protected
  44. FDocument: string;
  45. FProtocol: string;
  46. FURI: String;
  47. FPort: string;
  48. Fpath: string;
  49. FHost: string;
  50. FBookmark: string;
  51. FUserName: string;
  52. FPassword: string;
  53. FParams: string;
  54. //
  55. procedure SetURI(const Value: String);
  56. function GetURI: String;
  57. public
  58. constructor Create(const AURI: string = ''); virtual; {Do not Localize}
  59. function GetFullURI(const AOptionalFileds: TIdURIOptionalFieldsSet = [ofAuthInfo, ofBookmark]): String;
  60. class procedure NormalizePath(var APath: string);
  61. class function URLDecode(ASrc: string): string;
  62. class function URLEncode(const ASrc: string): string;
  63. class function ParamsEncode(const ASrc: string): string;
  64. class function PathEncode(const ASrc: string): string;
  65. //
  66. property Bookmark : string read FBookmark write FBookMark;
  67. property Document: string read FDocument write FDocument;
  68. property Host: string read FHost write FHost;
  69. property Password: string read FPassword write FPassword;
  70. property Path: string read FPath write FPath;
  71. property Params: string read FParams write FParams;
  72. property Port: string read FPort write FPort;
  73. property Protocol: string read FProtocol write FProtocol;
  74. property URI: string read GetURI write SetURI;
  75. property Username: string read FUserName write FUserName;
  76. end;
  77. EIdURIException = class(EIdException);
  78. implementation
  79. uses
  80. IdGlobal, IdResourceStrings,
  81. SysUtils;
  82. constructor TIdURI.Create(const AURI: string = ''); {Do not Localize}
  83. begin
  84. inherited Create;
  85. if length(AURI) > 0 then begin
  86. URI := AURI;
  87. end;
  88. end;
  89. class procedure TIdURI.NormalizePath(var APath: string);
  90. var
  91. i: Integer;
  92. begin
  93. // Normalize the directory delimiters to follow the UNIX syntax
  94. i := 1;
  95. while i <= Length(APath) do begin
  96. if APath[i] in LeadBytes then begin
  97. inc(i, 2)
  98. end else if APath[i] = '\' then begin {Do not Localize}
  99. APath[i] := '/'; {Do not Localize}
  100. inc(i, 1);
  101. end else begin
  102. inc(i, 1);
  103. end;
  104. end;
  105. end;
  106. procedure TIdURI.SetURI(const Value: String);
  107. var
  108. LBuffer: string;
  109. LTokenPos, LPramsPos: Integer;
  110. LURI: string;
  111. begin
  112. FURI := Value;
  113. NormalizePath(FURI);
  114. LURI := FURI;
  115. FHost := ''; {Do not Localize}
  116. FProtocol := ''; {Do not Localize}
  117. FPath := ''; {Do not Localize}
  118. FDocument := ''; {Do not Localize}
  119. FPort := ''; {Do not Localize}
  120. FBookmark := ''; {Do not Localize}
  121. FUsername := ''; {Do not Localize}
  122. FPassword := ''; {Do not Localize}
  123. FParams := ''; {Do not localise} //Peter Mee
  124. LTokenPos := IndyPos('://', LURI); {Do not Localize}
  125. if LTokenPos > 0 then begin
  126. // absolute URI
  127. // What to do when data don't match configuration ?? {Do not Localize}
  128. // Get the protocol
  129. FProtocol := Copy(LURI, 1, LTokenPos - 1);
  130. Delete(LURI, 1, LTokenPos + 2);
  131. // Get the user name, password, host and the port number
  132. LBuffer := Fetch(LURI, '/', True); {Do not Localize}
  133. // Get username and password
  134. LTokenPos := IndyPos('@', LBuffer); {Do not Localize}
  135. FPassword := Copy(LBuffer, 1, LTokenPos - 1);
  136. if LTokenPos > 0 then
  137. Delete(LBuffer, 1, LTokenPos);
  138. FUserName := Fetch(FPassword, ':', True); {Do not Localize}
  139. // Ignore cases where there is only password (http://:password@host/pat/doc)
  140. if Length(FUserName) = 0 then begin
  141. FPassword := ''; {Do not Localize}
  142. end;
  143. // Get the host and the port number
  144. FHost := Fetch(LBuffer, ':', True); {Do not Localize}
  145. FPort := LBuffer;
  146. // Get the path
  147. LPramsPos := IndyPos('?', LURI); {Do not Localize}
  148. if LPramsPos > 0 then begin // The case when there is parameters after the document name '?' {Do not Localize}
  149. LTokenPos := RPos('/', LURI, LPramsPos); {Do not Localize}
  150. end
  151. else begin
  152. LPramsPos := IndyPos('=', LURI); {Do not Localize}
  153. if LPramsPos > 0 then begin // The case when there is parameters after the document name '=' {Do not Localize}
  154. LTokenPos := RPos('/', LURI, LPramsPos); {Do not Localize}
  155. end
  156. else begin
  157. LTokenPos := RPos('/', LURI, -1); {Do not Localize}
  158. end;
  159. end;
  160. FPath := '/' + Copy(LURI, 1, LTokenPos); {Do not Localize}
  161. // Get the document
  162. if LPramsPos > 0 then begin
  163. FDocument := Copy(LURI, 1, LPramsPos - 1);
  164. Delete(LURI, 1, LPramsPos - 1);
  165. FParams := LURI;
  166. end
  167. else
  168. FDocument := LURI;
  169. Delete(FDocument, 1, LTokenPos);
  170. FBookmark := FDocument;
  171. FDocument := Fetch(FBookmark, '#'); {Do not Localize}
  172. end else begin
  173. // received an absolute path, not an URI
  174. LPramsPos := IndyPos('?', LURI); {Do not Localize}
  175. if LPramsPos > 0 then begin // The case when there is parameters after the document name '?' {Do not Localize}
  176. LTokenPos := RPos('/', LURI, LPramsPos); {Do not Localize}
  177. end else begin
  178. LPramsPos := IndyPos('=', LURI); {Do not Localize}
  179. if LPramsPos > 0 then begin // The case when there is parameters after the document name '=' {Do not Localize}
  180. LTokenPos := RPos('/', LURI, LPramsPos); {Do not Localize}
  181. end else begin
  182. LTokenPos := RPos('/', LURI, -1); {Do not Localize}
  183. end;
  184. end;
  185. FPath := Copy(LURI, 1, LTokenPos);
  186. // Get the document
  187. if LPramsPos > 0 then begin
  188. FDocument := Copy(LURI, 1, LPramsPos - 1);
  189. Delete(LURI, 1, LPramsPos - 1);
  190. FParams := LURI;
  191. end else begin
  192. FDocument := LURI;
  193. end;
  194. Delete(FDocument, 1, LTokenPos);
  195. end;
  196. // Parse the # bookmark from the document
  197. if Length(FBookmark) = 0 then begin
  198. FBookmark := FParams;
  199. FParams := Fetch(FBookmark, '#'); {Do not Localize}
  200. end;
  201. end;
  202. function TIdURI.GetURI: String;
  203. begin
  204. FURI := GetFullURI;
  205. // result must contain only the proto://host/path/document
  206. // If you need the full URI then you have to call GetFullURI
  207. result := GetFullURI([]);
  208. end;
  209. class function TIdURI.URLDecode(ASrc: string): string;
  210. var
  211. i: integer;
  212. ESC: string[2];
  213. CharCode: integer;
  214. begin
  215. Result := ''; {Do not Localize}
  216. // S.G. 27/11/2002: Spaces is NOT to be encoded as "+".
  217. // S.G. 27/11/2002: "+" is a field separator in query parameter, space is...
  218. // S.G. 27/11/2002: well, a space
  219. // ASrc := StringReplace(ASrc, '+', ' ', [rfReplaceAll]); {do not localize}
  220. i := 1;
  221. while i <= Length(ASrc) do begin
  222. if ASrc[i] <> '%' then begin {do not localize}
  223. Result := Result + ASrc[i]
  224. end else begin
  225. Inc(i); // skip the % char
  226. ESC := Copy(ASrc, i, 2); // Copy the escape code
  227. Inc(i, 1); // Then skip it.
  228. try
  229. CharCode := StrToInt('$' + ESC); {do not localize}
  230. if (CharCode > 0) and (CharCode < 256) then begin
  231. Result := Result + Char(CharCode);
  232. end;
  233. except end;
  234. end;
  235. Inc(i);
  236. end;
  237. end;
  238. class function TIdURI.ParamsEncode(const ASrc: string): string;
  239. var
  240. i: Integer;
  241. const
  242. UnsafeChars = ['*', '#', '%', '<', '>', ' ','[',']']; {do not localize}
  243. begin
  244. Result := ''; {Do not Localize}
  245. for i := 1 to Length(ASrc) do
  246. begin
  247. // S.G. 27/11/2002: Changed the parameter encoding: Even in parameters, a space
  248. // S.G. 27/11/2002: is much more likely to be meaning "space" than "this is
  249. // S.G. 27/11/2002: a new parameter"
  250. // S.G. 27/11/2002: ref: Message-ID: <[email protected]> borland.public.delphi.internet.winsock
  251. // S.G. 27/11/2002: Most low-ascii is actually Ok in parameters encoding.
  252. if (ASrc[i] in UnsafeChars) or (not (ord(ASrc[i])in [33..128])) then
  253. begin {do not localize}
  254. Result := Result + '%' + IntToHex(Ord(ASrc[i]), 2); {do not localize}
  255. end
  256. else
  257. begin
  258. Result := Result + ASrc[i];
  259. end;
  260. end;
  261. end;
  262. class function TIdURI.PathEncode(const ASrc: string): string;
  263. const
  264. UnsafeChars = ['*', '#', '%', '<', '>', '+', ' ']; {do not localize}
  265. var
  266. i: Integer;
  267. begin
  268. Result := ''; {Do not Localize}
  269. for i := 1 to Length(ASrc) do begin
  270. if (ASrc[i] in UnsafeChars) or (ASrc[i] >= #$80) or (ASrc[i] < #32) then begin
  271. Result := Result + '%' + IntToHex(Ord(ASrc[i]), 2); {do not localize}
  272. end else begin
  273. Result := Result + ASrc[i];
  274. end;
  275. end;
  276. end;
  277. class function TIdURI.URLEncode(const ASrc: string): string;
  278. Var
  279. LURI: TIdURI;
  280. begin
  281. LURI := TIdURI.Create(ASrc);
  282. try
  283. LURI.Path := PathEncode(LURI.Path);
  284. LURI.Document := PathEncode(LURI.Document);
  285. LURI.Params := ParamsEncode(LURI.Params);
  286. finally
  287. result := LURI.URI;
  288. LURI.Free;
  289. end;
  290. end;
  291. function TIdURI.GetFullURI(
  292. const AOptionalFileds: TIdURIOptionalFieldsSet): String;
  293. Var
  294. LURI: String;
  295. begin
  296. if Length(FProtocol) = 0 then
  297. raise EIdURIException.Create(RSURINoProto);
  298. LURI := FProtocol + '://'; {Do not Localize}
  299. if (Length(FUserName) > 0) and (ofAuthInfo in AOptionalFileds) then begin
  300. LURI := LURI + FUserName;
  301. if Length(FPassword) > 0 then begin
  302. LURI := LURI + ':' + FPassword; {Do not Localize}
  303. end;
  304. LURI := LURI + '@'; {Do not Localize}
  305. end;
  306. if Length(FHost) = 0 then
  307. raise EIdURIException.Create(RSURINoHost);
  308. LURI := LURI + FHost;
  309. if Length(FPort) > 0 then begin
  310. LURI := LURI + ':' + FPort; {Do not Localize}
  311. end;
  312. LURI := LURI + FPath + FDocument + FParams;
  313. if (Length(FBookmark) > 0) and (ofBookmark in AOptionalFileds) then begin
  314. LURI := LURI + '#' + FBookmark; {Do not Localize}
  315. end;
  316. result := LURI;
  317. end;
  318. end.