уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

В целом, идея перевода библиотеки OAuth для Twitter на рельсы ICS была озвучена ещё её разработчиком год назад, на в силу некоторых обстоятельств проект перестал развиваться и библиотека так и осталась, работающей на Indy.
Сегодня я решил переработать библиотеку OAuth, а заодно и класс, реализующий работу с API Twitter’а и реализовать работу, используя только ICS. Для работы нам понадобится предыдущая версия библиотеки, которую можно скачать отсюда.

В первоначальном варианте библиотеки OAuth для Delphi используется ряд модулей Indy для шифрования данных — с них мы и начнем работу, а заодно и разберемся как реализовать шифрование данных в ICS.
Открываем модуль OAuth и начинаем править исходники.

Кодирование URL’ов

Для кодирования URL в Indy используется класс TidURI из модуля idURI.pas. При этом используется классовая функция URLEncode:

TIdURI = class
  protected
    FDocument: string;
    [....]
  public
    [...]    
    class function URLDecode(ASrc: string): string;
    class function URLEncode(const ASrc: string): string;

В ICS функции URLEncode и URLDecode содержаться в модуле OverbyteIcsUrl.pas. Поэтому удаляем из uses библиотеки OAuth модуль idURI и подключаем вместо него OverbyteIcsUrl. После этого нам потребуется изменить код следующих функций (привожу сразу исправленные версии):

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('=') +UrlEncode(FParameters.ValueFromIndex[x]);
    end
    else
      parm := parm + TOAuthUtil.urlEncodeRFC3986('&') +
              FParameters.Names[x] + TOAuthUtil.urlEncodeRFC3986('=' + FParameters.ValueFromIndex[x])
  end;
  Result := parm;
end;
 
{ TOAuthUtil }
class function TOAuthUtil.urlDecodeRFC3986(URL: string): string;
begin
  result := 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;

На этом с URL’ами работа закончена. Переходим дальше.

Кодирование Base64

Отключаем из библиотеки модуль idCoder3To4 и вместо него подключаем OverbyteIcsMimeUtils.
Теперь меняем код функции на следующий:

function Base64Encode_(const Input: TBytes): string;
begin
  Result:=Base64Encode(StringOf(Input));
end;

Соответственно при сборке должна появиться ошибка в функции

function TOAuthSignatureMethod_HMAC_SHA1.build_signature(Request: TOAuthRequest;
  Consumer: TOAuthConsumer; Token: TOAuthToken): string;

Поэтому сразу исправим эти ошибки.

Шифрование HMAC-SHA1

Для использования в программе шифрования HMAC-SHA1 нам необходимо использовать модуль OverbyteIcsSha1.pas. Подключаем его в uses и изменяем код функции:

function EncryptHMACSha1(Input, AKey: string): TBytes;
  begin
    Result:=BytesOf(HMAC_SHA1_EX(Input,AKey));
  end;

Теперь возвращаемся к функции function TOAuthSignatureMethod_HMAC_SHA1.build_signature и меняем её код на этот:

{ TOAuthSignatureMethod_HMAC_SHA1 }
function TOAuthSignatureMethod_HMAC_SHA1.build_signature(Request: TOAuthRequest;
  Consumer: TOAuthConsumer; Token: TOAuthToken): string;
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;

Осталось разобраться только с одним методом шифрования, который используется в OAuth — MD5.

Шифрование MD5

Подключаем в uses ещё один модуль ICS — OverbyteIcsMD5.pas.
MD5-шифрование используется при генерации параметра nonce. Меняем код функции:

function TOAuthRequest.GenerateNonce: string;
begin
  Result := StrMd5(GenerateTimeStamp);
end;

На этом работу с модулем OAuth можно считать законченной. Можете удалить все оставшиеся модули Indy и собрать любой проект Delphi, подключив OAuth.pas. У меня после этих преобразований размер готового exe-шника «сдулся» примерно на 450-500 Kb.
Теперь перейдем к работе с классов Twitter API.

Убираем Indy класса TTwitter

Сам класс для работы с Twitter’ом Вы всегда можете скачать у разработчика. Я приведу только код функции, которые следует изменить. Их всего две — GetCommand и POSTCommand.
Функция GetCommand теперь будет выглядеть следующим образом:

function TTwitter.GETCommand(URL: string): string;
var
  pos: integer;
  HTTP: THTTPCli;
  Stream: TStringStream;
begin
  try
    FConsumer := nil;
    FConsumer := TOAuthConsumer.Create(FKey, FSecret, CallbackURL);
    FRequest := TOAuthRequest.Create(URL);
    FRequest := Request.FromConsumerAndToken(FConsumer, nil, URL);
    FRequest.HTTPURL := URL;
    FToken := TOAuthToken.Create(FOAuth_token, FOAuth_token_secret);
    FRequest := Request.FromConsumerAndToken(FConsumer, FToken, URL);
    FRequest.Sign_Request(HMAC, Consumer, Token);
    pos := AnsiPos('?', URL);
    if pos = 0 then
      URL := URL + '?' + Request.GetString
    else
      URL := URL + '&' + Request.GetString;
    Stream := TStringStream.Create;
    HTTP := THTTPCli.Create(nil);
    HTTP.URL := URL;
    HTTP.RcvdStream := Stream;
    HTTP.Get;
    Result := Stream.DataString;
  finally
    FreeAndNil(HTTP);
    FreeAndNil(Stream);
  end;
end;

Соответственно, POSTCommand будет выглядеть так:

function TTwitter.POSTCommand(URL: string; Params: TStringList): string;
var
  pos: integer;
  ah, Key: string;
  aa: TStringList;
  HTTP: THTTPCli;
  Send, Rcv: TStringStream;
begin
  try
    FRequest := TOAuthRequest.Create(URL);
    FToken := TOAuthToken.Create(FOAuth_token, FOAuth_token_secret);
    FRequest := Request.FromConsumerAndToken(FConsumer, FToken, URL);
    FRequest.HTTPURL := URL;
    ah := FRequest.genAuthHeader(Consumer, Token, Params, URL);
    aa := TStringList.Create;
    aa.Clear;
    aa.Add(FRequest.encodeParams(Params, '&', false, true));
    HTTP := THTTPCli.Create(nil);
    HTTP.Options := [];
    Send := TStringStream.Create;
    Rcv := TStringStream.Create;
    Send.WriteString(Trim(aa.Text));
    Send.Position := 0;
    HTTP.Accept := 'text/html';
    HTTP.SendStream := Send;
    HTTP.RcvdStream := Rcv;
    HTTP.RequestVer := '1.1';
    HTTP.OnBeforeHeaderSend := AddCustomHeader;
    FAuthHeader := 'Authorization: ' + ah;
    HTTP.URL := URL;
    HTTP.Post;
    Result:=Rcv.DataString
  finally
    FreeAndNil(aa);
    FreeAndNil(Send);
    FreeAndNil(Rcv);
    FreeAndNil(HTTP);
  end;
end;

Теперь Indy полностью искоренена из библиотеки. А сама библиотека стала меньше и быстрее.

Скачать исходник: Исходники —> API онлайн-сервисов —> Прочие API
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
17 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
SCHigi
SCHigi
24/05/2010 21:23

а почему не синапс?

Serg
Serg
24/05/2010 22:48

Просто пропаганда ICS :) .
Подводный камень №2: Как на счет сайтов с gzip сжатием. Попробуйте прикрутить его к ISC. Гарантированное зависание на одном из 10 сайтов, именно на этапе распаковки страницы. Вот тут как раз и пригодится модуль IdCompressorZLib из Indy.
P.S.: Я не поклонник Indy, просто потратил много времени на один и другой компонент.
Кстати, Vlad, на какой версии Delphi используете ICS?

Wybie
05/08/2010 20:03

люди как исправить или заменить

function TForm1.md5(s: string): string;
var
AsHex,HashValue:AnsiString;
begin
Result := »;
with TIdHashMessageDigest5.Create do
try
Result := AnsiLowerCase(AsHex(HashValue(s)));
finally
Free;
end;
end;
не может опознать AsHex и HashValue в delphi 2010

Wybie
06/08/2010 13:36

Даже если не стандартная
то как её туда всунуть или как заменить данный код?
я нубк0, но у меня есть код приложение и ошибка только сдесь
так что мне нужно как то это исправить

Wybie
06/08/2010 14:25

Вот код unit Unit1; //DelphiExpert.ru interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, IdComponent, IdHashMessageDigest, OleCtrls, SHDocVw; type TForm1 = class(TForm) WebBrowser1: TWebBrowser; Memo1: TMemo; Button2: TButton; Button4: TButton; Label3: TLabel; Label4: TLabel; Label5: TLabel; Button6: TButton; Label1: TLabel; Label2: TLabel; Label6: TLabel; Edit1: TEdit; Edit2: TEdit; Label7: TLabel; Label8: TLabel; Label9: TLabel; Label10: TLabel; procedure Button2Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button6Click(Sender: TObject); private { Private declarations } function md5(s: string): string; public { Public declarations } end; var Form1: TForm1; stroka2,stroka3, sig :string; implementation {$R *.dfm} // В исходнике я вставил 2 компонента Edit… Подробнее »

Wybie
06/08/2010 15:38

да я то читал спсиб за старание
но я не совсем понял куда в тыкать код
но пробовал, втыкнул, не сработало вот и кинул полный код

Григорий
Григорий
04/10/2010 19:52

Vlad спасибо вам за компоненты.
У меня вопрос. Вы использовали в файле OAuthUtils.pas функцию StringOf
Я использую Delphi 7, и к сожалению нет возможности установить Delphi 2010. (интернет медленный, а купить дистрибутив негде)

Можно ли каким то образом заменить эту функцию? Может быть есть легкий способ, без перехода на Delphi 2010?
Я сначала хотел выдрать из SysUtils (от Delphi 2010) функцию, но там столько вложенных функций, что я потерял логику ее работы.
 
 

Артем
Артем
11/07/2011 22:38

function stringOf(const Input: TBytes): string;
var
 i: integer;
 s: string;
begin
s := »;
 for i := 0 to High(Input) do
  s := s + chr(input[i]);
 Result := s;
end;

Дмитрий Белькевич

Надо  так:
function stringOf(const Input: TBytes): string;
var
i: integer;
begin
SetLength(Result, Length(Input);
for i := 0 to High(Input) do
Result[i] := chr(input[i]);
end;
Быстрее, и память не фрагменитруется.

trackback

[…] т.к. теже методы GETCommand и POSTCommand Вы можете посмотреть в этом посте, а такие методы как SetTwitterClientVersion, SetSource и пр. просто […]