В целом, идея перевода библиотеки 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 полностью искоренена из библиотеки. А сама библиотека стала меньше и быстрее.
а почему не синапс?
Можно и синапс. Только там есть проблема с заголовком Authorization: — почему-то срабатывает через раз…хз в чем проблема. Тот же самый код с ICS пашет без проблем
Просто пропаганда ICS :) .
Подводный камень №2: Как на счет сайтов с gzip сжатием. Попробуйте прикрутить его к ISC. Гарантированное зависание на одном из 10 сайтов, именно на этапе распаковки страницы. Вот тут как раз и пригодится модуль IdCompressorZLib из Indy.
P.S.: Я не поклонник Indy, просто потратил много времени на один и другой компонент.
Кстати, Vlad, на какой версии Delphi используете ICS?
Serg, никакой пропоганды :) Простое избавление от Indy и библиотека правда стала работать быстрее. Не могу объяснить всех причин почему, но результат на лицо. Я ведь с ICS особо не работал, поэтому и всех «граблей» пока не знаю. Может и с синапсом OAuth сделаю, если поборю глюк с заголовками — будет библиотека всех цветов и расцветок :)
Я использую последнюю версию ICS (из репозитория) на Delphi 2010 под Win7
люди как исправить или заменить
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, AsHex это НЕ стандартная функция. Погугли на тему перевода в HEX
Даже если не стандартная
то как её туда всунуть или как заменить данный код?
я нубк0, но у меня есть код приложение и ошибка только сдесь
так что мне нужно как то это исправить
о блин…чё-то я вчерась брякнул не подумавши :) Wybie, ты точно код предоставил? Именно так:
var
AsHex,HashValue:AnsiString;
??
Потому как чё-то тут не то её богу — хрень, пардон за выражение, да и только :)
Судя по подразумеваемой логике, тут кто-то хочет получить хэш MD5 средствами Indy. Вообще-то это делается несколько иначе. Смотри тут как получить MD5 в Indy
http://www.webdelphi.ru/2010/05/kriptografiya-v-delphi-sredstvami-indy-i-synapse/
Вот код 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, ты предыдущий комментарий мой читал? Ссылку открывал? там решение твоего вопроса.
да я то читал спсиб за старание
но я не совсем понял куда в тыкать код
но пробовал, втыкнул, не сработало вот и кинул полный код
Так понимай куда втыкать. В чём проблема-то? :) Надо программу чтоль за тебя написать? Так это бесплатно делать никто не будет не только на форумах, но и в блоге. Ответ дан и ответ этот судя по всем приведенным листингам — верный. И судя по всему, я бы посоветовал сначала немного разобраться с Delphi в принципе, т.к. при незнании куда «втыкивать» код как бы и программировать-то не стоит пытаться.
Vlad спасибо вам за компоненты.
У меня вопрос. Вы использовали в файле OAuthUtils.pas функцию StringOf
Я использую Delphi 7, и к сожалению нет возможности установить Delphi 2010. (интернет медленный, а купить дистрибутив негде)
Можно ли каким то образом заменить эту функцию? Может быть есть легкий способ, без перехода на Delphi 2010?
Я сначала хотел выдрать из SysUtils (от Delphi 2010) функцию, но там столько вложенных функций, что я потерял логику ее работы.
Григорий? к сожалению более легкого пути, чем переход на D2010 нету…по идее эта функция собирает строку из массива байтов
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;
Быстрее, и память не фрагменитруется.
[…] т.к. теже методы GETCommand и POSTCommand Вы можете посмотреть в этом посте, а такие методы как SetTwitterClientVersion, SetSource и пр. просто […]