home *** CD-ROM | disk | FTP | other *** search
/ Chip 2001 October / Chip_2001-10_cd1.bin / zkuste / delphi / nastroje / d3456 / KBMWABD.ZIP / WABD_HTMLRequest.pas < prev    next >
Pascal/Delphi Source File  |  2001-07-16  |  15KB  |  533 lines

  1. unit WABD_HTMLRequest;
  2.  
  3. interface
  4.  
  5. uses Classes, SysUtils, Windows, WABD_Request;
  6.  
  7. type
  8.    TWABD_CustomHTMLRequest = class(TWABD_CustomRequest)
  9.    protected
  10.       FMaxSize:integer;
  11.       FGotSize:integer;
  12.  
  13.       function GetMultipartChunk(Buffer:PChar; var BufSize:integer):boolean; virtual;
  14.    public
  15.       constructor Create(MaxSize:integer);
  16.       destructor Destroy; override;
  17.  
  18.       procedure ParseMultipart(Buffer:PChar; BufferSize:integer; Boundary:string; List:TStringList); virtual;
  19.       procedure ParseHeaders(Buffer:PChar; BufferSize:integer; List:TStringList);
  20.       procedure ParseURLEncoded(Buffer:PChar; Delimiter:char; List:TStringList);
  21.       function RemoveQuotes(AString:string):string; virtual;
  22.       procedure ParseCookies; virtual;
  23.    end;
  24.  
  25.    function  WABD_EncodeEscapes(Input: string): string;
  26.    procedure WABD_DecodeEscapes(var s: string);
  27.  
  28. implementation
  29.  
  30. const
  31.    INPUT_BUFFERSIZE=8192;
  32.    //                              0 1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
  33.    Hex2Dec:array[0..31] of byte = (0,10,11,12,13,14,15,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0);
  34.  
  35.  
  36. // Build and load a request from web server.
  37. constructor TWABD_CustomHTMLRequest.Create(MaxSize:integer);
  38. begin
  39.      inherited Create;
  40.      FMaxSize:=MaxSize;
  41.      FGotSize:=0;
  42. end;
  43.  
  44. destructor TWABD_CustomHTMLRequest.Destroy;
  45. begin
  46.      inherited;
  47. end;
  48.  
  49. function TWABD_CustomHTMLRequest.GetMultipartChunk(Buffer:PChar; var BufSize:integer):boolean;
  50. begin
  51.      Result:=false;
  52.      bufSize:=0;
  53. end;
  54.  
  55. // Basic coding/decoding methods.
  56. function WABD_EncodeEscapes(Input: string): string;
  57. var
  58.    i : integer;
  59.    t : string;
  60. begin
  61.      t:='';
  62.      for i:=1 to Length(Input) do
  63.      begin
  64.           if Input[i]=' ' then
  65.           begin
  66.                t:=t+'+';
  67.                continue;
  68.           end;
  69.           if not (Input[i] in ['a'..'z', 'A'..'Z', '0'..'9', '_']) then
  70.           begin
  71.                t:=t + Format('%%%0.2x', [Ord(Input[i])]);
  72.                continue;
  73.           end;
  74.           t:=t + Input[i];
  75.      end;
  76.      Result:=t;
  77. end;
  78.  
  79. procedure WABD_DecodeEscapes(var s: string);
  80. var
  81.    New    : string;
  82.    hexstr : string;
  83.    c      : byte;
  84.    i      : integer;
  85. begin
  86.      i:=1;
  87.      repeat
  88.            if s[i]='+' then
  89.               New:=New+' '
  90.            else if s[i]='%' then
  91.            begin
  92.                 hexstr:=Copy(s, i+1, 2);
  93.                 c:=StrToInt('$'+hexstr);
  94.                 New:=New + char(c);
  95.                 i:=i + 2;
  96.            end
  97.            else
  98.                New:=New + s[i];
  99.            i:=i + 1;
  100.      until i>Length(s);
  101.  
  102.      s:=New;
  103. end;
  104.  
  105. // Extract headers from buffer. Only look at the supplied buffer.
  106. procedure TWABD_CustomHTMLRequest.ParseHeaders(Buffer:PChar; BufferSize:integer; List:TStringList);
  107. var
  108.    s:string;
  109.    p:integer;
  110.    pCh:PChar;
  111.    pStartLine:PChar;
  112.    cnt:integer;
  113. begin
  114.      // Deciphre header part.
  115.      if Buffer=nil then exit;
  116.      cnt:=BufferSize;
  117.      pStartLine:=Buffer;
  118.      pCh:=pStartLine;
  119.      while true do
  120.      begin
  121.           // Check if end of line.
  122.           if pCh^ in [#10,#13,#0] then
  123.           begin
  124.                // Check if empty line. Then end of header list.
  125.                if pCh=pStartLine then break;
  126.  
  127.                // Get string.
  128.                SetString(s,pStartLine,pCh-pStartLine);
  129.                p:=pos(':',s);
  130.                if p>0 then s[p]:='=';
  131.                List.Add(s);
  132.  
  133.                // No more data to parse.
  134.                if pCh^=#0 then break;
  135.  
  136.                // Prepare for next char.
  137.                inc(pCh);
  138.                dec(cnt);
  139.  
  140.                // Skip optional #10
  141.                if (cnt>0) and (pCh^ = #10) then
  142.                begin
  143.                     inc(pCh);
  144.                     dec(cnt);
  145.                end;
  146.                pStartLine:=pCh;
  147.                continue;
  148.           end;
  149.           inc(pCh);
  150.           dec(cnt);
  151.      end;
  152. end;
  153.  
  154. // Parse URLEncoded null terminated buffer.
  155. procedure TWABD_CustomHTMLRequest.ParseURLEncoded(Buffer:PChar; Delimiter:char; List:TStringList);
  156. var
  157.    ch:Char;
  158.    pCh:PChar;
  159.    pBuf:PChar;
  160.    lBuf:integer;
  161.    pStart:PChar;
  162.    s:string;
  163.    buf:array [0..8191] of char;
  164. begin
  165.      if Buffer=nil then exit;
  166.      pStart:=Buffer;
  167.      pCh:=pStart;
  168.      pBuf:=buf;
  169.      lBuf:=sizeof(buf);
  170.      if pCh^ = #0 then exit;
  171.      while (lBuf>0) do
  172.      begin
  173.           // Decode space.
  174.           if pCh^ = '+' then
  175.           begin
  176.                pBuf^:=' ';
  177.                inc(pBuf);
  178.                dec(lBuf);
  179.           end
  180.  
  181.           // Decode Hex.
  182.           else if pCh^='%' then
  183.           begin
  184.                inc(pCh);
  185.                ch:=Char(Hex2Dec[ord(pCh^) and $1F] shl 4);
  186.                inc(pCh);
  187.                inc(ch,Hex2Dec[ord(pCh^) and $1F]);
  188.                pBuf^:=ch;
  189.                inc(pBuf);
  190.                dec(lBuf);
  191.           end
  192.  
  193.           // Field seperator.
  194.           else if (pCh^ = Delimiter) or (pCh^ = #0) then
  195.           begin
  196.                SetString(s,buf,pBuf-buf);
  197.                List.Add(trim(s));
  198.                if pCh^=#0 then break;
  199.                pBuf:=buf;
  200.                lBuf:=sizeof(buf);
  201.           end
  202.           else
  203.           begin
  204.                pBuf^:=pCh^;
  205.                inc(pBuf);
  206.                dec(lBuf);
  207.           end;
  208.           inc(pCh);
  209.      end;
  210. end;
  211.  
  212. function TWABD_CustomHTMLRequest.RemoveQuotes(AString:string):string;
  213. begin
  214.      Result:=Trim(AString);
  215.      if length(Result)=0 then exit;
  216.      if Result[1]<>'"' then exit;
  217.      Result:=copy(Result,2,length(Result)-2);
  218. end;
  219.  
  220. procedure TWABD_CustomHTMLRequest.ParseMultipart(Buffer:PChar; BufferSize:integer; Boundary:string; List:TStringList);
  221. var
  222.    Stream:TStream;
  223.    pBuffer:PChar;
  224.    Name,LocalFileName,Entity,Line,Mime:string;
  225.    Fn:string;
  226.    lst:TStringList;
  227.    DoStore:boolean;
  228.    Drop:boolean;
  229.    n:integer;
  230.    InputBuf:array [0..8191] of char;
  231.  
  232.    function GetChunk:boolean;
  233.    begin
  234.         // Check if should be more.
  235.         if FGotSize>=Size then
  236.         begin
  237.              Result:=false;
  238.              exit;
  239.         end;
  240.  
  241.         // Get next chunk.
  242.         BufferSize:=sizeof(InputBuf);
  243.  
  244.         // If nothing more, break.
  245.         Result:=GetMultipartChunk(InputBuf,BufferSize) and (BufferSize>0);
  246.         if Result then FGotSize:=FGotSize+BufferSize;
  247.         pBuffer:=InputBuf;
  248.    end;
  249.  
  250.    function GetLine:string;
  251.    var
  252.       GotEOL,ExitLoop:boolean;
  253.    begin
  254.         Result:='';
  255.         GotEOL:=false;
  256.         ExitLoop:=false;
  257.         while not ExitLoop do
  258.         begin
  259.              // Check if data in buffer.
  260.              if (BufferSize<=0) and not GetChunk then break;
  261.  
  262.              // Look for #10 or #13 (EOL).
  263.              while (BufferSize>0) do
  264.              begin
  265.                   if pBuffer^ in [#10,#13] then
  266.                   begin
  267.                        if GotEOL then
  268.                        begin
  269.                             ExitLoop:=true;
  270.                             inc(pBuffer);
  271.                             dec(BufferSize);
  272.                             break;
  273.                        end;
  274.                        GotEOL:=true;
  275.                   end
  276.                   else
  277.                   begin
  278.                        if GotEOL then
  279.                        begin
  280.                             ExitLoop:=true;
  281.                             break;
  282.                        end;
  283.                        Result:=Result+pBuffer^;
  284.                   end;
  285.                   inc(pBuffer);
  286.                   dec(BufferSize);
  287.              end;
  288.         end;
  289.    end;
  290.  
  291.    function SearchBoundary(Store:boolean):boolean;
  292.    var
  293.       ch:char;
  294.       pStartBuffer,pBoundaryCandidate:PChar;
  295.       pBoundary:PChar;
  296.       lBoundary:integer;
  297.       lMatch:integer;
  298.       found:boolean;
  299.       p:PChar;
  300.    begin
  301.         pStartBuffer:=pBuffer;
  302.         pBoundaryCandidate:=nil;
  303.         lBoundary:=length(Boundary);
  304.         pBoundary:=PChar(Boundary);
  305.         lMatch:=0;
  306.         found:=false;
  307.         while not found do
  308.         begin
  309.              // Check if data in buffer.
  310.              if (BufferSize<=0) then
  311.              begin
  312.                   // Check if to store data until just before half processed boundary match.
  313.                   if Store then
  314.                   begin
  315.                      if pBoundaryCandidate<>nil then
  316.                         Stream.Write(pStartBuffer^,pBoundaryCandidate-pStartBuffer)
  317.                      else
  318.                         Stream.Write(pStartBuffer^,pBuffer-pStartBuffer);
  319.                   end;
  320.                   if not GetChunk then break;
  321.                   pStartBuffer:=pBuffer;
  322.                   pBoundaryCandidate:=nil;
  323.              end;
  324.  
  325.              // Check for boundary.
  326.              ch:=pBuffer^;
  327.              if pBoundary^ = ch then
  328.              begin
  329.                   if lMatch=0 then pBoundaryCandidate:=pBuffer;
  330.                   inc(lMatch);
  331.                   inc(pBoundary);
  332.                   if (lMatch=lBoundary) then
  333.                   begin
  334.                        if Store then
  335.                        begin
  336.                             // Dont write ending #10#13 pair.
  337.                             p:=pBoundaryCandidate;
  338.                             if (p>pStartBuffer) then
  339.                             begin
  340.                                  // Check if previous 2 chars was #10 or #13.
  341.                                  dec(p);
  342.                                  if not (p^ in [#10,#13]) then inc(p)
  343.                                  else
  344.                                  begin
  345.                                       dec(p);
  346.                                       if p>pStartBuffer then
  347.                                          if not (p^ in [#10,#13]) then inc(p);
  348.                                  end;
  349.                             end;
  350.                             n:=p-pStartBuffer;
  351.                             if n>0 then
  352.                                Stream.Write(pStartBuffer^,n);
  353.                        end;
  354.  
  355.                        found:=true;
  356.                   end;
  357.              end
  358.              else
  359.              begin
  360.                   // Check if not matching boundary candidate to be written.
  361.                   if lMatch>0 then
  362.                   begin
  363.                        if Store then
  364.                        begin
  365.                             Stream.Write(pStartBuffer^,pBoundaryCandidate-pStartBuffer);
  366.                             Stream.Write(PChar(Boundary)^,lMatch);
  367.                        end;
  368.                        pStartBuffer:=pBuffer;
  369.                        lMatch:=0;
  370.                        pBoundaryCandidate:=nil;
  371.                   end;
  372.                   pBoundary:=PChar(Boundary);
  373.              end;
  374.              inc(pBuffer);
  375.              Dec(BufferSize);
  376.         end;
  377.         Result:=found;
  378.    end;
  379.  
  380. begin
  381.      // Check if to request chunk right away. Otherwise start with what we've got.
  382.      if Buffer=nil then exit;
  383.      pBuffer:=Buffer;
  384.      FGotSize:=BufferSize;
  385.      if (pBuffer=nil) or (BufferSize=0) then
  386.         if not GetChunk then exit;
  387.      Drop:=true;
  388.  
  389.      // Prepare entity line list.
  390.      lst:=TStringList.Create;
  391.      try
  392.  
  393.         // While data available loop.
  394.         while true do
  395.         begin
  396.              // Boundary end mark or ending CR/LF.
  397.              if not SearchBoundary(Drop) then break;
  398.              if (BufferSize>=2) then
  399.              begin
  400.                   if (pBuffer[0]='-') and (pBuffer[1]='-') then break;
  401.              end;
  402.  
  403.              // Get empty line.
  404.              Line:=GetLine;
  405.  
  406.              // Get entity line.
  407.              Entity:=GetLine;
  408.  
  409.              // Parse line.
  410.              lst.Clear;
  411.              ParseURLEncoded(PChar(Entity),';',lst);
  412.  
  413.              // Get entity name.
  414.              Name:=RemoveQuotes(lst.Values['name']);
  415.  
  416.              // If contains filename, save data.
  417.              if pos('filename=',lowercase(Entity))>0 then
  418.              begin
  419.                   Fn:=RemoveQuotes(lst.Values['filename']);
  420.                   DoStore:=Fn<>'';
  421.  
  422.                   // Local filename.
  423.                   if DoStore then LocalFileName:='WABD_'+FormatDateTime('yyyymmdd_hhnnss',now)+'_'+ExtractFileName(Fn)
  424.                   else LocalFileName:='';
  425.  
  426.                   // Get MIME type for file.
  427.                   Mime:=RemoveQuotes(GetLine);
  428.                   n:=pos(' ',Mime);
  429.                   if n>0 then Mime:=Copy(Mime,n+1,length(Mime));
  430.  
  431.                   // Get empty line.
  432.                   Line:=GetLine;
  433.  
  434.                   List.Add(Name+'='+LocalFileName+'; Filename='+Fn+'; Mime='+Mime);
  435.  
  436.                   // Get file contents.
  437.                   if DoStore then
  438.                   begin
  439.                        Stream:=TFileStream.Create(LocalFileName,fmCreate);
  440.                        try
  441.                           Stream.Position:=0;
  442.                           SearchBoundary(true);
  443.                        finally
  444.                           Stream.Free;
  445.                        end;
  446.                   end
  447.                   else
  448.                       Drop:=false;
  449.              end
  450.              else
  451.              begin
  452.                   // Get empty line.
  453.                   Line:=GetLine;
  454.  
  455.                   // Get Value line.
  456.                   Line:=GetLine;
  457.                   List.Add(Name+'='+Line);
  458.  
  459.                   // Search boundary.
  460.                   Drop:=false;
  461.              end;
  462.         end;
  463.      finally
  464.         lst.Free;
  465.      end;
  466. end;
  467.  
  468. procedure TWABD_CustomHTMLRequest.ParseCookies;
  469. var
  470.    ch:Char;
  471.    pCh:PChar;
  472.    pBuf:PChar;
  473.    pStart:PChar;
  474.    lBuf:integer;
  475.    s,sCookies:string;
  476.    p:integer;
  477.    InputBuf:array [0..8191] of char;
  478. begin
  479.      sCookies:=Headers.Values['COOKIE'];
  480.      if sCookies='' then exit;
  481.  
  482.      // Parse cookie string.
  483.      pStart:=PChar(sCookies);
  484.      pCh:=pStart;
  485.      pBuf:=InputBuf;
  486.      lBuf:=sizeof(InputBuf);
  487.      while (lBuf>0) do
  488.      begin
  489.           // Decode space.
  490.           if pCh^ = '+' then
  491.           begin
  492.                pBuf^:=' ';
  493.                inc(pBuf);
  494.                dec(lBuf);
  495.           end
  496.  
  497.           // Decode Hex.
  498.           else if pCh^='%' then
  499.           begin
  500.                inc(pCh);
  501.                ch:=Char(Hex2Dec[ord(pCh^) and $1F] shl 4);
  502.                inc(pCh);
  503.                inc(ch,Hex2Dec[ord(pCh^) and $1F]);
  504.                pBuf^:=ch;
  505.                inc(pBuf);
  506.                dec(lBuf);
  507.           end
  508.  
  509.           // Field seperator.
  510.           else if (pCh^ = ';') or (pCh^ = #0) then
  511.           begin
  512.                SetString(s,InputBuf,pBuf-InputBuf);
  513.                p:=Pos('=',s);
  514.                if p>0 then
  515.                   Cookies.Add(trim(copy(s,1,p-1)),copy(s,p+1,length(s)))
  516.                else
  517.                    Cookies.Add(trim(s),'');
  518.                pBuf:=InputBuf;
  519.                lBuf:=sizeof(InputBuf);
  520.                if pCh^=#0 then break;
  521.           end
  522.           else
  523.           begin
  524.                pBuf^:=pCh^;
  525.                inc(pBuf);
  526.                dec(lBuf);
  527.           end;
  528.           inc(pCh);
  529.      end;
  530. end;
  531.  
  532. end.
  533.