mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-07-22 21:56:05 +02:00
555 lines
13 KiB
ObjectPascal
555 lines
13 KiB
ObjectPascal
{
|
|
$Id$
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 1999-2000 by the Free Pascal development team
|
|
|
|
TCGIApplication class.
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
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.
|
|
|
|
**********************************************************************}
|
|
|
|
{$mode objfpc}
|
|
{$H+}
|
|
|
|
unit cgiapp;
|
|
|
|
Interface
|
|
|
|
uses
|
|
CustApp,Classes,SysUtils;
|
|
|
|
Const
|
|
CGIVarCount = 23;
|
|
|
|
Type
|
|
TCGIVarArray = Array[1..CGIVarCount] of String;
|
|
|
|
Const
|
|
CgiVarNames : TCGIVarArray =
|
|
('AUTH_TYPE',
|
|
'CONTENT_LENGTH',
|
|
'CONTENT_TYPE',
|
|
'GATEWAY_INTERFACE',
|
|
'PATH_INFO',
|
|
'PATH_TRANSLATED',
|
|
'QUERY_STRING', 'REMOTE_ADDR',
|
|
'REMOTE_HOST',
|
|
'REMOTE_IDENT',
|
|
'REMOTE_USER',
|
|
'REQUEST_METHOD',
|
|
'SCRIPT_NAME',
|
|
'SERVER_NAME',
|
|
'SERVER_PORT',
|
|
'SERVER_PROTOCOL',
|
|
'SERVER_SOFTWARE',
|
|
'HTTP_ACCEPT',
|
|
'HTTP_ACCEPT_CHARSET',
|
|
'HTTP_ACCEPT_ENCODING',
|
|
'HTTP_IF_MODIFIED_SINCE',
|
|
'HTTP_REFERER',
|
|
'HTTP_USER_AGENT');
|
|
|
|
Type
|
|
|
|
TCgiApplication = Class(TCustomApplication)
|
|
Private
|
|
FResponse : TStream;
|
|
FEmail : String;
|
|
FAdministrator : String;
|
|
FContentTypeEmitted : Boolean;
|
|
FCGIVars : TCGIVarArray;
|
|
FRequestVars : TStrings;
|
|
Function GetCGIVar (Index : Integer) : String;
|
|
Procedure InitCGIVars;
|
|
Procedure InitRequestVars;
|
|
Procedure InitPostVars;
|
|
Procedure InitGetVars;
|
|
Procedure SetContentLength (Value : Integer);
|
|
Procedure SetCGIVar(Index : Integer; Value : String);
|
|
Function GetContentLength : Integer;
|
|
Function GetServerPort : Word;
|
|
Function GetEmail : String;
|
|
Function GetAdministrator : String;
|
|
Procedure ProcessQueryString(Const FQueryString : String);
|
|
Function GetRequestVariable(Const VarName : String) : String;
|
|
Function GetRequestVariableCount : Integer;
|
|
Public
|
|
Constructor Create(AOwner : TComponent); override;
|
|
Destructor Destroy; override;
|
|
Procedure AddResponse(Const S : String);
|
|
Procedure AddResponse(Const Fmt : String; Args : Array of const);
|
|
Procedure AddResponseLn(Const S : String);
|
|
Procedure AddResponseLn(Const Fmt : String; Args : Array of const);
|
|
Procedure Initialize; override;
|
|
Procedure GetCGIVarList(List : TStrings);
|
|
Procedure GetRequestVarList(List : TStrings);
|
|
Procedure GetRequestVarList(List : TStrings; NamesOnly : Boolean);
|
|
Procedure ShowException(E: Exception);override;
|
|
Function EmitContentType : Boolean;
|
|
Property AuthType : String Index 1 Read GetCGIVar;
|
|
Property ContentLength : Integer Read GetContentLength Write SetContentLength; // Index 2
|
|
Property ContentType : String Index 3 Read GetCGIVar Write SetCGIVar;
|
|
Property GatewayInterface : String Index 4 Read GetCGIVar;
|
|
Property PathInfo : String index 5 read GetCGIvar;
|
|
Property PathTranslated : String Index 6 read getCGIVar;
|
|
Property QueryString : String Index 7 read getcgivar;
|
|
Property RemoteAddress : String Index 8 read GetCGIVar;
|
|
Property RemoteHost : String Index 9 read GetCGIVar;
|
|
Property RemoteIdent : String Index 10 read GetCGIVar;
|
|
Property RemoteUser : String Index 11 read GetCGIVar;
|
|
Property RequestMethod : String Index 12 read GetCGIVar;
|
|
Property ScriptName : String Index 13 read GetCGIVar;
|
|
Property ServerName : String Index 14 read GetCGIVar;
|
|
Property ServerPort : Word Read GetServerPort; // Index 15
|
|
Property ServerProtocol : String Index 16 read GetCGIVar;
|
|
Property ServerSoftware : String Index 17 read GetCGIVar;
|
|
Property HTTPAccept : String Index 18 read GetCGIVar;
|
|
Property HTTPAcceptCharset : String Index 19 read GetCGIVar;
|
|
Property HTTPAcceptEncoding : String Index 20 read GetCGIVar;
|
|
Property HTTPIfModifiedSince : String Index 21 read GetCGIVar; // Maybe change to TDateTime ??
|
|
Property HTTPReferer : String Index 22 read GetCGIVar;
|
|
Property HTTPUserAgent : String Index 23 read GetCGIVar;
|
|
Property Email : String Read GetEmail Write FEmail;
|
|
Property Administrator : String Read GetAdministrator Write FAdministrator;
|
|
Property RequestVariables[VarName : String] : String Read GetRequestVariable;
|
|
Property RequestVariableCount : Integer Read GetRequestVariableCount;
|
|
Property Response : TStream Read FResponse;
|
|
end;
|
|
|
|
ResourceString
|
|
SWebMaster = 'webmaster';
|
|
SCGIError = 'CGI Error';
|
|
SAppEncounteredError = 'The application encountered the following error:';
|
|
SError = 'Error: ';
|
|
SNotify = 'Notify: ';
|
|
SErrNoContentLength = 'No content length passed from server!';
|
|
SErrUnsupportedContentType = 'Unsupported content type: "%s"';
|
|
SErrNoRequestMethod = 'No REQUEST_METHOD passed from server.';
|
|
SErrInvalidRequestMethod = 'Invalid REQUEST_METHOD passed from server.';
|
|
|
|
Implementation
|
|
|
|
uses
|
|
iostream;
|
|
|
|
Constructor TCgiApplication.Create(AOwner : TComponent);
|
|
|
|
begin
|
|
Inherited Create(AOwner);
|
|
FRequestVars:=TStringList.Create;
|
|
end;
|
|
|
|
Destructor TCgiApplication.Destroy;
|
|
|
|
begin
|
|
FRequestVars.Free;
|
|
Inherited;
|
|
end;
|
|
|
|
Function TCgiApplication.GetCGIVar (Index : Integer) : String;
|
|
|
|
begin
|
|
Result:=FCGIVars[Index];
|
|
end;
|
|
|
|
Procedure TCgiApplication.InitCGIVars;
|
|
|
|
Var
|
|
I : Integer;
|
|
L : TStrings;
|
|
|
|
begin
|
|
L:=TStringList.Create;
|
|
Try
|
|
GetEnvironmentList(L);
|
|
For I:=1 to CGIVarCount do
|
|
FCGIVars[i]:=L.Values[CGIVarNames[i]];
|
|
Finally
|
|
L.Free;
|
|
end;
|
|
end;
|
|
|
|
Procedure TCgiApplication.Initialize;
|
|
|
|
begin
|
|
StopOnException:=True;
|
|
Inherited;
|
|
InitCGIVars;
|
|
InitRequestVars;
|
|
FResponse:=TIOStream.Create(iosOutput);
|
|
end;
|
|
|
|
Procedure TCgiApplication.GetCGIVarList(List : TStrings);
|
|
|
|
Var
|
|
I : Integer;
|
|
|
|
begin
|
|
List.Clear;
|
|
For I:=1 to cgiVarCount do
|
|
List.Add(CGIVarNames[i]+'='+FCGIVars[i]);
|
|
end;
|
|
|
|
Procedure TCgiApplication.GetRequestVarList(List : TStrings);
|
|
|
|
begin
|
|
GetRequestVarList(List,False);
|
|
end;
|
|
|
|
Procedure TCgiApplication.GetRequestVarList(List : TStrings; NamesOnly : Boolean);
|
|
|
|
Var
|
|
I,J : Integer;
|
|
S : String;
|
|
|
|
begin
|
|
List.BeginUpdate;
|
|
Try
|
|
List.Clear;
|
|
// Copy one by one, there may be CR/LF in the variables, causing 'Text' to go wrong.
|
|
If Assigned(FRequestVars) then
|
|
For I:=0 to FRequestVars.Count-1 do
|
|
begin
|
|
S:=FRequestVars[i];
|
|
If NamesOnly then
|
|
begin
|
|
J:=Pos('=',S);
|
|
If (J>0) then
|
|
S:=Copy(S,1,J-1);
|
|
end;
|
|
List.Add(S);
|
|
end;
|
|
finally
|
|
List.EndUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
Function TCgiApplication.GetContentLength : Integer;
|
|
|
|
begin
|
|
Result:=StrToIntDef(GetCGIVar(2),-1);
|
|
end;
|
|
|
|
Procedure TCgiApplication.SetContentLength (Value : Integer);
|
|
|
|
begin
|
|
SetCGIVar(2,IntToStr(Value));
|
|
end;
|
|
|
|
Procedure TCgiApplication.SetCGIVar(Index : Integer; Value : String);
|
|
|
|
begin
|
|
If Index in [1..cgiVarCount] then
|
|
FCGIVars[Index]:=Value;
|
|
end;
|
|
|
|
|
|
Function TCgiApplication.GetServerPort : Word;
|
|
begin
|
|
Result:=StrToIntDef(GetCGIVar(15),0);
|
|
end;
|
|
|
|
Function TCgiApplication.EmitContentType : Boolean;
|
|
|
|
Var
|
|
S: String;
|
|
|
|
begin
|
|
Result:=Not FContentTypeEmitted;
|
|
If result then
|
|
begin
|
|
S:=ContentType;
|
|
If (S='') then
|
|
S:='text/html';
|
|
AddResponseLn('Content-Type: '+ContentType);
|
|
AddResponseLn('');
|
|
FContentTypeEmitted:=True;
|
|
end;
|
|
end;
|
|
|
|
Procedure TCgiApplication.ShowException(E: Exception);
|
|
|
|
Var
|
|
TheEmail : String;
|
|
|
|
begin
|
|
If not FContentTypeEmitted then
|
|
begin
|
|
ContentType:='text/html';
|
|
EmitContentType;
|
|
end;
|
|
If (ContentType='text/html') then
|
|
begin
|
|
AddResponseLN('<html><head><title>'+Title+': '+SCGIError+'</title></head>');
|
|
AddResponseLN('<body>');
|
|
AddResponseLN('<center><hr><h1>'+Title+': ERROR</h1><hr></center><br><br>');
|
|
AddResponseLN(SAppEncounteredError+'<br>');
|
|
AddResponseLN('<ul>');
|
|
AddResponseLN('<li>'+SError+' <b>'+E.Message+'</b></ul><hr>');
|
|
TheEmail:=Email;
|
|
If (TheEmail<>'') then
|
|
AddResponseLN('<h5><p><i>'+SNotify+Administrator+': <a href="mailto:'+TheEmail+'">'+TheEmail+'</a></i></p></h5>');
|
|
AddResponseLN('</body></html>');
|
|
end;
|
|
end;
|
|
|
|
Function TCgiApplication.GetEmail : String;
|
|
|
|
Var
|
|
H : String;
|
|
|
|
begin
|
|
If (FEmail='') then
|
|
begin
|
|
H:=ServerName;
|
|
If (H<>'') then
|
|
Result:=Administrator+'@'+H
|
|
else
|
|
Result:='';
|
|
end
|
|
else
|
|
Result:=Email;
|
|
end;
|
|
|
|
Function TCgiApplication.GetAdministrator : String;
|
|
|
|
begin
|
|
If (FADministrator<>'') then
|
|
Result:=FAdministrator
|
|
else
|
|
Result:=SWebMaster;
|
|
end;
|
|
|
|
Procedure TCgiApplication.InitRequestVars;
|
|
|
|
var
|
|
R : String;
|
|
|
|
begin
|
|
R:=RequestMethod;
|
|
if (R='') then
|
|
Raise Exception.Create(SErrNoRequestMethod);
|
|
if CompareText(R,'POST')=0 then
|
|
InitPostVars
|
|
else if CompareText(R,'GET')=0 then
|
|
InitGetVars
|
|
else
|
|
Raise Exception.CreateFmt(SErrInvalidRequestMethod,[R]);
|
|
end;
|
|
|
|
Procedure TCgiApplication.InitPostVars;
|
|
|
|
var
|
|
FQueryString : String;
|
|
i : Integer;
|
|
ch : Char;
|
|
|
|
begin
|
|
if (FCGIVars[2]='') then
|
|
Raise Exception.Create(SErrNoContentLength);
|
|
if CompareText(ContentType,'APPLICATION/X-WWW-FORM-URLENCODED')<>0 then
|
|
Raise Exception.CreateFmt(SErrUnsupportedContentType,[ContentType]);
|
|
SetLength(FQueryString,ContentLength);
|
|
for I:= 1 to contentLength Do
|
|
begin
|
|
Read(ch);
|
|
FQueryString[i]:=ch;
|
|
end;
|
|
ProcessQueryString(FQueryString);
|
|
end;
|
|
|
|
Procedure TCgiApplication.InitGetVars;
|
|
|
|
Var
|
|
FQueryString : String;
|
|
|
|
begin
|
|
FQueryString:=QueryString;
|
|
If (FQueryString<>'') then
|
|
ProcessQueryString(FQueryString);
|
|
end;
|
|
|
|
const
|
|
hexTable = '0123456789ABCDEF';
|
|
|
|
Procedure TCgiApplication.ProcessQueryString(Const FQueryString : String);
|
|
|
|
|
|
var
|
|
queryItem : String;
|
|
delimiter : Char;
|
|
aString : String;
|
|
aSepStr : String;
|
|
aPos : Integer;
|
|
aLenStr : Integer;
|
|
aLenSep : Integer;
|
|
|
|
function hexConverter(h1, h2 : Char) : Char;
|
|
|
|
var
|
|
B : Byte;
|
|
|
|
begin
|
|
B:=(Pos(upcase(h1),hexTable)-1)*16;
|
|
B:=B+Pos(upcase(h2),hexTable)-1;
|
|
Result:=chr(B);
|
|
end;
|
|
|
|
procedure Convert_ESC_Chars;
|
|
|
|
var
|
|
index : Integer;
|
|
|
|
begin
|
|
For Index:=1 to Length(QueryItem) do
|
|
Index:=Length(QueryItem);
|
|
While (Index>0) do
|
|
begin
|
|
If QueryItem[Index]='+' then
|
|
QueryItem[Index]:=' '
|
|
else If (QueryItem[Index]='%') and (Index<Length(QueryItem)-1) then
|
|
begin
|
|
QueryItem[Index]:=hexConverter(QueryItem[Index+1],QueryItem[index+2]);
|
|
System.Delete(QueryItem,Index+1,2);
|
|
end;
|
|
dec(Index);
|
|
end;
|
|
end;
|
|
|
|
procedure InitToken(aStr, aSep : String);
|
|
|
|
begin
|
|
aString := aStr;
|
|
aSepStr := aSep;
|
|
aPos := 1;
|
|
aLenStr := Length(aString);
|
|
aLenSep := Length(aSepStr);
|
|
end;
|
|
|
|
function NextToken(var aToken : String; var aSepChar : Char) : Boolean;
|
|
|
|
var
|
|
i : Integer;
|
|
j : Integer;
|
|
BoT : Integer;
|
|
EoT : Integer;
|
|
isSep : Boolean;
|
|
|
|
begin
|
|
BoT:=aPos;
|
|
EoT:=aPos;
|
|
for i:=aPos to aLenStr do
|
|
begin
|
|
IsSep := false;
|
|
for j := 1 to aLenSep do
|
|
begin
|
|
if aString[i] = aSepStr[j] then
|
|
begin
|
|
IsSep := true;
|
|
Break;
|
|
end;
|
|
end;
|
|
if IsSep then
|
|
begin
|
|
EoT := i;
|
|
aPos := i + 1;
|
|
aSepChar := aString[i];
|
|
Break;
|
|
end
|
|
else
|
|
begin
|
|
if i = aLenStr then
|
|
begin
|
|
EoT := i;
|
|
aPos := i;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
if aPos < aLenStr then
|
|
begin
|
|
aToken := Copy(aString, BoT, EoT - BoT);
|
|
Result := true;
|
|
end
|
|
else
|
|
begin
|
|
if aPos = aLenStr then
|
|
begin
|
|
aToken := Copy(aString, BoT, EoT - BoT + 1);
|
|
Result := true;
|
|
aPos := aPos + 1;
|
|
end
|
|
else
|
|
begin
|
|
Result := false;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
begin
|
|
InitToken(FQueryString, '&');
|
|
while NextToken(QueryItem, delimiter) do
|
|
begin
|
|
if (QueryItem<>'') then
|
|
begin
|
|
Convert_ESC_Chars;
|
|
FRequestVars.Add(QueryItem);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
Function TCGIApplication.GetRequestVariable(Const VarName : String) : String;
|
|
|
|
begin
|
|
If Assigned(FRequestVars) then
|
|
Result:=FRequestVars.Values[VarName];
|
|
end;
|
|
|
|
Function TCGIApplication.GetRequestVariableCount : Integer;
|
|
|
|
begin
|
|
If Assigned(FRequestVars) then
|
|
Result:=FRequestVars.Count
|
|
else
|
|
Result:=0;
|
|
end;
|
|
|
|
Procedure TCGIApplication.AddResponse(Const S : String);
|
|
|
|
Var
|
|
L : Integer;
|
|
|
|
begin
|
|
L:=Length(S);
|
|
If L>0 then
|
|
FResponse.Write(S[1],L);
|
|
end;
|
|
|
|
Procedure TCGIApplication.AddResponse(Const Fmt : String; Args : Array of const);
|
|
|
|
begin
|
|
AddResponse(Format(Fmt,Args));
|
|
end;
|
|
|
|
Procedure TCGIApplication.AddResponseLN(Const S : String);
|
|
|
|
|
|
begin
|
|
AddResponse(S+LineEnding);
|
|
end;
|
|
|
|
Procedure TCGIApplication.AddResponseLN(Const Fmt : String; Args : Array of const);
|
|
|
|
begin
|
|
AddResponseLN(Format(Fmt,Args));
|
|
end;
|
|
|
|
end. |