123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2000 by the Free Pascal development team
- TCGIApplication class.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$mode objfpc}
- {$H+}
- unit cgiapp;
- Interface
- uses
- CustApp,Classes,SysUtils;
- Const
- CGIVarCount = 23;
- Type
- TCGIVarArray = Array[1..CGIVarCount] of String;
- Const
- CgiVarNames : TCGIVarArray =
- ('AUTH_TYPE',
- 'CONTENT_LENGTH',
- 'CONTENT_TYPE',
- 'GATEWAY_INTERFACE',
- 'PATH_INFO',
- 'PATH_TRANSLATED',
- 'QUERY_STRING', 'REMOTE_ADDR',
- 'REMOTE_HOST',
- 'REMOTE_IDENT',
- 'REMOTE_USER',
- 'REQUEST_METHOD',
- 'SCRIPT_NAME',
- 'SERVER_NAME',
- 'SERVER_PORT',
- 'SERVER_PROTOCOL',
- 'SERVER_SOFTWARE',
- 'HTTP_ACCEPT',
- 'HTTP_ACCEPT_CHARSET',
- 'HTTP_ACCEPT_ENCODING',
- 'HTTP_IF_MODIFIED_SINCE',
- 'HTTP_REFERER',
- 'HTTP_USER_AGENT');
- Type
- TCgiApplication = Class(TCustomApplication)
- Private
- FResponse : TStream;
- FEmail : String;
- FAdministrator : String;
- FContentTypeEmitted : Boolean;
- FCGIVars : TCGIVarArray;
- FRequestVars,
- FFormFiles : TStrings;
- Function GetCGIVar (Index : Integer) : String;
- Procedure InitCGIVars;
- Procedure InitRequestVars;
- Procedure InitPostVars;
- Procedure InitGetVars;
- Procedure SetContentLength (Value : Integer);
- Procedure SetCGIVar(Index : Integer; Value : String);
- Function GetContentLength : Integer;
- Function GetServerPort : Word;
- Function GetEmail : String;
- Function GetAdministrator : String;
- Procedure ProcessQueryString(Const FQueryString : String);
- Function GetRequestVariable(Const VarName : String) : String;
- Function GetRequestVariableCount : Integer;
- Procedure ProcessURLEncoded(M : TMemoryStream);
- Procedure ProcessMultiPart(M : TMemoryStream; Const Boundary : String);
- Public
- Constructor Create(AOwner : TComponent); override;
- Destructor Destroy; override;
- Procedure AddResponse(Const S : String);
- Procedure AddResponse(Const Fmt : String; Args : Array of const);
- Procedure AddResponseLn(Const S : String);
- Procedure AddResponseLn(Const Fmt : String; Args : Array of const);
- Procedure Initialize; override;
- Procedure GetCGIVarList(List : TStrings);
- Procedure GetRequestVarList(List : TStrings);
- Procedure GetRequestVarList(List : TStrings; NamesOnly : Boolean);
- Procedure ShowException(E: Exception);override;
- Procedure DeleteFormFiles;
- Function EmitContentType : Boolean;
- Function GetTempCGIFileName : String;
- Function VariableIsUploadedFile(Const VarName : String) : boolean;
- Function UploadedFileName(Const VarName : String) : String;
- Property AuthType : String Index 1 Read GetCGIVar;
- Property ContentLength : Integer Read GetContentLength Write SetContentLength; // Index 2
- Property ContentType : String Index 3 Read GetCGIVar Write SetCGIVar;
- Property GatewayInterface : String Index 4 Read GetCGIVar;
- Property PathInfo : String index 5 read GetCGIvar;
- Property PathTranslated : String Index 6 read getCGIVar;
- Property QueryString : String Index 7 read getcgivar;
- Property RemoteAddress : String Index 8 read GetCGIVar;
- Property RemoteHost : String Index 9 read GetCGIVar;
- Property RemoteIdent : String Index 10 read GetCGIVar;
- Property RemoteUser : String Index 11 read GetCGIVar;
- Property RequestMethod : String Index 12 read GetCGIVar;
- Property ScriptName : String Index 13 read GetCGIVar;
- Property ServerName : String Index 14 read GetCGIVar;
- Property ServerPort : Word Read GetServerPort; // Index 15
- Property ServerProtocol : String Index 16 read GetCGIVar;
- Property ServerSoftware : String Index 17 read GetCGIVar;
- Property HTTPAccept : String Index 18 read GetCGIVar;
- Property HTTPAcceptCharset : String Index 19 read GetCGIVar;
- Property HTTPAcceptEncoding : String Index 20 read GetCGIVar;
- Property HTTPIfModifiedSince : String Index 21 read GetCGIVar; // Maybe change to TDateTime ??
- Property HTTPReferer : String Index 22 read GetCGIVar;
- Property HTTPUserAgent : String Index 23 read GetCGIVar;
- Property Email : String Read GetEmail Write FEmail;
- Property Administrator : String Read GetAdministrator Write FAdministrator;
- Property RequestVariables[VarName : String] : String Read GetRequestVariable;
- Property RequestVariableCount : Integer Read GetRequestVariableCount;
- Property Response : TStream Read FResponse;
- end;
- ResourceString
- SWebMaster = 'webmaster';
- SCGIError = 'CGI Error';
- SAppEncounteredError = 'The application encountered the following error:';
- SError = 'Error: ';
- SNotify = 'Notify: ';
- SErrNoContentLength = 'No content length passed from server!';
- SErrUnsupportedContentType = 'Unsupported content type: "%s"';
- SErrNoRequestMethod = 'No REQUEST_METHOD passed from server.';
- SErrInvalidRequestMethod = 'Invalid REQUEST_METHOD passed from server.';
- Implementation
- uses
- iostream;
- {$ifdef cgidebug}
- Var
- flog : Text;
- Procedure Log(Msg : String);
- begin
- Writeln(flog,Msg);
- end;
- Procedure Log(Msg : String;Args : Array of const);
- begin
- Writeln(flog,Format(Msg,Args));
- end;
- Procedure InitLog;
- begin
- Assign(flog,'/tmp/cgi.log');
- Rewrite(flog);
- Log('---- Start of log session ---- ');
- end;
- Procedure DoneLog;
- begin
- Close(Flog);
- end;
- {$endif}
- Constructor TCgiApplication.Create(AOwner : TComponent);
- begin
- Inherited Create(AOwner);
- FRequestVars:=TStringList.Create;
- FFormFiles:=TStringList.Create;
- end;
- Destructor TCgiApplication.Destroy;
- begin
- DeleteFormFiles;
- FFormFiles.Free;
- FRequestVars.Free;
- Inherited;
- end;
- Function TCgiApplication.GetCGIVar (Index : Integer) : String;
- begin
- Result:=FCGIVars[Index];
- end;
- Procedure TCgiApplication.InitCGIVars;
- Var
- I : Integer;
- L : TStrings;
- begin
- L:=TStringList.Create;
- Try
- GetEnvironmentList(L);
- For I:=1 to CGIVarCount do
- FCGIVars[i]:=L.Values[CGIVarNames[i]];
- Finally
- L.Free;
- end;
- end;
- Function TCgiApplication.GetTempCGIFileName : String;
- begin
- Result:=GetTempFileName('/tmp/','CGI')
- end;
- Procedure TCgiApplication.DeleteFormFiles;
- Var
- I,P : Integer;
- FN : String;
- begin
- For I:=0 to FFormFiles.Count-1 do
- begin
- FN:=FFormFiles[i];
- P:=Pos('=',FN);
- Delete(FN,1,P);
- If FileExists(FN) then
- DeleteFile(FN);
- end;
- end;
- Procedure TCgiApplication.Initialize;
- begin
- StopOnException:=True;
- Inherited;
- InitCGIVars;
- InitRequestVars;
- FResponse:=TIOStream.Create(iosOutput);
- end;
- Procedure TCgiApplication.GetCGIVarList(List : TStrings);
- Var
- I : Integer;
- begin
- List.Clear;
- For I:=1 to cgiVarCount do
- List.Add(CGIVarNames[i]+'='+FCGIVars[i]);
- end;
- Procedure TCgiApplication.GetRequestVarList(List : TStrings);
- begin
- GetRequestVarList(List,False);
- end;
- Procedure TCgiApplication.GetRequestVarList(List : TStrings; NamesOnly : Boolean);
- Var
- I,J : Integer;
- S : String;
- begin
- List.BeginUpdate;
- Try
- List.Clear;
- // Copy one by one, there may be CR/LF in the variables, causing 'Text' to go wrong.
- If Assigned(FRequestVars) then
- For I:=0 to FRequestVars.Count-1 do
- begin
- S:=FRequestVars[i];
- If NamesOnly then
- begin
- J:=Pos('=',S);
- If (J>0) then
- S:=Copy(S,1,J-1);
- end;
- List.Add(S);
- end;
- finally
- List.EndUpdate;
- end;
- end;
- Function TCgiApplication.GetContentLength : Integer;
- begin
- Result:=StrToIntDef(GetCGIVar(2),-1);
- end;
- Procedure TCgiApplication.SetContentLength (Value : Integer);
- begin
- SetCGIVar(2,IntToStr(Value));
- end;
- Procedure TCgiApplication.SetCGIVar(Index : Integer; Value : String);
- begin
- If Index in [1..cgiVarCount] then
- FCGIVars[Index]:=Value;
- end;
- Function TCgiApplication.GetServerPort : Word;
- begin
- Result:=StrToIntDef(GetCGIVar(15),0);
- end;
- Function TCgiApplication.EmitContentType : Boolean;
- Var
- S: String;
- begin
- Result:=Not FContentTypeEmitted;
- If result then
- begin
- S:=ContentType;
- If (S='') then
- S:='text/html';
- AddResponseLn('Content-Type: '+ContentType);
- AddResponseLn('');
- FContentTypeEmitted:=True;
- end;
- end;
- Procedure TCgiApplication.ShowException(E: Exception);
- Var
- TheEmail : String;
- FrameCount: integer;
- Frames: PPointer;
- FrameNumber:Integer;
- begin
- If not FContentTypeEmitted then
- begin
- ContentType:='text/html';
- EmitContentType;
- end;
- If (ContentType='text/html') then
- begin
- AddResponseLN('<html><head><title>'+Title+': '+SCGIError+'</title></head>');
- AddResponseLN('<body>');
- AddResponseLN('<center><hr><h1>'+Title+': ERROR</h1><hr></center><br><br>');
- AddResponseLN(SAppEncounteredError+'<br>');
- AddResponseLN('<ul>');
- AddResponseLN('<li>'+SError+' <b>'+E.Message+'</b>');
- AddResponseLn('<li> Stack trace:<br>');
- AddResponseLn(BackTraceStrFunc(ExceptAddr)+'<br>');
- FrameCount:=ExceptFrameCount;
- Frames:=ExceptFrames;
- for FrameNumber := 0 to FrameCount-1 do
- AddResponseLn(BackTraceStrFunc(Frames[FrameNumber])+'<br>');
- AddResponseLn('</ul><hr>');
- TheEmail:=Email;
- If (TheEmail<>'') then
- AddResponseLN('<h5><p><i>'+SNotify+Administrator+': <a href="mailto:'+TheEmail+'">'+TheEmail+'</a></i></p></h5>');
- AddResponseLN('</body></html>');
- end;
- end;
- Function TCgiApplication.GetEmail : String;
- Var
- H : String;
- begin
- If (FEmail='') then
- begin
- H:=ServerName;
- If (H<>'') then
- Result:=Administrator+'@'+H
- else
- Result:='';
- end
- else
- Result:=Email;
- end;
- Function TCgiApplication.GetAdministrator : String;
- begin
- If (FADministrator<>'') then
- Result:=FAdministrator
- else
- Result:=SWebMaster;
- end;
- Procedure TCgiApplication.InitRequestVars;
- var
- R : String;
- begin
- R:=RequestMethod;
- if (R='') then
- Raise Exception.Create(SErrNoRequestMethod);
- if CompareText(R,'POST')=0 then
- InitPostVars
- else if CompareText(R,'GET')=0 then
- InitGetVars
- else
- Raise Exception.CreateFmt(SErrInvalidRequestMethod,[R]);
- end;
- Procedure TCgiApplication.ProcessURLEncoded(M : TMemoryStream);
- var
- FQueryString : String;
- begin
- SetLength(FQueryString,M.Size); // Skip added Null.
- M.Read(FQueryString[1],M.Size);
- ProcessQueryString(FQueryString);
- end;
- Type
- TFormItem = Class(TObject)
- DisPosition : String;
- Name : String;
- isFile : Boolean;
- FileName : String;
- ContentType : String;
- DLen : Integer;
- Data : String;
- Procedure Process;
- end;
- Procedure TFormItem.Process;
- Function GetLine(Var S : String) : String;
- Var
- P : Integer;
- begin
- P:=Pos(#13#10,S);
- If (P<>0) then
- begin
- Result:=Copy(S,1,P-1);
- Delete(S,1,P+1);
- end;
- end;
- Function GetWord(Var S : String) : String;
- Var
- I,len : Integer;
- Quoted : Boolean;
- C : Char;
- begin
- len:=length(S);
- quoted:=false;
- Result:='';
- for i:=1 to len do
- Begin
- c:=S[i];
- if (c='"') then
- Quoted:=Not Quoted
- else
- begin
- if not (c in [' ','=',';',':']) or Quoted then
- Result:=Result+C;
- if (c in [';',':','=']) and (not quoted) then
- begin
- Delete(S,1,I);
- Exit;
- end;
- end;
- end;
- S:='';
- end;
- Var
- Line : String;
- Words : TStringList;
- i,len : integer;
- c : char;
- S : string;
- quoted : boolean;
- begin
- Line:=GetLine(Data);
- While (Line<>'') do
- begin
- S:=GetWord(Line);
- While (S<>'') do
- begin
- If CompareText(S,'Content-Disposition')=0 then
- Disposition:=GetWord(Line)
- else if CompareText(S,'name')=0 Then
- Name:=GetWord(Line)
- else if CompareText(S,'filename')=0 then
- begin
- FileName:=GetWord(Line);
- isFile:=True;
- end
- else if CompareText(S,'Content-Type')=0 then
- ContentType:=GetWord(Line);
- S:=GetWord(Line);
- end;
- Line:=GetLine(Data);
- end;
- // Now Data contains the rest of the data, plus a CR/LF. Strip the CR/LF
- Len:=Length(Data);
- If (len>2) then
- Data:=Copy(Data,1,Len-2);
- end;
- Function MakeString(PStart,PEnd : Pchar) : String;
- begin
- SetLength(Result,PEnd-PStart);
- If Length(Result)>0 then
- Move(PStart^,Result[1],Length(Result));
- end;
- procedure FormSplit(var Cnt : String; boundary: String; List : TList);
- // Splits the form into items
- var
- Sep : string;
- Clen,slen, p:longint;
- FI : TFormItem;
- begin
- Sep:='--'+boundary+#13+#10;
- Slen:=length(Sep);
- CLen:=Pos('--'+Boundary+'--',Cnt);
- // Cut last marker
- Cnt:=Copy(Cnt,1,Clen-1);
- // Cut first marker
- Delete(Cnt,1,Slen);
- Clen:=Length(Cnt);
- While Clen>0 do
- begin
- Fi:=TFormItem.Create;
- List.Add(Fi);
- P:=pos(Sep,Cnt);
- If (P=0) then
- P:=CLen+1;
- FI.Data:=Copy(Cnt,1,P-1);
- delete(Cnt,1,P+SLen-1);
- CLen:=Length(Cnt);
- end;
- end;
- function GetNextLine(Var Data: String):string;
- Var
- p : Integer;
- begin
- P:=Pos(#13#10,Data);
- If (P<>0) then
- begin
- Result:=Copy(Data,1,P-1);
- Delete(Data,1,P+1);
- end;
- end;
- Procedure TCgiApplication.ProcessMultiPart(M : TMemoryStream; Const Boundary : String);
- Var
- L : TList;
- B : String;
- I,Index : Integer;
- S,FF,key, Value : String;
- FI : TFormItem;
- F : TStream;
- begin
- i:=Pos('=',Boundary);
- B:=Copy(Boundary,I+1,Length(Boundary)-I);
- I:=Length(B);
- If (I>0) and (B[1]='"') then
- B:=Copy(B,2,I-2);
- L:=TList.Create;
- Try
- SetLength(S,M.Size);
- If Length(S)>0 then
- Move(M.Memory^,S[1],M.Size);
- FormSplit(S,B,L);
- For I:=L.Count-1 downto 0 do
- begin
- FI:=TFormItem(L[i]);
- FI.Process;
- If (FI.Name='') then
- Raise Exception.CreateFmt('Invalid multipart encoding: %s',[FI.Data]);
- Key:=FI.Name;
- If Not FI.IsFile Then
- begin
- Value:=FI.Data
- end
- else
- begin
- Value:=FI.FileName;
- FF:=GetTempCGIFileName;
- FFormFiles.Add(Key+'='+FF);
- F:=TFileStream.Create(FF,fmCreate);
- Try
- if Length(FI.Data)>0 then
- F.Write(FI.Data[1],Length(FI.Data));
- finally
- F.Free;
- end;
- FI.Free;
- L[i]:=Nil;
- end;
- FRequestVars.Add(Key+'='+Value)
- end;
- Finally
- For I:=0 to L.Count-1 do
- TObject(L[i]).Free;
- L.Free;
- end;
- end;
- Type
- TCapacityStream = Class(TMemoryStream)
- Public
- Property Capacity;
- end;
- Procedure TCgiApplication.InitPostVars;
- Var
- M : TCapacityStream;
- I : TIOStream;
- Cl : Integer;
- B : Byte;
- begin
- CL:=ContentLength;
- M:=TCapacityStream.Create;
- Try
- I:=TIOStream.Create(iosInput);
- Try
- if (CL<>0) then
- begin
- M.Capacity:=(Cl);
- M.CopyFrom(I,Cl);
- end
- else
- begin
- While (I.Read(B,1)>0) do
- M.Write(B,1)
- end;
- Finally
- I.Free;
- end;
- if Pos(ContentType,'MULTIPART/FORM-DATA')=0 then
- ProcessMultiPart(M,ContentType)
- else if CompareText(ContentType,'APPLICATION/X-WWW-FORM-URLENCODED')=0 then
- ProcessUrlEncoded(M)
- else
- Raise Exception.CreateFmt(SErrUnsupportedContentType,[ContentType]);
- finally
- M.Free;
- end;
- end;
- Procedure TCgiApplication.InitGetVars;
- Var
- FQueryString : String;
- begin
- FQueryString:=QueryString;
- If (FQueryString<>'') then
- ProcessQueryString(FQueryString);
- end;
- const
- hexTable = '0123456789ABCDEF';
- Procedure TCgiApplication.ProcessQueryString(Const FQueryString : String);
- var
- queryItem : String;
- delimiter : Char;
- aString : String;
- aSepStr : String;
- aPos : Integer;
- aLenStr : Integer;
- aLenSep : Integer;
- function hexConverter(h1, h2 : Char) : Char;
- var
- B : Byte;
- begin
- B:=(Pos(upcase(h1),hexTable)-1)*16;
- B:=B+Pos(upcase(h2),hexTable)-1;
- Result:=chr(B);
- end;
- procedure Convert_ESC_Chars;
- var
- index : Integer;
- begin
- Index:=Length(QueryItem);
- While (Index>0) do
- begin
- If QueryItem[Index]='+' then
- QueryItem[Index]:=' '
- else If (QueryItem[Index]='%') and (Index<Length(QueryItem)-1) then
- begin
- QueryItem[Index]:=hexConverter(QueryItem[Index+1],QueryItem[index+2]);
- System.Delete(QueryItem,Index+1,2);
- end;
- dec(Index);
- end;
- end;
- procedure InitToken(aStr, aSep : String);
- begin
- aString := aStr;
- aSepStr := aSep;
- aPos := 1;
- aLenStr := Length(aString);
- aLenSep := Length(aSepStr);
- end;
- function NextToken(var aToken : String; var aSepChar : Char) : Boolean;
- var
- i : Integer;
- j : Integer;
- BoT : Integer;
- EoT : Integer;
- isSep : Boolean;
- begin
- BoT:=aPos;
- EoT:=aPos;
- for i:=aPos to aLenStr do
- begin
- IsSep := false;
- for j := 1 to aLenSep do
- begin
- if aString[i] = aSepStr[j] then
- begin
- IsSep := true;
- Break;
- end;
- end;
- if IsSep then
- begin
- EoT := i;
- aPos := i + 1;
- aSepChar := aString[i];
- Break;
- end
- else
- begin
- if i = aLenStr then
- begin
- EoT := i;
- aPos := i;
- Break;
- end;
- end;
- end;
- if aPos < aLenStr then
- begin
- aToken := Copy(aString, BoT, EoT - BoT);
- Result := true;
- end
- else
- begin
- if aPos = aLenStr then
- begin
- aToken := Copy(aString, BoT, EoT - BoT + 1);
- Result := true;
- aPos := aPos + 1;
- end
- else
- begin
- Result := false;
- end;
- end;
- end;
- begin
- InitToken(FQueryString, '&');
- while NextToken(QueryItem, delimiter) do
- begin
- if (QueryItem<>'') then
- begin
- Convert_ESC_Chars;
- FRequestVars.Add(QueryItem);
- end;
- end;
- end;
- Function TCGIApplication.GetRequestVariable(Const VarName : String) : String;
- begin
- If Assigned(FRequestVars) then
- Result:=FRequestVars.Values[VarName];
- end;
- Function TCGIApplication.GetRequestVariableCount : Integer;
- begin
- If Assigned(FRequestVars) then
- Result:=FRequestVars.Count
- else
- Result:=0;
- end;
- Procedure TCGIApplication.AddResponse(Const S : String);
- Var
- L : Integer;
- begin
- L:=Length(S);
- If L>0 then
- FResponse.Write(S[1],L);
- end;
- Procedure TCGIApplication.AddResponse(Const Fmt : String; Args : Array of const);
- begin
- AddResponse(Format(Fmt,Args));
- end;
- Procedure TCGIApplication.AddResponseLN(Const S : String);
- begin
- AddResponse(S+LineEnding);
- end;
- Procedure TCGIApplication.AddResponseLN(Const Fmt : String; Args : Array of const);
- begin
- AddResponseLN(Format(Fmt,Args));
- end;
- Function TCGIApplication.VariableIsUploadedFile(Const VarName : String) : boolean;
- begin
- Result:=FFormFiles.IndexOfName(VarName)<>-1;
- end;
- Function TCGIApplication.UploadedFileName(Const VarName : String) : String;
- begin
- Result:=FFormFiles.Values[VarName];
- end;
- {$ifdef cgidebug}
- Initialization
- initLog;
- Finalization
- DoneLog;
- {$endif}
- end.
|