Browse Source

* Better and more configurable multipart-formdata handling.

git-svn-id: trunk@24223 -
michael 12 years ago
parent
commit
fe7bfc4e93
2 changed files with 391 additions and 172 deletions
  1. 389 170
      packages/fcl-web/src/base/httpdefs.pp
  2. 2 2
      packages/fcl-web/src/base/webutil.pp

+ 389 - 170
packages/fcl-web/src/base/httpdefs.pp

@@ -136,11 +136,13 @@ type
     Function IndexOfCookie(AName : String) : Integer;
     Function IndexOfCookie(AName : String) : Integer;
     property Items[Index: Integer]: TCookie read GetCookie write SetCookie; default;
     property Items[Index: Integer]: TCookie read GetCookie write SetCookie; default;
   end;
   end;
+
   { TUploadedFile }
   { TUploadedFile }
 
 
   TUploadedFile = Class(TCollectionItem)
   TUploadedFile = Class(TCollectionItem)
   Private
   Private
     FContentType: String;
     FContentType: String;
+    FDescription: String;
     FDisposition: String;
     FDisposition: String;
     FFieldName: String;
     FFieldName: String;
     FFileName: String;
     FFileName: String;
@@ -148,6 +150,7 @@ type
     FSize: Int64;
     FSize: Int64;
     FStream : TStream;
     FStream : TStream;
   Protected
   Protected
+    Procedure DeleteTempUploadedFile; virtual;
     function GetStream: TStream; virtual;
     function GetStream: TStream; virtual;
   Public
   Public
     Destructor Destroy; override;
     Destructor Destroy; override;
@@ -158,20 +161,66 @@ type
     Property ContentType : String Read FContentType Write FContentType;
     Property ContentType : String Read FContentType Write FContentType;
     Property Disposition : String Read FDisposition Write FDisposition;
     Property Disposition : String Read FDisposition Write FDisposition;
     Property LocalFileName : String Read FLocalFileName Write FLocalFileName;
     Property LocalFileName : String Read FLocalFileName Write FLocalFileName;
+    Property Description : String Read FDescription Write FDescription;
   end;
   end;
-  
+  TUploadedFileClass = Class of TUploadedFile;
+
   { TUploadedFiles }
   { TUploadedFiles }
 
 
   TUploadedFiles = Class(TCollection)
   TUploadedFiles = Class(TCollection)
   private
   private
+    FRequest : TRequest; // May be nil
     function GetFile(Index : Integer): TUploadedFile;
     function GetFile(Index : Integer): TUploadedFile;
     procedure SetFile(Index : Integer; const AValue: TUploadedFile);
     procedure SetFile(Index : Integer; const AValue: TUploadedFile);
+  Protected
+    Function GetTempUploadFileName(Const AName, AFileName : String; ASize : Int64): String;
+    Procedure DeleteTempUploadedFiles; virtual;
   public
   public
     Function IndexOfFile(AName : String) : Integer;
     Function IndexOfFile(AName : String) : Integer;
     Function FileByName(AName : String) : TUploadedFile;
     Function FileByName(AName : String) : TUploadedFile;
     Function FindFile(AName : String) : TUploadedFile;
     Function FindFile(AName : String) : TUploadedFile;
     Property Files[Index : Integer] : TUploadedFile read GetFile Write SetFile; default;
     Property Files[Index : Integer] : TUploadedFile read GetFile Write SetFile; default;
   end;
   end;
+  TUploadedFilesClass = Class of TUploadedFiles;
+
+  { TMimeItem }
+  // Used to decode multipart encoded content
+
+  TMimeItem = Class(TCollectionItem)
+  private
+  protected
+    Function CreateUploadedFile(Files : TUploadedFiles) : TUploadedFile; virtual;
+    Function ProcessHeader(Const AHeader,AValue : String) : Boolean; virtual;
+    procedure SaveToFile(const AFileName: String); virtual;
+    function GetIsFile: Boolean; virtual;
+    // These must be implemented in descendents;
+    function GetDataSize: Int64; virtual; abstract;
+    function GetHeader(AIndex: Integer): String; virtual; abstract;
+    Procedure SetHeader(AIndex: Integer; Const AValue: String); virtual; abstract;
+  Public
+    Procedure Process(Stream : TStream); virtual; abstract;
+    Property Data : String index 0 Read GetHeader Write SetHeader;
+    Property Name : String index 1 Read GetHeader Write SetHeader;
+    Property Disposition : String index 2 Read GetHeader Write SetHeader;
+    Property FileName : String index 3 Read GetHeader Write SetHeader;
+    Property ContentType : String index 4 Read GetHeader Write SetHeader;
+    Property Description : String index 5 Read GetHeader Write SetHeader;
+    Property IsFile : Boolean  Read GetIsFile;
+    Property DataSize : Int64 Read GetDataSize;
+  end;
+  TMimeItemClass = Class of TMimeItem;
+  { TMimeItems }
+
+  TMimeItems = Class(TCollection)
+  private
+    function GetP(AIndex : Integer): TMimeItem;
+  Protected
+    Procedure CreateUploadFiles(Files : TUploadedFiles; Vars : TStrings); virtual;
+    procedure FormSplit(var Cnt: String; boundary: String); virtual;
+  Public
+    Property Parts[AIndex : Integer] : TMimeItem Read GetP; default;
+  end;
+  TMimeItemsClass = Class of TMimeItems;
 
 
   { THTTPHeader }
   { THTTPHeader }
 
 
@@ -286,6 +335,8 @@ type
   Protected
   Protected
     FContentRead : Boolean;
     FContentRead : Boolean;
     FContent : String;
     FContent : String;
+    Function CreateUploadedFiles : TUploadedFiles; virtual;
+    Function CreateMimeItems : TMimeItems; virtual;
     procedure HandleUnknownEncoding(Const AContentType : String;Stream : TStream); virtual;
     procedure HandleUnknownEncoding(Const AContentType : String;Stream : TStream); virtual;
     procedure ParseFirstHeaderLine(const line: String);override;
     procedure ParseFirstHeaderLine(const line: String);override;
     procedure ReadContent; virtual;
     procedure ReadContent; virtual;
@@ -295,7 +346,7 @@ type
     Procedure ProcessQueryString(Const FQueryString : String; SL:TStrings); virtual;
     Procedure ProcessQueryString(Const FQueryString : String; SL:TStrings); virtual;
     procedure ProcessURLEncoded(Stream : TStream;SL:TStrings); virtual;
     procedure ProcessURLEncoded(Stream : TStream;SL:TStrings); virtual;
     Function RequestUploadDir : String; virtual;
     Function RequestUploadDir : String; virtual;
-    Function  GetTempUploadFileName(Const AName, AFileName : String; ASize : Int64) : String; virtual;
+    Function GetTempUploadFileName(Const AName, AFileName : String; ASize : Int64) : String; virtual;
     Procedure DeleteTempUploadedFiles; virtual;
     Procedure DeleteTempUploadedFiles; virtual;
     Procedure InitRequestVars; virtual;
     Procedure InitRequestVars; virtual;
     Procedure InitPostVars; virtual;
     Procedure InitPostVars; virtual;
@@ -417,6 +468,13 @@ Function HTTPDecode(const AStr: String): String;
 Function HTTPEncode(const AStr: String): String;
 Function HTTPEncode(const AStr: String): String;
 Function IncludeHTTPPathDelimiter(const AStr: String): String;
 Function IncludeHTTPPathDelimiter(const AStr: String): String;
 
 
+Var
+  // Default classes used when instantiating the collections.
+  UploadedFilesClass : TUploadedFilesClass = TUploadedFiles;
+  UploadedFileClass : TUploadedFileClass = TUploadedFile;
+  MimeItemsClass : TMimeItemsClass = TMimeItems;
+  MimeItemClass : TMimeItemClass = nil;
+
 implementation
 implementation
 
 
 uses
 uses
@@ -556,6 +614,127 @@ begin
     Result:=Result+'/';
     Result:=Result+'/';
 end;
 end;
 
 
+{ -------------------------------------------------------------------
+  THTTPMimeItem, default used by TRequest to process Multipart-encoded data.
+  -------------------------------------------------------------------}
+
+Type
+  { THTTPMimeItem }
+
+  THTTPMimeItem = Class(TMimeItem)
+  private
+    FData : Array[0..5] of string;
+  protected
+    Procedure SetHeader(AIndex: Integer; Const AValue: String); override;
+    function GetDataSize: Int64; override;
+    function GetHeader(AIndex: Integer): String; override;
+    function GetIsFile: Boolean; override;
+  public
+    Procedure Process(Stream : TStream); override;
+  end;
+
+
+procedure THTTPMimeItem.SetHeader(AIndex: Integer; const AValue: String);
+begin
+  FData[AIndex]:=Avalue;
+end;
+
+function THTTPMimeItem.GetDataSize: int64;
+begin
+  Result:=Length(Data);
+end;
+
+function THTTPMimeItem.GetHeader(AIndex: Integer): String;
+begin
+  Result:=FData[AIndex];
+end;
+
+function THTTPMimeItem.GetIsFile: Boolean;
+begin
+  Result:=inherited GetIsFile;
+end;
+
+procedure THTTPMimeItem.Process(Stream: TStream);
+
+  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;
+  len : integer;
+  S : string;
+  D : String;
+
+begin
+  {$ifdef CGIDEBUG}SendMethodEnter('THTTPMimeItem.Process');{$ENDIF}
+  If Stream is TStringStream then
+    D:=TStringStream(Stream).Datastring
+  else
+    begin
+    SetLength(D,Stream.Size);
+    Stream.ReadBuffer(D[1],Stream.Size);
+    end;
+  Line:=GetLine(D);
+  While (Line<>'') do
+    begin
+    {$ifdef CGIDEBUG}SendDebug('Process data line: '+line);{$ENDIF}
+    S:=GetWord(Line);
+    While (S<>'') do
+      begin
+      ProcessHeader(lowercase(S),GetWord(Line));
+      S:=GetWord(Line);
+      end;
+    Line:=GetLine(D);
+    end;
+  // Now Data contains the rest of the data, plus a CR/LF. Strip the CR/LF
+  Len:=Length(D);
+  If (len>2) then
+    Data:=Copy(D,1,Len-2)
+  else
+    Data:='';
+  {$ifdef CGIDEBUG}SendMethodExit('THTTPMimeItem.Process');{$ENDIF}
+end;
 
 
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   THTTPHeader
   THTTPHeader
@@ -812,101 +991,127 @@ begin
     SetFieldValue(i,AValue);
     SetFieldValue(i,AValue);
 end;
 end;
 
 
-{ -------------------------------------------------------------------
-  TFormItem, used by TRequest to process Multipart-encoded data.
-  -------------------------------------------------------------------}
-
-Type
-  TFormItem = Class(TObject)
-    Disposition : String;
-    Name : String;
-    IsFile : Boolean;
-    FileName : String;
-    ContentType : String;
-    DLen : Integer;
-    Data : String;
-    Procedure Process;
-  end;
+{ ---------------------------------------------------------------------
+  TMimeItems
+  ---------------------------------------------------------------------}
 
 
-Procedure TFormItem.Process;
+function TMimeItems.GetP(AIndex : Integer): TMimeItem;
+begin
+  Result:=TMimeItem(Items[Aindex]);
+end;
 
 
-  Function GetLine(Var S : String) : String;
+procedure TMimeItems.CreateUploadFiles(Files: TUploadedFiles; Vars : TStrings);
 
 
-  Var
-    P : Integer;
+Var
+  I,j : Integer;
+  P : TMimeItem;
+  LFN,Name,Value : String;
+  U : TUploadedFile;
 
 
-  begin
-    P:=Pos(#13#10,S);
-    If (P<>0) then
+begin
+  For I:=Count-1 downto 0 do
+    begin
+    P:=GetP(i);
+    If (P.Name='') then
+      P.Name:='DummyFileItem'+IntToStr(i);
+      //Raise Exception.CreateFmt('Invalid multipart encoding: %s',[FI.Data]);
+{$ifdef CGIDEBUG}
+    With P Do
       begin
       begin
-      Result:=Copy(S,1,P-1);
-      Delete(S,1,P+1);
+      SendSeparator;
+      SendDebug  ('PMP item Name        : '+Name);
+      SendDebug  ('PMP item Disposition : '+Disposition);
+      SendDebug  ('PMP item FileName    : '+FileName);
+      SendBoolean('PMP item IsFile      : ',IsFile);
+      SendDebug  ('PMP item ContentType : '+ContentType);
+      SendDebug  ('PMP item Description : '+Description);
+      SendInteger('PMP item DLen        : ',Datasize);
+      SendDebug  ('PMP item Data        : '+Data);
+      end;
+{$endif CGIDEBUG}
+    Name:=P.Name;
+    If Not P.IsFile Then
+      Value:=P.Data
+    else
+      begin
+      Value:=P.FileName;
+      P.CreateUploadedFile(Files);
       end;
       end;
+    Vars.Add(Name+'='+Value)
+    end;
+end;
+
+function TMimeItem.GetIsFile: Boolean;
+begin
+  Result:=(FileName<>'');
+end;
+
+function TMimeItem.ProcessHeader(const AHeader, AValue: String): Boolean;
+
+begin
+  Result:=True;
+  Case AHeader of
+   'content-disposition' : Disposition:=Avalue;
+   'name': Name:=Avalue;
+   'filename' : FileName:=AValue;
+   'content-description' :  description:=AValue;
+   'content-type' : ContentType:=AValue;
+  else
+    Result:=False;
   end;
   end;
+end;
 
 
-  Function GetWord(Var S : String) : String;
+Procedure TMimeItem.SaveToFile(Const AFileName: String);
 
 
-  Var
-    I,len : Integer;
-    Quoted : Boolean;
-    C : Char;
+Var
+  D : String;
+  F : TFileStream;
 
 
-  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:='';
+begin
+  F:=TFileStream.Create(AFileName,fmCreate);
+  Try
+    D:=Data;
+    F.Write(D[1],DataSize);
+  finally
+    F.Free;
   end;
   end;
+end;
+
+function TMimeItem.CreateUploadedFile(Files: TUploadedFiles): TUploadedFile;
 
 
 Var
 Var
-  Line : String;
-  len : integer;
-  S : string;
+  J : Int64;
+  D,LFN : String;
 
 
 begin
 begin
-  Line:=GetLine(Data);
-  While (Line<>'') do
+  Result:=Nil;
+  D:=Data;
+  J:=DataSize;
+  if (J=0){zero lenght file} or
+     ((J=2)and (D=#13#10)){empty files come as a simple empty line} then
+    LFN:='' //No tmp file will be created for empty files
+  else
     begin
     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);
+    LFN:=Files.GetTempUploadFileName(Name,FileName,J);
+    SaveToFile(LFN);
     end;
     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);
+  if (LFN<>'') then
+   begin
+   Result:=Files.Add as TUploadedFile;
+   with Result do
+     begin
+     FieldName:=Self.Name;
+     FileName:=Self.FileName;
+     ContentType:=Self.ContentType;
+     Disposition:=Self.Disposition;
+     Size:=Self.Datasize;
+     LocalFileName:=LFN;
+     Description:=Self.Description;
+     end;
+   end;
 end;
 end;
 
 
+
 {
 {
   This needs MASSIVE improvements for large files.
   This needs MASSIVE improvements for large files.
   Best would be to do this directly from the input stream
   Best would be to do this directly from the input stream
@@ -914,34 +1119,41 @@ end;
   certain size is reached.)
   certain size is reached.)
 }
 }
 
 
-procedure FormSplit(var Cnt : String; boundary: String; List : TList);
+procedure TMimeItems.FormSplit(var Cnt : String; boundary: String);
 
 
 // Splits the form into items
 // Splits the form into items
 var
 var
   Sep : string;
   Sep : string;
   Clen,slen, p:longint;
   Clen,slen, p:longint;
-  FI : TFormItem;
+  FI : TMimeItem;
+  S : TStringStream;
 
 
 begin
 begin
+  {$ifdef CGIDEBUG}SendMethodEnter('TMimeItems.FormSplit');{$ENDIF}
   Sep:='--'+boundary+#13+#10;
   Sep:='--'+boundary+#13+#10;
   Slen:=length(Sep);
   Slen:=length(Sep);
   CLen:=Pos('--'+Boundary+'--',Cnt);
   CLen:=Pos('--'+Boundary+'--',Cnt);
   // Cut last marker
   // Cut last marker
   Cnt:=Copy(Cnt,1,Clen-1);
   Cnt:=Copy(Cnt,1,Clen-1);
   // Cut first marker
   // Cut first marker
-  Delete(Cnt,1,Slen);
+  system.Delete(Cnt,1,Slen);
   Clen:=Length(Cnt);
   Clen:=Length(Cnt);
   While Clen>0 do
   While Clen>0 do
     begin
     begin
-    Fi:=TFormItem.Create;
-    List.Add(Fi);
     P:=pos(Sep,Cnt);
     P:=pos(Sep,Cnt);
     If (P=0) then
     If (P=0) then
       P:=CLen+1;
       P:=CLen+1;
-    FI.Data:=Copy(Cnt,1,P-1);
-    delete(Cnt,1,P+SLen-1);
+    S:=TStringStream.Create(Copy(Cnt,1,P-1));
+    try
+      FI:=Add as TMimeItem;
+      FI.Process(S)
+    finally
+      S.Free;
+    end;
+    system.delete(Cnt,1,P+SLen-1);
     CLen:=Length(Cnt);
     CLen:=Length(Cnt);
     end;
     end;
+  {$ifdef CGIDEBUG}SendMethodExit('TMimeItems.FormSplit');{$ENDIF}
 end;
 end;
 
 
 { -------------------------------------------------------------------
 { -------------------------------------------------------------------
@@ -952,13 +1164,45 @@ constructor TRequest.create;
 begin
 begin
   inherited create;
   inherited create;
   FHandleGetOnPost:=True;
   FHandleGetOnPost:=True;
-  FFiles:=TUploadedFiles.Create(TUPloadedFile);
+  FFiles:=CreateUploadedFiles;
+  FFiles.FRequest:=Self;
   FLocalPathPrefix:='-';
   FLocalPathPrefix:='-';
 end;
 end;
 
 
+Function  TRequest.CreateUploadedFiles : TUploadedFiles;
+
+Var
+  CC : TUploadedFilesClass;
+  CI : TUploadedFileClass;
+
+begin
+  CC:=UploadedFilesClass;
+  CI:=UploadedFileClass;
+  if (CC=Nil) then
+    CC:=TUploadedFiles;
+  if (CI=Nil) then
+    CI:=TUploadedFile;
+  Result:=CC.Create(CI);
+end;
+
+function TRequest.CreateMimeItems: TMimeItems;
+
+Var
+  CC : TMimeItemsClass;
+  CI : TMimeItemClass;
+
+begin
+  CC:=MimeItemsClass;
+  CI:=MimeItemClass;
+  if (CC=Nil) then
+    CC:=TMimeItems;
+  if (CI=Nil) then
+    CI:=TMimeItem;
+  Result:=CC.Create(CI);
+end;
+
 destructor TRequest.destroy;
 destructor TRequest.destroy;
 begin
 begin
-  DeleteTempUploadedFiles;
   FreeAndNil(FFiles);
   FreeAndNil(FFiles);
   inherited destroy;
   inherited destroy;
 end;
 end;
@@ -1206,17 +1450,8 @@ begin
 end;
 end;
 
 
 Procedure TRequest.DeleteTempUploadedFiles;
 Procedure TRequest.DeleteTempUploadedFiles;
-var
-  i: Integer;
-  s: String;
 begin
 begin
-  //delete all temporary uploaded files created for this request if there is any
-  i := FFiles.Count;
-  if i > 0 then for i := i - 1 downto 0 do
-    begin
-    s := FFiles[i].LocalFileName;
-    if FileExists(s) then DeleteFile(s);
-    end;
+  FFiles.DeleteTempUploadedFiles;
 end;
 end;
 
 
 procedure TRequest.InitRequestVars;
 procedure TRequest.InitRequestVars;
@@ -1306,11 +1541,11 @@ end;
 Procedure TRequest.ProcessMultiPart(Stream : TStream; Const Boundary : String; SL:TStrings);
 Procedure TRequest.ProcessMultiPart(Stream : TStream; Const Boundary : String; SL:TStrings);
 
 
 Var
 Var
-  L : TList;
+  L : TMimeItems;
   B : String;
   B : String;
   I,J : Integer;
   I,J : Integer;
   S,FF,key, Value : String;
   S,FF,key, Value : String;
-  FI : TFormItem;
+  FI : TMimeItem;
   F : TStream;
   F : TStream;
 
 
 begin
 begin
@@ -1320,78 +1555,26 @@ begin
   I:=Length(B);
   I:=Length(B);
   If (I>0) and (B[1]='"') then
   If (I>0) and (B[1]='"') then
     B:=Copy(B,2,I-2);
     B:=Copy(B,2,I-2);
-  L:=TList.Create;
+  L:=CreateMimeItems;
   Try
   Try
-    SetLength(S,Stream.Size);
-    If Length(S)>0 then
-      if Stream is TCustomMemoryStream then
-        // Faster.
-        Move(TCustomMemoryStream(Stream).Memory^,S[1],Length(S))
-      else
-        begin
-        Stream.Read(S[1],Length(S));
-        Stream.Position:=0;
-        end;
-    FormSplit(S,B,L);
-    For I:=L.Count-1 downto 0 do
+    if Stream is TStringStream then
+      S:=TStringStream(Stream).DataString
+    else
       begin
       begin
-      FI:=TFormItem(L[i]);
-      FI.Process;
-      If (FI.Name='') then
-        Fi.Name:='DummyFileItem'+IntToStr(i);
-        //Raise Exception.CreateFmt('Invalid multipart encoding: %s',[FI.Data]);
-{$ifdef CGIDEBUG}
-      With FI Do
-        begin
-        SendSeparator;
-        SendDebug  ('PMP item Name        : '+Name);
-        SendDebug  ('PMP item Disposition : '+Disposition);
-        SendDebug  ('PMP item FileName    : '+FileName);
-        SendBoolean('PMP item IsFile      : ',IsFile);
-        SendDebug  ('PMP item ContentType : '+ContentType);
-        SendInteger('PMP item DLen        : ',DLen);
-        SendDebug  ('PMP item Data        : '+Data);
-        end;
-{$endif CGIDEBUG}
-      Key:=FI.Name;
-      If Not FI.IsFile Then
-        Value:=FI.Data
-      else
-        begin
-        Value:=FI.FileName;
-        J := Length(FI.Data);
-        if (J=0){zero lenght file} or
-           ((J=2)and(FI.Data=#13#10)){empty files come as a simple empty line} then
-          FF:='' //No tmp file will be created for empty files
+      SetLength(S,Stream.Size);
+      If Length(S)>0 then
+        if Stream is TCustomMemoryStream then
+          // Faster.
+          Move(TCustomMemoryStream(Stream).Memory^,S[1],Length(S))
         else
         else
           begin
           begin
-          FI.DLen:=J;
-          FF:=GetTempUploadFileName(FI.name,FI.FileName,J);
-          F:=TFileStream.Create(FF,fmCreate);
-          Try
-            F.Write(FI.Data[1],J);
-          finally
-            F.Free;
-          end;
-          end;
-        if (Value <> '') or (FI.DLen > 0)then{only non zero length files or files with non empty names will be considered}
-         With Files.Add as TUploadedFile do
-          begin
-          FieldName:=FI.Name;
-          FileName:=FI.FileName;
-          ContentType:=FI.ContentType;
-          Disposition:=FI.Disposition;
-          Size:=FI.DLen;
-          LocalFileName:=FF;
+          Stream.Read(S[1],Length(S));
+          Stream.Position:=0;
           end;
           end;
-        end;
-      FI.Free;
-      L[i]:=Nil;
-      SL.Add(Key+'='+Value)
       end;
       end;
+    L.FormSplit(S,B);
+    L.CreateUploadFiles(Files,SL);
   Finally
   Finally
-    For I:=0 to L.Count-1 do
-      TObject(L[i]).Free;
     L.Free;
     L.Free;
   end;
   end;
 {$ifdef CGIDEBUG}  SendMethodExit('ProcessMultiPart');{$endif CGIDEBUG}
 {$ifdef CGIDEBUG}  SendMethodExit('ProcessMultiPart');{$endif CGIDEBUG}
@@ -1425,6 +1608,15 @@ begin
   Items[Index]:=AValue;
   Items[Index]:=AValue;
 end;
 end;
 
 
+function TUploadedFiles.GetTempUploadFileName(const AName, AFileName: String;
+  ASize: Int64): String;
+begin
+  If Assigned(FRequest) then
+    Result:=FRequest.GetTempUploadFileName(AName,AFileName,ASize)
+  else
+    Result:=GetTempFileName;
+end;
+
 function TUploadedFiles.IndexOfFile(AName: String): Integer;
 function TUploadedFiles.IndexOfFile(AName: String): Integer;
 
 
 begin
 begin
@@ -1455,10 +1647,32 @@ begin
     Result:=Files[I];
     Result:=Files[I];
 end;
 end;
 
 
+Procedure TUPloadedFiles.DeleteTempUploadedFiles;
+
+var
+  i: Integer;
+
+begin
+  //delete all temporary uploaded files created for this request if there are any
+  for i := Count-1 downto 0 do
+    Files[i].DeleteTempUploadedFile;
+end;
+
+
 { ---------------------------------------------------------------------
 { ---------------------------------------------------------------------
   TUploadedFile
   TUploadedFile
   ---------------------------------------------------------------------}
   ---------------------------------------------------------------------}
 
 
+procedure TUploadedFile.DeleteTempUploadedFile;
+
+Var
+  s: String;
+
+begin
+  if (LocalFileName<>'') and FileExists(LocalFileName) then
+    DeleteFile(LocalFileName);
+end;
+
 function TUploadedFile.GetStream: TStream;
 function TUploadedFile.GetStream: TStream;
 begin
 begin
   If (FStream=Nil) then
   If (FStream=Nil) then
@@ -1660,8 +1874,9 @@ begin
 end;
 end;
 
 
 
 
-{ TCookie }
-
+{ ---------------------------------------------------------------------
+  TCookie
+  ---------------------------------------------------------------------}
 
 
 function TCookie.GetAsString: string;
 function TCookie.GetAsString: string;
 
 
@@ -1729,7 +1944,9 @@ begin
   FExpires := EncodeDate(1970, 1, 1);
   FExpires := EncodeDate(1970, 1, 1);
 end;
 end;
 
 
-{ TCookieCollection }
+{ ---------------------------------------------------------------------
+  TCookies
+  ---------------------------------------------------------------------}
 
 
 function TCookies.GetCookie(Index: Integer): TCookie;
 function TCookies.GetCookie(Index: Integer): TCookie;
 begin
 begin
@@ -1775,8 +1992,9 @@ begin
     Dec(Result);
     Dec(Result);
 end;
 end;
 
 
-{ TCustomSession }
-
+{ ---------------------------------------------------------------------
+  TCustomSession
+  ---------------------------------------------------------------------}
 
 
 procedure TCustomSession.SetSessionCookie(const AValue: String);
 procedure TCustomSession.SetSessionCookie(const AValue: String);
 begin
 begin
@@ -1816,5 +2034,6 @@ begin
   // Do nothing
   // Do nothing
 end;
 end;
 
 
-
+initialization
+  MimeItemClass:=THTTPMimeItem;
 end.
 end.

+ 2 - 2
packages/fcl-web/src/base/webutil.pp

@@ -121,13 +121,13 @@ begin
       Add('<H1>Uploaded files: ('+IntToStr(Files.Count)+') </H1>');
       Add('<H1>Uploaded files: ('+IntToStr(Files.Count)+') </H1>');
       Add('<TABLE BORDER="1">');
       Add('<TABLE BORDER="1">');
       Add('<TR><TD>Name</TD><TD>FileName</TD><TD>Size</TD>');
       Add('<TR><TD>Name</TD><TD>FileName</TD><TD>Size</TD>');
-      Add('<TD>Temp FileName</TD><TD>Disposition</TD><TD>Content-Type</TD></TR>');
+      Add('<TD>Temp FileName</TD><TD>Disposition</TD><TD>Content-Type</TD><TD>Description</TD></TR>');
       For I:=0 to Files.Count-1 do
       For I:=0 to Files.Count-1 do
         With Files[i] do
         With Files[i] do
           begin
           begin
           Add('<TR><TD>'+FieldName+'</TD><TD>'+FileName+'</TD>');
           Add('<TR><TD>'+FieldName+'</TD><TD>'+FileName+'</TD>');
           Add('<TD>'+IntToStr(Size)+'</TD><TD>'+LocalFileName+'</TD>');
           Add('<TD>'+IntToStr(Size)+'</TD><TD>'+LocalFileName+'</TD>');
-          Add('<TD>'+Disposition+'</TD><TD>'+ContentType+'</TD></TR>');
+          Add('<TD>'+Disposition+'</TD><TD>'+ContentType+'</TD><TD>'+Description+'</TD></TR>');
           end;
           end;
       Add('</TABLE><P>');
       Add('</TABLE><P>');
       end;
       end;