123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- { Utility routines for HTTP server component
- Copyright (C) 2006-2008 by Micha Nelissen
- This library is Free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at your
- option) any later version.
- This program is diStributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
- for more details.
- You should have received a Copy of the GNU Library General Public License
- along with This library; if not, Write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-
- This license has been modified. See file LICENSE.ADDON for more information.
- Should you find these sources without a LICENSE File, please contact
- me at [email protected]
- }
- unit lHTTPUtil;
- {$mode objfpc}{$h+}
- {$inline on}
- interface
- uses
- sysutils,
- strutils;
- const
- HTTPDateFormat: string = 'ddd, dd mmm yyyy hh:nn:ss';
- HTTPAllowedChars = ['A'..'Z','a'..'z', '*','@','.','_','-',
- '0'..'9', '$','!','''','(',')'];
- type
- PSearchRec = ^TSearchRec;
- function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
- function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
- function TryHTTPDateStrToDateTime(ADateStr: pansichar; var ADest: TDateTime): boolean;
- function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint;
- ASearchRec: PSearchRec = nil): boolean;
- function CheckPermission(const ADocument: pansichar): boolean;
- function HTTPDecode(AStr: pansichar): pansichar;
- function HTTPEncode(const AStr: string): string;
- function HexToNum(AChar: char): byte;
-
- function DecomposeURL(const URL: string; out Host, URI: string; out Port: Word): Boolean;
- function ComposeURL(Host, URI: string; const Port: Word): string;
- implementation
- uses
- lCommon;
- function GMTToLocalTime(ADateTime: TDateTime): TDateTime;
- begin
- Result := ADateTime + (TZSeconds*1000/MSecsPerDay);
- end;
- function LocalTimeToGMT(ADateTime: TDateTime): TDateTime;
- begin
- Result := ADateTime - (TZSeconds*1000/MSecsPerDay);
- end;
- function TryHTTPDateStrToDateTime(ADateStr: pansichar; var ADest: TDateTime): boolean;
- var
- lYear, lMonth, lDay: word;
- lTime: array[0..2] of word;
- I, lCode: integer;
- begin
- if StrLen(ADateStr) < Length(HTTPDateFormat)+4 then exit(false);
- { skip redundant short day string }
- Inc(ADateStr, 5);
- { day }
- if ADateStr[2] = ' ' then
- ADateStr[2] := #0
- else
- exit(false);
- Val(ADateStr, lDay, lCode);
- if lCode <> 0 then exit(false);
- Inc(ADateStr, 3);
- { month }
- lMonth := 1;
- repeat
- if CompareMem(ADateStr, @ShortMonthNames[lMonth][1], 3) then break;
- inc(lMonth);
- if lMonth = 13 then exit(false);
- until false;
- Inc(ADateStr, 4);
- { year }
- if ADateStr[4] = ' ' then
- ADateStr[4] := #0
- else
- exit(false);
- Val(ADateStr, lYear, lCode);
- if lCode <> 0 then exit(false);
- Inc(ADateStr, 5);
- { hour, minute, second }
- for I := 0 to 2 do
- begin
- ADateStr[2] := #0;
- Val(ADateStr, lTime[I], lCode);
- Inc(ADateStr, 3);
- if lCode <> 0 then exit(false);
- end;
- ADest := EncodeDate(lYear, lMonth, lDay) + EncodeTime(lTime[0], lTime[1], lTime[2], 0);
- Result := true;
- end;
- function SeparatePath(var InPath: string; out ExtraPath: string; const Mode:Longint;
- ASearchRec: PSearchRec = nil): boolean;
- var
- lFullPath: string;
- lPos: integer;
- lSearchRec: TSearchRec;
- begin
- if ASearchRec = nil then
- ASearchRec := @lSearchRec;
- ExtraPath := '';
- if Length(InPath) <= 2 then exit(false);
- lFullPath := InPath;
- if InPath[Length(InPath)] = PathDelim then
- SetLength(InPath, Length(InPath)-1);
- repeat
- Result := SysUtils.FindFirst(InPath, Mode, ASearchRec^) = 0;
- SysUtils.FindClose(ASearchRec^);
- if Result then
- begin
- ExtraPath := Copy(lFullPath, Length(InPath)+1, Length(lFullPath)-Length(InPath));
- break;
- end;
- lPos := RPos(PathDelim, InPath);
- if lPos > 0 then
- SetLength(InPath, lPos-1)
- else
- break;
- until false;
- end;
- function HexToNum(AChar: char): byte;
- begin
- if ('0' <= AChar) and (AChar <= '9') then
- Result := ord(AChar) - ord('0')
- else if ('A' <= AChar) and (AChar <= 'F') then
- Result := ord(AChar) - (ord('A') - 10)
- else if ('a' <= AChar) and (AChar <= 'f') then
- Result := ord(AChar) - (ord('a') - 10)
- else
- Result := 0;
- end;
- function HTTPDecode(AStr: pansichar): pansichar;
- var
- lPos, lNext, lDest: pansichar;
- begin
- lDest := AStr;
- repeat
- lPos := AStr;
- while not (lPos^ in ['%', '+', #0]) do
- Inc(lPos);
- if (lPos[0]='%') and (lPos[1] <> #0) and (lPos[2] <> #0) then
- begin
- lPos^ := ansichar((HexToNum(lPos[1]) shl 4) + HexToNum(lPos[2]));
- lNext := lPos+2;
- end else if lPos[0] = '+' then
- begin
- lPos^ := ' ';
- lNext := lPos+1;
- end else
- lNext := nil;
- Inc(lPos);
- if lDest <> AStr then
- Move(AStr^, lDest^, lPos-AStr);
- Inc(lDest, lPos-AStr);
- AStr := lNext;
- until lNext = nil;
- Result := lDest;
- end;
- function HTTPEncode(const AStr: string): string;
- { code from MvC's web }
- var
- src, srcend, dest: pchar;
- hex: string[2];
- len: integer;
- begin
- len := Length(AStr);
- SetLength(Result, len*3); // Worst case scenario
- if len = 0 then
- exit;
- dest := pchar(Result);
- src := pchar(AStr);
- srcend := src + len;
- while src < srcend do
- begin
- if src^ in HTTPAllowedChars then
- dest^ := src^
- else if src^ = ' ' then
- dest^ := '+'
- else begin
- dest^ := '%';
- inc(dest);
- hex := HexStr(Ord(src^),2);
- dest^ := hex[1];
- inc(dest);
- dest^ := hex[2];
- end;
- inc(dest);
- inc(src);
- end;
- SetLength(Result, dest - pchar(Result));
- end;
- function CheckPermission(const ADocument: pansichar): boolean;
- var
- lPos: pansichar;
- begin
- lPos := ADocument;
- repeat
- lPos := StrScan(lPos, '/');
- if lPos = nil then exit(true);
- if (lPos[1] = '.') and (lPos[2] = '.') and ((lPos[3] = '/') or (lPos[3] = #0)) then
- exit(false);
- inc(lPos);
- until false;
- end;
- function DecomposeURL(const URL: string; out Host, URI: string; out Port: Word): Boolean;
- var
- n: Integer;
- tmp: string;
- begin
- Result := False;
- try
- tmp := Trim(URL);
- if Length(tmp) < 1 then // don't do empty
- Exit;
- Port := 80;
- if tmp[Length(tmp)] = '/' then // remove trailing /
- Delete(tmp, Length(tmp), 1);
- if Pos('https://', tmp) = 1 then begin // check for HTTPS
- Result := True;
- Port := 443;
- Delete(tmp, 1, 8); // delete the https part for parsing reasons
- end else if Pos('http://', tmp) = 1 then begin
- Delete(tmp, 1, 7); // delete the http part for parsing reasons
- end;
- n := Pos(':', tmp); // find if we have a port at the end
- if n > 0 then begin
- Port := StrToInt(Copy(tmp, n + 1, Length(tmp)));
- Delete(tmp, n, Length(tmp));
- end;
- n := Pos('/', tmp); // find if we have a uri section
- if n > 0 then begin
- URI := Copy(tmp, n, Length(tmp));
- Delete(tmp, n, Length(tmp));
- end;
- Host := tmp;
- except
- Host := 'error';
- URI := '';
- Port := 0;
- end;
- end;
- function ComposeURL(Host, URI: string; const Port: Word): string;
- begin
- Host := Trim(Host);
- URI := StringReplace(Trim(URI), '%20', ' ', [rfReplaceAll]);
- if (Pos('http://', Host) <> 1)
- and (Pos('https://', Host) <> 1) then
- Host := 'http://' + Host;
- if URI[Length(URI)] = '/' then
- Delete(URI, Length(URI), 1);
- if (Host[Length(Host)] = '/')
- and (URI[1] = '/') then
- Delete(Host, Length(Host), 1)
- else if (URI[1] <> '/')
- and (Host[Length(Host)] <> '/') then
- Host := Host + '/';
- Result := Host + URI + ':' + IntToStr(Port);
- end;
- end.
|