{
  $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.3    2004.02.03 5:44:54 PM  czhower
  Name changes

  Rev 1.2    2/1/2004 3:33:48 AM  JPMugaas
  Reenabled.  Should work in DotNET.

  Rev 1.1    2003.10.12 3:36:26 PM  czhower
  todo item

  Rev 1.0    11/14/2002 02:13:44 PM  JPMugaas
}
{

  Implementation of the NTLM authentication as specified in
  http://www.innovation.ch/java/ntlm.html with some fixes

  Author: Doychin Bondzhev (doychin@dsoft-bg.com)
  Copyright: (c) Chad Z. Hower and The Winshoes Working Group.

  S.G. 12/7/2002: Moved the user query one step up: the domain name is required
                  to properly format the Type 1 message.
}


unit IdAuthenticationNTLM;

interface

{$i IdCompilerDefines.inc}

uses
  Classes, 
  IdAuthentication;

Type
  TIdNTLMAuthentication = class(TIdAuthentication)
  protected
    FNTLMInfo: String;
    FHost, FDomain, FUser: String;
    function DoNext: TIdAuthWhatsNext; override;
    function GetSteps: Integer; override;
    procedure SetUserName(const Value: String); override;
  public
    constructor Create; override;
    function Authentication: String; override;
    function KeepAlive: Boolean; override;
  end;

  // RLebeau 4/17/10: this forces C++Builder to link to this unit so
  // RegisterAuthenticationMethod can be called correctly at program startup...

  {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT}
    {$HPPEMIT LINKUNIT}
  {$ELSE}
    {$HPPEMIT '#pragma link "IdAuthenticationNTLM"'}
  {$ENDIF}

implementation

uses
  IdGlobal,
  IdGlobalProtocols,
  IdException,
  IdCoderMIME,
  IdResourceStringsOpenSSL,
  IdSSLOpenSSLHeaders,
  IdSSLOpenSSL,
  IdNTLM,
  SysUtils;

{ TIdNTLMAuthentication }

constructor TIdNTLMAuthentication.Create;
begin
  inherited Create;
  if not LoadOpenSSLLibrary then begin
    raise EIdOSSLCouldNotLoadSSLLibrary.Create(RSOSSLCouldNotLoadSSLLibrary);
  end;
end;

function TIdNTLMAuthentication.DoNext: TIdAuthWhatsNext;
begin
  Result := wnDoRequest;
  case FCurrentStep of
    0:
      begin
        if Length(UserName) > 0 then begin
          FCurrentStep := 1;
          Result := wnDoRequest;
        end else begin
          Result := wnAskTheProgram;
        end;
      end;
    1:
      begin
        FCurrentStep := 2;
        Result := wnDoRequest;
      end;
    2:
      begin
        FCurrentStep := 3;
        Result := wnDoRequest;
      end;
    3:
      begin
        Reset;
        Result := wnFail;
      end;
  end;
end;

function TIdNTLMAuthentication.Authentication: String;
var
  buf: TIdBytes;
  Type2: type_2_message_header;
  LDecoder: TIdDecoderMIME;
begin
  Result := '';    {do not localize}
  SetLength(buf, 0);

  case FCurrentStep of
    1:
      begin
        FHost := IndyComputerName;
        Result := 'NTLM ' + BuildType1Message(FDomain, FHost);    {do not localize}
      end;
    2:
      begin
        if Length(FNTLMInfo) = 0 then
        begin
          FNTLMInfo := ReadAuthInfo('NTLM');   {do not localize}
          Fetch(FNTLMInfo);
        end;

        if Length(FNTLMInfo) = 0 then
        begin
          Reset;
          Abort;
        end;

        LDecoder := TIdDecoderMIME.Create;
        try
          buf := LDecoder.DecodeBytes(FNTLMInfo);
        finally
          LDecoder.Free;
        end;
        BytesToRaw(buf, Type2, SizeOf(Type2));

        buf := RawToBytes(Type2.Nonce, SizeOf(Type2.Nonce));
        Result := 'NTLM ' + BuildType3Message(FDomain, FHost, FUser, Password, buf); {do not localize}

        FCurrentStep := 2;
      end;
  end;
end;

function TIdNTLMAuthentication.KeepAlive: Boolean;
begin
  Result := True;
end;

function TIdNTLMAuthentication.GetSteps: Integer;
begin
  Result := 3;
end;

procedure TIdNTLMAuthentication.SetUserName(const Value: String);
begin
  if Value <> Username then
  begin
    inherited SetUserName(Value);
    GetDomain(Username, FUser, FDomain);
  end;
end;

initialization
  RegisterAuthenticationMethod('NTLM', TIdNTLMAuthentication);    {do not localize}
finalization
  UnregisterAuthenticationMethod('NTLM');                         {do not localize}
end.
