{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}
{
  Rev 1.6    11/12/2004 11:31:06 AM  JPMugaas
  IPv6 expansions.

  Rev 1.5    2004.02.03 5:45:00 PM  czhower
  Name changes

  Rev 1.4    10/19/2003 11:48:12 AM  DSiders
  Added localization comments.

  Rev 1.3    4/5/2003 7:27:48 PM  BGooijen
  Checks for errors, added authorisation

  Rev 1.2    4/1/2003 4:14:22 PM  BGooijen
  Fixed + cleaned up

  Rev 1.1    2/24/2003 08:20:46 PM  JPMugaas
  Now should compile with new code.

  Rev 1.0    11/14/2002 02:16:10 PM  JPMugaas
}

unit IdConnectThroughHttpProxy;

{
  implements:
  http://www.web-cache.com/Writings/Internet-Drafts/draft-luotonen-web-proxy-tunneling-01.txt
}

interface
{$i IdCompilerDefines.inc}

uses
  IdCustomTransparentProxy, IdGlobal, IdIOHandler;

type
  TIdConnectThroughHttpProxy = class(TIdCustomTransparentProxy)
  private
    FAuthorizationRequired: Boolean;
  protected
    FEnabled: Boolean;
    function  GetEnabled: Boolean; override;
    procedure SetEnabled(AValue: Boolean); override;
    procedure MakeConnection(AIOHandler: TIdIOHandler; const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
    procedure DoMakeConnection(AIOHandler: TIdIOHandler; const AHost: string;
      const APort: TIdPort; const ALogin:boolean; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);virtual;
  public
  published
    property Enabled;
    property Host;
    property Port;
    property ChainedProxy;
    property Username;
    property Password;
  end;

implementation

uses
  IdCoderMIME, IdExceptionCore, IdHeaderList, IdGlobalProtocols, SysUtils;

{ TIdConnectThroughHttpProxy }

function TIdConnectThroughHttpProxy.GetEnabled: Boolean;
begin
  Result := FEnabled;
end;

procedure TIdConnectThroughHttpProxy.DoMakeConnection(AIOHandler: TIdIOHandler;
  const AHost: string; const APort: TIdPort; const ALogin: Boolean; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
var
  LStatus: string;
  LResponseCode: Integer;
  LHeaders: TIdHeaderList;
  LContentLength: Int64;
  LEncoder: TIdEncoderMIME;
begin
  LHeaders := TIdHeaderList.Create(QuoteHTTP);
  try
    AIOHandler.WriteLn(IndyFormat('CONNECT %s:%d HTTP/1.0', [AHost,APort])); {do not localize}
    if ALogin then begin
      LEncoder := TIdEncoderMIME.Create;
      try
        AIOHandler.WriteLn('Proxy-Authorization: Basic ' + LEncoder.Encode(Username + ':' + Password));  {do not localize}
      finally
        LEncoder.Free;
      end;
    end;
    AIOHandler.WriteLn;
    LStatus := AIOHandler.ReadLn;
    if LStatus <> '' then begin // if empty response then we assume it succeeded
      AIOHandler.Capture(LHeaders, '', False);
      // TODO: support chunked replies...
      LContentLength := IndyStrToInt64(LHeaders.Values['Content-Length'], -1); {do not localize}
      if LContentLength > 0 then begin
        AIOHandler.Discard(LContentLength);
      end;
      Fetch(LStatus);// to remove the http/1.0 or http/1.1
      LResponseCode := IndyStrToInt(Fetch(LStatus, ' ', False), 200); // if invalid response then we assume it succeeded
      if (LResponseCode = 407) and (not ALogin) and ((Length(Username) > 0) or (Length(Password) > 0)) then begin // authorization required
        if TextIsSame(LHeaders.Values['Proxy-Connection'], 'close') or {do not localize}
           TextIsSame(LHeaders.Values['Connection'], 'close') then begin {do not localize}
          // need to reconnect before trying again with login
          AIOHandler.Close;
          FAuthorizationRequired := True;
          try
            AIOHandler.Open;
          finally
            FAuthorizationRequired := False;
          end;
        end else begin
          // still connected so try again with login
          DoMakeConnection(AIOHandler, AHost, APort, True);
        end;
      end
      else if not (LResponseCode in [200]) then begin // maybe more responsecodes to add
        raise EIdHttpProxyError.Create(LStatus);//BGO: TODO: maybe split into more exceptions?
      end;
    end;
  finally
    LHeaders.Free;
  end;
end;

procedure TIdConnectThroughHttpProxy.MakeConnection(AIOHandler: TIdIOHandler;
  const AHost: string; const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
begin
  DoMakeConnection(AIOHandler, AHost, APort, FAuthorizationRequired);
end;

procedure TIdConnectThroughHttpProxy.SetEnabled(AValue: Boolean);
begin
  FEnabled := AValue;
end;

end.
