Чиним отображение курсов валют в IP телефонах Grandstream [ОБНОВЛЕНО]
Над моим рабочим столом висит телефон Grandstream GXP2110. Достаточно удобная и беспроблемная модель. На экране можно просматривать погоду (достаточно точно и правильно) и курсы валют. И вот эти самые курсы внезапно перестали отображаться.
Пришлось взять в руки tcpdump и посмотреть, откуда телефон берет эти данные. Оказалось, что он шлет запросы типа:
GET http://download.finance.yahoo.com/d/333.txt?s=BTCUSD=X&s=EURUSD=X&s=USDRUB=X&s=EURRUB=X&s=CNYRUB=X&f=l1s
И получает в ответ 404 Not Found. Исправить адрес в настройках нет никакой возможности.
Некоторое время я ждал, что производитель как-нибудь на это отреагирует и поправит прошивку, но этого так и не случилось. Сегодня я зашел на сайт за новой версией прошивки и обнаружил, что модель снята с поддержки, а проблема так и не решена. Значит, пришло время подставлять костыль.
Из множества вариантов подмены адреса я выбрал самый простой – использовать прокси. Под рукой имелся тестовый веб-сервер с nginx. Хотя то же самое можно было бы провернуть с apache или squid.
В расширенных сетевых настройках телефона я задал HTTP proxy = server_address:8000 и убедился, что запросы уходят куда надо. Кстати, эта настройка применяется только после перезагрузки телефона.
Конфигурационный файл nginx:
server { listen 8000; access_log /var/log/nginx/proxy.access.log main; error_log /var/log/nginx/proxy.error.log notice; # для погоды location /forecastrss { proxy_pass http://xml.weather.yahoo.com/forecastrss; } # для валют location /d/333.txt { proxy_pass http://localhost/grandstream/currency.php; } }
Файл currency.php:
<?php $url = 'http://download.finance.yahoo.com/d/quotes.csv?'; $pairs = array(); $f = ""; $params = preg_split('/&/', $_SERVER['QUERY_STRING']); foreach ($params as $param) { if (preg_match('/^s=/', $param)) $pairs[] = $param; elseif (preg_match('/^f=/', $param)) $f = $param; } foreach ($pairs as $pair) { echo file_get_contents($url . $pair . '&' . $f); } ?>
И все завертелось! Конечно, это не самое оптимальное, но вполне работоспособное решение. Одним словом, костыль. Если кто-нибудь напишет что-нибудь более интересное на эту тему, обязательно посмотрю, а может, и использую.
UPDATE:
После более месяца успешной работы оказалось, что провайдер почему-то забанил адрес, по которому я получал данные. Пришлось добавить в скрипт поддержку прокси и заодно ускорить работу путем параллельной обработки запросов.
UPDATE 2: Добавлена поддержка округления курса, потому что на телефоне помещается максимум 8-9 знаков и обрезаются первые цифры.
<?php $url = 'http://download.finance.yahoo.com/d/quotes.csv?'; $proxy_addr = 'addr:port'; $proxy_user = 'user:password'; $round = 2; $pairs = array(); $f = ""; $params = preg_split('/&/', $_SERVER['QUERY_STRING']); foreach ($params as $param) { if (preg_match('/^s=/', $param)) $pairs[] = $param; elseif (preg_match('/^f=/', $param)) $f = $param; } $cm = curl_multi_init(); $cmh = array(); foreach ($pairs as $pair) { $h = curl_setup($url . $pair . '&' . $f); curl_multi_add_handle($cm, $h); $cmh[] = $h; } $running = false; do { curl_multi_exec($cm, $running); curl_multi_select($cm); } while ($running); foreach($cmh as $h) { $content = curl_multi_getcontent($h); if ($round) { list($rate, $name) = explode(',', $content, 2); $rate = round((float)$rate, $round); echo "${rate},${name} "; } else { echo $content; } curl_multi_remove_handle($cm, $h); } curl_multi_close($cm); // === function curl_setup($url) { global $proxy_addr, $proxy_user; $h = curl_init($url); curl_setopt($h, CURLOPT_RETURNTRANSFER, true); curl_setopt($h, CURLOPT_HEADER, false); curl_setopt($h, CURLOPT_TIMEOUT, 10); if ($proxy_addr) curl_setopt($h, CURLOPT_PROXY, $proxy_addr); if ($proxy_user) curl_setopt($h, CURLOPT_USERPWD, $proxy_user); return $h; } ?>