Search code examples
delphicurlindy

use TIdHTTP in Delphi to simulate curl command


I am trying to use IdHTTP to equivalence the following curl operation:

curl -X POST -F "message=@C:\Users\santon\Desktop\ESM_download\token.txt" "https://esm-db.eu/esmws/eventdata/1/query?eventid=IT-1997-0004&station=CLF&format=ascii" -o RecordFileName.zip

The curl command is used to download a file from the server that is then saved on the hard drive as DownloadedFileName.zip. An authorization is required through a token file on the hard drive called token.txt. The path of the token file is specified as a parameter of curl. The best I could do is the following code:

procedure TMainForm.HTTPGetFile;
var
  IdHTTP: TIdHTTP;
  Params: TIdMultipartFormDataStream;
  LHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
  try
    Params := TIdMultipartFormDataStream.Create;
    Params.AddFormField('message', '@"C:\Users\santon\Desktop\ESM_download\token.txt"');

      IdHTTP := TIdHTTP.Create(nil);
      LHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(self);
      LHandler.SSLOptions.Method := sslvTLSv1;
      try
        IdHTTP.IOHandler := LHandler;
        IdHTTP.Post('https://esm-db.eu/esmws/eventdata/1/query?eventid=IT-1997-0004&station=CLF&format=ascii',Params);
      finally
        IdHTTP.Free;
        LHandler.Free;
        Params.Free;
      end;
  except
    on E: Exception do
      ShowMessage('Error: '+E.ToString);
  end;
end;

But I keep on getting a HTTP/1.1 403 Forbidden error. Any ideas of what I am doing wrong?

Thanks in advance


Solution

  • You are not loading the token file into TIdMultiPartFormDataStream correctly.

    Per the curl documentation:

    https://curl.se/docs/manpage.html#-F

    -F, --form <name=content>

    (HTTP SMTP IMAP) For HTTP protocol family, this lets curl emulate a filled-in form in which a user has pressed the submit button. This causes curl to POST data using the Content-Type multipart/form-data according to RFC 2388.

    ...

    This enables uploading of binary files etc. To force the 'content' part to be a file, prefix the file name with an @ sign. To just get the content part from a file, prefix the file name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a file.

    ...

    Example: send an image to an HTTP server, where 'profile' is the name of the form-field to which the file portrait.jpg will be the input:

     curl -F [email protected] https://example.com/upload.cgi

    ...

    In your code, you are creating a text field whose content is the filename itself. You are not creating a file upload field whose content is the data from the file.

    Try this instead:

    procedure TMainForm.HTTPGetFile;
    var
      IdHTTP: TIdHTTP;
      Params: TIdMultipartFormDataStream;
      LHandler: TIdSSLIOHandlerSocketOpenSSL;
      LOutFile: TFileStream;
    begin
      try
        Params := TIdMultipartFormDataStream.Create;
        try
          Params.AddFile('message', 'C:\Users\santon\Desktop\ESM_download\token.txt');
    
          IdHTTP := TIdHTTP.Create(nil);
          try
            LHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP);
            LHandler.SSLOptions.Method := sslvTLSv1;
            IdHTTP.IOHandler := LHandler;
    
            LOutFile := TFileStream.Create('<path>\RecordFileName.zip', fmCreate);
            try
              IdHTTP.Post('https://esm-db.eu/esmws/eventdata/1/query?eventid=IT-1997-0004&station=CLF&format=ascii', Params, LOutFile);
            finally
              LOutFile.Free;
            end;
          finally
            IdHTTP.Free;
          end;
        finally
          Params.Free;
        end;
      except
        on E: Exception do
          ShowMessage('Error: ' + E.ToString);
      end;
    end;