program httpget;

{$mode objfpc}
{$h+}

uses sysutils, ssockets, gnutls, uriparser;

Const
  logLevel = 0; // Set to positive value to enable logging.
  // Correct this for your system.
  DefaultCerts : PAnsiChar =  '/etc/ssl/certs/ca-certificates.crt';

  MAX_BUF = 1024*256;
  MSG = 'GET %s HTTP/1.0'#13#10'Host: %s'#13#10#13#10;


Procedure MyLogFunc(level : longint; msg : PAnsiChar); cdecl;
begin
  writeln(StdErr,'Log[',Level:2,']: ',msg);
end;

Var
  sock : TInetSocket;
  ret : integer;
  session : tgnutls_session_t;
  buf : Array[0..MAX_BUF] of AnsiChar;
  cred : tgnutls_certificate_credentials_t;
  errptr,desc : PAnsiChar;
  FN, URL,S, HostName : String;
  port : word;
  A : AnsiString;
  uri : TURI;

begin
  if paramCount<1 then
    begin
    writeln('Usage : ',ExtractFileName(ParamStr(0)),' url');
    Halt(1);
    end;
  url:=ParamStr(1);
  uri:=ParseURI(URL,'https',443);
  hostname:=uri.Host;
  if uri.Protocol<>'https' then
    begin
    Writeln('Only https supported');
    Halt(1);
    end;
  Port:=URI.Port;
  FN:=uri.Path+URI.Document;
  if (URI.Params<>'') then
    FN:=FN+'?'+URI.Params;
  if FN='' then FN:='/';
  LoadGNutls();
  gnutls_global_init();
  ret := gnutls_certificate_allocate_credentials (@cred);
  if (ret <> GNUTLS_E_SUCCESS) then
    begin
    writeln(stderr, 'error: gnutls_certificate_allocate_credentials: ', gnutls_strerror(ret));
    halt(1);
    end;
  ret := gnutls_certificate_set_x509_trust_file(cred, defaultcerts, GNUTLS_X509_FMT_PEM);
  if (ret = 0) then
    begin
    writeln(stderr, 'error: no certificates found in:', defaultcerts);
    halt(1);
    end
  else if (ret < 0) then
    begin
    writeln(stderr, 'error: gnutls_certificate_set_x509_trust_files(',defaultcerts,'): ',
	     gnutls_strerror(ret));
    halt(1);
    end;
  if (logLevel>0) then
    begin
    gnutls_global_set_log_function(@MyLogFunc);
    gnutls_global_set_log_level(logLevel);
    end;
  gnutls_init(@session, GNUTLS_CLIENT);
  // We can also use
  //  ret:=gnutls_set_default_priority(session);
  ret := gnutls_priority_set_direct(session, 'NORMAL', @errptr);
  if (ret <> GNUTLS_E_SUCCESS) then
    begin
    writeln(stderr, 'error: gnutls_priority_set_direct: ',gnutls_strerror(ret) , ' error: at: ', errptr);
    halt(1);
    end;

  ret := gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred);
  if (ret <> GNUTLS_E_SUCCESS) then
    begin
    writeln(stderr, 'error: gnutls_credentials_set: ', gnutls_strerror(ret));
    halt(1);
    end;

  Sock:=TINetSocket.Create(HostName,Port);
  gnutls_transport_set_int(session, Sock.Handle);
  gnutls_handshake_set_timeout(session,GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
  A:=HostName;
  ret := gnutls_server_name_set(session, GNUTLS_NAME_DNS,PAnsiChar(A), length(A));
  if (ret <> GNUTLS_E_SUCCESS) then
    begin
    writeln(stderr, 'error: gnutls_server_name_set: ', gnutls_strerror(ret));
    halt(1);
    end;
  
  gnutls_session_set_verify_cert(session,PAnsiChar(A),0);
  
  Repeat
    ret:=gnutls_handshake(session);
    if Ret<>GNUTLS_E_SUCCESS then
      Case ret of
        GNUTLS_E_AGAIN : Writeln(StdErr,'Handshake again');
        GNUTLS_E_INTERRUPTED : Writeln(StdErr,'Handshake interrupted');
      else
        Writeln(StdErr,'Error ',ret,' received, fatal : ',gnutls_error_is_fatal(ret));
      end;
  until (ret>=0)  or (gnutls_error_is_fatal(ret) <> 0);
  if (ret < 0) then
    begin
    writeln(stderr, '*** Handshake failed');
    gnutls_perror(ret);
    end
  else  
    begin
    desc := gnutls_session_get_desc(session);
    writeln(StdErr,'- Session info: ', desc);
//    gnutls_free(desc);
    end;
  S:=Format(Msg,[FN,HostName]);
  Writeln(StdErr,'Sending request : ',S);
  A:=S;
  gnutls_record_send(session, PAnsiChar(A), length(A));
  repeat
    ret := gnutls_record_recv(session, @buf, MAX_BUF);
    if (ret=0) then
      writeln(StdErr,'- Peer has closed the TLS connection\n')
    else if ((ret < 0) and (gnutls_error_is_fatal(ret) = 0)) then
      writeln(stderr, '*** Warning: ', gnutls_strerror(ret))
    else if (ret < 0) and (ret<>GNUTLS_E_PREMATURE_TERMINATION) then
      Writeln(stderr, '*** Error: ', ret, ' : ',gnutls_strerror(ret))
    else if (ret > 0) then
        begin
        writeln(StdErr,'- Received ',ret,' bytes: ');
        SetLength(S,Ret);
        Move(Buf[0],S[1],Ret);
        Write(S);
        end;
  until (ret<=0) and Not ((ret=GNUTLS_E_INTERRUPTED) or (Ret=GNUTLS_E_AGAIN));
  Writeln;
  gnutls_bye(session, GNUTLS_SHUT_RDWR);
  Sock.Free;
  gnutls_deinit(session);
  gnutls_certificate_free_credentials(cred);
  gnutls_global_deinit();
  FreeGnuTLS;
end.
