////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2023 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// CGI scripting utilities.                                                   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20060929 - Creation                                                        //
// 20061006 - First tested version                                            //
// 20061008 - First accepted version                                          //
// 20230608 - Modified for FPC (A.K.)                                         //
////////////////////////////////////////////////////////////////////////////////

unit _crw_cgi; // Common Gateway Interface routines

{$I _crw_sysdef}

{$IFDEF FPC}{$mode Delphi}{$ENDIF}{$H+}

interface

uses
 _crw_sharemem, // NB: THIS UNIT MUST BE FIRST !!!
 {$IFDEF UNIX} cthreads, dl, {$ENDIF}
 {$IFDEF WINDOWS} windows, {$ENDIF}
 sysutils, classes, math,
 _crw_alloc, _crw_fio, _crw_str, _crw_rtc;

 /////////////////////////////////////////////////////////////////////////
 // TCgi help you to work with Common Gateway Interface in easy way.
 // Note that Cgi is one and only instance of TCgi class for any program.
 // TCgi constructor will automatically read and decode content data,
 // so client code makes much more easy.
 // Next note: CGI application should be {$APPTYPE CONSOLE} and do not use
 // readln/writeln, because StdIn/StdOut uses by CGI to get/put content.
 // Use Cgi.CONTENT instead of readln, Cgi.OutText instead of writeln.
 // Usage is like:
 // program CgiDemo;
 // uses _cgi;
 // begin
 //  Cgi.Output:='Content-Type: text/html';
 //  Cgi.Output:='';
 //  Cgi.Output:='<HTML>';
 //  Cgi.Output:='<HEAD>';
 //  Cgi.Output:='<TITLE>Test</TITLE>';
 //  Cgi.Output:='</HEAD>';
 //  Cgi.Output:='<BODY>';
 //  Cgi.Output:=' Hello, world!';
 //  Cgi.Output:=' Request method : '+Cgi.REQUEST_METHOD;
 //  Cgi.Output:=' Content data   : '+Cgi.CONTENT;
 //  Cgi.Output:='</BODY>';
 //  Cgi.Output:='</HTML>'
 // end;
 /////////////////////////////////////////////////////////////////////////
 // TinyWeb 1.93 suppors next CGI variables:
 // PATH_INFO, PATH_TRANSLATED, REMOTE_HOST, REMOTE_ADDR, GATEWAY_INTERFACE,
 // SCRIPT_NAME, REQUEST_METHOD, HTTP_ACCEPT, HTTP_ACCEPT_CHARSET, AUTH_TYPE
 // HTTP_ACCEPT_ENCODING, HTTP_ACCEPT_LANGUAGE, HTTP_FROM, HTTP_HOST, HTTP_REFERER,
 // HTTP_USER_AGENT, HTTP_COOKIE, QUERY_STRING, SERVER_SOFTWARE, SERVER_NAME,
 // SERVER_PROTOCOL, SERVER_PORT, CONTENT_TYPE, CONTENT_LENGTH, USER_NAME, USER_PASSWORD,
 /////////////////////////////////////////////////////////////////////////
type
 TCgi = class(TMasterObject)
 private
  myStdIn       : THandle;
  myStdOut      : THandle;
  myContent     : LongString;
  myQueryList   : array of LongString;
  myCookieList  : array of LongString;
  myContentList : array of LongString;
  procedure   SetOutStr(const S:LongString);
  procedure   SetOutText(const S:LongString);
  function    GetGATEWAY_INTERFACE:LongString;
  function    GetREQUEST_METHOD:LongString;
  function    GetQUERY_STRING:LongString;
  function    GetSERVER_NAME:LongString;
  function    GetSERVER_PORT:LongString;
  function    GetSERVER_PROTOCOL:LongString;
  function    GetSERVER_SOFTWARE:LongString;
  function    GetREMOTE_HOST:LongString;
  function    GetREMOTE_ADDR:LongString;
  function    GetAUTH_TYPE:LongString;
  function    GetREMOTE_USER:LongString;
  function    GetREMOTE_IDENT:LongString;
  function    GetUSER_NAME:LongString;
  function    GetUSER_PASSWORD:LongString;
  function    GetSCRIPT_NAME:LongString;
  function    GetPATH_INFO:LongString;
  function    GetPATH_TRANSLATED:LongString;
  function    GetURL:LongString;
  function    GetHTTP_ACCEPT:LongString;
  function    GetHTTP_ACCEPT_CHARSET:LongString;
  function    GetHTTP_ACCEPT_ENCODING:LongString;
  function    GetHTTP_ACCEPT_LANGUAGE:LongString;
  function    GetHTTP_ACCEPT_RANGES:LongString;
  function    GetHTTP_AGE:LongString;
  function    GetHTTP_ALLOW:LongString;
  function    GetHTTP_AUTHORIZATION:LongString;
  function    GetHTTP_CACHE_CONTROL:LongString;
  function    GetHTTP_CONNECTION:LongString;
  function    GetHTTP_CONTENT:LongString;
  function    GetHTTP_CONTENT_BASE:LongString;
  function    GetHTTP_CONTENT_ENCODING:LongString;
  function    GetHTTP_CONTENT_LANGUAGE:LongString;
  function    GetHTTP_CONTENT_LENGTH:LongString;
  function    GetHTTP_CONTENT_LOCATION:LongString;
  function    GetHTTP_CONTENT_MD5:LongString;
  function    GetHTTP_CONTENT_RANGE:LongString;
  function    GetHTTP_CONTENT_TYPE:LongString;
  function    GetHTTP_CONTENT_VERSION:LongString;
  function    GetHTTP_COOKIE:LongString;
  function    GetHTTP_DATE:LongString;
  function    GetHTTP_DERIVED_FROM:LongString;
  function    GetHTTP_ETAG:LongString;
  function    GetHTTP_EXPIRES:LongString;
  function    GetHTTP_FROM:LongString;
  function    GetHTTP_HOST:LongString;
  function    GetHTTP_IF_MODIFIED_SINCE:LongString;
  function    GetHTTP_IF_MATCH:LongString;
  function    GetHTTP_IF_NONE_MATCH:LongString;
  function    GetHTTP_IF_RANGE:LongString;
  function    GetHTTP_IF_UNMODIFIED_SINCE:LongString;
  function    GetHTTP_LAST_MODIFIED:LongString;
  function    GetHTTP_LOCATION:LongString;
  function    GetHTTP_MAX_FORWARDS:LongString;
  function    GetHTTP_PRAGMA:LongString;
  function    GetHTTP_PROXY_AUTHENTICATE:LongString;
  function    GetHTTP_PROXY_AUTHORIZATION:LongString;
  function    GetHTTP_PUBLIC:LongString;
  function    GetHTTP_RANGE:LongString;
  function    GetHTTP_REFERER:LongString;
  function    GetHTTP_RETRY_AFTER:LongString;
  function    GetHTTP_SERVER:LongString;
  function    GetHTTP_TITLE:LongString;
  function    GetHTTP_TRANSFER_ENCODING:LongString;
  function    GetHTTP_UPGRADE:LongString;
  function    GetHTTP_USER_AGENT:LongString;
  function    GetHTTP_VARY:LongString;
  function    GetHTTP_VIA:LongString;
  function    GetHTTP_WARNING:LongString;
  function    GetHTTP_WWW_AUTHENTICATE:LongString;
  function    GetQueryCount:Integer;
  function    GetQueryItem(i:Integer):LongString;
  function    GetCookieCount:Integer;
  function    GetCookieItem(i:Integer):LongString;
  function    GetContentCount:Integer;
  function    GetContentItem(i:Integer):LongString;
  function    GetRequestAsText:LongString;
 public
  ////////////////////////////////////////////////////////////////////////////
  // Construction/destruction routines.
  // Note that CGI application may have one and only CGI object, see Cgi func.
  ////////////////////////////////////////////////////////////////////////////
  constructor Create;
  destructor  Destroy; override;
 public
  ////////////////////////////////////////////////////////////////////////////
  // General purpose routines.
  // Note that CGI redirect Input, Output to NULL.
  // CGI application should not use readln/writeln.
  // Use cgi.CONTENT instead of readln, cgi.PutText instead of Writeln.
  // cgi.OutStr:='s'  - sends 's' to StdOut,      instead of Write('s').
  // cgi.OutText:='s' - sends 's'+EOL to StdOut, instead of WriteLn('s').
  ////////////////////////////////////////////////////////////////////////////
  property OutStr               : LongString write SetOutStr;
  property OutText              : LongString write SetOutText;
  ////////////////////////////////////////////////////////////////////////////
  // General CGI variables, see RFC 3875, www.rfc-editor.org.
  // GATEWAY_INTERFACE          CGI version,    like CGI/1.1.
  // REQUEST_METHOD             Request method, like GET/POST/HEAD...
  // QUERY_STRING               Query string, read from URL, like USER=A&PASSW=B.
  //                            In GET request: http://demo/demo.cgi?USER=A&PASSW=B.
  // CONTENT                    Content data, read from StdIn, like USER=A&PASSW=B.
  // CONTENT_BASE               Base URL to resolve relative links, like http://main/index.html.
  // CONTENT_ENCODING           Encoding method of content data, like gzip.
  // CONTENT_LANGUAGE           May contain content languare, like ru.
  // CONTENT_LENGTH             Should be Length(CONTENT).
  // CONTENT_LOCATION           May contain content resource link, like http://demo/demo.jpg.
  // CONTENT_MD5                May contain content MD5 checksum, like 0123456789ABCDEF.
  // CONTENT_TYPE               Type of content data, like application/x-www-form-urlencoded.
  ////////////////////////////////////////////////////////////////////////////
  property GATEWAY_INTERFACE    : LongString read GetGATEWAY_INTERFACE;
  property REQUEST_METHOD       : LongString read GetREQUEST_METHOD;
  property QUERY_STRING         : LongString read GetQUERY_STRING;
  property CONTENT              : LongString read GetHTTP_CONTENT;
  property CONTENT_BASE         : LongString read GetHTTP_CONTENT_BASE;
  property CONTENT_ENCODING     : LongString read GetHTTP_CONTENT_ENCODING;
  property CONTENT_LANGUAGE     : LongString read GetHTTP_CONTENT_LANGUAGE;
  property CONTENT_LENGTH       : LongString read GetHTTP_CONTENT_LENGTH;
  property CONTENT_LOCATION     : LongString read GetHTTP_CONTENT_LOCATION;
  property CONTENT_MD5          : LongString read GetHTTP_CONTENT_MD5;
  property CONTENT_RANGE        : LongString read GetHTTP_CONTENT_RANGE;
  property CONTENT_TYPE         : LongString read GetHTTP_CONTENT_TYPE;
  property CONTENT_VERSION      : LongString read GetHTTP_CONTENT_VERSION;
  ////////////////////////////////////////////////////////////////////////////
  // Server information.
  // SERVER_NAME                Web server name, like RITLABS S.R.L.
  // SERVER_PORT                Port to which request was sent, like 80.
  // SERVER_PROTOCOL            Web server uses protocol, like HTTP/1.1.
  // SERVER_SOFTWARE            Web server software name, like TinyWeb/1.93.
  ////////////////////////////////////////////////////////////////////////////
  property SERVER_NAME          : LongString read GetSERVER_NAME;
  property SERVER_PORT          : LongString read GetSERVER_PORT;
  property SERVER_PROTOCOL      : LongString read GetSERVER_PROTOCOL;
  property SERVER_SOFTWARE      : LongString read GetSERVER_SOFTWARE;
  ////////////////////////////////////////////////////////////////////////////
  // Client information.
  // REMOTE_HOST                Client host name, if available, like alex.mshome.net.
  // REMOTE_ADDR                Client IP address, like 192.168.0.2.
  // AUTH_TYPE                  Client autentification method, optional.
  // REMOTE_USER                Client user name, optional.
  // REMOTE_IDENT               Client user name, RFC 931, optional.
  // USER_NAME                  Client user name, optional.
  // USER_PASSWORD              Client user password, optional.
  ////////////////////////////////////////////////////////////////////////////
  property REMOTE_HOST          : LongString read GetREMOTE_HOST;
  property REMOTE_ADDR          : LongString read GetREMOTE_ADDR;
  property AUTH_TYPE            : LongString read GetAUTH_TYPE;
  property REMOTE_USER          : LongString read GetREMOTE_USER;
  property REMOTE_IDENT         : LongString read GetREMOTE_IDENT;
  property USER_NAME            : LongString read GetUSER_NAME;
  property USER_PASSWORD        : LongString read GetUSER_PASSWORD;
  ////////////////////////////////////////////////////////////////////////////
  // Script information.
  // SCRIPT_NAME                Name of script to execute, like /cgi-bin/demo.cgi.
  // PATH_INFO                  Path of script, like /demo.cgi.
  // PATH_TRANSLATED            Local server script path, like c:\webroot\demo.cgi.
  ////////////////////////////////////////////////////////////////////////////
  property SCRIPT_NAME          : LongString read GetSCRIPT_NAME;
  property PATH_INFO            : LongString read GetPATH_INFO;
  property PATH_TRANSLATED      : LongString read GetPATH_TRANSLATED;
  property URL                  : LongString read GetURL;
  ////////////////////////////////////////////////////////////////////////////
  // HTTP information, see RFC 2068, www.rfc-editor.org.
  // HTTP_ACCEPT                List of client supported MIME types, like text/plain, text/html
  // HTTP_ACCEPT_CHARSET        Client supported charset, like iso-8859-5, unicode-1-1;q=0.8.
  // HTTP_ACCEPT_ENCODING       Client supported encoding, like gzip, deflate.
  // HTTP_ACCEPT_LANGUAGE       Client supported language, like ru, en.
  // HTTP_ALLOW                 List of supported request methods, like GET, POST
  // HTTP_AUTHORIZATION         Uses for authorization, like Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==.
  // HTTP_CACHE_CONTROL         Uses for cache control, like nocache.
  // HTTP_CONNECTION            Uses for connection control, like close.
  // HTTP_COOKIE                User information.
  // HTTP_DATE                  Date of request, like Tue, 15 Nov 1994 08:12:31 GMT.
  // HTTP_EXPIRES               Date after which data expires, like 15 Nov 1994 08:12:31 GMT.
  // HTTP_FROM                  Client email, like pupkin@mail.ru.
  // HTTP_HOST                  Server host name, like main.mshome.net.
  // HTTP_IF_MODIFIED_SINCE     Condition to not/send document, like Tue, 15 Nov 1994 08:12:31 GMT.
  // HTTP_LAST_MODIFIED         Date of last modification, like Tue, 15 Nov 1994 08:12:31 GMT.
  // HTTP_REFERER               URL from which CGI script called, like http://main/demo.htm.
  // HTTP_USER_AGENT            Client browser information, like Mozilla/4.0.
  ////////////////////////////////////////////////////////////////////////////
  property HTTP_ACCEPT          : LongString read GetHTTP_ACCEPT;
  property HTTP_ACCEPT_CHARSET  : LongString read GetHTTP_ACCEPT_CHARSET;
  property HTTP_ACCEPT_ENCODING : LongString read GetHTTP_ACCEPT_ENCODING;
  property HTTP_ACCEPT_LANGUAGE : LongString read GetHTTP_ACCEPT_LANGUAGE;
  property HTTP_ACCEPT_RANGES   : LongString read GetHTTP_ACCEPT_RANGES;
  property HTTP_AGE             : LongString read GetHTTP_AGE;
  property HTTP_ALLOW           : LongString read GetHTTP_ALLOW;
  property HTTP_AUTHORIZATION   : LongString read GetHTTP_AUTHORIZATION;
  property HTTP_CACHE_CONTROL   : LongString read GetHTTP_CACHE_CONTROL;
  property HTTP_CONNECTION      : LongString read GetHTTP_CONNECTION;
  property HTTP_CONTENT         : LongString read GetHTTP_CONTENT;
  property HTTP_CONTENT_BASE    : LongString read GetHTTP_CONTENT_BASE;
  property HTTP_CONTENT_ENCODING: LongString read GetHTTP_CONTENT_ENCODING;
  property HTTP_CONTENT_LANGUAGE: LongString read GetHTTP_CONTENT_LANGUAGE;
  property HTTP_CONTENT_LENGTH  : LongString read GetHTTP_CONTENT_LENGTH;
  property HTTP_CONTENT_LOCATION: LongString read GetHTTP_CONTENT_LOCATION;
  property HTTP_CONTENT_MD5     : LongString read GetHTTP_CONTENT_MD5;
  property HTTP_CONTENT_RANGE   : LongString read GetHTTP_CONTENT_RANGE;
  property HTTP_CONTENT_TYPE    : LongString read GetHTTP_CONTENT_TYPE;
  property HTTP_CONTENT_VERSION : LongString read GetHTTP_CONTENT_VERSION;
  property HTTP_COOKIE          : LongString read GetHTTP_COOKIE;
  property HTTP_DATE            : LongString read GetHTTP_DATE;
  property HTTP_DERIVED_FROM    : LongString read GetHTTP_DERIVED_FROM;
  property HTTP_ETAG            : LongString read GetHTTP_ETAG;
  property HTTP_EXPIRES         : LongString read GetHTTP_EXPIRES;
  property HTTP_FROM            : LongString read GetHTTP_FROM;
  property HTTP_HOST            : LongString read GetHTTP_HOST;
  property HTTP_IF_MODIFIED_SINCE : LongString read GetHTTP_IF_MODIFIED_SINCE;
  property HTTP_IF_MATCH        : LongString read GetHTTP_IF_MATCH;
  property HTTP_IF_NONE_MATCH   : LongString read GetHTTP_IF_NONE_MATCH;
  property HTTP_IF_RANGE        : LongString read GetHTTP_IF_RANGE;
  property HTTP_IF_UNMODIFIED_SINCE : LongString read GetHTTP_IF_UNMODIFIED_SINCE;
  property HTTP_LAST_MODIFIED   : LongString read GetHTTP_LAST_MODIFIED;
  property HTTP_LOCATION        : LongString read GetHTTP_LOCATION;
  property HTTP_MAX_FORWARDS    : LongString read GetHTTP_MAX_FORWARDS;
  property HTTP_PRAGMA          : LongString read GetHTTP_PRAGMA;
  property HTTP_PROXY_AUTHENTICATE : LongString read GetHTTP_PROXY_AUTHENTICATE;
  property HTTP_PROXY_AUTHORIZATION : LongString read GetHTTP_PROXY_AUTHORIZATION;
  property HTTP_PUBLIC          : LongString read GetHTTP_PUBLIC;
  property HTTP_RANGE           : LongString read GetHTTP_RANGE;
  property HTTP_REFERER         : LongString read GetHTTP_REFERER;
  property HTTP_RETRY_AFTER     : LongString read GetHTTP_RETRY_AFTER;
  property HTTP_SERVER          : LongString read GetHTTP_SERVER;
  property HTTP_TITLE           : LongString read GetHTTP_TITLE;
  property HTTP_TRANSFER_ENCODING : LongString read GetHTTP_TRANSFER_ENCODING;
  property HTTP_UPGRADE         : LongString read GetHTTP_UPGRADE;
  property HTTP_USER_AGENT      : LongString read GetHTTP_USER_AGENT;
  property HTTP_VARY            : LongString read GetHTTP_VARY;
  property HTTP_VIA             : LongString read GetHTTP_VIA;
  property HTTP_WARNING         : LongString read GetHTTP_WARNING;
  property HTTP_WWW_AUTHENTICATE: LongString read GetHTTP_WWW_AUTHENTICATE;
 public
  ////////////////////////////////////////////////////////////////////////////
  // Easy access to request, query, cookie and content lists
  ////////////////////////////////////////////////////////////////////////////
  property QueryCount             : Integer read GetQueryCount;
  property QueryItem[i:Integer]   : LongString read GetQueryItem;
  property CookieCount            : Integer read GetCookieCount;
  property CookieItem[i:Integer]  : LongString read GetCookieItem;
  property ContentCount           : Integer read GetContentCount;
  property ContentItem[i:Integer] : LongString read GetContentItem;
  property RequestAsText          : LongString read GetRequestAsText;
 end;

function Cgi:TCgi;

 ////////////////////////////////////////////////////////////////////////////
 // Utility routines.
 ////////////////////////////////////////////////////////////////////////////
function  GetDateTime:LongString;
procedure HTTP_ShowEchoPage;
procedure HTTP_ShowErrorPage(const Msg:LongString);

implementation

function GetDateTime:LongString;
begin
 Result:=StdDateTimeStr(mSecNow);
end;

function GetEnvHttp(const Name:LongString):LongString;
begin
 Result:=GetEnv(Name);
 if Length(Result)=0 then
 if PosI('HTTP_',Name)=1 then Result:=GetEnv(Copy(Name,6,MaxInt));
end;

 //////////////////////
 // TCgi implementation
 //////////////////////

constructor TCgi.Create;
var
 Len,Code:Integer;
 procedure AddItems(id:Integer; const s:LongString; Delimiter:Char);
 var
  i,j:Integer;
  item:LongString;
 begin
  j:=1;
  if s<>'' then
  if s<>Delimiter then
  for i:=1 to Length(s) do
  if s[i]=Delimiter then begin
   item:=Copy(s,j,i-j);
   if Length(item)>0 then
   case Delimiter of
    '&' : item:=URL_Decode(item);
    ';' : item:=Trim(item);
   end;
   if Length(item)>0 then
   case id of
    1: begin
        SetLength(myQueryList,Length(myQueryList)+1);
        myQueryList[Length(myQueryList)-1]:=item;
       end;
    2: begin
        SetLength(myCookieList,Length(myCookieList)+1);
        myCookieList[Length(myCookieList)-1]:=item;
       end;
    3: begin
        SetLength(myContentList,Length(myContentList)+1);
        myContentList[Length(myContentList)-1]:=item;
       end;
   end;
   j:=i+1;
  end;
 end;
begin
 inherited Create;
 myStdIn:=GetStdHandle(STD_INPUT_HANDLE);
 myStdOut:=GetStdHandle(STD_OUTPUT_HANDLE);
 Val(CONTENT_LENGTH,Len,Code);
 if Code<>0 then Len:=0;
 SetLength(myContent,Max(Len,0));
 if Length(myContent)>0 then begin
  if not (FileRead(myStdIn, myContent[1], Length(myContent))=Length(myContent))
  then myContent:='';
 end;
 AddItems(1,QUERY_STRING+'&','&');
 AddItems(2,HTTP_COOKIE+';',';');
 AddItems(3,CONTENT+'&','&');
end;

destructor TCgi.Destroy;
var i:Integer;
begin
 for i:=0 to Length(myQueryList)-1 do   myQueryList[i]:='';
 for i:=0 to Length(myCookieList)-1 do  myCookieList[i]:='';
 for i:=0 to Length(myContentList)-1 do myContentList[i]:='';
 SetLength(myQueryList,0);
 SetLength(myCookieList,0);
 SetLength(myContentList,0);
 myContent:='';
 inherited Destroy;
end;

procedure TCgi.SetOutStr(const S:LongString);
begin
 if Assigned(Self) and (S<>'') then FileWrite(myStdOut, S[1], Length(S));
end;

procedure TCgi.SetOutText(const S:LongString);
begin
 if Assigned(Self) then OutStr:=S+EOL;
end;

function TCgi.GetGATEWAY_INTERFACE:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('GATEWAY_INTERFACE') else Result:='';
end;

function TCgi.GetREQUEST_METHOD:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('REQUEST_METHOD') else Result:='';
end;

function TCgi.GetQUERY_STRING:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('QUERY_STRING') else Result:='';
end;

function TCgi.GetSERVER_NAME:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('SERVER_NAME') else Result:='';
end;

function TCgi.GetSERVER_PORT:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('SERVER_PORT') else Result:='';
end;

function TCgi.GetSERVER_PROTOCOL:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('SERVER_PROTOCOL') else Result:='';
end;

function TCgi.GetSERVER_SOFTWARE:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('SERVER_SOFTWARE') else Result:='';
end;

function TCgi.GetREMOTE_HOST:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('REMOTE_HOST') else Result:='';
end;

function TCgi.GetREMOTE_ADDR:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('REMOTE_ADDR') else Result:='';
end;

function TCgi.GetAUTH_TYPE:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('AUTH_TYPE') else Result:='';
end;

function TCgi.GetREMOTE_USER:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('REMOTE_USER') else Result:='';
end;

function TCgi.GetREMOTE_IDENT:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('REMOTE_IDENT') else Result:='';
end;

function TCgi.GetUSER_NAME:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('USER_NAME') else Result:='';
end;

function TCgi.GetUSER_PASSWORD:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('USER_PASSWORD') else Result:='';
end;

function TCgi.GetSCRIPT_NAME:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('SCRIPT_NAME') else Result:='';
end;

function TCgi.GetPATH_INFO:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('PATH_INFO') else Result:='';
end;

function TCgi.GetPATH_TRANSLATED:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('PATH_TRANSLATED') else Result:='';
end;

function TCgi.GetURL:LongString;
begin
 if Assigned(Self) then Result:=GetEnv('URL') else Result:='';
end;

function TCgi.GetHTTP_ACCEPT:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ACCEPT') else Result:='';
end;

function TCgi.GetHTTP_ACCEPT_CHARSET:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ACCEPT_CHARSET') else Result:='';
end;

function TCgi.GetHTTP_ACCEPT_ENCODING:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ACCEPT_ENCODING') else Result:='';
end;

function TCgi.GetHTTP_ACCEPT_LANGUAGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ACCEPT_LANGUAGE') else Result:='';
end;

function TCgi.GetHTTP_ACCEPT_RANGES:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ACCEPT_RANGES') else Result:='';
end;

function TCgi.GetHTTP_AGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_AGE') else Result:='';
end;

function TCgi.GetHTTP_ALLOW:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ALLOW') else Result:='';
end;

function TCgi.GetHTTP_AUTHORIZATION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_AUTHORIZATION') else Result:='';
end;

function TCgi.GetHTTP_CACHE_CONTROL:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CACHE_CONTROL') else Result:='';
end;

function TCgi.GetHTTP_CONNECTION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONNECTION') else Result:='';
end;

function TCgi.GetHTTP_CONTENT:LongString;
begin
 if Assigned(Self) then Result:=myContent else Result:='';
end;

function TCgi.GetHTTP_CONTENT_BASE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_BASE') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_ENCODING:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_ENCODING') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_LANGUAGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_LANGUAGE') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_LENGTH:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_LENGTH') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_LOCATION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_LOCATION') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_MD5:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_MD5') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_RANGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_RANGE') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_TYPE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_TYPE') else Result:='';
end;

function TCgi.GetHTTP_CONTENT_VERSION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_CONTENT_VERSION') else Result:='';
end;

function TCgi.GetHTTP_COOKIE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_COOKIE') else Result:='';
end;

function TCgi.GetHTTP_DATE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_DATE') else Result:='';
end;

function TCgi.GetHTTP_DERIVED_FROM:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_DERIVED_FROM') else Result:='';
end;

function TCgi.GetHTTP_ETAG:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_ETAG') else Result:='';
end;

function TCgi.GetHTTP_EXPIRES:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_EXPIRES') else Result:='';
end;

function TCgi.GetHTTP_FROM:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_FROM') else Result:='';
end;

function TCgi.GetHTTP_HOST:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_HOST') else Result:='';
end;

function TCgi.GetHTTP_IF_MODIFIED_SINCE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_IF_MODIFIED_SINCE') else Result:='';
end;

function TCgi.GetHTTP_IF_MATCH:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_IF_MATCH') else Result:='';
end;

function TCgi.GetHTTP_IF_NONE_MATCH:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_IF_NONE_MATCH') else Result:='';
end;

function TCgi.GetHTTP_IF_RANGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_IF_RANGE') else Result:='';
end;

function TCgi.GetHTTP_IF_UNMODIFIED_SINCE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_IF_UNMODIFIED_SINCE') else Result:='';
end;

function TCgi.GetHTTP_LAST_MODIFIED:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_LAST_MODIFIED') else Result:='';
end;

function TCgi.GetHTTP_LOCATION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_LOCATION') else Result:='';
end;

function TCgi.GetHTTP_MAX_FORWARDS:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_MAX_FORWARDS') else Result:='';
end;

function TCgi.GetHTTP_PRAGMA:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_PRAGMA') else Result:='';
end;

function TCgi.GetHTTP_PROXY_AUTHENTICATE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_PROXY_AUTHENTICATE') else Result:='';
end;

function TCgi.GetHTTP_PROXY_AUTHORIZATION:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_PROXY_AUTHORIZATION') else Result:='';
end;

function TCgi.GetHTTP_PUBLIC:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_PUBLIC') else Result:='';
end;

function TCgi.GetHTTP_RANGE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_RANGE') else Result:='';
end;

function TCgi.GetHTTP_REFERER:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_REFERER') else Result:='';
end;

function TCgi.GetHTTP_RETRY_AFTER:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_RETRY_AFTER') else Result:='';
end;

function TCgi.GetHTTP_SERVER:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_SERVER') else Result:='';
end;

function TCgi.GetHTTP_TITLE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_TITLE') else Result:='';
end;

function TCgi.GetHTTP_TRANSFER_ENCODING:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_TRANSFER_ENCODING') else Result:='';
end;

function TCgi.GetHTTP_UPGRADE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_UPGRADE') else Result:='';
end;

function TCgi.GetHTTP_USER_AGENT:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_USER_AGENT') else Result:='';
end;

function TCgi.GetHTTP_VARY:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_VARY') else Result:='';
end;

function TCgi.GetHTTP_VIA:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_VIA') else Result:='';
end;

function TCgi.GetHTTP_WARNING:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_WARNING') else Result:='';
end;

function TCgi.GetHTTP_WWW_AUTHENTICATE:LongString;
begin
 if Assigned(Self) then Result:=GetEnvHttp('HTTP_WWW_AUTHENTICATE') else Result:='';
end;

function TCgi.GetQueryCount:Integer;
begin
 if Assigned(Self) then Result:=Length(myQueryList) else Result:=0;
end;

function TCgi.GetQueryItem(i:Integer):LongString;
begin
 if Assigned(Self) and (i>=0) and (i<Length(myQueryList)) then Result:=myQueryList[i] else Result:='';
end;

function TCgi.GetCookieCount:Integer;
begin
 if Assigned(Self) then Result:=Length(myCookieList) else Result:=0;
end;

function TCgi.GetCookieItem(i:Integer):LongString;
begin
 if Assigned(Self) and (i>=0) and (i<Length(myCookieList)) then Result:=myCookieList[i] else Result:='';
end;

function TCgi.GetContentCount:Integer;
begin
 if Assigned(Self) then Result:=Length(myContentList) else Result:=0;
end;

function TCgi.GetContentItem(i:Integer):LongString;
begin
 if Assigned(Self) and (i>=0) and (i<Length(myContentList)) then Result:=myContentList[i] else Result:='';
end;

function TCgi.GetRequestAsText:LongString;
var
 i,Len:Integer;
 procedure AddItem(const Name,Data:LongString; Force:Boolean=false);
 var Line:LongString; LenLine,LenResult:Integer;
 begin
  if (Length(Name)>0) then
  if (Length(Data)>0) or Force then begin
   Line:=Name+'='+Data+EOL;
   LenLine:=Length(Line);
   LenResult:=Length(Result);
   if LenResult<=Len+LenLine
   then SetLength(Result,Max(Len+LenLine+1,LenResult*2));
   Move(PChar(Line)[0],PChar(Result)[Len],LenLine);
   Inc(Len,LenLine);
  end;
 end;
begin
 Len:=0;
 Result:='';
 try
  if QueryCount>0 then begin
   AddItem('QUERY.COUNT',IntToStr(QueryCount),True);
   for i:=0 to QueryCount-1 do
   AddItem('QUERY.ITEM'+IntToStr(i),QueryItem[i],True);
  end;
  if CookieCount>0 then begin
   AddItem('COOKIE.COUNT',IntToStr(CookieCount),True);
   for i:=0 to CookieCount-1 do
   AddItem('COOKIE.ITEM'+IntToStr(i),CookieItem[i],True);
  end;
  if ContentCount>0 then begin
   AddItem('CONTENT.COUNT',IntToStr(ContentCount),True);
   for i:=0 to ContentCount-1 do
   AddItem('CONTENT.ITEM'+IntToStr(i),ContentItem[i],True);
  end;
  AddItem('GATEWAY_INTERFACE',        GATEWAY_INTERFACE);
  AddItem('REQUEST_METHOD',           REQUEST_METHOD);
  AddItem('QUERY_STRING',             QUERY_STRING);
  AddItem('CONTENT',                  CONTENT);
  AddItem('CONTENT_BASE',             CONTENT_BASE);
  AddItem('CONTENT_ENCODING',         CONTENT_ENCODING);
  AddItem('CONTENT_LANGUAGE',         CONTENT_LANGUAGE);
  AddItem('CONTENT_LENGTH',           CONTENT_LENGTH);
  AddItem('CONTENT_LOCATION',         CONTENT_LOCATION);
  AddItem('CONTENT_MD5',              CONTENT_MD5);
  AddItem('CONTENT_RANGE',            CONTENT_RANGE);
  AddItem('CONTENT_TYPE',             CONTENT_TYPE);
  AddItem('SERVER_NAME',              SERVER_NAME);
  AddItem('SERVER_PORT',              SERVER_PORT);
  AddItem('SERVER_PROTOCOL',          SERVER_PROTOCOL);
  AddItem('SERVER_SOFTWARE',          SERVER_SOFTWARE);
  AddItem('REMOTE_HOST',              REMOTE_HOST);
  AddItem('REMOTE_ADDR',              REMOTE_ADDR);
  AddItem('AUTH_TYPE',                AUTH_TYPE);
  AddItem('REMOTE_USER',              REMOTE_USER);
  AddItem('REMOTE_IDENT',             REMOTE_IDENT);
  AddItem('USER_NAME',                USER_NAME);
  AddItem('USER_PASSWORD',            USER_PASSWORD);
  AddItem('SCRIPT_NAME',              SCRIPT_NAME);
  AddItem('PATH_INFO',                PATH_INFO);
  AddItem('PATH_TRANSLATED',          PATH_TRANSLATED);
  AddItem('URL',                      URL);
  AddItem('HTTP_ACCEPT',              HTTP_ACCEPT);
  AddItem('HTTP_ACCEPT_CHARSET',      HTTP_ACCEPT_CHARSET);
  AddItem('HTTP_ACCEPT_ENCODING',     HTTP_ACCEPT_ENCODING);
  AddItem('HTTP_ACCEPT_LANGUAGE',     HTTP_ACCEPT_LANGUAGE);
  AddItem('HTTP_ACCEPT_RANGES',       HTTP_ACCEPT_RANGES);
  AddItem('HTTP_AGE',                 HTTP_AGE);
  AddItem('HTTP_ALLOW',               HTTP_ALLOW);
  AddItem('HTTP_AUTHORIZATION',       HTTP_AUTHORIZATION);
  AddItem('HTTP_CACHE_CONTROL',       HTTP_CACHE_CONTROL);
  AddItem('HTTP_CONNECTION',          HTTP_CONNECTION);
  AddItem('HTTP_COOKIE',              HTTP_COOKIE);
  AddItem('HTTP_DATE',                HTTP_DATE);
  AddItem('HTTP_DERIVED_FROM',        HTTP_DERIVED_FROM);
  AddItem('HTTP_ETAG',                HTTP_ETAG);
  AddItem('HTTP_EXPIRES',             HTTP_EXPIRES);
  AddItem('HTTP_FROM',                HTTP_FROM);
  AddItem('HTTP_HOST',                HTTP_HOST);
  AddItem('HTTP_IF_MODIFIED_SINCE',   HTTP_IF_MODIFIED_SINCE);
  AddItem('HTTP_IF_MATCH',            HTTP_IF_MATCH);
  AddItem('HTTP_IF_NONE_MATCH',       HTTP_IF_NONE_MATCH);
  AddItem('HTTP_IF_RANGE',            HTTP_IF_RANGE);
  AddItem('HTTP_IF_UNMODIFIED_SINCE', HTTP_IF_UNMODIFIED_SINCE);
  AddItem('HTTP_LAST_MODIFIED',       HTTP_LAST_MODIFIED);
  AddItem('HTTP_LOCATION',            HTTP_LOCATION);
  AddItem('HTTP_MAX_FORWARDS',        HTTP_MAX_FORWARDS);
  AddItem('HTTP_PRAGMA',              HTTP_PRAGMA);
  AddItem('HTTP_PROXY_AUTHENTICATE',  HTTP_PROXY_AUTHENTICATE);
  AddItem('HTTP_PROXY_AUTHORIZATION', HTTP_PROXY_AUTHORIZATION);
  AddItem('HTTP_PUBLIC',              HTTP_PUBLIC);
  AddItem('HTTP_RANGE',               HTTP_RANGE);
  AddItem('HTTP_REFERER',             HTTP_REFERER);
  AddItem('HTTP_RETRY_AFTER',         HTTP_RETRY_AFTER);
  AddItem('HTTP_SERVER',              HTTP_SERVER);
  AddItem('HTTP_TITLE',               HTTP_TITLE);
  AddItem('HTTP_TRANSFER_ENCODING',   HTTP_TRANSFER_ENCODING);
  AddItem('HTTP_UPGRADE',             HTTP_UPGRADE);
  AddItem('HTTP_USER_AGENT',          HTTP_USER_AGENT);
  AddItem('HTTP_VARY',                HTTP_VARY);
  AddItem('HTTP_VIA',                 HTTP_VIA);
  AddItem('HTTP_WARNING',             HTTP_WARNING);
  AddItem('HTTP_WWW-AUTHENTICATE',    HTTP_WWW_AUTHENTICATE);
  SetLength(Result,Len);
 except
  Result:='';
 end;
end;

procedure HTTP_ShowEchoPage;
begin
 with Cgi do begin
  OutText:='Content-Type: text/html; charset=utf-8'+EOL
          +''+EOL
          +'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">'+EOL
          +'<html>'+EOL
          +'<head>'+EOL
          +'<title>HTTP echo</title>'+EOL
          +'<meta http-equiv="Refresh" content="5">';
  if IsSameText(REQUEST_METHOD,'POST') then
  OutText:='<meta http-equiv="Set-Cookie" content="User=Demo">'+EOL
          +'<meta http-equiv="Set-Cookie" content="Password=a%b&c%">';
  OutText:='</head>'+EOL
          +'<body>'+EOL
          +'<hr>'+EOL
          +'<h1>HTTP echo</h1>'+EOL
          +'<hr>'+EOL
          +'Server time now: '+GetDateTime+EOL
          +'<pre>'+EOL
          +RequestAsText+EOL
          +'</pre>'+EOL
          +'<hr>'+EOL
          +'</body>'+EOL
          +'</html>';
 end;
end;

procedure HTTP_ShowErrorPage(const Msg:LongString);
begin
 with Cgi do begin
  OutText:='Content-Type: text/html; charset=utf-8'+EOL
          +''+EOL
          +'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">'+EOL
          +'<html>'+EOL
          +'<head>'+EOL
          +'<title>Error!</title>'+EOL
          +'</head>'+EOL
          +'<body>'+EOL
          +'<hr>'+EOL
          +'<font color="red"><h1>Error!</h1></font>';
  if Length(Msg)>0 then
  OutText:='<hr>'+EOL
          +Msg;
  OutText:='<hr>'+EOL
          +'</body>'+EOL
          +'</html>';
 end;
end;

 /////////////////////
 // Cgi implementation
 /////////////////////

const
 TheCgi : TCgi = nil; // One and only Cgi instance

function Cgi:TCgi;
begin
 if not Assigned(TheCgi) then begin
  TheCgi:=TCgi.Create;
  TheCgi.Master:=@TheCgi;
 end;
 Result:=TheCgi;
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_cgi;
begin
end;

procedure Free_crw_cgi;
begin
 Kill(TObject(TheCgi));
end;

initialization

 Init_crw_cgi;

finalization

 Free_crw_cgi;

end.

//////////////
// END OF FILE
//////////////

