lhttputil.pp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. { Utility routines for HTTP server component
  2. Copyright (C) 2006-2008 by Micha Nelissen
  3. This library is Free software; you can redistribute it and/or modify it
  4. under the terms of the GNU Library General Public License as published by
  5. the Free Software Foundation; either version 2 of the License, or (at your
  6. option) any later version.
  7. This program is diStributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
  9. FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
  10. for more details.
  11. You should have received a Copy of the GNU Library General Public License
  12. along with This library; if not, Write to the Free Software Foundation,
  13. Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  14. This license has been modified. See file LICENSE.ADDON for more information.
  15. Should you find these sources without a LICENSE File, please contact
  16. me at [email protected]
  17. }
  18. unit lHTTPUtil;
  19. {$mode objfpc}{$h+}
  20. {$inline on}
  21. interface
  22. uses
  23. sysutils,
  24. strutils;
  25. const
  26. HTTPDateFormat: string = 'ddd, dd mmm yyyy hh:nn:ss';
  27. HTTPAllowedChars = ['A'..'Z','a'..'z', '*','@','.','_','-',
  28. '0'..'9', '$','!','''','(',')'];
  29. type
  30. PSearchRec = ^TSearchRec;
  31. function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
  32. function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
  33. function TryHTTPDateStrToDateTime(ADateStr: pansichar; var ADest: TDateTime): boolean;
  34. function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint;
  35. ASearchRec: PSearchRec = nil): boolean;
  36. function CheckPermission(const ADocument: pansichar): boolean;
  37. function HTTPDecode(AStr: pansichar): pansichar;
  38. function HTTPEncode(const AStr: string): string;
  39. function HexToNum(AChar: char): byte;
  40. function DecomposeURL(const URL: string; out Host, URI: string; out Port: Word): Boolean;
  41. function ComposeURL(Host, URI: string; const Port: Word): string;
  42. implementation
  43. uses
  44. lCommon;
  45. function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
  46. begin
  47. Result := ADateTime + (TZSeconds*1000/MSecsPerDay);
  48. end;
  49. function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
  50. begin
  51. Result := ADateTime - (TZSeconds*1000/MSecsPerDay);
  52. end;
  53. function TryHTTPDateStrToDateTime(ADateStr: pansichar; var ADest: TDateTime): boolean;
  54. var
  55. lYear, lMonth, lDay: word;
  56. lTime: array[0..2] of word;
  57. I, lCode: integer;
  58. begin
  59. if StrLen(ADateStr) < Length(HTTPDateFormat)+4 then exit(false);
  60. { skip redundant short day string }
  61. Inc(ADateStr, 5);
  62. { day }
  63. if ADateStr[2] = ' ' then
  64. ADateStr[2] := #0
  65. else
  66. exit(false);
  67. Val(ADateStr, lDay, lCode);
  68. if lCode <> 0 then exit(false);
  69. Inc(ADateStr, 3);
  70. { month }
  71. lMonth := 1;
  72. repeat
  73. if CompareMem(ADateStr, @ShortMonthNames[lMonth][1], 3) then break;
  74. inc(lMonth);
  75. if lMonth = 13 then exit(false);
  76. until false;
  77. Inc(ADateStr, 4);
  78. { year }
  79. if ADateStr[4] = ' ' then
  80. ADateStr[4] := #0
  81. else
  82. exit(false);
  83. Val(ADateStr, lYear, lCode);
  84. if lCode <> 0 then exit(false);
  85. Inc(ADateStr, 5);
  86. { hour, minute, second }
  87. for I := 0 to 2 do
  88. begin
  89. ADateStr[2] := #0;
  90. Val(ADateStr, lTime[I], lCode);
  91. Inc(ADateStr, 3);
  92. if lCode <> 0 then exit(false);
  93. end;
  94. ADest := EncodeDate(lYear, lMonth, lDay) + EncodeTime(lTime[0], lTime[1], lTime[2], 0);
  95. Result := true;
  96. end;
  97. function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint;
  98. ASearchRec: PSearchRec = nil): boolean;
  99. var
  100. lFullPath: string;
  101. lPos: integer;
  102. lSearchRec: TSearchRec;
  103. begin
  104. if ASearchRec = nil then
  105. ASearchRec := @lSearchRec;
  106. ExtraPath := '';
  107. if Length(InPath) <= 2 then exit(false);
  108. lFullPath := InPath;
  109. if InPath[Length(InPath)] = PathDelim then
  110. SetLength(InPath, Length(InPath)-1);
  111. repeat
  112. Result := SysUtils.FindFirst(InPath, Mode, ASearchRec^) = 0;
  113. SysUtils.FindClose(ASearchRec^);
  114. if Result then
  115. begin
  116. ExtraPath := Copy(lFullPath, Length(InPath)+1, Length(lFullPath)-Length(InPath));
  117. break;
  118. end;
  119. lPos := RPos(PathDelim, InPath);
  120. if lPos > 0 then
  121. SetLength(InPath, lPos-1)
  122. else
  123. break;
  124. until false;
  125. end;
  126. function HexToNum(AChar: char): byte;
  127. begin
  128. if ('0' <= AChar) and (AChar <= '9') then
  129. Result := ord(AChar) - ord('0')
  130. else if ('A' <= AChar) and (AChar <= 'F') then
  131. Result := ord(AChar) - (ord('A') - 10)
  132. else if ('a' <= AChar) and (AChar <= 'f') then
  133. Result := ord(AChar) - (ord('a') - 10)
  134. else
  135. Result := 0;
  136. end;
  137. function HTTPDecode(AStr: pansichar): pansichar;
  138. var
  139. lPos, lNext, lDest: pansichar;
  140. begin
  141. lDest := AStr;
  142. repeat
  143. lPos := AStr;
  144. while not (lPos^ in ['%', '+', #0]) do
  145. Inc(lPos);
  146. if (lPos[0]='%') and (lPos[1] <> #0) and (lPos[2] <> #0) then
  147. begin
  148. lPos^ := ansichar((HexToNum(lPos[1]) shl 4) + HexToNum(lPos[2]));
  149. lNext := lPos+2;
  150. end else if lPos[0] = '+' then
  151. begin
  152. lPos^ := ' ';
  153. lNext := lPos+1;
  154. end else
  155. lNext := nil;
  156. Inc(lPos);
  157. if lDest <> AStr then
  158. Move(AStr^, lDest^, lPos-AStr);
  159. Inc(lDest, lPos-AStr);
  160. AStr := lNext;
  161. until lNext = nil;
  162. Result := lDest;
  163. end;
  164. function HTTPEncode(const AStr: string): string;
  165. { code from MvC's web }
  166. var
  167. src, srcend, dest: pchar;
  168. hex: string[2];
  169. len: integer;
  170. begin
  171. len := Length(AStr);
  172. SetLength(Result, len*3); // Worst case scenario
  173. if len = 0 then
  174. exit;
  175. dest := pchar(Result);
  176. src := pchar(AStr);
  177. srcend := src + len;
  178. while src < srcend do
  179. begin
  180. if src^ in HTTPAllowedChars then
  181. dest^ := src^
  182. else if src^ = ' ' then
  183. dest^ := '+'
  184. else begin
  185. dest^ := '%';
  186. inc(dest);
  187. hex := HexStr(Ord(src^),2);
  188. dest^ := hex[1];
  189. inc(dest);
  190. dest^ := hex[2];
  191. end;
  192. inc(dest);
  193. inc(src);
  194. end;
  195. SetLength(Result, dest - pchar(Result));
  196. end;
  197. function CheckPermission(const ADocument: pansichar): boolean;
  198. var
  199. lPos: pansichar;
  200. begin
  201. lPos := ADocument;
  202. repeat
  203. lPos := StrScan(lPos, '/');
  204. if lPos = nil then exit(true);
  205. if (lPos[1] = '.') and (lPos[2] = '.') and ((lPos[3] = '/') or (lPos[3] = #0)) then
  206. exit(false);
  207. inc(lPos);
  208. until false;
  209. end;
  210. function DecomposeURL(const URL: string; out Host, URI: string; out Port: Word): Boolean;
  211. var
  212. n: Integer;
  213. tmp: string;
  214. begin
  215. Result := False;
  216. try
  217. tmp := Trim(URL);
  218. if Length(tmp) < 1 then // don't do empty
  219. Exit;
  220. Port := 80;
  221. if tmp[Length(tmp)] = '/' then // remove trailing /
  222. Delete(tmp, Length(tmp), 1);
  223. if Pos('https://', tmp) = 1 then begin // check for HTTPS
  224. Result := True;
  225. Port := 443;
  226. Delete(tmp, 1, 8); // delete the https part for parsing reasons
  227. end else if Pos('http://', tmp) = 1 then begin
  228. Delete(tmp, 1, 7); // delete the http part for parsing reasons
  229. end;
  230. n := Pos(':', tmp); // find if we have a port at the end
  231. if n > 0 then begin
  232. Port := StrToInt(Copy(tmp, n + 1, Length(tmp)));
  233. Delete(tmp, n, Length(tmp));
  234. end;
  235. n := Pos('/', tmp); // find if we have a uri section
  236. if n > 0 then begin
  237. URI := Copy(tmp, n, Length(tmp));
  238. Delete(tmp, n, Length(tmp));
  239. end;
  240. Host := tmp;
  241. except
  242. Host := 'error';
  243. URI := '';
  244. Port := 0;
  245. end;
  246. end;
  247. function ComposeURL(Host, URI: string; const Port: Word): string;
  248. begin
  249. Host := Trim(Host);
  250. URI := StringReplace(Trim(URI), '%20', ' ', [rfReplaceAll]);
  251. if (Pos('http://', Host) <> 1)
  252. and (Pos('https://', Host) <> 1) then
  253. Host := 'http://' + Host;
  254. if URI[Length(URI)] = '/' then
  255. Delete(URI, Length(URI), 1);
  256. if (Host[Length(Host)] = '/')
  257. and (URI[1] = '/') then
  258. Delete(Host, Length(Host), 1)
  259. else if (URI[1] <> '/')
  260. and (Host[Length(Host)] <> '/') then
  261. Host := Host + '/';
  262. Result := Host + URI + ':' + IntToStr(Port);
  263. end;
  264. end.