Componentes.Terceros.FastRe.../internal/4.2/1/Source/ClientServer/frxServer.pas
2007-11-18 19:40:07 +00:00

1365 lines
42 KiB
ObjectPascal

{******************************************}
{ }
{ FastReport v4.0 }
{ HTTP Report Server }
{ }
{ Copyright (c) 1998-2007 }
{ by Alexander Fediachov, }
{ Fast Reports Inc. }
{ }
{******************************************}
unit frxServer;
{$I frx.inc}
interface
uses
Forms, Windows, Classes, frxClass, ScktComp, Registry,
WinSock, frxVariables, frxGZip, frxServerLog,
frxServerSessionManager, frxServerStat, frxServerReports,
frxServerVariables, frxServerSSI, frxServerUtils, frxNetUtils, frxMD5,
frxServerCache, frxServerReportsList, frxUnicodeUtils, frxUsers
, SysUtils, frxServerConfig, frxServerTemplates;
type
TfrxHTTPServer = class;
TfrxServerSession = class;
TfrxServerData = class;
TfrxServerGuard = class;
TfrxServerGetReportEvent = procedure(const ReportName: String;
Report: TfrxReport; User: String = '') of object;
TfrxServerGetVariablesEvent = procedure(const ReportName: String;
Variables: TfrxVariables; User: String = '') of object;
TfrxServerAfterBuildReport = procedure(const ReportName: String;
Variables: TfrxVariables; User: String = '') of object;
TfrxReportServer = class(TComponent)
private
FActive: Boolean;
FAllow: TStrings;
FConfig: TfrxServerConfig; // obsolete
FDeny: TStrings;
FGetReport: TfrxServerGetReportEvent;
FPDFPrint: Boolean;
FTotals: TStrings;
FVariables: TfrxServerVariables;
FWebServer: TfrxHTTPServer;
FGetVariables: TfrxServerGetVariablesEvent;
FBuildReport: TfrxServerAfterBuildReport;
FReportList: TfrxServerReportsList;
FConfigFileName: String;
FGuard: TfrxServerGuard;
function GetTotals: TStrings;
procedure SetActive(const Value: Boolean);
procedure SetConfig(const Value: TfrxServerConfig);
procedure StatToVar;
// procedure IdleEventHandler(Sender: TObject; var Done: Boolean);
procedure Initialize;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Open;
procedure Close;
procedure LoadConfigs;
property Totals: TStrings read GetTotals;
property Variables: TfrxServerVariables read FVariables;
property ReportsList: TfrxServerReportsList read FReportList;
published
property Configuration: TfrxServerConfig read FConfig write SetConfig; // obsolete
property Active: Boolean read FActive write SetActive;
property AllowIP: TStrings read FAllow write FAllow;
property DenyIP: TStrings read FDeny write FDeny;
property PrintPDF: Boolean read FPDFPrint write FPDFPrint;
property OnGetReport: TfrxServerGetReportEvent read FGetReport
write FGetReport;
property OnGetVariables: TfrxServerGetVariablesEvent read FGetVariables
write FGetVariables;
property OnAfterBuildReport: TfrxServerAfterBuildReport read FBuildReport
write FBuildReport;
property WebServer: TfrxHTTPServer read FWebServer;
end;
TfrxHTTPServer = class(TServerSocket)
private
FBasePath: String;
FGzip: Boolean;
FMainDocument: String;
FNoCacheHeader: Boolean;
FParentReportServer: TfrxReportServer;
FReportPath: String;
FSocketTimeOut: Integer;
procedure ClientAccept(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket;
var SocketThread: TServerClientThread);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property BasePath: String read FBasePath write FBasePath;
property Gzip: Boolean read FGzip write FGzip;
property MainDocument: String read FMainDocument write FMainDocument;
property NoCacheHeader: Boolean read FNoCacheHeader write FNoCacheHeader;
property ParentReportServer: TfrxReportServer read FParentReportServer
write FParentReportServer;
property ReportPath: String read FReportPath write FReportPath;
property SocketTimeOut: Integer read FSocketTimeOut write FSocketTimeOut;
end;
TfrxServerSession = class(TServerClientThread)
private
FAuthNeeded: Boolean;
FDialog: Boolean;
FDialogSessionId: String;
FErrorCode: Integer;
FErrorText: String;
FFormat: TfrxServerFormat;
FGzip: Boolean;
FHeader: String;
FHost: String;
FHTTPVersion: String;
FIsReport: Boolean;
FKeepAlive: boolean;
FMethod: AnsiString;
FMIMEType: String;
FMultipage: Boolean;
FName: String;
FNoCacheHeader: Boolean;
FPageNavigator: Boolean;
FPageRange: String;
FParentHTTPServer: TfrxHTTPServer;
FParentReportServer: TfrxReportServer;
FRedirect: Boolean;
FReferer: String;
FRemoteIP: String;
FReplyBody: TStringList;
FReplyHeader: TStringList;
FRepSession: TfrxReportSession;
FResultPage: String;
FServerReplyData: TStringList;
FSessionId: String;
FSessionItem: TfrxSessionItem;
FSize: integer;
FUserAgent: String;
FVariables: TfrxVariables;
FStream: TMemoryStream;
FFileDate: TDateTime;
FCacheId: String;
FLogin: String;
FPassword: String;
FReportMessage: String;
FReturnData: String;
FInParams: TStringList;
FOutParams: TStringList;
FData: TfrxServerData;
FActive: Boolean;
function CheckBadPath: Boolean;
function CheckDeflate(FileName: String): Boolean;
function CheckSSI(FileName: String): Boolean;
function ParseHeaderField(Field: String): String;
function ParseParam(S: String): String;
procedure CheckAuth;
procedure CloseSession;
procedure CreateReplyHTTPData;
procedure ErrorLog;
procedure GetFileMIMEType;
procedure MakeServerReply;
procedure ParseHTTPHeader;
procedure UpdateSessionFName;
procedure WriteLogs;
procedure DoGetVariables;
procedure AddOutData(const Name: String; const Value: String);
public
constructor Create(CreateSuspended: Boolean;
ASocket: TServerClientWinSocket);
destructor Destroy; override;
procedure ClientExecute; override;
procedure PrepareReportQuery;
property NoCacheHeader: Boolean read FNoCacheHeader write FNoCacheHeader;
property ParentHTTPServer: TfrxHTTPServer read FParentHTTPServer
write FParentHTTPServer;
property ParentReportServer: TfrxReportServer read FParentReportServer
write FParentReportServer;
property SessionId: String read FSessionId write FSessionId;
property SessionItem: TfrxSessionItem read FSessionItem write FSessionItem;
property Login: String read FLogin;
property Password: String read FPassword;
property Data: TfrxServerData read FData write FData;
property Active: Boolean read FActive write FActive;
end;
TfrxServerData = class(TObject)
private
FErrorCode: Integer;
FInParams: TStringList;
FOutParams: TStringList;
FStream: TMemoryStream;
FFileName: String;
FHeader: String;
FRepHeader: String;
FHTTPVer: String;
FLastMod: TDateTime;
FExpires: TDateTime;
public
constructor Create;
destructor Destroy; override;
procedure Assign(Source: TfrxServerData);
property InParams: TStringList read FInParams;
property OutParams: TStringList read FOutParams;
property ErrorCode: Integer read FErrorCode write FErrorCode;
property Stream: TMemoryStream read FStream;
property FileName: String read FFileName write FFileName;
property Header: String read FHeader write FHeader;
property RepHeader: String read FRepHeader write FRepHeader;
property HTTPVer: String read FHTTPVer write FHTTPVer;
property Expires: TDateTime read FExpires write FExpires;
property LastMod: TDateTime read FLastMod write FLastMod;
end;
TfrxServerGuard = class(TThread)
private
FTimeOut: Integer;
FServer: TfrxReportServer;
FListTimeOut: Integer;
procedure DoLoadConf;
protected
procedure Execute; override;
public
constructor Create(Server: TfrxReportServer);
destructor Destroy; override;
property TimeOut: Integer read FTimeOut write FTimeOut;
property ListTimeOut: Integer read FListTimeOut write FListTimeOut;
end;
const
MAX_IE_GZIP = 4096;
SERVER_NAME = 'FastReport Server';
SERVER_VERSION = {$I frxServerVersion.inc};
SERVER_DATA = '';
SID_SIGN = 'sid_f';
implementation
uses frxUtils, frxFileUtils, SyncObjs;
const
SERVER_COPY = '© Copyright 1998-2007 by Fast Reports Inc.';
METHOD_GET = 'GET';
HTML = 'text/html';
ERR_UNKNOWN_METHOD = '1';
ERR_OK = '0';
{ TfrxReportServer }
procedure TfrxReportServer.LoadConfigs;
begin
ServerConfig.LoadFromFile(FConfigFileName);
ServerUsers.LoadFromFile(frxGetAbsPathDir(ServerConfig.GetValue('server.security.usersfile'), ServerConfig.ConfigFolder));
end;
procedure TfrxReportServer.Initialize;
var
s: String;
begin
LogWriter := TfrxServerLog.Create;
FConfig := TfrxServerConfig.Create; // obsolete
FConfigFileName := ServerConfig.ConfigFolder + 'config.xml';
LoadConfigs;
LogWriter.MaxLogSize := StrToInt(ServerConfig.GetValue('server.logs.rotatesize'));
LogWriter.MaxLogFiles := StrToInt(ServerConfig.GetValue('server.logs.rotatefiles'));
LogWriter.LogDir := frxGetAbsPathDir(ServerConfig.GetValue('server.logs.path'), ServerConfig.ConfigFolder);
LogWriter.AddLevel(ServerConfig.GetValue('server.logs.errorlog'));
LogWriter.AddLevel(ServerConfig.GetValue('server.logs.accesslog'));
LogWriter.AddLevel(ServerConfig.GetValue('server.logs.refererlog'));
LogWriter.AddLevel(ServerConfig.GetValue('server.logs.agentlog'));
LogWriter.AddLevel(ServerConfig.GetValue('server.logs.serverlog'));
FAllow := TStringList.Create;
FDeny := TStringList.Create;
s := frxGetAbsPathDir(ServerConfig.GetValue('server.security.allowfile'), ServerConfig.ConfigFolder);
if FileExists(s) then
FAllow.LoadFromFile(s);
s := frxGetAbsPathDir(ServerConfig.GetValue('server.security.denyfile'), ServerConfig.ConfigFolder);
if FileExists(s) then
FDeny.LoadFromFile(s);
FTotals := TStringList.Create;
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'Started');
LogWriter.Write(SERVER_LEVEL, 'Logs path:' + #9 + frxGetAbsPathDir(ServerConfig.GetValue('server.logs.path'), ServerConfig.ConfigFolder));
LogWriter.Write(SERVER_LEVEL, 'Reports path:' + #9 + frxGetAbsPathDir(ServerConfig.GetValue('server.reports.path'), ServerConfig.ConfigFolder));
LogWriter.Write(SERVER_LEVEL, 'Reports cache path:' + #9 + frxGetAbsPathDir(ServerConfig.GetValue('server.cache.path'), ServerConfig.ConfigFolder));
LogWriter.Write(SERVER_LEVEL, 'Root path:' + #9 + frxGetAbsPathDir(ServerConfig.GetValue('server.http.rootpath'), ServerConfig.ConfigFolder));
if FileExists(FConfigFileName) then
LogWriter.Write(SERVER_LEVEL, 'Config file:' + #9 + FConfigFileName)
else
LogWriter.Write(SERVER_LEVEL, 'ERROR! Config file ' + FConfigFileName + ' not found!');
SessionManager := TfrxSessionManager.Create;
FWebServer := TfrxHTTPServer.Create(nil);
FWebServer.ParentReportServer := Self;
ReportCache := TfrxServerCache.Create;
ServerStatistic := TfrxServerStatistic.Create;
FVariables := TfrxServerVariables.Create;
ServerUsers.LoadFromFile(ServerConfig.GetValue('server.security.usersfile'));
FVariables.AddVariable('SERVER_NAME', ServerConfig.GetValue('server.name'));
FVariables.AddVariable('SERVER_COPYRIGHT', SERVER_COPY);
FVariables.AddVariable('SERVER_SOFTWARE', SERVER_VERSION);
FVariables.AddVariable('SERVER_LAST_UPDATE', SERVER_DATA);
FPDFPrint := True;
Active := False;
ReportCache.Clear;
FReportList := TfrxServerReportsList.Create;
LogWriter.Active := ServerConfig.GetBool('server.logs.active');
FGuard := TfrxServerGuard.Create(Self);
end;
constructor TfrxReportServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ServerConfig.ConfigFolder := GetAppPath;
Initialize;
end;
destructor TfrxReportServer.Destroy;
begin
FGuard.Free;
FReportList.Free;
ReportCache.Free;
FAllow.Free;
FDeny.Free;
FWebServer.Free;
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'Stopped'#9 + #13#10 + Totals.Text);
LogWriter.Flush;
Active := False;
ServerStatistic.Free;
SessionManager.Free;
FConfig.Free;
FTotals.Free;
FVariables.Free;
LogWriter.Free;
PMessages;
inherited;
end;
procedure TfrxReportServer.SetActive(const Value: Boolean);
begin
try
FWebServer.Active := Value;
except
if Value then
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'Port open failed. ' + #13#10)
else
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'Port close failed. ' + #13#10)
end;
if FWebServer.Active = Value then
FActive := Value;
// if Value and LogWriter.Suspended then
// LogWriter.Resume;
end;
procedure TfrxReportServer.Open;
begin
if ServerConfig.GetBool('server.security.reportslist') then
begin
FReportList.ReportsPath := frxGetAbsPathDir(ServerConfig.GetValue('server.reports.path'), ServerConfig.ConfigFolder);
FReportList.BuildListOfReports;
FVariables.AddVariable('SERVER_REPORTS_LIST', FReportList.Lines.Text);
FVariables.AddVariable('SERVER_REPORTS_HTML', FReportList.Html);
end;
Active := True;
end;
procedure TfrxReportServer.Close;
begin
Active := False;
ReportCache.Clear;
end;
procedure TfrxReportServer.SetConfig(const Value: TfrxServerConfig);
begin
FConfig.Assign(Value);
end;
function TfrxReportServer.GetTotals: TStrings;
begin
FTotals.Clear;
FTotals.Add('Uptime: ' + ServerStatistic.FormatUpTime);
FTotals.Add('Total sessions: ' + IntToStr(ServerStatistic.TotalSessionsCount));
FTotals.Add('Total reports: ' + IntToStr(ServerStatistic.TotalReportsCount));
FTotals.Add('Total cache hits: ' + IntToStr(ServerStatistic.TotalCacheHits));
FTotals.Add('Total errors: ' + IntToStr(ServerStatistic.TotalErrors));
FTotals.Add('Max sessions: ' + IntToStr(ServerStatistic.MaxSessionsCount));
FTotals.Add('Max reports: ' + IntToStr(ServerStatistic.MaxReportsCount));
Result := FTotals;
end;
procedure TfrxReportServer.StatToVar;
begin
FVariables.AddVariable('SERVER_UPTIME', ServerStatistic.FormatUpTime);
FVariables.AddVariable('SERVER_TOTAL_SESSIONS', IntToStr(ServerStatistic.TotalSessionsCount));
FVariables.AddVariable('SERVER_TOTAL_REPORTS', IntToStr(ServerStatistic.TotalReportsCount));
FVariables.AddVariable('SERVER_TOTAL_ERRORS', IntToStr(ServerStatistic.TotalErrors));
FVariables.AddVariable('SERVER_TOTAL_CACHE', IntToStr(ServerStatistic.TotalCacheHits));
FVariables.AddVariable('SERVER_MAX_SESSIONS', IntToStr(ServerStatistic.MaxSessionsCount));
FVariables.AddVariable('SERVER_MAX_REPORTS', IntToStr(ServerStatistic.MaxReportsCount));
end;
{ TfrxHTTPServer }
constructor TfrxHTTPServer.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
Active := False;
ServerType := stThreadBlocking;
Port := StrToInt(ServerConfig.GetValue('server.http.port'));
FGzip := ServerConfig.GetBool('server.http.compression');
FMainDocument := ServerConfig.GetValue('server.http.indexfile');
FBasePath := frxGetAbsPathDir(ServerConfig.GetValue('server.http.rootpath'), ServerConfig.ConfigFolder);
FSocketTimeOut := StrToInt(ServerConfig.GetValue('server.http.sockettimeout'));
FNoCacheHeader := ServerConfig.GetBool('server.http.nocacheheader');
OnClientError := ClientError;
OnClientDisconnect := ClientDisconnect;
OnAccept := ClientAccept;
OnGetThread := GetThread;
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'HTTP server created');
end;
destructor TfrxHTTPServer.Destroy;
begin
LogWriter.Write(SERVER_LEVEL, DateTimeToStr(Now) + #9'HTTP server closed');
PMessages;
inherited;
end;
procedure TfrxHTTPServer.ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + Socket.RemoteAddress + #9 + GetSocketErrorText(ErrorCode));
LogWriter.ErrorReached;
ErrorCode := 0;
SessionManager.CompleteSessionId(String(TCustomWinSocket(Socket).Data));
Socket.Close;
end;
procedure TfrxHTTPServer.ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
begin
SessionManager.CompleteSessionId(String(TCustomWinSocket(Socket).Data));
end;
procedure TfrxHTTPServer.GetThread(Sender: TObject;
ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread);
begin
try
SocketThread := TfrxServerSession.Create(True, ClientSocket);
SocketThread.FreeOnTerminate := True;
SocketThread.KeepInCache := False;
TfrxServerSession(SocketThread).ParentReportServer := ParentReportServer;
TfrxServerSession(SocketThread).ParentHTTPServer := Self;
if ClientSocket <> nil then
ClientSocket.Data := PChar(TfrxServerSession(SocketThread).SessionId);
TfrxServerSession(SocketThread).SessionItem := SessionManager.AddSession(TfrxServerSession(SocketThread).SessionId, TCustomWinSocket(ClientSocket));
SocketThread.Resume;
FParentReportServer.StatToVar;
except
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + ClientSocket.RemoteAddress + ' client session creation error');
end;
end;
procedure TfrxHTTPServer.ClientAccept(Sender: TObject;
Socket: TCustomWinSocket);
begin
if ParentReportServer.DenyIP.IndexOf(Socket.RemoteAddress) <> -1 then
begin
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + Socket.RemoteAddress + ' denial of client connection');
Socket.Close;
end
else if (ParentReportServer.AllowIP.Count > 0) and
(ParentReportServer.AllowIP.IndexOf(Socket.RemoteAddress) = -1) then
begin
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + Socket.RemoteAddress + ' client connection not allowed');
Socket.Close;
end;
end;
{ TfrxServerSession }
constructor TfrxServerSession.Create(CreateSuspended: Boolean; ASocket: TServerClientWinSocket);
begin
inherited Create(CreateSuspended, ASocket);
FSessionId := SID_SIGN + MakeSessionId;
FIsReport := False;
FSize := 0;
FKeepAlive := False;
FRemoteIP := ClientSocket.RemoteAddress;
FServerReplyData := TStringList.Create;
FReplyHeader := TStringList.Create;
FReplyBody := TStringList.Create;
FFormat := sfHTM;
FPageRange := '';
FGzip := False;
FResultPage := '';
FRedirect := False;
FStream := TMemoryStream.Create;
FInParams := TStringList.Create;
FOutParams := TStringList.Create;
FData := nil;
end;
destructor TfrxServerSession.Destroy;
begin
PMessages;
FInParams.Free;
FOutParams.Free;
FStream.Free;
FServerReplyData.Free;
FReplyHeader.Free;
FReplyBody.Free;
inherited;
end;
function TfrxServerSession.ParseHeaderField(Field: String): String;
var
i: integer;
s: string;
begin
i := Pos(Field, FHeader);
Result := '';
if i > 0 then
begin
s := Copy(FHeader, i + Length(Field), Length(FHeader) - i + Length(Field));
i := Pos(#13#10, s);
if i > 0 then
Result := Copy(s, 1, i - 1);
end;
end;
procedure TfrxServerSession.ParseHTTPHeader;
var
i, j: Integer;
s: string;
P, V: String;
RepName: String;
begin
FMethod := ERR_UNKNOWN_METHOD;
FErrorCode := 0;
FReturnData := '';
if Pos(METHOD_GET, FHeader) > 0 then
begin
i := Pos('/', FHeader);
if i > 0 then
begin
FName := Trim(Copy(FHeader, i + 1, Pos('HTTP', FHeader) - i - 2));
FHTTPVersion := Copy(FHeader, Pos('HTTP/', FHeader), 8);
FHost := ParseHeaderField('Host: ');
FKeepAlive := ParseHeaderField('Connection: ') = 'keep-alive';
FReferer := ParseHeaderField('Referer: ');
FUserAgent := ParseHeaderField('User-Agent: ');
s := ParseHeaderField('Accept-Encoding: ');
if Length(s) > 0 then
if (Pos('gzip', LowerCase(s)) > 0) and (FParentHTTPServer.Gzip) then
FGzip := True;
CheckAuth;
FMethod := METHOD_GET;
WriteLogs;
if not FAuthNeeded then
begin
i := Pos('?', FName);
if i > 0 then
FName := Copy(FName, i + 1, Length(FName) - i);
s := ParseParam('getvariable');
if Length(s) = 0 then
begin
RepName := ParseParam('report');
if (Length(RepName) > 0) then
begin
FIsReport := True;
FDialogSessionId := ParseParam('sessionid');
FCacheId := ParseParam('cacheid');
if Length(FDialogSessionId) > 0 then
FDialog := True;
s := ParseParam('format');
if Length(s) > 0 then
begin
s := UpperCase(s);
if s = 'PDF' then FFormat := sfPDF else
if s = 'ODS' then FFormat := sfODS else
if s = 'ODT' then FFormat := sfODT else
if s = 'XML' then FFormat := sfXML else
if s = 'XLS' then FFormat := sfXLS else
if s = 'RTF' then FFormat := sfRTF else
if s = 'TXT' then FFormat := sfTXT else
if s = 'CSV' then FFormat := sfCSV else
if s = 'JPG' then FFormat := sfJPG else
if s = 'BMP' then FFormat := sfBMP else
if s = 'GIF' then FFormat := sfGIF else
if (s = 'TIFF') or (s = 'TIF') then FFormat := sfTIFF else
if (s = 'FRP') or (s = 'FP3') then FFormat := sfFRP else
FFormat := sfHTM;
end;
s := ParseParam('multipage');
if s = '0' then FMultipage := False
else if s = '1' then FMultipage := True
else FMultipage := not ServerConfig.GetBool('server.exports.html.singlepage');
s := ParseParam('pagenav');
if s = '0' then FPageNavigator := False
else if s = '1' then FPageNavigator := True
else FPageNavigator := ServerConfig.GetBool('server.exports.html.navigator');
s := ParseParam('pagerange');
FPageRange := s;
if Pos('=', FName) > 0 then
begin
FVariables := TfrxVariables.Create;
i := 1;
while i > 0 do
begin
j := 1;
while (j <= i) and (j <> 0) do
begin
i := Pos('=', FName);
j := Pos('&', FName);
if (j < i) and (j <> 0) then
FName := Copy(FName, j + 1, Length(FName) - j);
end;
if i > 0 then
begin
P := Copy(FName, 1, i - 1);
V := '''' + UTF8Decode(ParseParam(P)) + '''';
FVariables[P] := V;
end;
end;
end else
FVariables := nil;
FName := frxGetAbsPathDir(ServerConfig.GetValue('server.reports.path'), ServerConfig.ConfigFolder) + RepName;
PrepareReportQuery;
if Assigned(FVariables) then
FVariables.Free;
end else
if i > 0 then
FErrorCode := 403;
end else
begin
FReturnData := TfrxReportServer(ParentReportServer).Variables.GetValue(s);
if Length(FReturnData) = 0 then
FErrorCode := 404;
end;
end;
end
end;
end;
function TfrxServerSession.ParseParam(S: String): String;
var
i, j: integer;
begin
i := Pos(UpperCase(S) + '=', UpperCase(FName));
if i > 0 then
begin
Result := Copy(FName, i + Length(S) + 1, Length(FName) - i + Length(S) + 1);
j := Pos('&', Result);
if j > 0 then
Result := Copy(Result, 1, j - 1);
Delete(FName, i, Length(S) + Length(Result) + 1);
end else
Result := '';
if Length(FName) > 0 then
begin
i := 1;
while (FName[i] = '&') and (i < Length(FName)) do
Inc(i);
Delete(FName, 1, i - 1);
end;
Result := HTML2Str(Result);
end;
function TfrxServerSession.CheckBadPath: Boolean;
begin
Result := (Pos('..\', FName) > 0) or (Pos('../', FName) > 0);
end;
procedure TfrxServerSession.CreateReplyHTTPData;
var
SearchRec: TSearchRec;
s, sn: String;
begin
FServerReplyData.Clear;
FReplyHeader.Clear;
if Length(FReturnData) > 0 then
FErrorCode := 200;
if (FErrorCode = 0) then
if CheckBadPath then
FErrorCode := 403
else if FAuthNeeded then
FErrorCode := 401
else if (Length(FResultPage) > 0) and FileExists(FParentHTTPServer.BasePath + FResultPage) then
begin
FErrorCode := 301;
FRedirect := True;
end else
begin
if FName = '' then
FName := FParentHTTPServer.MainDocument;
if (FindFirst(FParentHTTPServer.BasePath + FName, faReadOnly + faArchive, SearchRec) = 0) or
(FindFirst(FParentHTTPServer.BasePath + FName + FParentHTTPServer.MainDocument, faReadOnly + faArchive, SearchRec) = 0)
then
begin
FErrorCode := 200;
FSize := SearchRec.Size;
FFileDate := FileDateToDateTime(SearchRec.Time);
end else
FErrorCode := 404;
FindClose(SearchRec);
end;
UpdateSessionFName;
GetFileMIMEType;
s := '';
if FErrorCode = 401 then
s := ' Unauthorized'
else if FErrorCode = 403 then
s := ' Forbidden';
if FData <> nil then
FData.HTTPVer := FHTTPVersion;
if FData <> nil then
FData.ErrorCode := FErrorCode;
FReplyHeader.Add(FHTTPVersion + ' ' + IntToStr(FErrorCode) + s);
if Length(s) = 0 then
begin
sn := 'Server';
FReplyHeader.Add(sn + ': ' + SERVER_NAME);
AddOutData(sn, SERVER_NAME);
if FErrorCode = 200 then
begin
sn := 'Content-type';
AddOutData(sn, FMIMEType);
FReplyHeader.Add(sn + ': ' + FMIMEType);
end;
if (FParentHTTPServer.FNoCacheHeader) and (not FRedirect) then
begin
sn := 'Cache-Control';
s := 'must-revalidate, max-age=0';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
sn := 'Pragma';
s := 'no-cache';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
end;
sn := 'Accept-ranges';
s := 'none';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
sn := 'Last-Modified';
s := DateTimeToRFCDateTime(FFileDate);
AddOutData(sn, s);
FReplyHeader.Add(sn + ':' + s);
sn := 'Expires';
s := DateTimeToRFCDateTime(FFileDate);
if FData <> nil then
begin
FData.Expires := FFileDate;
FData.LastMod := FFileDate;
end;
AddOutData(sn, s);
FReplyHeader.Add(sn + ':' + s);
if FGzip and CheckDeflate(FName) and (FErrorCode = 200)
then
begin
sn := 'Content-Encoding';
s := 'gzip';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s)
end else
FGzip := False;
if FRedirect then
begin
sn := 'Location';
s := FResultPage;
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
end;
if FIsReport then
begin
sn := 'SessionId';
if FDialogSessionId <> '' then
s := FDialogSessionId
else
s := FSessionId;
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
end;
end;
end;
procedure TfrxServerSession.PrepareReportQuery;
var
Path: String;
SecAtrtrs: TSecurityAttributes;
begin
if FIsReport then
begin
Path := FParentHTTPServer.BasePath + FSessionId;
SecAtrtrs.nLength := SizeOf(TSecurityAttributes) ;
SecAtrtrs.lpSecurityDescriptor := nil;
SecAtrtrs.bInheritHandle := true;
CreateDirectory(PChar(Path), @SecAtrtrs);
if not FDialog then
begin
FRepSession := TfrxReportSession.Create;
FRepSession.ParentThread := Self;
FRepSession.NativeClient := Pos('FastReport', FUserAgent) > 0;
FRepSession.Stream := FStream;
FRepSession.ParentReportServer := ParentReportServer;
FRepSession.SessionId := FSessionId;
FRepSession.CacheId := FCacheId;
FRepSession.FileName := FName;
FRepSession.ReportPath := FParentHTTPServer.ReportPath;
FRepSession.IndexFileName := FParentHTTPServer.MainDocument;
FRepSession.RootPath := FParentHTTPServer.BasePath;
FRepSession.PageRange := FPageRange;
FRepSession.Format := FFormat;
if Assigned(ParentReportServer.OnGetVariables) then
Synchronize(DoGetVariables);
FRepSession.Variables := FVariables;
FRepSession.FreeOnTerminate := True;
FRepSession.Password := FPassword;
FSessionItem.ReportThread := FRepSession;
FRepSession.PageNav := FPageNavigator;
FRepSession.Multipage := FMultipage;
FRepSession.UserLogin := FLogin;
FRepSession.Resume;
end else
begin
FSessionItem := SessionManager.FindSessionById(FDialogSessionId);
if FSessionItem <> nil then
begin
FRepSession := FSessionItem.ReportThread;
if FRepSession <> nil then
begin
FRepSession.Stream := FStream;
FRepSession.Variables := FVariables;
FRepSession.Continue := True;
while FRepSession.DialogActive and (not Terminated) do
PMessages;
end
end
end;
if (FRepSession <> nil) and (not Terminated) then
begin
while (not Terminated) and (FRepSession.Active) and (not FRepSession.DialogActive) do
begin
Sleep(10);
PMessages;
end;
if FDialog then
FName := '\' + FDialogSessionId + FRepSession.ResultPage
else
begin
FName := '\' + FSessionId + FRepSession.ResultPage;
end;
FReportMessage := FRepSession.ReportMessage;
if FRepSession.Auth then
FAuthNeeded := True;
if (not FRepSession.DialogActive) then
if FDialog then
begin
FRepSession.Terminate;
SessionManager.FindSessionById(FDialogSessionId).ReportThread := nil;
end else
SessionManager.FindSessionById(FSessionId).ReportThread := nil;
FRepSession.Readed := True;
end else
FName := '';
FResultPage := StringReplace(FName, '\', '/', [rfReplaceAll]);
FFileDate := Now;
end;
end;
procedure TfrxServerSession.MakeServerReply;
var
FStream: TFileStream;
Buffer, sn, s: String;
i: Integer;
MemStream, MemStreamOut: TMemoryStream;
FSSIStream: TfrxSSIStream;
FTemplate: TfrxServerTemplate;
begin
if FData <> nil then
FData.FileName := FName;
if FErrorCode = 200 then
begin
if ClientSocket.Connected
then
begin
MemStream := TMemoryStream.Create;
FSSIStream := TfrxSSIStream.Create;
FSSIStream.BasePath := FParentHTTPServer.BasePath;
FSSIStream.Variables := FParentReportServer.Variables;
try
try
if Length(FReturnData) = 0 then
begin
FStream := TFileStream.Create(FParentHTTPServer.BasePath + FName, fmOpenRead + fmShareDenyWrite);
try
FSSIStream.CopyFrom(FStream, 0);
finally
FStream.Free;
end;
if CheckSSI(FName) then
FSSIStream.Prepare
end
else
FSSIStream.Write(FReturnData[1], Length(FReturnData));
FSSIStream.Position := 0;
if FGzip
then
begin
try
frxCompressStream(FSSIStream, MemStream, gzMax, FName);
except
FErrorText := 'GZIP pack error';
ErrorLog;
end;
end else
MemStream.CopyFrom(FSSIStream, 0);
MemStream.Position := 0;
sn := 'Content-length';
s := IntToStr(MemStream.Size);
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
if ServerConfig.GetBool('server.http.mic') then
begin
sn := 'Content-MD5';
s := MD5Stream(MemStream);
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
end;
FReplyHeader.Add('');
Buffer := FReplyHeader.Text;
except
FErrorText := 'error prepare output result';
ErrorLog;
end;
MemStreamOut := TMemoryStream.Create;
try
MemStream.SaveToStream(MemStreamOut);
MemStreamOut.Position := 0;
ClientSocket.SendBuf(Buffer[1], Length(Buffer));
ClientSocket.SendStreamThenDrop(MemStreamOut);
except
MemStreamOut.Free;
FErrorText := 'error socket stream output result';
ErrorLog;
end;
finally
MemStream.Free;
FSSIStream.Free;
end
end;
end else
begin
sn := 'Content-type';
s := 'text/html';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
if FErrorCode = 404 then
begin
FTemplate := TfrxServerTemplate.Create;
try
FTemplate.SetTemplate('error404');
FTemplate.Variables.AddVariable('ERROR', FReportMessage + '<br>' + ServerConfig.ServerMessage);
FTemplate.Prepare;
Buffer := FTemplate.TemplateStrings.Text;
finally
FTemplate.Free;
end;
i := Length(Buffer);
FErrorText := FName + ' document not found ' + FReportMessage;
ErrorLog;
end else
if FRedirect or (FErrorCode = 403) then
begin
i := 0;
Buffer := '';
end else
if FErrorCode = 401 then
begin
i := 0;
Buffer := '';
sn := 'WWW-Authenticate';
s := 'Basic realm="' + SERVER_NAME + '"';
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
end else
begin
FTemplate := TfrxServerTemplate.Create;
try
FTemplate.SetTemplate('error500');
FTemplate.Variables.AddVariable('ERROR', '');
FTemplate.Prepare;
Buffer := FTemplate.TemplateStrings.Text;
finally
FTemplate.Free;
end;
i := Length(Buffer);
FErrorText := 'unknown error';
ErrorLog;
end;
sn := 'Content-length';
s := IntToStr(i);
AddOutData(sn, s);
FReplyHeader.Add(sn + ': ' + s);
FReplyHeader.Add('');
// Buffer := FReplyHeader.Text + Buffer;
try
ClientSocket.SendText(FReplyHeader.Text);
ClientSocket.SendText(Buffer);
ClientSocket.Close;
except
FErrorText := 'error socket stream output answer';
ErrorLog;
end;
end;
end;
procedure TfrxServerSession.ClientExecute;
var
FDSet: TFDSet;
TimeVal: TTimeVal;
TempStream: TMemoryStream;
i: Integer;
Len: Integer;
begin
LogWriter.StatAddCurrentSession;
FD_ZERO(FDSet);
FD_SET(ClientSocket.SocketHandle, FDSet);
TimeVal.tv_sec := FParentHTTPServer.SocketTimeOut;
TimeVal.tv_usec := 0;
if (select(0, @FDSet, nil, nil, @TimeVal) > 0) and not Terminated then
begin
TempStream := TMemoryStream.Create;
try
i := ClientSocket.ReceiveLength;
TempStream.SetSize(i);
try
ClientSocket.ReceiveBuf(TempStream.Memory^, i);
except
FErrorText := 'error socket stream read';
ErrorLog;
end;
TempStream.Position := 0;
i := StreamSearch(TempStream, 0, #13#10#13#10);
if i <> 0 then
begin
Len := i + 4;
SetLength(FHeader, Len);
try
TempStream.Position := 0;
TempStream.ReadBuffer(FHeader[1], Len);
try
FStream.CopyFrom(TempStream, TempStream.Size - Len);
except
FErrorText := 'error client query';
ErrorLog;
end;
except
FErrorText := 'error client stream parsing';
ErrorLog;
end;
end;
finally
TempStream.Free;
end;
end;
if (select(0, nil, @FDSet, nil, @TimeVal) > 0) and not Terminated then
if (Length(FHeader) > 0) and ClientSocket.Connected then
begin
ParseHTTPHeader;
CreateReplyHTTPData;
MakeServerReply;
end;
CloseSession;
LogWriter.StatRemoveCurrentSession;
end;
procedure TfrxServerSession.GetFileMIMEType;
var
Registry: TRegistry;
ext: String;
begin
ext := ExtractFileExt(FName);
Registry := TRegistry.Create;
try
{$IFNDEF Delphi4}
Registry.Access := KEY_READ;
{$ENDIF}
Registry.RootKey := HKEY_CLASSES_ROOT;
FMIMEType := '';
if Registry.KeyExists(ext) then
begin
Registry.OpenKey(ext, false);
FMIMEType := Registry.ReadString('Content Type');
Registry.CloseKey;
end;
finally
Registry.Free;
end;
end;
procedure TfrxServerSession.WriteLogs;
begin
LogWriter.Write(ACCESS_LEVEL, DateTimeToStr(Now) + #9 + FSessionId + #9 + FRemoteIP + #9 + FName);
if Length(FReferer) > 0 then
LogWriter.Write(REFERER_LEVEL, DateTimeToStr(Now) + #9 + FRemoteIP + #9 + FReferer);
if Length(FUserAgent) > 0 then
LogWriter.Write(AGENT_LEVEL, DateTimeToStr(Now) + #9 + FRemoteIP + #9 + FUserAgent);
end;
procedure TfrxServerSession.ErrorLog;
begin
LogWriter.Write(ERROR_LEVEL, DateTimeToStr(Now) + #9 + FRemoteIP + #9 + FErrorText);
LogWriter.ErrorReached;
end;
procedure TfrxServerSession.UpdateSessionFName;
begin
SessionManager.FindSessionById(FSessionId).FileName := FName;
end;
procedure TfrxServerSession.CloseSession;
begin
SessionManager.CompleteSessionId(SessionId);
end;
function TfrxServerSession.CheckSSI(FileName: String): Boolean;
var
ext: String;
begin
ext := LowerCase(ExtractFileExt(FileName));
Result := (ext = '.htm') or (ext = '.html') or
(ext = '.shtm') or (ext = '.shtml');
end;
function TfrxServerSession.CheckDeflate(FileName: String): Boolean;
var
ext: String;
begin
ext := LowerCase(ExtractFileExt(FileName));
if Pos('MSIE', FUserAgent) > 0 then
Result := ((ext = '.htm') or (ext = '.html') or
(ext = '.shtm') or (ext = '.shtml') or
(ext = '.css') or (ext = '.txt') or
(ext = '.bmp')) and (FSize > MAX_IE_GZIP)
else
Result := (ext <> '.jpg') and (ext <> '.jpeg') and
(ext <> '.gif') and (ext <> '.png') and
(ext <> '.ods') and (ext <> '.odt') and
(ext <> '.zip') and (ext <> '.rar');
end;
procedure TfrxServerSession.CheckAuth;
var
i: Integer;
s: String;
L, P: String;
begin
FAuthNeeded := ((Length(ServerConfig.GetValue('server.security.login')) > 0) and
(Length(ServerConfig.GetValue('server.security.password')) > 0))
or (ServerConfig.GetBool('server.security.userauth'));
s := ParseHeaderField('Authorization: ');
if Length(s) > 0 then
begin
i := Pos('Basic ', s);
if i > 0 then
begin
s := Copy(s, i + 6, Length(s) - i - 5);
s := Base64Decode(s);
i := Pos(':', s);
if i > 0 then
begin
L := Copy(s, 1, i - 1);
P := Copy(s, i + 1, Length(s) - i);
FLogin := L;
FPassword := P;
if ServerConfig.GetBool('server.security.userauth') then
begin
FAuthNeeded := not ServerUsers.AllowLogin(L, P);
if FName = '' then
FName := ServerUsers.GetUserIndex(L);
end
else
if (L = ServerConfig.GetValue('server.security.login')) and
(P = ServerConfig.GetValue('server.security.password')) then
FAuthNeeded := False
end;
end;
end;
end;
procedure TfrxServerSession.DoGetVariables;
begin
ParentReportServer.OnGetVariables(FName, FVariables, FLogin);
end;
procedure TfrxServerSession.AddOutData(const Name: String; const Value: String);
begin
if FData <> nil then
FData.OutParams.Add(Name + '=' + Value);
end;
{ TfrxServerData }
procedure TfrxServerData.Assign(Source: TfrxServerData);
begin
FInParams.Assign(Source.InParams);
FOutParams.Assign(Source.FOutParams);
FErrorCode := Source.ErrorCode;
FStream.Clear;
if Source.Stream.Size > 0 then
begin
Source.Stream.Position := 0;
FStream.CopyFrom(Source.Stream, 0);
end;
FErrorCode := Source.ErrorCode;
FFileName := Source.FileName;
end;
constructor TfrxServerData.Create;
begin
FInParams := TStringList.Create;
FOutParams := TStringList.Create;
FStream := TMemoryStream.Create;
FErrorCode := 0;
end;
destructor TfrxServerData.Destroy;
begin
FStream.Free;
FInParams.Free;
FOutParams.Free;
inherited;
end;
{ TfrxServerGuard }
constructor TfrxServerGuard.Create(Server: TfrxReportServer);
begin
inherited Create(True);
FServer := Server;
FTimeOut := 10;
FListTimeOut := 30;
Priority := tpLowest;
Resume;
end;
destructor TfrxServerGuard.Destroy;
begin
Terminate;
PMessages;
WaitFor;
inherited;
end;
procedure TfrxServerGuard.DoLoadConf;
begin
FServer.LoadConfigs;
end;
procedure TfrxServerGuard.Execute;
var
time1, time2, out1, out2: Cardinal;
begin
time1 := GetTickCount;
time2 := time1;
out1 := FTimeOut * 1000;
out2 := FListTimeOut * 1000;
while not Terminated do
begin
if (GetTickCount - time1) > out1 then
begin
Synchronize(DoLoadConf);
time1 := GetTickCount;
end;
if (GetTickCount - time2) > out2 then
begin
// FServer.ReportsList.BuildListOfReports;
time2 := GetTickCount;
end;
Sleep(1);
end;
end;
initialization
finalization
end.