home *** CD-ROM | disk | FTP | other *** search
/ Delphi 5 for Professionals / DELPHI5.iso / Runimage / Delphi50 / Source / Internet / cgiapp.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1999-08-11  |  17.1 KB  |  603 lines

  1.  
  2. {*******************************************************}
  3. {                                                       }
  4. {       Borland Delphi Visual Component Library         }
  5. {       CGI/WinCGI Web server application components    }
  6. {                                                       }
  7. {       Copyright (c) 1997,99 Inprise Corporation       }
  8. {                                                       }
  9. {*******************************************************}
  10.  
  11. {$DENYPACKAGEUNIT}
  12.  
  13. unit CGIApp;
  14.  
  15. interface
  16.  
  17. uses Windows, Classes, HTTPApp, WebBroker, IniFiles;
  18.  
  19. type
  20.   TCGIRequest = class(TWebRequest)
  21.   private
  22.     FContent: string;
  23.   protected
  24.     function GetStringVariable(Index: Integer): string; override;
  25.     function GetDateVariable(Index: Integer): TDateTime; override;
  26.     function GetIntegerVariable(Index: Integer): Integer; override;
  27.   public
  28.     constructor Create;
  29.     function GetFieldByName(const Name: string): string; override;
  30.     function ReadClient(var Buffer; Count: Integer): Integer; override;
  31.     function ReadString(Count: Integer): string; override;
  32.     function TranslateURI(const URI: string): string; override;
  33.     function WriteClient(var Buffer; Count: Integer): Integer; override;
  34.     function WriteString(const AString: string): Boolean; override;
  35.   end;
  36.  
  37.   TCGIResponse = class(TWebResponse)
  38.   private
  39.     FStatusCode: Integer;
  40.     FStringVariables: array[0..MAX_STRINGS - 1] of string;
  41.     FIntegerVariables: array[0..MAX_INTEGERS - 1] of Integer;
  42.     FDateVariables: array[0..MAX_DATETIMES - 1] of TDateTime;
  43.     FContent: string;
  44.     FSent: Boolean;
  45.   protected
  46.     function GetContent: string; override;
  47.     function GetDateVariable(Index: Integer): TDateTime; override;
  48.     function GetIntegerVariable(Index: Integer): Integer; override;
  49.     function GetLogMessage: string; override;
  50.     function GetStatusCode: Integer; override;
  51.     function GetStringVariable(Index: Integer): string; override;
  52.     procedure SetContent(const Value: string); override;
  53.     procedure SetDateVariable(Index: Integer; const Value: TDateTime); override;
  54.     procedure SetIntegerVariable(Index: Integer; Value: Integer); override;
  55.     procedure SetLogMessage(const Value: string); override;
  56.     procedure SetStatusCode(Value: Integer); override;
  57.     procedure SetStringVariable(Index: Integer; const Value: string); override;
  58.   public
  59.     constructor Create(HTTPRequest: TWebRequest);
  60.     procedure SendResponse; override;
  61.     procedure SendRedirect(const URI: string); override;
  62.     procedure SendStream(AStream: TStream); override;
  63.     function Sent: Boolean; override;
  64.   end;
  65.  
  66.   TWinCGIRequest = class(TCGIRequest)
  67.   private
  68.     FIniFile: TIniFile;
  69.     FClientData, FServerData: TFileStream;
  70.   protected
  71.     function GetStringVariable(Index: Integer): string; override;
  72.   public
  73.     constructor Create(IniFileName, ContentFile, OutputFile: string);
  74.     destructor Destroy; override;
  75.     function GetFieldByName(const Name: string): string; override;
  76.     function ReadClient(var Buffer; Count: Integer): Integer; override;
  77.     function ReadString(Count: Integer): string; override;
  78.     function TranslateURI(const URI: string): string; override;
  79.     function WriteClient(var Buffer; Count: Integer): Integer; override;
  80.     function WriteString(const AString: string): Boolean; override;
  81.   end;
  82.  
  83.   TWinCGIResponse = class(TCGIResponse);
  84.  
  85.   TCGIApplication = class(TWebApplication)
  86.   private
  87.     FOutputFileName: string;
  88.     function NewRequest: TCGIRequest;
  89.     function NewResponse(CGIRequest: TCGIRequest): TCGIResponse;
  90.   public
  91.     procedure Run; override;
  92.   end;
  93.  
  94. implementation
  95.  
  96. uses SysUtils, BrkrConst;
  97.  
  98. const
  99.   CGIServerVariables: array[0..28] of string = (
  100.     'REQUEST_METHOD',
  101.     'SERVER_PROTOCOL',
  102.     'URL',
  103.     'QUERY_STRING',
  104.     'PATH_INFO',
  105.     'PATH_TRANSLATED',
  106.     'HTTP_CACHE_CONTROL',
  107.     'HTTP_DATE',
  108.     'HTTP_ACCEPT',
  109.     'HTTP_FROM',
  110.     'HTTP_HOST',
  111.     'HTTP_IF_MODIFIED_SINCE',
  112.     'HTTP_REFERER',
  113.     'HTTP_USER_AGENT',
  114.     'HTTP_CONTENT_ENCODING',
  115.     'HTTP_CONTENT_TYPE',
  116.     'HTTP_CONTENT_LENGTH',
  117.     'HTTP_CONTENT_VERSION',
  118.     'HTTP_DERIVED_FROM',
  119.     'HTTP_EXPIRES',
  120.     'HTTP_TITLE',
  121.     'REMOTE_ADDR',
  122.     'REMOTE_HOST',
  123.     'SCRIPT_NAME',
  124.     'SERVER_PORT',
  125.     '',
  126.     'HTTP_CONNECTION',
  127.     'HTTP_COOKIE',
  128.     'HTTP_AUTHORIZATION');
  129.  
  130. { TCGIRequest }
  131.  
  132. constructor TCGIRequest.Create;
  133. begin
  134.   inherited Create;
  135.   FContent := ReadString(ContentLength);
  136. end;
  137.  
  138. function TCGIRequest.GetFieldByName(const Name: string): string;
  139. var
  140.   Buffer: array[0..4095] of Char;
  141.  
  142.   function StripHTTP(const Name: string): string;
  143.   begin
  144.     if Pos('HTTP_', Name) = 1 then
  145.       Result := Copy(Name, 6, MaxInt)
  146.     else Result := Name;
  147.   end;
  148.  
  149. begin
  150.   SetString(Result, Buffer, GetEnvironmentVariable(PChar(Name), Buffer, SizeOf(Buffer)));
  151.   if Result = '' then
  152.     SetString(Result, Buffer, GetEnvironmentVariable(PChar(StripHTTP(Name)), Buffer, SizeOf(Buffer)));
  153. end;
  154.  
  155. function TCGIRequest.GetStringVariable(Index: Integer): string;
  156. begin
  157.   if Index = 25 then
  158.     Result := FContent
  159.   else Result := GetFieldByName(CGIServerVariables[Index]);
  160. end;
  161.  
  162. function TCGIRequest.GetDateVariable(Index: Integer): TDateTime;
  163. var
  164.   Value: string;
  165. begin
  166.   Value := GetStringVariable(Index);
  167.   if Value <> '' then
  168.     Result := ParseDate(Value)
  169.   else Result := -1;
  170. end;
  171.  
  172. function TCGIRequest.GetIntegerVariable(Index: Integer): Integer;
  173. var
  174.   Value: string;
  175. begin
  176.   Value := GetStringVariable(Index);
  177.   Result := StrToIntDef(Value, -1)
  178. end;
  179.  
  180. function TCGIRequest.ReadClient(var Buffer; Count: Integer): Integer;
  181. begin
  182.   Result := FileRead(TTextRec(Input).Handle, Buffer, Count);
  183. end;
  184.  
  185. function TCGIRequest.ReadString(Count: Integer): string;
  186. begin
  187.   SetLength(Result, Count);
  188.   if Count > 0 then
  189.     SetLength(Result, FileRead(TTextRec(Input).Handle, Pointer(Result)^, Count));
  190. end;
  191.  
  192. function TCGIRequest.TranslateURI(const URI: string): string;
  193. begin
  194. end;
  195.  
  196. function TCGIRequest.WriteClient(var Buffer; Count: Integer): Integer;
  197. begin
  198.   Result := FileWrite(TTextRec(Output).Handle, Buffer, Count);
  199. end;
  200.  
  201. function TCGIRequest.WriteString(const AString: string): Boolean;
  202. begin
  203.   if AString <> '' then
  204.     Result := FileWrite(TTextRec(Output).Handle, Pointer(AString)^, Length(AString)) = Length(AString)
  205.   else Result := False;
  206. end;
  207.  
  208. { TCGIResponse }
  209.  
  210. constructor TCGIResponse.Create(HTTPRequest: TWebRequest);
  211. begin
  212.   inherited Create(HTTPRequest);
  213.   if FHTTPRequest.ProtocolVersion = '' then
  214.     Version := '1.0';
  215.   StatusCode := 200;
  216.   LastModified := -1;
  217.   Expires := -1;
  218.   Date := -1;
  219.   ContentType := 'text/html';
  220. end;
  221.  
  222. function TCGIResponse.GetContent: string;
  223. begin
  224.   Result := FContent;
  225. end;
  226.  
  227. function TCGIResponse.GetDateVariable(Index: Integer): TDateTime;
  228. begin
  229.   if (Index >= 0) and (Index < 3) then
  230.     Result := FDateVariables[Index]
  231.   else Result := -1;
  232. end;
  233.  
  234. function TCGIResponse.GetIntegerVariable(Index: Integer): Integer;
  235. begin
  236.   if (Index >= 0) and (Index < 2) then
  237.     Result := FIntegerVariables[Index]
  238.   else Result := -1;
  239. end;
  240.  
  241. function TCGIResponse.GetLogMessage: string;
  242. begin
  243. //  Result := TCGIRequest(HTTPRequest).ECB.lpszLogData;
  244. end;
  245.  
  246. function TCGIResponse.GetStatusCode: Integer;
  247. begin
  248.   Result := FStatusCode;
  249. end;
  250.  
  251. function TCGIResponse.GetStringVariable(Index: Integer): string;
  252. begin
  253.   if (Index >= 0) and (Index < 12) then
  254.     Result := FStringVariables[Index];
  255. end;
  256.  
  257. function TCGIResponse.Sent: Boolean;
  258. begin
  259.   Result := FSent;
  260. end;
  261.  
  262. procedure TCGIResponse.SetContent(const Value: string);
  263. begin
  264.   FContent := Value;
  265.   if ContentStream = nil then
  266.     ContentLength := Length(FContent);
  267. end;
  268.  
  269. procedure TCGIResponse.SetDateVariable(Index: Integer; const Value: TDateTime);
  270. begin
  271.   if (Index >= Low(FDateVariables)) and (Index <= High(FDateVariables)) then
  272.     if Value <> FDateVariables[Index] then
  273.       FDateVariables[Index] := Value;
  274. end;
  275.  
  276. procedure TCGIResponse.SetIntegerVariable(Index: Integer; Value: Integer);
  277. begin
  278.   if (Index >= Low(FIntegerVariables)) and (Index <= High(FIntegerVariables)) then
  279.     if Value <> FDateVariables[Index] then
  280.       FIntegerVariables[Index] := Value;
  281. end;
  282.  
  283. procedure TCGIResponse.SetLogMessage(const Value: string);
  284. begin
  285. //  StrPLCopy(TCGIRequest(HTTPRequest).ECB.lpszLogData, Value, HSE_LOG_BUFFER_LEN);
  286. end;
  287.  
  288. procedure TCGIResponse.SetStatusCode(Value: Integer);
  289. begin
  290.   if FStatusCode <> Value then
  291.   begin
  292.     FStatusCode := Value;
  293.     ReasonString := StatusString(Value);
  294.   end;
  295. end;
  296.  
  297. procedure TCGIResponse.SetStringVariable(Index: Integer; const Value: string);
  298. begin
  299.   if (Index >= Low(FStringVariables)) and (Index <= High(FStringVariables)) then
  300.     FStringVariables[Index] := Value;
  301. end;
  302.  
  303. procedure TCGIResponse.SendResponse;
  304. var
  305.   StatusString: string;
  306.   Headers: string;
  307.   I: Integer;
  308.  
  309.   procedure AddHeaderItem(const Item, FormatStr: string);
  310.   begin
  311.     if Item <> '' then
  312.       Headers := Headers + Format(FormatStr, [Item]);
  313.   end;
  314.  
  315. begin
  316.   if HTTPRequest.ProtocolVersion <> '' then
  317.   begin
  318.     if (ReasonString <> '') and (StatusCode > 0) then
  319.       StatusString := Format('%d %s', [StatusCode, ReasonString])
  320.     else StatusString := '200 OK';
  321.     AddHeaderItem(StatusString, 'Status: %s'#13#10);
  322.     AddHeaderItem(Location, 'Location: %s'#13#10);
  323.     AddHeaderItem(Allow, 'Allow: %s'#13#10);
  324.     for I := 0 to Cookies.Count - 1 do
  325.       AddHeaderItem(Cookies[I].HeaderValue, 'Set-Cookie: %s'#13#10);
  326.     AddHeaderItem(DerivedFrom, 'Derived-From: %s'#13#10);
  327.     if Expires > 0 then
  328.       Format(FormatDateTime('"Expires: "' + sDateFormat + ' "GMT"'#13#10, Expires),
  329.         [DayOfWeekStr(Expires), MonthStr(Expires)]);
  330.     if LastModified > 0 then
  331.       Format(FormatDateTime('"Last-Modified: "' + sDateFormat + ' "GMT"'#13#10,
  332.         LastModified), [DayOfWeekStr(LastModified), MonthStr(LastModified)]);
  333.     AddHeaderItem(Title, 'Title: %s'#13#10);
  334.     AddHeaderItem(WWWAuthenticate, 'WWW-Authenticate: %s'#13#10);
  335.     AddCustomHeaders(Headers);
  336.     AddHeaderItem(ContentVersion, 'Content-Version: %s'#13#10);
  337.     AddHeaderItem(ContentEncoding, 'Content-Encoding: %s'#13#10);
  338.     AddHeaderItem(ContentType, 'Content-Type: %s'#13#10);
  339.     if (Content <> '') or (ContentStream <> nil) then
  340.       AddHeaderItem(IntToStr(ContentLength), 'Content-Length: %s'#13#10);
  341.     Headers := Headers + 'Content:'#13#10#13#10;
  342.     HTTPRequest.WriteString(Headers);
  343.   end;
  344.   if ContentStream = nil then
  345.     HTTPRequest.WriteString(Content)
  346.   else if ContentStream <> nil then
  347.   begin
  348.     SendStream(ContentStream);
  349.     ContentStream := nil; // Drop the stream
  350.   end;
  351.   FSent := True;
  352. end;
  353.  
  354. procedure TCGIResponse.SendRedirect(const URI: string);
  355. begin
  356.   Location := URI;
  357.   StatusCode := 302;
  358.   ContentType := 'text/html';
  359.   Content := Format(sDocumentMoved, [URI]);
  360.   SendResponse;
  361. end;
  362.  
  363. procedure TCGIResponse.SendStream(AStream: TStream);
  364. var
  365.   Buffer: array[0..8191] of Byte;
  366.   BytesToSend: Integer;
  367. begin
  368.   while AStream.Position < AStream.Size do
  369.   begin
  370.     BytesToSend := AStream.Read(Buffer, SizeOf(Buffer));
  371.     FHTTPRequest.WriteClient(Buffer, BytesToSend);
  372.   end;
  373. end;
  374.  
  375. const
  376.   WinCGIServerVariables: array[0..28] of string = (
  377.     'Request Method',
  378.     'Request Protocol',
  379.     'Url',
  380.     'Query String',
  381.     'Logical Path',
  382.     'Physical Path',
  383.     'Cache Control',
  384.     'Date',
  385.     'Accept',
  386.     'From',
  387.     'Host',
  388.     'If-Modified-Since',
  389.     'Referer',
  390.     'User-Agent',
  391.     'Content-Encoding',
  392.     'Content Type',
  393.     'Content Length',
  394.     'Content Version',
  395.     'Derived-From',
  396.     'Expires',
  397.     'Title',
  398.     'Remote Address',
  399.     'Remote Host',
  400.     'Executable Path',
  401.     'Server Port',
  402.     '',
  403.     'Connection',
  404.     'Cookie',
  405.     'Authorization');
  406.  
  407. { TWinCGIRequest }
  408.  
  409. constructor TWinCGIRequest.Create(IniFileName, ContentFile, OutputFile: string);
  410. begin
  411.   FIniFile := TIniFile.Create(IniFileName);
  412.   if ContentFile = '' then
  413.     ContentFile := FIniFile.ReadString('System', 'Content File', '');
  414.   if OutputFile = '' then
  415.     OutputFile := FIniFile.ReadString('System', 'Output File', '');
  416.   if FileExists(ContentFile) then
  417.     FClientData := TFileStream.Create(ContentFile, fmOpenRead or fmShareDenyNone);
  418.   if FileExists(OutputFile) then
  419.     FServerData := TFileStream.Create(OutputFile, fmOpenWrite or fmShareDenyNone)
  420.   else FServerData := TFileStream.Create(OutputFile, fmCreate);
  421.   inherited Create;
  422. end;
  423.  
  424. destructor TWinCGIRequest.Destroy;
  425. begin
  426.   FIniFile.Free;
  427.   if FClientData <> nil then
  428.     FClientData.Free;
  429.   FServerData.Free;
  430.   inherited Destroy;
  431. end;
  432.  
  433. function TWinCGIRequest.GetFieldByName(const Name: string): string;
  434. begin
  435.   Result := FIniFile.ReadString('Extra Headers', Name, '');
  436. end;
  437.  
  438. function TWinCGIRequest.GetStringVariable(Index: Integer): string;
  439.  
  440.   function AcceptSection: string;
  441.   var
  442.     Section: TStringList;
  443.     I: Integer;
  444.   begin
  445.     Section := TStringList.Create;
  446.     try
  447.       FIniFile.ReadSection('Accept', Section);
  448.       Result := '';
  449.       for I := 0 to Section.Count - 1 do
  450.         Result := Result + Section[I] + ',';
  451.       if Result <> '' then SetLength(Result, Length(Result) - 1);
  452.     finally
  453.       Section.Free;
  454.     end;
  455.   end;
  456.  
  457. begin
  458.   case Index of
  459.     0..1,3..5,15..16,
  460.     21..24, 26, 28:
  461.       Result := FIniFile.ReadString('CGI', WinCGIServerVariables[Index], '');
  462.     25: Result := FContent;
  463.     27: Result := FIniFile.ReadString('Extra Headers', WinCGIServerVariables[Index], '');
  464.     8: Result := AcceptSection;
  465.   else
  466.     if (Index >= Low(WinCGIServerVariables)) and (Index <= High(WinCGIServerVariables)) then
  467.       Result := GetFieldByName(WinCGIServerVariables[Index])
  468.     else Result := '';
  469.   end;
  470. end;
  471.  
  472. function TWinCGIRequest.ReadClient(var Buffer; Count: Integer): Integer;
  473. begin
  474.   if FClientData <> nil then
  475.     Result := FClientData.Read(Buffer, Count)
  476.   else Result := 0;
  477. end;
  478.  
  479. function TWinCGIRequest.ReadString(Count: Integer): string;
  480. begin
  481.   if (Count > 0) and (FClientData <> nil) then
  482.   begin
  483.     SetLength(Result, Count);
  484.     SetLength(Result, FClientData.Read(Pointer(Result)^, Count));
  485.   end else Result := '';
  486. end;
  487.  
  488. function TWinCGIRequest.TranslateURI(const URI: string): string;
  489. begin
  490. end;
  491.  
  492. function TWinCGIRequest.WriteClient(var Buffer; Count: Integer): Integer;
  493. begin
  494.   Result := FServerData.Write(Buffer, Count);
  495. end;
  496.  
  497. function TWinCGIRequest.WriteString(const AString: string): Boolean;
  498. begin
  499.   if AString <> '' then
  500.     Result := FServerData.Write(Pointer(AString)^, Length(AString)) = Length(AString)
  501.   else Result := False;
  502. end;
  503.  
  504. { TCGIApplication }
  505.  
  506. procedure HandleServerException(E: Exception; const OutputFile: string);
  507. var
  508.   ResultText, ResultHeaders: string;
  509.   OutFile: TStream;
  510. begin
  511.   ResultText := Format(sInternalServerError, [E.ClassName, E.Message]);
  512.   ResultHeaders := Format(
  513.     'Status: 500 %s'#13#10+               //Not resourced
  514.     'Content-Type: text/html'#13#10 +     //Not resourced
  515.     'Content-Length: %d'#13#10 +          //Not resourced
  516.     'Content:'#13#10#13#10, [E.Message, Length(ResultText)]); //Not resourced
  517.   if IsConsole then
  518.   begin
  519.     FileWrite(TTextRec(Output).Handle, Pointer(ResultHeaders)^, Length(ResultHeaders));
  520.     FileWrite(TTextRec(Output).Handle, Pointer(ResultText)^, Length(ResultText));
  521.   end else
  522.   begin
  523.     if FileExists(OutputFile) then
  524.       OutFile := TFileStream.Create(OutputFile, fmOpenWrite or fmShareDenyNone)
  525.     else OutFile := TFileStream.Create(OutputFile, fmCreate);
  526.     try
  527.       OutFile.Write(Pointer(ResultHeaders)^, Length(ResultHeaders));
  528.       OutFile.Write(Pointer(ResultText)^, Length(ResultText));
  529.     finally
  530.       OutFile.Free;
  531.     end;
  532.   end;
  533. end;
  534.  
  535. function TCGIApplication.NewRequest: TCGIRequest;
  536. var
  537.   Buffer: array[0..MAX_PATH] of Char;
  538. begin
  539.   if IsConsole then
  540.     Result := TCGIRequest.Create
  541.   else
  542.   begin
  543.     Result := TWinCGIRequest.Create(ParamStr(1), ParamStr(2), ParamStr(3));
  544.     FOutputFileName := ParamStr(3);
  545.     if FOutputFileName = '' then
  546.       SetString(FOutputFileName, Buffer, GetPrivateProfileString('System',
  547.         'Output File', '', Buffer, SizeOf(Buffer), PChar(ParamStr(1))));
  548.   end;
  549. end;
  550.  
  551. function TCGIApplication.NewResponse(CGIRequest: TCGIRequest): TCGIResponse;
  552. begin
  553.   if IsConsole then
  554.     Result := TCGIResponse.Create(CGIRequest)
  555.   else Result := TWinCGIResponse.Create(CGIRequest);
  556. end;
  557.  
  558. procedure TCGIApplication.Run;
  559. var
  560.   HTTPRequest: TCGIRequest;
  561.   HTTPResponse: TCGIResponse;
  562. begin
  563.   inherited Run;
  564.   if IsConsole then
  565.   begin
  566.     Rewrite(Output);
  567.     Reset(Input);
  568.   end;
  569.   try
  570.     HTTPRequest := NewRequest;
  571.     try
  572.       HTTPResponse := NewResponse(HTTPRequest);
  573.       try
  574.         HandleRequest(HTTPRequest, HTTPResponse);
  575.       finally
  576.         HTTPResponse.Free;
  577.       end;
  578.     finally
  579.       HTTPRequest.Free;
  580.     end;
  581.   except
  582.     HandleServerException(Exception(ExceptObject), FOutputFileName);
  583.   end;
  584. end;
  585.  
  586. procedure InitApplication;
  587. begin
  588.   Application := TCGIApplication.Create(nil);
  589. end;
  590.  
  591. procedure DoneApplication;
  592. begin
  593.   Application.Free;
  594.   Application := nil;
  595. end;
  596.  
  597. initialization
  598.   InitApplication;
  599. finalization
  600.   DoneApplication;
  601. end.
  602.  
  603.