123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- {
- 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.
- **********************************************************************}
- {$define CGIDEBUG}
- {$mode objfpc}
- {$H+}
- unit custcgi;
- Interface
- uses
- CustApp,Classes,SysUtils, httpdefs;
- Const
- CGIVarCount = 34;
- Type
- TCGIVarArray = Array[1..CGIVarCount] of String;
- Const
- CgiVarNames : TCGIVarArray =
- ({ 1 } 'AUTH_TYPE',
- { 2 } 'CONTENT_LENGTH',
- { 3 } 'CONTENT_TYPE',
- { 4 } 'GATEWAY_INTERFACE',
- { 5 } 'PATH_INFO',
- { 6 } 'PATH_TRANSLATED',
- { 7 } 'QUERY_STRING',
- { 8 } 'REMOTE_ADDR',
- { 9 } 'REMOTE_HOST',
- { 10 } 'REMOTE_IDENT',
- { 11 } 'REMOTE_USER',
- { 12 } 'REQUEST_METHOD',
- { 13 } 'SCRIPT_NAME',
- { 14 } 'SERVER_NAME',
- { 15 } 'SERVER_PORT',
- { 16 } 'SERVER_PROTOCOL',
- { 17 } 'SERVER_SOFTWARE',
- { 18 } 'HTTP_ACCEPT',
- { 19 } 'HTTP_ACCEPT_CHARSET',
- { 20 } 'HTTP_ACCEPT_ENCODING',
- { 21 } 'HTTP_IF_MODIFIED_SINCE',
- { 22 } 'HTTP_REFERER',
- { 23 } 'HTTP_USER_AGENT',
- { 24 } 'HTTP_COOKIE',
- // Additional Apache vars
- { 24 } 'HTTP_CONNECTION',
- { 25 } 'HTTP_ACCEPT_LANGUAGE',
- { 26 } 'HTTP_HOST',
- { 27 } 'SERVER_SIGNATURE',
- { 28 } 'SERVER_ADDR',
- { 29 } 'DOCUMENT_ROOT',
- { 30 } 'SERVER_ADMIN',
- { 31 } 'SCRIPT_FILENAME',
- { 32 } 'REMOTE_PORT',
- { 33 } 'REQUEST_URI'
- );
-
- Type
- { TCGIRequest }
- TCustomCGIApplication = Class;
- TCGIRequest = Class(TRequest)
- Private
- FCGI : TCustomCGIApplication;
- function GetCGIVar(Index: integer): String;
- Protected
- Function GetFieldValue(Index : Integer) : String; override;
- Procedure InitFromEnvironment;
- Procedure InitPostVars;
- Procedure InitGetVars;
- Public
- Constructor CreateCGI(ACGI : TCustomCGIApplication);
- Property GatewayInterface : String Index 1 Read GetCGIVar;
- Property RemoteIdent : String Index 2 read GetCGIVar;
- Property RemoteUser : String Index 3 read GetCGIVar;
- Property RequestMethod : String Index 4 read GetCGIVar;
- Property ServerName : String Index 5 read GetCGIVar;
- Property ServerProtocol : String Index 6 read GetCGIVar;
- Property ServerSoftware : String Index 7 read GetCGIVar;
- end;
-
- { TCGIResponse }
- TCGIResponse = Class(TResponse)
- private
- FCGI : TCustomCGIApplication;
- FOutput : TStream;
- Protected
- Procedure DoSendHeaders(Headers : TStrings); override;
- Procedure DoSendContent; override;
- Public
- Constructor CreateCGI(ACGI : TCustomCGIApplication; AStream : TStream);
- end;
- { TCustomCgiApplication }
- TCustomCGIApplication = Class(TCustomApplication)
- Private
- FResponse : TCGIResponse;
- FRequest : TCGIRequest;
- FEmail : String;
- FAdministrator : String;
- FOutput : TStream;
- Procedure InitRequestVars;
- Function GetEmail : String;
- Function GetAdministrator : String;
- Function GetRequestVariable(Const VarName : String) : String;
- Function GetRequestVariableCount : Integer;
- Public
- Destructor Destroy; override;
- Property Request : TCGIRequest read FRequest;
- Property Response: TCGIResponse Read FResponse;
- 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 ShowException(E: Exception);override;
- Procedure DeleteFormFiles;
- Procedure DoRun; override;
- Procedure handleRequest(ARequest : TRequest; AResponse : TResponse); virtual;
- Function GetTempCGIFileName : String;
- Function VariableIsUploadedFile(Const VarName : String) : boolean;
- Function UploadedFileName(Const VarName : String) : String;
- 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;
- 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
- {$ifdef CGIDEBUG}
- dbugintf,
- {$endif}
- iostream;
- Const
- MapCgiToHTTP : TCGIVarArray =
- ({ 1: 'AUTH_TYPE' } fieldWWWAuthenticate, // ?
- { 2: 'CONTENT_LENGTH' } FieldContentLength,
- { 3: 'CONTENT_TYPE' } FieldContentType,
- { 4: 'GATEWAY_INTERFACE' } '',
- { 5: 'PATH_INFO' } '',
- { 6: 'PATH_TRANSLATED' } '',
- { 7: 'QUERY_STRING' } '',
- { 8: 'REMOTE_ADDR' } '',
- { 9: 'REMOTE_HOST' } '',
- { 10: 'REMOTE_IDENT' } '',
- { 11: 'REMOTE_USER' } '',
- { 12: 'REQUEST_METHOD' } '',
- { 13: 'SCRIPT_NAME' } '',
- { 14: 'SERVER_NAME' } '',
- { 15: 'SERVER_PORT' } '',
- { 16: 'SERVER_PROTOCOL' } '',
- { 17: 'SERVER_SOFTWARE' } '',
- { 18: 'HTTP_ACCEPT' } FieldAccept,
- { 19: 'HTTP_ACCEPT_CHARSET' } FieldAcceptCharset,
- { 20: 'HTTP_ACCEPT_ENCODING' } FieldAcceptEncoding,
- { 21: 'HTTP_IF_MODIFIED_SINCE' } FieldIfModifiedSince,
- { 22: 'HTTP_REFERER' } FieldReferer,
- { 23: 'HTTP_USER_AGENT' } FieldUserAgent,
- { 23: 'HTTP_USER_AGENT' } FieldCookie,
- // Additional Apache vars
- { 24: 'HTTP_CONNECTION' } FieldConnection,
- { 25: 'HTTP_ACCEPT_LANGUAGE' } FieldAcceptLanguage,
- { 26: 'HTTP_HOST' } '',
- { 27: 'SERVER_SIGNATURE' } '',
- { 28: 'SERVER_ADDR' } '',
- { 29: 'DOCUMENT_ROOT' } '',
- { 30: 'SERVER_ADMIN' } '',
- { 31: 'SCRIPT_FILENAME' } '',
- { 32: 'REMOTE_PORT' } '',
- { 33: 'REQUEST_URI' } ''
- );
- Destructor TCustomCGIApplication.Destroy;
- begin
- DeleteFormFiles;
- FreeAndNil(FRequest);
- FreeAndNil(FResponse);
- FreeAndNil(FOutPut);
- Inherited;
- end;
- Function TCustomCGIApplication.GetTempCGIFileName : String;
- begin
- Result:=GetTempFileName('/tmp/','CGI')
- end;
- Procedure TCustomCGIApplication.DeleteFormFiles;
- Var
- I : Integer;
- FN : String;
- begin
- If Assigned(FRequest) then
- For I:=0 to FRequest.Files.Count-1 do
- begin
- FN:=FRequest.Files[I].LocalFileName;
- If FileExists(FN) then
- DeleteFile(FN);
- end;
- end;
- procedure TCustomCGIApplication.DoRun;
- begin
- HandleRequest(FRequest,FResponse);
- If Not FResponse.ContentSent then
- begin
- FResponse.SendContent;
- end;
- Terminate;
- end;
- procedure TCustomCGIApplication.HandleRequest(ARequest: TRequest; AResponse: TResponse);
- begin
- // Needs overriding;
- end;
- Procedure TCustomCGIApplication.Initialize;
- begin
- StopOnException:=True;
- Inherited;
- FRequest:=TCGIRequest.CreateCGI(Self);
- InitRequestVars;
- FOutput:=TIOStream.Create(iosOutput);
- FResponse:=TCGIResponse.CreateCGI(Self,Self.FOutput);
- end;
- Procedure TCustomCGIApplication.GetCGIVarList(List : TStrings);
- Var
- I : Integer;
- begin
- List.Clear;
- For I:=1 to cgiVarCount do
- List.Add(CGIVarNames[i]+'='+GetEnvironmentVariable(CGIVarNames[i]));
- end;
- Procedure TCustomCGIApplication.ShowException(E: Exception);
- Var
- TheEmail : String;
- FrameCount: integer;
- Frames: PPointer;
- FrameNumber:Integer;
- S : TStrings;
- begin
- If not FResponse.HeadersSent then
- FResponse.ContentType:='text/html';
- If (FResponse.ContentType='text/html') then
- begin
- S:=TStringList.Create;
- Try
- With S do
- begin
- Add('<html><head><title>'+Title+': '+SCGIError+'</title></head>'+LineEnding);
- Add('<body>');
- Add('<center><hr><h1>'+Title+': ERROR</h1><hr></center><br><br>');
- Add(SAppEncounteredError+'<br>');
- Add('<ul>');
- Add('<li>'+SError+' <b>'+E.Message+'</b>');
- Add('<li> Stack trace:<br>');
- Add(BackTraceStrFunc(ExceptAddr)+'<br>');
- FrameCount:=ExceptFrameCount;
- Frames:=ExceptFrames;
- for FrameNumber := 0 to FrameCount-1 do
- Add(BackTraceStrFunc(Frames[FrameNumber])+'<br>');
- Add('</ul><hr>');
- TheEmail:=Email;
- If (TheEmail<>'') then
- Add('<h5><p><i>'+SNotify+Administrator+': <a href="mailto:'+TheEmail+'">'+TheEmail+'</a></i></p></h5>');
- Add('</body></html>');
- end;
- FResponse.Content:=S.Text;
- FResponse.SendContent;
- Finally
- FreeAndNil(S);
- end;
- end;
- end;
- Function TCustomCGIApplication.GetEmail : String;
- Var
- H : String;
- begin
- If (FEmail='') then
- begin
- H:=Request.ServerName;
- If (H<>'') then
- Result:=Administrator+'@'+H
- else
- Result:='';
- end
- else
- Result:=Email;
- end;
- Function TCustomCGIApplication.GetAdministrator : String;
- begin
- If (FADministrator<>'') then
- Result:=FAdministrator
- else
- Result:=SWebMaster;
- end;
- Procedure TCustomCGIApplication.InitRequestVars;
- var
- R : String;
- begin
- R:=GetEnvironmentVariable('REQUEST_METHOD');
- if (R='') then
- Raise Exception.Create(SErrNoRequestMethod);
- FRequest.InitFromEnvironment;
- if CompareText(R,'POST')=0 then
- Request.InitPostVars
- else if CompareText(R,'GET')=0 then
- Request.InitGetVars
- else
- Raise Exception.CreateFmt(SErrInvalidRequestMethod,[R]);
- end;
- constructor TCGIRequest.CreateCGI(ACGI: TCustomCGIApplication);
- begin
- Inherited Create;
- FCGI:=ACGI;
- end;
- Type
- TCapacityStream = Class(TMemoryStream)
- Public
- Property Capacity;
- end;
- Procedure TCGIRequest.InitPostVars;
- Var
- M : TCapacityStream;
- I : TIOStream;
- Cl : Integer;
- B : Byte;
- CT : String;
- begin
- {$ifdef CGIDEBUG}
- SendMethodEnter('InitPostVars');
- {$endif}
- CL:=ContentLength;
- M:=TCapacityStream.Create;
- Try
- I:=TIOStream.Create(iosInput);
- Try
- if (CL<>0) then
- begin
- M.Capacity:=Cl;
- M.CopyFrom(I,Cl);
- end
- else
- begin
- While (I.Read(B,1)>0) do
- M.Write(B,1)
- end;
- Finally
- I.Free;
- end;
- M.Position:=0;
- With TFileStream.Create('/tmp/query',fmCreate) do
- try
- CopyFrom(M,0);
- M.Position:=0;
- Finally
- Free;
- end;
- CT:=ContentType;
- if Pos('MULTIPART/FORM-DATA',Uppercase(CT))<>0 then
- ProcessMultiPart(M,CT)
- else if CompareText('APPLICATION/X-WWW-FORM-URLENCODED',CT)=0 then
- ProcessUrlEncoded(M)
- else
- begin
- {$ifdef CGIDEBUG}
- SendDebug('InitPostVars: unsupported content type:'+CT);
- {$endif}
- Raise Exception.CreateFmt(SErrUnsupportedContentType,[CT]);
- end;
- finally
- M.Free;
- end;
- {$ifdef CGIDEBUG}
- SendMethodExit('InitPostVars');
- {$endif}
- end;
- Procedure TCGIRequest.InitGetVars;
- Var
- FQueryString : String;
- begin
- {$ifdef CGIDEBUG}
- SendMethodEnter('InitGetVars');
- {$endif}
- FQueryString:=GetEnvironmentVariable('QUERY_STRING');
- If (FQueryString<>'') then
- ProcessQueryString(FQueryString);
- {$ifdef CGIDEBUG}
- SendMethodExit('InitGetVars');
- {$endif}
- end;
- Function TCustomCGIApplication.GetRequestVariable(Const VarName : String) : String;
- begin
- If Assigned(Request) then
- Result:=FRequest.QueryFields.Values[VarName]
- else
- Result:='';
- end;
- Function TCustomCGIApplication.GetRequestVariableCount : Integer;
- begin
- If Assigned(Request) then
- Result:=FRequest.QueryFields.Count
- else
- Result:=0;
- end;
- Procedure TCustomCGIApplication.AddResponse(Const S : String);
- Var
- L : Integer;
- begin
- L:=Length(S);
- If L>0 then
- Response.Content:=Response.Content+S;
- end;
- Procedure TCustomCGIApplication.AddResponse(Const Fmt : String; Args : Array of const);
- begin
- AddResponse(Format(Fmt,Args));
- end;
- Procedure TCustomCGIApplication.AddResponseLN(Const S : String);
- begin
- AddResponse(S+LineEnding);
- end;
- Procedure TCustomCGIApplication.AddResponseLN(Const Fmt : String; Args : Array of const);
- begin
- AddResponseLN(Format(Fmt,Args));
- end;
- Function TCustomCGIApplication.VariableIsUploadedFile(Const VarName : String) : boolean;
- begin
- Result:=FRequest.Files.IndexOfFile(VarName)<>-1;
- end;
- Function TCustomCGIApplication.UploadedFileName(Const VarName : String) : String;
- begin
- If VariableIsUploadedFile(VarName) then
- Result:=FRequest.Files.FileByName(VarName).LocalFileName
- else
- Result:='';
- end;
- { TCGIHTTPRequest }
- function TCGIRequest.GetCGIVar(Index: integer): String;
- Var
- R : String;
- begin
- Case Index of
- 1 : R:=GetEnvironmentVariable(CGIVarNames[4]); // Property GatewayInterface : String Index 1 Read GetCGIVar;
- 2 : R:=GetEnvironmentVariable(CGIVarNames[10]); // Property RemoteIdent : String Index 2 read GetCGIVar;
- 3 : R:=GetEnvironmentVariable(CGIVarNames[11]); // Property RemoteUser : String Index 3 read GetCGIVar;
- 4 : R:=GetEnvironmentVariable(CGIVarNames[12]); // Property RequestMethod : String Index 4 read GetCGIVar;
- 5 : R:=GetEnvironmentVariable(CGIVarNames[14]); // Property ServerName : String Index 5 read GetCGIVar;
- 6 : R:=GetEnvironmentVariable(CGIVarNames[16]); // Property ServerProtocol : String Index 6 read GetCGIVar;
- 7 : R:=GetEnvironmentVariable(CGIVarNames[17]); // Property ServerSoftware : String Index 7 read GetCGIVar;
- end;
- Result:=HTTPDecode(R);
- end;
- Procedure TCGIRequest.InitFromEnvironment;
- Var
- I : Integer;
- N,V,OV : String;
-
-
- begin
- For I:=1 to CGIVarCount do
- begin
- N:=MapCgiToHTTP[i];
- if (N<>'') then
- begin
- OV:=GetFieldByName(N);
- V:=GetEnvironmentVariable(CGIVarNames[I]);
- If (OV='') or (V<>'') then
- SetFieldByName(N,HTTPDecode(V));
- end;
- end;
- end;
- Function TCGIRequest.GetFieldValue(Index : Integer) : String;
- Function DecodeVar(I : Integer) : String;
-
- begin
- Result:=HTTPDecode(GetEnvironmentVariable(CGIVarNames[I]));
- end;
- begin
- Case Index of
- 25 : Result:=Decodevar(5); // Property PathInfo
- 26 : Result:=DecodeVar(6); // Property PathTranslated
- 27 : Result:=DecodeVar(8); // Property RemoteAddress
- 28 : Result:=DecodeVar(9); // Property RemoteHost
- 29 : Result:=DecodeVar(13); // Property ScriptName
- 30 : Result:=DecodeVar(15); // Property ServerPort
- else
- Result:=Inherited GetFieldValue(Index);
- end;
- end;
- { TCGIResponse }
- procedure TCGIResponse.DoSendHeaders(Headers : TStrings);
- begin
- {$ifdef CGIDEBUG}
- SendMethodEnter('TCGIResponse.DoSendHeaders');
- SendDebug('Headers: '+Headers.Text);
- {$endif}
- if Assigned(FOutput) then
- Headers.SaveToStream(FOutput);
- {$ifdef CGIDEBUG}
- SendMethodExit('TCGIResponse.DoSendHeaders');
- {$endif}
- end;
- procedure TCGIResponse.DoSendContent;
- begin
- {$ifdef CGIDEBUG}
- SendMethodEnter('TCGIResponse.DoSendContent');
- {$endif}
- If Assigned(ContentStream) then
- FOutput.CopyFrom(ContentStream,0)
- else
- Contents.SaveToStream(FOutput);
- {$ifdef CGIDEBUG}
- SendMethodExit('TCGIResponse.DoSendContent');
- {$endif}
- end;
- constructor TCGIResponse.CreateCGI(ACGI: TCustomCGIApplication; AStream: TStream);
- begin
- inherited Create(ACGI.Request);
- FCGI:=ACGI;
- FOutput:=AStream;
- end;
- initialization
- finalization
- {$ifdef CGIDEBUG}
- if (SendError<>'') then
- Writeln('Debug failed: ',SendError);
- {$endif}
- end.
|