unit OAuth;
    interface
    uses
      Classes, SysUtils, IdURI, Windows;
    type
      EOAuthException = class(Exception);
      TOAuthConsumer = class;
      TOAuthToken = class;
      TOAuthRequest = class;
      TOAuthSignatureMethod = class;
      TOAuthSignatureMethod_HMAC_SHA1 = class;
      TOAuthSignatureMethod_PLAINTEXT = class;
      TOAuthConsumer = class
      private
        FKey: string;
        FSecret: string;
        FCallback_URL: string;
        procedure SetKey(const Value: string);
        procedure SetSecret(const Value: string);
        procedure SetCallback_URL(const Value: string);
      public
        constructor Create(Key, Secret: string); overload;
        constructor Create(Key, Secret: string; Callback_URL: string); overload;
        property Key: string read FKey write SetKey;
        property Secret: string read FSecret write SetSecret;
        property Callback_URL: string read Fcallback_URL write SetCallback_URL;
      end;
      TOAuthToken = class
      private
        FKey: string;
        FSecret: string;
        procedure SetKey(const Value: string);
        procedure SetSecret(const Value: string);
      public
        constructor Create(Key, Secret: string);
        function AsString: string; virtual;
        property Key: string read FKey write SetKey;
        property Secret: string read FSecret write SetSecret;
      end;
      TOAuthRequest = class
      private
        FParameters: TStringList;
        FHTTPURL: string;
        FScheme: string;
        FHost: string;
        FPath: string;
        FFields: string;
        FVersion: string;
        FBaseString: string;
        FGetString: string;
        procedure SetHTTPURL(const Value: string);
        procedure SetBaseString(const Value: string);
        procedure SetVersion(const Value: string);
        function GenerateNonce: string;
        function GenerateTimeStamp: string;
        function GetSignableParameters: string;
      public
        constructor Create(HTTPURL: string);
        function FromConsumerAndToken(Consumer: TOAuthConsumer; Token: TOAuthToken;
                                      HTTPURL: string): TOAuthRequest;
        procedure Sign_Request(Signature_Method: TOAuthSignatureMethod; Consumer: TOAuthConsumer;
                              Token: TOAuthToken);
        function Build_Signature(Signature_Method: TOAuthSignatureMethod; Consumer: TOAuthConsumer;
                              Token: TOAuthToken): string;
        property BaseString: string read FBaseString write SetBaseString;
        property Version: string read FVersion write SetVersion;
        property Parameters: TStringList read FParameters;
        property HTTPURL: string read FHTTPURL write SetHTTPURL;
        property Scheme: string read FScheme;
        property Host: string read FHost;
        property Path: string read FPath;
        property Fields: string read FFields;
        property GetString: string read FGetString;
      end;
      TOAuthSignatureMethod = class
      public
        function check_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer;
                                 Token: TOAuthToken; Signature: string): boolean;
        function get_name(): string; virtual; abstract;
        function build_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer;
                                 Token: TOAuthToken): string; virtual; abstract;
      end;
      TOAuthSignatureMethod_HMAC_SHA1 = class(TOAuthSignatureMethod)
      public
        function get_name(): string; override;
        function build_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer;
                                 Token: TOAuthToken): string; override;
      end;
      TOAuthSignatureMethod_PLAINTEXT = class(TOAuthSignatureMethod)
      public
        function get_name(): string; override;
        function build_signature(Request: TOAuthRequest; Consumer: TOAuthConsumer;
                                 Token: TOAuthToken): string; override;
      end;
      TOAuthUtil = class
      public
        class function urlEncodeRFC3986(URL: string):string;
        class function urlDecodeRFC3986(URL: string):string;
      end;
    const
      UnixStartDate : TDateTime = 25569;
    implementation
    uses
      IdGlobal, IdHash, IdHashMessageDigest, IdHMACSHA1, IdCoderMIME;
    function DateTimeToUnix(ConvDate: TDateTime): Longint;
    var
      x: double;
      lTimeZone: TTimeZoneInformation;
    begin
      GetTimeZoneInformation(lTimeZone);
      ConvDate := ConvDate + (lTimeZone.Bias / 1440);
      x := (ConvDate - UnixStartDate) * 86400;
      Result := Trunc(x);
    end;
    function _IntToHex(Value: Integer; Digits: Integer): String;
    begin
        Result := SysUtils.IntToHex(Value, Digits);
    end;
    function XDigit(Ch : Char) : Integer;
    begin
        if (Ch >= '0') and (Ch <= '9') then
            Result := Ord(Ch) - Ord('0')
        else
            Result := (Ord(Ch) and 15) + 9;
    end;
    function IsXDigit(Ch : Char) : Boolean;
    begin
        Result := ((Ch >= '0') and (Ch <= '9')) or
                  ((Ch >= 'a') and (Ch <= 'f')) or
                  ((Ch >= 'A') and (Ch <= 'F'));
    end;
    function htoin(Value : PChar; Len : Integer) : Integer;
    var
        I : Integer;
    begin
        Result := 0;
        I      := 0;
        while (I < Len) and (Value[I] = ' ') do
            I := I + 1;
        while (I < len) and (IsXDigit(Value[I])) do begin
            Result := Result * 16 + XDigit(Value[I]);
            I := I + 1;
        end;
    end;
    function htoi2(Value : PChar) : Integer;
    begin
        Result := htoin(Value, 2);
    end;
    function UrlEncode(const S : String) : String;
    var
        I : Integer;
        Ch : Char;
    begin
        Result := '';
        for I := 1 to Length(S) do begin
            Ch := S[I];
            if ((Ch >= '0') and (Ch <= '9')) or
               ((Ch >= 'a') and (Ch <= 'z')) or
               ((Ch >= 'A') and (Ch <= 'Z')) or
               (Ch = '.') or (Ch = '-') or (Ch = '_') or (Ch = '~')then
                Result := Result + Ch
            else
                Result := Result + '%' + _IntToHex(Ord(Ch), 2);
        end;
    end;
    function UrlDecode(const Url : String) : String;
    var
        I, J, K, L : Integer;
    begin
        Result := Url;
        L      := Length(Result);
        I      := 1;
        K      := 1;
        while TRUE do begin
            J := I;
            while (J <= Length(Result)) and (Result[J] <> '%') do begin
                if J <> K then
                    Result[K] := Result[J];
                Inc(J);
                Inc(K);
            end;
            if J > Length(Result) then
                break;                   { End of string }
            if J > (Length(Result) - 2) then begin
                while J <= Length(Result) do begin
                    Result[K] := Result[J];
                    Inc(J);
                    Inc(K);
                end;
                break;
            end;
            Result[K] := Char(htoi2(@Result[J + 1]));
            Inc(K);
            I := J + 3;
            Dec(L, 2);
        end;
        SetLength(Result, L);
    end;
    { TOAuthConsumer }
    constructor TOAuthConsumer.Create(Key, Secret: string);
    begin
      FKey := Key;
      FSecret := Secret;
      FCallBack_URL  := '';
    end;
    constructor TOAuthConsumer.Create(Key, Secret, Callback_URL: string);
    begin
      FKey := Key;
      FSecret := Secret;
      FCallBack_URL  := Callback_URL;
    end;
    procedure TOAuthConsumer.SetCallback_URL(const Value: string);
    begin
      FCallback_URL := Value;
    end;
    procedure TOAuthConsumer.SetKey(const Value: string);
    begin
      FKey := Value;
    end;
    procedure TOAuthConsumer.SetSecret(const Value: string);
    begin
      FSecret := Value;
    end;
    { TOAuthToken }
    function TOAuthToken.AsString: string;
    begin
      result := 'oauth_token=' + Self.Key + '&oauth_token_secret=' + Self.Secret;
    end;
    constructor TOAuthToken.Create(Key, Secret: string);
    begin
      FKey := Key;
      FSecret := Secret;
    end;
    procedure TOAuthToken.SetKey(const Value: string);
    begin
      FKey := Value;
    end;
    procedure TOAuthToken.SetSecret(const Value: string);
    begin
      FSecret := Value;
    end;
    { TOAuthRequest }
    function TOAuthRequest.Build_Signature(Signature_Method: TOAuthSignatureMethod;
      Consumer: TOAuthConsumer; Token: TOAuthToken): string;
    begin
      Result := Signature_Method.build_signature(Self, Consumer, Token);
    end;
    constructor TOAuthRequest.Create(HTTPURL: string);
    var
      x,y: integer;
    begin
      FHTTPURL := HTTPURL;
      FScheme := Copy(FHTTPURL, 0, 7);
      x := AnsiPos('.com', FHTTPURL);
      y := AnsiPos('?', FHTTPURL);
      FHost := Copy(FHTTPURL, 8, x-4);
      FPath := Copy(FHTTPURL, x + 4, Length(HTTPURL) - y - 1);
      if y > 0 then
        FFields := Copy(FHTTPURL, y + 1, Length(HTTPURL));
      FVersion := '1.0';
      FParameters := TStringList.Create;
    end;
    function TOAuthRequest.FromConsumerAndToken(Consumer: TOAuthConsumer;
      Token: TOAuthToken; HTTPURL: string): TOAuthRequest;
    begin
      Self.FParameters.Clear;
      Self.FParameters.Add('oauth_consumer_key=' + Consumer.Key);
      Self.FParameters.Add('oauth_nonce=' + Self.GenerateNonce);
      Self.FParameters.Add('oauth_timestamp=' + Self.GenerateTimeStamp);
      if Token <> nil then
        FParameters.Add('oauth_token=' + Token.Key);
      Self.FParameters.Add('oauth_version=' + Self.Version);
      Result := Self;
    end;
    function TOAuthRequest.GenerateNonce: string;
    var
      md5: TIdHashMessageDigest;
    begin
      md5 := TIdHashMessageDigest5.Create;
      Result := md5.HashStringAsHex(GenerateTimeStamp);
      md5.Free;
    end;
    function TOAuthRequest.GenerateTimeStamp: string;
    begin
      Result := IntToStr(DateTimeToUnix(Now));
    end;
    function TOAuthRequest.GetSignableParameters: string;
    var
      x: integer;
      parm: string;
    begin
      parm := '';
      x := FParameters.IndexOfName('oauth_signature');
      if x <> -1 then
        FParameters.Delete(x);
      for x := 0 to FParameters.Count - 1 do
      begin
        if x = 0 then
        begin
          FParameters.ValueFromIndex[x] := TOAuthUtil.urlEncodeRFC3986(FParameters.ValueFromIndex[x]);
          parm := FParameters.Names[x] + TOAuthUtil.urlEncodeRFC3986('=') + TIdURI.PathEncode(FParameters.ValueFromIndex[x]);
        end
        else
          parm := parm + TOAuthUtil.urlEncodeRFC3986('&') +
                  FParameters.Names[x] + TOAuthUtil.urlEncodeRFC3986('=' + FParameters.ValueFromIndex[x])
      end;
      Result := parm;
    end;
    procedure TOAuthRequest.SetBaseString(const Value: string);
    begin
      FBaseString := Value;
    end;
    procedure TOAuthRequest.SetHTTPURL(const Value: string);
    var
      x,y: integer;
    begin
      FHTTPURL := Value;
      FScheme := Copy(FHTTPURL, 0, 7);
      x := AnsiPos('.com', FHTTPURL);
      y := AnsiPos('?', FHTTPURL);
      FHost := Copy(FHTTPURL, 8, x-4);
      if y > 0 then
        FPath := Copy(FHTTPURL, x + 4, y - (x + 4))
      else
        FPath := Copy(FHTTPURL, x + 4, Length(HTTPURL) - y - 1);
      if y > 0 then
        FFields := Copy(FHTTPURL, y + 1, Length(HTTPURL));
    end;
    procedure TOAuthRequest.SetVersion(const Value: string);
    begin
      FVersion := Value;
    end;
    procedure TOAuthRequest.Sign_Request(Signature_Method: TOAuthSignatureMethod;
      Consumer: TOAuthConsumer; Token: TOAuthToken);
    var
      signature: string;
      x: integer;
    begin
      FParameters.Insert(2 ,'oauth_signature_method=' + Signature_Method.get_name);
      //FParameters.Sort;
      signature := Self.Build_Signature(Signature_Method, Consumer, Token);
      signature := TOAuthUtil.urlEncodeRFC3986(signature);
      FParameters.Insert(3, 'oauth_signature=' + signature);
      for x := 0 to FParameters.Count - 1 do
      begin
        if x = 0 then
          FGetString := FParameters.Names[X] + '=' + FParameters.ValueFromIndex[x]
        else
          FGetString := FGetString + '&' + FParameters.Names[X] + '=' + FParameters.ValueFromIndex[x];
      end;
    end;
    { TOAuthUtil }
    class function TOAuthUtil.urlDecodeRFC3986(URL: string): string;
    begin
      result := TIdURI.URLDecode(URL);
    end;
    class function TOAuthUtil.urlEncodeRFC3986(URL: string): string;
    var
      URL1: string;
    begin
      URL1 := URLEncode(URL);
      URL1 := StringReplace(URL1, '+', ' ', [rfReplaceAll, rfIgnoreCase]);
      result := URL1;
    end;
    { TOAuthSignatureMethod }
    function TOAuthSignatureMethod.check_signature(Request:TOAuthRequest;
      Consumer: TOAuthConsumer; Token: TOAuthToken; Signature: string): boolean;
    var
      newsig: string;
    begin
       newsig:= Self.build_signature(Request, Consumer, Token);
      if (newsig = Signature) then
        Result := True
      else
        Result := False;
    end;
    { TOAuthSignatureMethod_HMAC_SHA1 }
    function TOAuthSignatureMethod_HMAC_SHA1.build_signature(Request: TOAuthRequest;
      Consumer: TOAuthConsumer; Token: TOAuthToken): string;
      function Base64Encode(const Input: TIdBytes): string;
      begin
        Result := TIdEncoderMIME.EncodeBytes(Input);
      end;
      function EncryptHMACSha1(Input, AKey: string): TIdBytes;
      begin
        with TIdHMACSHA1.Create do
        try
          Key := ToBytes(AKey);
          Result := HashValue(ToBytes(Input));
        finally
          Free;
        end;
      end;
    var
      parm1, parm: string;
      consec, toksec: string;
    begin
      parm1 := Request.GetSignableParameters;
      parm := TOAuthUtil.urlEncodeRFC3986(Request.Scheme) +
              TOAuthUtil.urlEncodeRFC3986(Request.Host) +
              TOAuthUtil.urlEncodeRFC3986(Request.Path);
      if Request.Fields <> '' then
      begin
        parm := parm + '&' + TOAuthUtil.urlEncodeRFC3986(Request.Fields);
        parm := parm +  TOAuthUtil.urlEncodeRFC3986('&') + parm1;
      end
      else
        parm :=  parm + '&' + parm1;
      Request.BaseString := 'GET&' + parm;
      if Token <> nil then
      begin
        consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
        toksec := TOAuthUtil.urlEncodeRFC3986(Token.Secret);
        consec := consec + '&' + toksec;
        Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec))
      end
      else
      begin
        consec := TOAuthUtil.urlEncodeRFC3986(Consumer.Secret);
        consec := consec + '&';
        Result := Base64Encode(EncryptHMACSha1(Request.BaseString, consec));
      end;
    end;
    function TOAuthSignatureMethod_HMAC_SHA1.get_name: string;
    begin
      result := 'HMAC-SHA1';
    end;
    { TOAuthSignatureMethod_PLAINTEXT }
    function TOAuthSignatureMethod_PLAINTEXT.build_signature(Request: TOAuthRequest;
      Consumer: TOAuthConsumer; Token: TOAuthToken): string;
    begin
      if Token <> nil then
        Result := TOAuthUtil.urlEncodeRFC3986((Consumer.Secret + '&' + Token.Secret))
      else
        Result := TOAuthUtil.urlEncodeRFC3986((Consumer.Secret));
    end;
    function TOAuthSignatureMethod_PLAINTEXT.get_name: string;
    begin
      Result := 'PLAINTEXT';
    end;
    end.