unit ezcgi; {$mode delphi} {$H+ } interface uses classes, strings, sysutils; const hexTable = '0123456789ABCDEF'; type ECGIException = class(Exception); TEZcgi = class(TObject) private { Private declarations } FVariables : TStringList; FName : String; FEmail : String; FQueryString : String; { Token variables } aString : String; aSepStr : String; aPos : Byte; aLenStr : Byte; aLenSep : Byte; procedure InitToken(aStr, aSep : String); function NextToken(var aToken : String; var aSepChar : Char) : Boolean; procedure GetQueryItems; procedure ProcessRequest; procedure LoadEnvVariables; function GetVal(Index : String) : String; function GetName(Index : Integer) : String; function GetVariable(Index : Integer) : String; function GetVarCount : Integer; procedure ReadPostQuery; procedure ReadGetQuery; protected { Protected declarations } procedure OutputError(errorMessage : String); public { Public declarations } constructor Create; destructor Destroy; override; procedure Run; procedure WriteContent(ctype : String); procedure PutLine(sOut : String); function GetValue(Index : String; defaultValue : String) : String; procedure DoPost; virtual; procedure DoGet; virtual; property Values[Index : String] : String read GetVal; property Names[Index : Integer] : String read GetName; property Variables[Index : Integer] : String read GetVariable; property VariableCount : Integer read GetVarCount; property Name : String read FName write FName; property Email : String read FEmail write FEmail; end; implementation { *********** Include OS-dependent Getenv Call ************ } {$I ezcgi.inc} { *********** Public Methods *************** } constructor TEZcgi.Create; begin FName := 'No name available'; FEmail := 'Email address unavailable'; FVariables := TStringList.Create; LoadEnvVariables; end; destructor TEZcgi.Destroy; begin FVariables.Free; end; procedure TEZcgi.Run; begin ProcessRequest; end; procedure TEZcgi.DoPost; begin // Must be overriden by child class end; procedure TEZcgi.DoGet; begin // Must be overriden by child class end; procedure TEZcgi.WriteContent(ctype : String); begin writeln('Content-Type: ',ctype); writeln; end; procedure TEZcgi.PutLine(sOut : String); begin writeln(sOut); end; function TEZcgi.GetValue(Index, defaultValue : String) : String; begin result := GetVal(Index); if result = '' then result := defaultValue; end; { *********** Private Methods *************** } procedure TEZcgi.LoadEnvVariables; procedure GetEData(variable : String); var tempStr : String; begin // This is a system dependent call !! tempStr := GetEnv(variable); if tempStr <> '' then FVariables.Add(variable + '=' + tempStr); end; begin { Standard CGI Environment Variables } GetEData('AUTH_TYPE'); GetEData('CONTENT_LENGTH'); GetEData('CONTENT_TYPE'); GetEData('GATEWAY_INTERFACE'); GetEData('PATH_INFO'); GetEData('PATH_TRANSLATED'); GetEData('QUERY_STRING'); GetEData('REMOTE_ADDR'); GetEData('REMOTE_HOST'); GetEData('REMOTE_IDENT'); GetEData('REMOTE_USER'); GetEData('REQUEST_METHOD'); GetEData('SCRIPT_NAME'); GetEData('SERVER_NAME'); GetEData('SERVER_PORT'); GetEData('SERVER_PROTOCOL'); GetEData('SERVER_SOFTWARE'); { Standard HTTP Environment Variables } GetEData('HTTP_ACCEPT'); GetEData('HTTP_ACCEPT_CHARSET'); GetEData('HTTP_ACCEPT_ENCODING'); GetEData('HTTP_IF_MODIFIED_SINCE'); GetEData('HTTP_REFERER'); GetEData('HTTP_USER_AGENT'); end; procedure TEZcgi.ProcessRequest; var request : String; begin request := GetVal('REQUEST_METHOD'); if request = '' then OutputError('No REQUEST_METHOD passed from server!') else if request = 'POST' then begin ReadPostQuery; DoPost; end else if request = 'GET' then begin ReadGetQuery; DoGet; end else OutputError('Invalid REQUEST_METHOD passed from server!'); end; function TEZcgi.GetVal(Index : String) : String; begin result := FVariables.Values[Index]; end; function TEZcgi.GetName(Index : Integer) : String; begin result := FVariables.Names[Index]; end; function TEZcgi.GetVariable(Index : Integer) : String; begin result := FVariables[Index]; end; function TEZcgi.GetVarCount : Integer; begin result := FVariables.Count; end; procedure TEZcgi.ReadPostQuery; var index : Integer; ch : Char; temp : String; code : Word; contentLength : Integer; theType : String; begin temp := GetVal('CONTENT_LENGTH'); if Length(temp) > 0 then begin Val(temp, contentLength, code); if code <> 0 then contentLength := 0; end; if contentLength = 0 then OutputError('No content length passed from server!'); theType := UpperCase(GetVal('CONTENT_TYPE')); if theType <> 'APPLICATION/X-WWW-FORM-URLENCODED' then OutputError('No content type passed from server!'); FQueryString := ''; for index := 0 to contentLength do begin Read(ch); FQueryString := FQueryString + ch; end; GetQueryItems; end; procedure TEZcgi.ReadGetQuery; begin FQueryString := GetVal('QUERY_STRING'); if FQueryString = '' then OutputError('No QUERY_STRING passed from server!'); GetQueryItems; end; procedure TEZcgi.GetQueryItems; var queryItem : String; delimiter : Char; function hexConverter(h1, h2 : Char) : Char; var thex : byte; begin tHex := (Pos(upcase(h1), hexTable) - 1) * 16; tHex := tHex + Pos(upcase(h2), hexTable) - 1; result := chr(thex); end; procedure Convert_ESC_Chars; var index : Integer; begin repeat index := Pos('+', queryItem); if index > 0 then queryItem[index] := Chr(32); until index = 0; repeat index := Pos('%', queryItem); if index > 0 then begin queryItem[index] := hexConverter(queryItem[index + 1], queryItem[index + 2]); system.Delete(queryItem, index + 1, 2); end; until index = 0; end; begin InitToken(FQueryString, '&'); while NextToken(queryItem, delimiter) do begin if queryItem <> '' then begin Convert_ESC_Chars; FVariables.Add(queryItem); end; end; end; procedure TEZcgi.OutputError(errorMessage : String); begin WriteContent('text/html'); writeln('CGI ERROR'); writeln(''); writeln('

CGI ERROR




'); writeln('This CGI application encountered the following error:
'); writeln('