Как свалить с платформы Google App Engine

Логотип Google App Engine

Примерно с 17 февраля 2011 года гугл без предупреждения стал блокировать доступ к сайтам на платформе GAE. Причем блокировались IP адреса из Германии, Канады, США и многих других “правильных” стран. Прошло 3 месяца, а воз и ныне там, что-либо менять они не собираются.

При таком раскладе можно только распрощаться с этой говноплатформой, но сделать это не так-то просто. Очень жаль потраченного времени на написание CMS под GAE, разработку шаблонов, да и времени на миграцию остается не так много. Но тем не менее, если вам дороги постоянные клиенты, Google не оставляет другого выбора.

К счастью, для работы с приложениями на платформе GAE предусмотрен так называемый remote api. Он то и позволяет забрать или залить данные без использования веб интерфейса. В моем приложении этот интерфейс был реализован, и забрать данные для меня не составило особого труда.

Забрать дамп базы можно так:

./bulkloader.py –dump –app_id=your-app-id –url=http://your-app-id .appspot.com/remote_api –filename=dump.sql3

Дальше надо бы перенести данные на новую CMS, или на худой конец, согнать данные в статику.

Понятно, что статика не позволит интерактивно взаимодействовать с клиентами, и эту часть придется реализовать самостоятельно; однако страницы с описанием товаров/услуг можно оставить в виде статических файлов HTML.

Получить статику можно, натравив на сайт обычный wget с рекурсией; но при невысокой скорости интернета этот процесс может затянуться. Поэтому будет рационально развернуть скачанный дамп на локальной машине, и натравить wget на localhost.

Сначала стартуем свое приложение:
./dev_appserver.py your-app-id
В другой консоли разворачиваем скачанный дамп, используя remote api:
./bulkloader.py –restore –app_id=your-app-id –url=http://127.0.0.1:8080/remote_api –filename=dump.sql3
Используем wget для получения статических файлов HTML:
wget -r http://localhost:8080

Все, теперь у нас есть куча статики, которую можно использовать.

Не исключено, что гугл все-таки одумается, и снимет блокировки айпи адресов на своей стороне. Поэтому окончательно отказываться от GAE я бы не стал, пока что есть возможность поддерживать CMS на локальной машине.

Однако неожиданно выяснилось, что так же просто забрать данные с локальной машины и залить их в гугловский GAE не получится. При попытке скачать dump с локальной машины в один файл, bulk_uploader ругается примерно так:

[ERROR ] Unable to download kind stats for all-kinds download.
[ERROR ] Kind stats are generated periodically by the appserver
[ERROR ] Kind stats are not available on dev_appserver.

Девелоперская среда не умеет сливать все данные локального хранилища в один файл, поэтому данные с локалхоста можно забрать в несколько приемов, по числу сохраненных моделей в локальном хранилище. Примерно так:

./bulkloader.py –dump –app_id=your-app-id –kind=table1 –url=http://127.0.0.1:8080/remote_api –filename=table1.sql3

./bulkloader.py –dump –app_id= your-app-id –kind=table2 –url=http://127.0.0.1:8080/remote_api –filename=table2.sql3

Сохраненные таким образом дампы таблиц без проблем заливаются по очереди на Google App Engine.

Обновление Mplayer и файлы WMV

Очередное обновление Mplayer оказалось неудачным, при попытке проиграть любой файл WMV программа сваливается с вот таким текстом ошибки:

MPlayer interrupted by signal 11 in module: init_video_codec

- MPlayer crashed by bad usage of CPU/FPU/RAM.

Recompile MPlayer with --enable-debug and make a 'gdb' backtrace and

disassembly. Details in DOCS/HTML/en/bugreports_what.html#bugreports_crash.

- MPlayer crashed. This shouldn't happen.

It can be a bug in the MPlayer code _or_ in your drivers _or_ in your

gcc version. If you think it's MPlayer's fault, please read

DOCS/HTML/en/bugreports.html and follow the instructions there. We can't and

won't help unless you provide this information when reporting a possible bug.

Чтобы все заработало снова, надо всего лишь удалить пакет w32codec-all из системы, и все заработает.

Решение весьма странное, поскольку видеокодеки всегда помогали Mplayer’у в обработке различных видеофайлов. Похоже, что последние версии Mplayer’а больше не нуждаются в этом пакете и более того, конфликтуют с кодеками из w32codec-all.

Как скачать видео с сайта: продолжение.

В своей предыдущей статье Как скачать видео с сайта я рассказал о 4-х способах сохранения видеофайлов с сайтов, и самый популярный из них – достать файл из кэша браузера. Однако сейчас ситуация изменилась, после обновления Flash Player стал себя вести по-другому. Он по-прежнему кэширует видеофайлы на диске в директории /tmp, однако невооруженным взглядом такие файлы не видны; команда ls ничего не покажет.

Однако, если начать загрузку интересующего нас видео ролика:

и в командной строке запустить:

$ lsof | grep Flash
plugin-co 12081 ed 16u REG 8,4 32839768 473170 /tmp/FlashXXqNVerC (deleted)

Интересное кино получается. Файл открыт, используется, и при этом удален! Ну и хрен с ним, все равно его можно будет «оживить» простой командой копирования из поддиректории /proc/

Не буду томить, вот оживляющий скрипт:

#!/bin/bash
pid=`pgrep -f flashplayer | tail -1`
file=`lsof -p $pid | grep '/tmp/Flash' | awk 'END{print "/proc/" $2 "/fd/" $4}' | sed 's/[rwu]$//'`
/bin/cp $file "/tmp/movie.flv"

после запуска скрипта интересующее нас видео будет лежать в /tmp/movie.flv

Просмотр первого канала онлайн

Дело было вечером, отходить от компа было лень. И тут начинается какая-то передача, которую не хотел пропустить. Думаю, а не посмотреть ли мне ее онлайн? Зашел на сайт первого канала, кликнул ссылку – и вроде бы заработало все под моей OpenSuSe 11.2. Да только спустя 15 минут просмотра в полноэкранном режиме изображение свернулось в маленький квадрат, оставив остальную часть экрана в черном цвете.

«Интересно девки пляшут», подумал я, поперхнувшись пивом. А не обновить ли мне mplayer и браузерный плагин к нему? Нарушил я золотое правило админа и таки обновил его до последней версии. И получил не совсем то, что хотел, ибо онлайн трансляции первого канала перестали показываться.

Стал я разбираться в чем дело, и нашел вот что. Если взять URL online трансляции и скормить его обновленному mplayer’у, то видим примерно вот такие сообщения об ошибках:

$ mplayer "http://live3.1internet.tv/1tv-efir?key=+qs2TPeN6JUcMK7AQRfDiBKE&wmbitrate=350000"

....
Ahhhh, stream_chunck size is too small: 0
Error while parsing chunk header
Ahhhh, stream_chunck size is too small: 4
Error while parsing chunk header
Ahhhh, stream_chunck size is too small: 0
Error while parsing chunk header
Ahhhh, stream_chunck size is too small: 4
Error while parsing chunk header
Ahhhh, stream_chunck size is too small: 0
Error while parsing chunk header
Ahhhh, stream_chunck size is too small: 4
Error while parsing chunk header
...

Ну все, думаю, приплыли… Сломался mplayer.

Весь мозг себе сломал, пока пытался починить. К слову, mplayer так его и не стал показывать, зато нашлось совсем другое решение. Первый канал еще транслирует версию для Silverlight, которую стоило попробовать.

Silverlight – это поделка Microsoft, который с линуксом не шибко дружит. Однако добрые люди написали порт под Linux c открытым кодом, и назвали его Moonlight. И даже сделали плагин для Firefox, и что самое интересное, плагин работает! Только ставить надо не последнюю версию плагина, а последнюю стабильную (2.4).

После установи плагина можно зайти на страницу онлайн трансляции первого канала, и плагин предложит скачать майкрософтовские кодеки для просмотра трансляции. Скачав их и согласившись с лицензионным соглашением Microsoft, онлайн трансляции первого канала будут доступны для просмотра.

Но опять же, первый канал не позволяет смотреть свои трансляции кому попало, и может отсекать зарубежных посетителей. Тогда для просмотра первого канала вам потребуется российский IP адрес, который легко можно получить, заказав лучший хостинг в своем классе.

JQuery, ColorBox и передача значений из iframe в родительское окно

Однажды мне понадобилась не совсем обычная форма поиска. Она должна была выглядеть симпатично, находиться в модальном окне и возвращать несколько значений в окно ее вызвавшее.

Длительное гугление готовое решение не подсказало, поэтому пришлось все писать самому. В качестве движка я выбрал плагин ColorBox для JQuery, и вот что у меня получилось.

За основу был взят пример Outside Webpage (Iframe), и совсем немного допилен до подходящего состояния.

Интересный момент, изначально открытие iframe происходило по нажатию на ссылку с заданным классом; движок читал свойство href у ссылки и открывал iframe с URLом из атрибута href. Мне же надо было открывать модальное окно по нажатию на кнопку (), а атрибута href у нее стандартами не предусмотрено. Пришлось изобрести свой атрибут, и вроде бы оно работает

Исходный код примера вместе со всеми библиотеками также доступен для скачивания, может кому-то и пригодится.

Первое разочарование от Google App Engine

Логотип Google App Engine

Как я уже писал, Google App Engine представляет собой очень интересную платформу для разработки веб-приложений на Python или Java. Однако совсем недавно Google в одностороннем порядке и без каких-либо уведомлений стал блокировать доступ к платформе googleappengine из некоторых стран. Причем, часть IP адресов были взяты просто от балды, и вместе с Ираном и Северной Кореей в список запрещенных стран попали и многие IP адреса из Германии. Просьба ко всем, кто хостит свои приложения на Google App Engine, отписаться здесь в случае обнаружения блокирования доступа к приложению из “правильной” страны, из которой у вас есть свои посетители.

Голосовые звонки с сайта

Для многих компаний очень важную роль играет обратная связь с клиентами. Компании даже готовы сами оплачивать звонки от клиентов, арендуя номера бесплатного дозвона 8-800-…. Но подобные звонки оплачиваются за счет компании, что делает затраты на связь непредсказуемыми.

Недавно мы разработали одну очень интересную фишку, которая позволит подобным компаниям существенно сократить затраты на связь. Вот небольшая демонстрация того, как это работает:

1. Зебра телеком, кнопка справа с изображением зебры и телефона. Если на нее нажать и разрешить Flash-приложению доступ к микрофону, звонок пойдет в службу техподдержки Зебры.

2. Ростелеком, кнопка справа (Позвоните в справочную службу Ростелеком прямо сейчас). Здесь добавлена возможность посылать сигналы DTMF во время разговора, т.е. можно набрать добавочный номер сотрудника и позвонить ему.

3. Ну и совсем уж маленькая демка для тех, кто хочет оставить голосовое сообщение мне. Если там нажать на кнопку Call, вы дозвонитесь до нашей АТС, где автоответчик предложит вам оставить мне сообщение. Специально оставил все на английском, чтобы вы смогли представить себе возможности нашей кнопки – все можно перерисовать, перевести на любой язык и настроить конкретно под пожелания клиента.

Как это работает. На вашем сайте размещается наше Flash-приложение, которое инициирует звонок и отвечает за передачу голоса. Звонок отправляется на нашу ATC, с которой он может быть направлен куда угодно – на городской номер, на SIP номер, на голосовую почту или на вашу АТС. Система очень гибкая и легко настраивается, от вас лишь требуется указать, куда отправить звонок с сайта.

Звонки на SIP номера, зарегистрированные у нас, будут для вас бесплатными (мы будем брать только фиксированную ежемесячную абонентскую плату за сервис, а сами звонки оплачиваться не будут). Приземление же звонка на городской или мобильный номер будет стоить дополнительных денег, и это обсуждается отдельно.

Если у вас возникнет желание проверить качество передачи голоса, оставьте мне голосовое сообщение и сообщите, куда переслать записанный файл – все сделаем в лучше виде. Если у Вас возникли вопросы, пишите мне или оставьте комментарий к этому сообщению.

Как защитить Asterisk от подбора паролей.

Решил я как-то раз поднять SIP-сервер для своих нужд. Выход во внешние сети был не нужен, поднял я этот сервис только для своих. Не успел его поднять, как доблестные тайваньские хакеры дружно начали брутфорсить логины и пароли на мой астериск. Причем делали это внаглую в многопоточном режиме, цифры подставляли методом научного перебора:

[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"0"' failed for '210.240.176.51' - No matching peer found 
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"1"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"test"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"2"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"3"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"4"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"5"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"6"' failed for '210.240.176.51' - No matching peer found
[Jan 13 09:45:12] NOTICE[15489] chan_sip.c: Registration from '"7"' failed for '210.240.176.51' - No matching peer found
....

С такими темпами они могут вычислить реально существующие внутренние номера телефонов и перейти ко второму этапу – подбору паролей. Чтобы не выдавать проклятым буржуином государственных секретов информацию о существующих номерах, надо в sip.conf в секцию [general] добавить всего одну строчку:

alwaysauthreject = yes

Тогда астериск при неуспешной авторизации будет давать им отбивку 401 Unauthorized, и они не будут знать, верный ли логин был введен. Такая настройка осложнит им жизнь, но полностью от них не избавит.

Вторая вещь, которую следует сделать, это анализировать логи астериска на предмет наличия неверной аутентификации. И в случае обнаружения попыток подбора пароля отправлять подозрительные айпишника в бан.

Для Linux есть подходящее решение, связанное с установкой и настройкой fail2ban, которое построено на iptables. Мне же это решение не подошло, потому что мой Астериск крутится на хостинге с FreeBSD, а там используется ipfw вместо до боли знакомого iptables. Путем долгих экспериментов пришел к тому, что не стоит добавлять все вражеские айпишники в общую очередь в баню. Список правил на пару сотен айпи прилично нагружает VDS, что не есть гуд.

Но ipfw оказался намного лучше, чем казалось на первый взгляд. У него предусмотрен механизм обработки тысяч правил обработки IP адресов, если их загнать в так называемые таблицы ipfw:

Если в firewall.conf добавить одну единственную строчку:

add reject ip from table(15) to me

то все айпишники, оказавшиеся в таблице 15 будут забанены. Причем уже не так уж и важно, сколько их там будет сотня или тысяча, скорость обработки таких списков остается очень высокой, и это намного лучше, чем их обрабатывать обычными правилами вроде таких:

add reject ip from 1.2.3.0/24 to me
add reject ip from 175.12.135.0/24 to me

Правила добавляются в таблицу в любой момент времени, и моментально начинают свою работу. Погуглив немного, я нашел готовый скрипт для FreeBSD, который всех неудачно авторизовавшихся загоняет в бан на сутки. Его можно аккуратно допилить, немного ужесточить правила и все будет работать на отлично.

У таблиц ipfw есть один недостаток, они работают только с IPv4. А так как переход на IPv6 не за горами, то работоспособность чудо-скрипта в ближайшем будущем останется под вопросом.

Как правильно удалять временные файлы

У меня OpenSuSe 11.2 живет уже довольно долго, и пока что ее менять ни на что не собираюсь. Однако в директории /tmp скапливается очень много временных файлов, которые система по умолчанию чистить не хочет. Я уж подумывал над написанием скрипта, который бы при перезагрузке очищал бы содержимое /tmp, но как оказалось, это был бы неправильный подход к решению проблемы.

В системе для этого предусмотрен совсем другой механизм. В Yast имеется редактор /etc/sysconfig, в котором можно настроить периодичность очистки временных файлов, а так же список директорий с временными файлами. Если компьютер с OpenSuSe используется как десктоп, то очистки /tmp при перезагрузке вполне достаточно. Если же он месяцами не перезагружается, то настройка должна быть более тонкой.

В редакторе /etc/sysconfig внутри ветки System => Cron есть специальные переменные, которые все это регулируют. Мои настройки выглядят так:

MAX_DAYS_IN_TMP = 100
MAX_DAYS_IN_LONG_TMP = 100
TMP_DIRS_TO_CLEAR = /tmp
LONG_TMP_DIRS_TO_CLEAR = /var/tmp
OWNER_TO_KEEP_IN_TMP = root
CLEAR_TMP_DIRS_AT_BOOTUP = yes

Пишу в основном для себя, чтобы не забыть. Но если кому-то пригодится, буду рад.

Как докачивать файлы по протоколу SFTP

Однажды мне потребовалось забрать бэкап с одного правильного хостинга. Архив получился большой, примерно в 4 Гб. Скачал где-то 92%, и тут бац – интернет-провайдер подвел, соединение померло.

Я качал файл с помощью утилиты sftp, и она стала показывать stalled вместо прогресса скачивания. Жалко файл было до безумия, заново качать 4 Гб очень не хотелось. Ну должен же быть способ докачивать файлы по протоколу SFTP, сам протокол докачку поддерживает

Стал курить мануалы, и выяснил – надо было качать с помощью scp, а не sftp – эта утилита 100% поддерживает докачку, в отличие от sftp. Ну как говориться, поздно пить боржоми, файл-то уже на 92% скачан. Оказалось, что докачать его все-таки можно, только вместо sftp надо использовать lftp. Выглядело это примерно так:

lftp sftp://mylogin@host.tld
cd /path/to/backup
get -c backup.tgz

lftp смотрит текущую директорию на наличие недокачанного файла, смотрит, сколько байт скачалось и забирает только то, что не удалось скачать в первый раз