В целом, идея перевода библиотеки 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 полностью искорена из библиотеки. А сама библиотека стала меньше и быстрее. На всякий случай выкладываю готовую библиотеку OAuth для Delphi.
В архиве два модуля. Первый OAuth - содержит все классы и методы, необходимые для работы с OAuth. Второй - OAuthUtils - вспомогательные функции для работы с OAuth - преобразование величин, проверка, кодирование и декодирование и т.д. В uses приложений достаточно подключить только OAuth.pas.
24 мая...всего 1 неделя до первого дня лета. А отдых летом - это самое что ни есть замечательное занятие. Кто-то поедет как турист в Турцию, Италию и т.д., а я, видимо в июле рвану в Абхазию :) Говорят там можно отдохнуть неплохо и не дорого. И не важно, что раскрутка сайта будет остановлена на некоторый срок - это все мелочи. Хочу отдыхать! :)
------------------------
| Делись! | Загружай! | Плюсуй! |
| | |









24 мая 2010 в 9:23 пп
а почему не синапс?
24 мая 2010 в 9:31 пп
Можно и синапс. Только там есть проблема с заголовком Authorization: — почему-то срабатывает через раз…хз в чем проблема. Тот же самый код с ICS пашет без проблем
24 мая 2010 в 10:48 пп
Просто пропаганда ICS :) .
Подводный камень №2: Как на счет сайтов с gzip сжатием. Попробуйте прикрутить его к ISC. Гарантированное зависание на одном из 10 сайтов, именно на этапе распаковки страницы. Вот тут как раз и пригодится модуль IdCompressorZLib из Indy.
P.S.: Я не поклонник Indy, просто потратил много времени на один и другой компонент.
Кстати, Vlad, на какой версии Delphi используете ICS?
25 мая 2010 в 8:00 дп
Serg, никакой пропоганды :) Простое избавление от Indy и библиотека правда стала работать быстрее. Не могу объяснить всех причин почему, но результат на лицо. Я ведь с ICS особо не работал, поэтому и всех «граблей» пока не знаю. Может и с синапсом OAuth сделаю, если поборю глюк с заголовками — будет библиотека всех цветов и расцветок :)
Я использую последнюю версию ICS (из репозитория) на Delphi 2010 под Win7
05 Авг 2010 в 8: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
05 Авг 2010 в 8:41 пп
Wybie, AsHex это НЕ стандартная функция. Погугли на тему перевода в HEX
06 Авг 2010 в 1:36 пп
Даже если не стандартная
то как её туда всунуть или как заменить данный код?
я нубк0, но у меня есть код приложение и ошибка только сдесь
так что мне нужно как то это исправить
06 Авг 2010 в 1:58 пп
о блин…чё-то я вчерась брякнул не подумавши :) Wybie, ты точно код предоставил? Именно так:
varAsHex,HashValue:AnsiString;
??
Потому как чё-то тут не то её богу — хрень, пардон за выражение, да и только :)
Судя по подразумеваемой логике, тут кто-то хочет получить хэш MD5 средствами Indy. Вообще-то это делается несколько иначе. Смотри тут как получить MD5 в Indy
http://www.webdelphi.ru/2010/05/kriptografiya-v-delphi-sredstvami-indy-i-synapse/
06 Авг 2010 в 2: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 для ввода
// Id Вашего приложения и ID пользователя вконтаке
// Для того что бы все не ломились комне на страницу)))
//Проходим авторизацию, предполагается что пользователь уже добавил приложение к
// себе на страницу вконтакте.
procedure TForm1.Button2Click(Sender: TObject);
begin
WebBrowser1.Navigate(‘http://vkontakte.ru/login.php?app='+Edit2.text+'&layout=popup&type=browser‘);
end;
// Отправляем запрос на сервер
procedure TForm1.Button6Click(Sender: TObject);
begin
//Составляем подпись запроса
sig:=Edit1.text+’api_id=’+Edit2.Text+’format=XMLmethod=getUserInfoExuids=’+Edit1.Text+’v=3.0′+stroka3;
sig:=md5(sig); // Кодируем в Md5
label5.Caption:=sig;
// Отсылаем все на сервер
WebBrowser1.Navigate(‘http://api.vkontakte.ru/api.php?api_id='+Edit2.Text+'&format=XML&method=getUserInfoEx&sid='+stroka2+'&sig='+sig+'&uids='+Edit1.Text+'&v=3.0‘);
end;
//Из адресной строки выдергиваем переменные secret и sid
procedure TForm1.Button4Click(Sender: TObject);
var start2,start3:integer;
URL:string;
begin
URL:=WebBrowser1.LocationURL;
Memo1.Text:=Url; // Для наглядности выводим адресную строку браузера в Memo, в принципе строку можно удалить !)
start3:=Pos(‘secret%22%3A%22′,URL);
stroka3:=Copy(URL, start3+15, 900);
Delete(stroka3, Pos(‘%22%2C%22expire’,stroka3) , 5900);
Label4.caption:=stroka3;
start2:=Pos(‘sid%22%3A%22′,URL);
stroka2:=Copy(URL, start2+12, 500);
Delete(stroka2, Pos(‘%22%2C%22secret’,stroka2) , 5900);
Label3.caption:=stroka2;
end;
// Функция Md5
function TForm1.md5(s: string): string;
begin
Result := »;
with TIdHashMessageDigest5.Create do
try
Result := AnsiLowerCase(AsHex(HashValue(s)));
finally
Free;
end;
end;
//DelphiExpert.ru
//Удачной компиляции…!
end.
Исправте кто знает плиз под delphi 2010 установка стандартная без доп баз и тд
если надо, что приклеплять то пожалуйста пишите что
откуда брать
и как установить
спасибо
06 Авг 2010 в 3:08 пп
Wybie, ты предыдущий комментарий мой читал? Ссылку открывал? там решение твоего вопроса.
06 Авг 2010 в 3:38 пп
да я то читал спсиб за старание
но я не совсем понял куда в тыкать код
но пробовал, втыкнул, не сработало вот и кинул полный код
06 Авг 2010 в 3:51 пп
Так понимай куда втыкать. В чём проблема-то? :) Надо программу чтоль за тебя написать? Так это бесплатно делать никто не будет не только на форумах, но и в блоге. Ответ дан и ответ этот судя по всем приведенным листингам — верный. И судя по всему, я бы посоветовал сначала немного разобраться с Delphi в принципе, т.к. при незнании куда «втыкивать» код как бы и программировать-то не стоит пытаться.
04 Окт 2010 в 7:52 пп
Vlad спасибо вам за компоненты.
У меня вопрос. Вы использовали в файле OAuthUtils.pas функцию StringOf
Я использую Delphi 7, и к сожалению нет возможности установить Delphi 2010. (интернет медленный, а купить дистрибутив негде)
Можно ли каким то образом заменить эту функцию? Может быть есть легкий способ, без перехода на Delphi 2010?
Я сначала хотел выдрать из SysUtils (от Delphi 2010) функцию, но там столько вложенных функций, что я потерял логику ее работы.
04 Окт 2010 в 8:42 пп
Григорий? к сожалению более легкого пути, чем переход на D2010 нету…по идее эта функция собирает строку из массива байтов
11 Июл 2011 в 10: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;
07 Янв 2012 в 8:32 пп
Надо так:
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;
Быстрее, и память не фрагменитруется.