Sep
2

Установка g-client в emacs под Windows

Для чтения новостной ленты Google Reader в emacs есть специальный плагин g-client. Кроме Google Reader g-client поддерживает и другие сервисы Google – Calendar, Blogger. Рассмотрим как настроить и использовать greader.
G-client входит в состав проекта Emacspeak. Проект Emacspeak разрабатывает T.V. Raman из Корнелльского университета. Raman активно работает над проектом Emacspeak, но g-client не обновлялся с марта 2007.

G-client использует аутентификацию на Google Reader через токен SID. Весной 2010 аутентификация по токену SID в Google API была отключена. Сейчас не-web приложения должны аутентифицироваться через токен Auth. Следовательно, если сейчас просто установить g-client, то он не будет работать. До внесения в механизм аутентификации g-client curl, запущенный из консоли cygwin bash, с теми же параметрами, которые ему передает g-client, у меня возвращал следующее:

Google  
Error

Forbidden

Your client does not have permission to get URL /reader/api/0/token from this server.
Не в упрёк Raman’у будет сказано, но я написал ему об этой проблеме 31.08.2010 и не получил никакого ответа. К SVN с исходниками g-client есть доступ только на чтение. Поэтому, далее я опишу как сделать так, чтобы g-client работал.
Я ставил g-client на компьютер с ОС Windows XP S3. Поэтому в качестве emacs использовался EmacsW32. Установка под Linux в целом аналогична, но намного проще. В Linux не надо решать проблемы отсутствия некоторых консольных утилит. Кроме того, Emacspeak есть в Ubuntu 10.04 в SoftwareCenter.
Итак, для установки и работы g-client потребуется ряд консольных утилит. Я использовал cygwin. Но это не единственный вариант. Можно, например, использовать MiniGW. Есть и другие альтернативы. Cygwin я выбрал потому что он хорошо интегрируется с emacs и потому что в cygwin есть большой набор консольных утилит. Про процесс настройки cygwin для emacs я уже писал тут.Можно сначала поставить самые необходимые, а потом по мере необходимости доустановить то, что требуется.
Итак, необходимы следующие *nix консольные утилиты:
  • для компиляции исходников g-client на ELisp потребуется утилита make;
  • для работы g-client с HTTP используется утилита curl;
  • для осуществления XSLT-преобразования полученного XML от Google Reader используется утилита xsltproc.

Утилита xsltproc присутствует в cygwin в составе пакета libxslt в категории Libs.

Для просмотра результирующей HTML-страницы я использовал emacs браузер emacs-w3m. Разумеется, может быть использован любой другой браузер. Но тогда какой смысл разбираться с g-client под emacs если для просмотра будет использоваться сторонний браузер. Хотя, emacs-w3m имеет свои недостатки, но об этом после. Emacs-w3m – это набор скриптов на ELisp, которые реализуют интерфейс в emacs к консольному браузеру w3m. Соответственно, в cygwin я устанавливал ещё и w3m. Но установка и настройка emacs-w3m – это отдельная песня. Не будет отвлекаться.

Вообще говоря, после того, как вы разместили el-скрипты в какой-либо подходящей директории (например, C:\Documents and Settings\<username>\Application Data\.emacs.d\g-client) их необходимо скомпилировать. скомпилированные el-файлы имеют расширение elc. Для компиляции el-файлов необходимо в emacs выполнить команду “M-x byte-recompile-directory” для компиляции всех файлов в директории или “M-x byte-compile-file” для компиляции конкретного файла. Перекомпиляцию скриптов потребуется делать ещё и далее после внесения изменений в файлы.

При компиляции файлов g-client постоянно возникают предупреждения “cl package required at runtime”. Эти предупреждения можно просто игнорировать.

Если в процессе работы с emacs у вас будет возникать ошибка “Symbol’s function definition is void: <function_name>” и если вы уверены. что эта функция должна быть видима попробуйте перекомпилировать модуль с этой функцией командой “M-x byte-compile-file”. Команда “M-x byte-recompile-directory” в таких случаях мне не помогала.

В файл .emacs необходимо внести следующие строки:

(add-to-list 'load-path "~/.emacs.d/g-client")
(load-library "g-loaddefs")
(setq g-user-email "your_email@gmail.com")
(setq g-html-handler 'w3m-buffer)
(autoload 'w3m-buffer "w3m")
(setq max-lisp-eval-depth 1000)

Строка “(setq g-html-handler ‘w3m-buffer)” определяет, что просмотр результирующей html-страницы будет осуществляться браузером emacs-w3m. Последующие две строки – “(autoload ‘w3m-buffer “w3m”)” и “(setq max-lisp-eval-depth 1000)” необходимы для корректной работы браузера emacs-w3m с g-client.

Функция w3m-buffer используется для рендеринга текущего буфера. Она была добавлена в в emacs-w3m в модуль w3m.el 2006-10-02.

Параметр max-lisp-eval-depth по умолчанию у меня был равна 800. При этом в процессе рендеринга результирующей html-страницы браузером emacs-w3m возникало сообщение ”with-current-buffer: Lisp nesting exceeds `max-lisp-eval-depth’”. При увеличении значения этой переменной до 1000 такая ошибка больше не возникала.

Теперь перейдем к исправлению механизма аутентификации в модуле greader.el. Google Reader больше не поддерживает аутентификацию по токену SID (proof link), которая используется в модуле greader.el. Надо использовать аутентификацию по токену Auth.

1. В модуле g-auth.el:

1.1. в структуре g-auth удалим (или безопаснее закомментировать) поле “session-id” за ненадобностью

(defstruct g-auth
  (username  (user-login-name))
  email
  password
  token
  ;session-id ;gsession-id
  cookie-alist
  service
  (lifetime  g-auth-lifetime)
  timestamp
  post-auth-action)

1.2. в функции g-authenticate при вызове shell-command-on-region добавил параметр g-curl-common-options после g-curl-program. Соответственно в строку функции format необходимо добавить “%s” в соответствующую позицию.  В параметр g-curl-common-options в конце добавил ключ “–insecure”. Этот ключ необходим для того, чтобы пока не заморачиваться с настройкой SSL-сертификатов для curl при взаимодействии с Google API. Установить значение параметра g-curl-common-options можно или через setq в файле .emacs или можно использовать команду “M-x customize-option”. Параметр g-curl-common options у меня в итоге равен “–compressed –silent –location –location-trusted –insecure”. Если SSL для curl + g-client не настроить, то может возникать сообщение “cURL ERROR: 60: SSL certificate problem”.

       (format "%s %s %s -X POST --data-binary @- %s 2>/dev/null"
               g-curl-program g-curl-common-options g-cookie-options
               (g-auth-url (g-auth-service auth-handle)))

2. В модуле greader.el:

2.1. В функции greader-post-authenticate-function оставим только проверку auth handle, так как токен был уже установлен в функции g-authenticate в модуле g-auth.el:

(unless (greader-p (g-auth-service auth-handle))
    (error "This auth handle is not for Google Reader."))) 

Функция greader-post-authenticate-function у меня в итоге выглядит так:

(defun greader-post-authenticate-function (auth-handle)
  "Run Googlre Reader post-auth steps."
  (unless (greader-p (g-auth-service auth-handle))
    (error "This auth handle is not for Google Reader.")))

2.2. В функции greader-preferences при обращении к функции g-json-get-result в строке format вместо “–cookie SID=’%s’” необходимо оставить просто “%s”. А в значениях параметров вместо “(g-cookie “SID” greader-auth-handle)” указать “(g-authorization greader-auth-handle)”. Этот параметр задаёт header с токеном Auth для нового вида авторизации. Аналогичные исправления необходимо сделать во всех остальных местах – в функциях greader-feed-list, greader-tag-list, greader-tagged-reading-list, greader-update-subscription, greader-add-label, greader-star. То есть, фрагмент кода выглядел так:

(format
  "%s %s --cookie SID='%s' %s 2>/dev/null"
  g-curl-program g-curl-common-options
  (g-cookie "SID" greader-auth-handle)

А исправленный вариант выглядит так:

(format
  "%s %s %s %s 2>/dev/null"
  g-curl-program g-curl-common-options
  (g-authorization greader-auth-handle)

Теперь необходимо изменить кодировку в файле atom-view.xsl – “iso8859-15″ на “utf-8″. Это необходимо для корректного преобразования кириллицы в процессе XSLT-преобразования.

В общем-то это всё. Теперь можно перекомпилировать измененные файлы и запускать команду “M-x greader-reading-list”. Если все будет успешно, то запросится пароль Google, а затем отобразится html-страница с новостями Google Reader.

Если возникает сообщение “Error=BadAuthentication”, значит некорректно настроена аутентификация curl. Используйте отладку. Я для отладки curl собирал результирующую командную строку curl и запускал её в cygwin bash. А вообще, в процессе решения этой проблемы с g-client мне пришлось разобраться в основах языка ELisp, его компиляции и в механизмах его отладки в emacs (директива debug и интерактивный отладчик edebug). Но отладка Lisp – это отдельная большая тема. Которая, к тому же хорошо освещена в документации. Интерактивный отладчик edebug сильно помогает, но почему-то у меня не получилось с помощью edebug отлаживать функцию, которая вызывается не непосредственно, а через цепочку вызовов из других функций. В случае таких цепочек вызовов приходилось включать edebug для всей цепочки. И еще одно неудобство – нельзя посмотреть (или я просто пока не нашел как) для каких функций включен edebug.

Долгое время у меня после выполнения команды “M-x greader-reading-list” и ввода пароля в буфере “g scratch” возникала следующая ошибка: “warning: failed to load external entity “c:/Documents” cannot parse c:/Documents”. Проблема решилась перекомпиляцией некоторых модулей emacs-w3m командой “M-x byte-compile-file”.

У меня браузер emacs-w3m отображает результирующую html-страницу отвратительно. Зачем-то добавляет в тэги a параметр HSEQ (см. приведенные ниже скриншоты). Многие гиперссылки не отображает как гиперссылки. Но это проблемы emacs-w3m, а не g-client. Тот же самый html-файл прекрасно отображается в Google Chrome.

После всех настроек после запуска функции g-display-result (она вызывается из функции greader-reading-list) у меня html-страница отображается, но возникает сообщение “edebug-after: Wrong type argument: char-or-string-p, #”. Эта ошибка возникает из-за передачи имени буфера без кавычек в emacs-w3m:

w3m-string-match-url-components-1(#)
(condition-case nil (string-match w3m-url-components-regexp string) (error (w3m-string-match-url-components-1 string)))
(let ((string base)) (condition-case nil (string-match w3m-url-components-regexp string) (error ...)))
(w3m-string-match-url-components base)

У меня стояла достаточно новая, но не самая последняя версия emacs-w3m – от 30 июля 2010. Поставил самую последную девелоперсую версию emacs-w3m от 2 сентября.

Я использовал самую последнюю девелоперскую версию emacs-w3m.

Вот скриншоты того, что у нас получилось:

Содержание greader в emacs-w3m

Пример статьи greader в emacs-w3m

Пример содержания из greader в Google Chrome

Пример статьи из greader в Google Chrome

Настройка SSL

Для того, чтобы curl смог работать с сертификатами SSL ему необходимо сообщить где лежат файлы сертификатов. Опция –cacert позволяет задать SSL-сертификат (CA certificate to verify peer against (SSL)). Файл сертификата для curl должен быть в PEM-формате (Privacy Enhanced Mail). Если вы уже пользовались какими-либо сервисами Google, то сертификат у вас скорее всего уже есть. Но браузеры хранят SSL-сертификаты в своем внутреннем формате. Для примера рассмотрим как экспортировать SSl-сертификат из Firefox в pem-формат.

Зайдите на какой-нибудь сервис Google, требующий авторизации (например, на GMail) через Firefox. Будет установлено HTTPS-соединение и строка адреса станет желтой. Кликните на значок замка в строке статуса браузера. Нажмите на кнопку “Посмотреть сертификат” и перейдите на вкладку “Подробности”. С помощью кнопки “Экспортировать” можно экспортировать данный сертификат в файл. Теперь надо выбрать формат файла. Как вы видите в списке есть два формата PEM – “Сертификат X.509 в формате PEM” и “Цепочка сертификатов X.509 в формате PEM” надо выбирать цепочку сертификатов, ввести имя файла и указать расширение “.pem”. Файл сертификата можно положить, например, в директорию /usr/ssl/certs.

Если бы вы выбрали формат “Сертификат X.509 в формате PEM”, то curl возвращал бы ошибку

curl: (60) SSL certificate problem, verify that the CA cert is OK. Details: error:14090086:SSL routines:SSL3_GET_SERVER_CERTIFICATE:certificate verify failed

Теперь надо указать curl использовать данный сертификат. Для этого выполним в emacs команду “M-x customize-option” и введём “g-curl-common-options”. Добавим параметр cacert. В итоге значение переменной g-curl-common-options должно выглядеть так:

--compressed --silent --location --location-trusted --cacert /usr/ssl/certs/mail.google.com.chain.pem

Всё. Сохраняем значение параметра и можем наслаждаться работой greader через SSL.

Заключение

В ходе разбирательств с g-client я много искал в Интернет по этой теме. Информации мало. Есть пост Raman’а от 02.03. 2007 в блоге Emacspeak. Есть Google группа emacs-g-client (организованная также Раманом), но практически неактивная. Есть пост Билла Клементсона (Bill Clemenston) от 6.03.2007, в котором он подробно описывает процесс установки. Я написал Биллу письмо и он ответил следующее: “At some point, the g-client code changed and no longer worked with the code in my blog post. However, at that stage, I had already stopped using both w3m and g-client so I never bothered trying to debug the issues.”

Что ещё осталось сделать:

1. Проверить и при необходимости исправить время жизни токена в соответствии с настройками сервисов Google “Typically, tokens remain valid for 2 weeks”.

Эту задачу я оставляю как домашнее упражнение заинтересовавшимся читателям.

Post comment