Подписка

добавить на Яндекс

Наши проекты

Delphi+Google

Google API

Google API в Delphi - проект с открытым исходным кодом.

Chrono

Chrono

Хронометр - программа для ведения списка задач.

ODFProc

ODFProc

ODFProc - работа с документами OpenOffice в Lazarus и FreePascal.

Поддержка блога

А тут я коплю на лицензию Delphi XE на iPad =).
Сумма пожертвования не фиксирована.

Публикации

Год назад

Случайный пост

Последние

Сообщения форума

Комментарии

Социальные сети

Google

Facebook

Twitter

Опрос

Вы сейчас или в ближайшем обозримом будущем планируете разрабатывать кроссплатформенное приложение с использованием Firemonkey?



Loading ... Loading ...

Блоги и сообщества

Статьи по Delphi DelphiFeeds.ru - Все Delphi-блоги Рунета Сообщество умных людей VR-Online.RU Бесплатный журнал для программистов и всех, кто интересуется IT Статьи и уроки по Delphi Новостной блог о высоких технологиях
Система Orphus
Опубликовал Vlad 24 мая 2010 в 17:19.
Категории: Delphi в Web.


В целом, идея перевода библиотеки 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.

zip OAuth в Delphi(5.28 KB)

В архиве два модуля. Первый OAuth - содержит все классы и методы, необходимые для работы с OAuth. Второй - OAuthUtils - вспомогательные функции для работы с OAuth - преобразование величин, проверка, кодирование и декодирование и т.д. В uses приложений достаточно подключить только OAuth.pas.

------------------------
24 мая...всего 1 неделя до первого дня лета. А отдых летом - это самое что ни есть замечательное занятие. Кто-то поедет как турист в Турцию, Италию и т.д., а я, видимо в июле рвану в Абхазию :) Говорят там можно отдохнуть неплохо и не дорого. И не важно, что раскрутка сайта будет остановлена на некоторый срок - это все мелочи. Хочу отдыхать! :)
------------------------
Понравилась статья? Тогда:
Делись! Загружай! Плюсуй!
   Отправить PDF на   
Читай ещё статьи на WebDelphi.ru

Комментарии (16)

WP_Cloudy
  • SCHigi пишет:

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

  • Vlad пишет:

    Можно и синапс. Только там есть проблема с заголовком Authorization: — почему-то срабатывает через раз…хз в чем проблема. Тот же самый код с ICS пашет без проблем

  • Serg пишет:

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

  • Vlad пишет:

    Serg, никакой пропоганды :) Простое избавление от Indy и библиотека правда стала работать быстрее. Не могу объяснить всех причин почему, но результат на лицо. Я ведь с ICS особо не работал, поэтому и всех «граблей» пока не знаю. Может и с синапсом OAuth сделаю, если поборю глюк с заголовками — будет библиотека всех цветов и расцветок :)
    Я использую последнюю версию ICS (из репозитория) на Delphi 2010 под Win7

  • Wybie пишет:

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

    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

  • Vlad пишет:

    Wybie, AsHex это НЕ стандартная функция. Погугли на тему перевода в HEX

  • Wybie пишет:

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

  • Vlad пишет:

    о блин…чё-то я вчерась брякнул не подумавши :) Wybie, ты точно код предоставил? Именно так:
    var
    AsHex,HashValue:AnsiString;

    ??
    Потому как чё-то тут не то её богу — хрень, пардон за выражение, да и только :)
    Судя по подразумеваемой логике, тут кто-то хочет получить хэш MD5 средствами Indy. Вообще-то это делается несколько иначе. Смотри тут как получить MD5 в Indy
    http://www.webdelphi.ru/2010/05/kriptografiya-v-delphi-sredstvami-indy-i-synapse/

  • Wybie пишет:

    Вот код
    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 установка стандартная без доп баз и тд
    если надо, что приклеплять то пожалуйста пишите что
    откуда брать
    и как установить
    спасибо

  • Vlad пишет:

    Wybie, ты предыдущий комментарий мой читал? Ссылку открывал? там решение твоего вопроса.

  • Wybie пишет:

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

  • Vlad пишет:

    Так понимай куда втыкать. В чём проблема-то? :) Надо программу чтоль за тебя написать? Так это бесплатно делать никто не будет не только на форумах, но и в блоге. Ответ дан и ответ этот судя по всем приведенным листингам — верный. И судя по всему, я бы посоветовал сначала немного разобраться с Delphi в принципе, т.к. при незнании куда «втыкивать» код как бы и программировать-то не стоит пытаться.

  • Григорий пишет:

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

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

  • Vlad пишет:

    Григорий? к сожалению более легкого пути, чем переход на 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;
    Быстрее, и память не фрагменитруется.

Ваш ответ

Внимание: Все комментарии модерируются, и это может вызвать задержку их публикации. Отправлять комментарий заново не требуется.

Пожалуйста, заключайте исходный код в тэги [code][/code].
Если код большой, то воспользуйтесь Вставкой кода на отдельной странице и оставьте в комментарии ссылку на исходник

   


ремонт стиральных машин