{ $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('
'+SNotify+Administrator+': '+TheEmail+'
'); AddResponseLN(''); 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