123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555 |
- {
- $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.
|