mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 11:49:25 +02:00
302 lines
7.7 KiB
ObjectPascal
302 lines
7.7 KiB
ObjectPascal
{ 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 ales@chello.sk
|
|
}
|
|
|
|
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.
|