- PVSM.RU - https://www.pvsm.ru -
Как известно, практически во всех вагонах Московского Метро действуют Wi-Fi точки доступа, с помощью которых пользователи могут получить доступ в интернет и приятно скоротать время поездки в метро с работы домой: почитать новости, проверить почту, посмотреть котиков на YouTube и т.д.
Каждому устройству, прежде чем ему будет предоставлен доступ к сети интернет, необходимо аутентифицироваться. В первый раз пользователю на указанный номер телефона посылается СМС с кодом, после чего система запоминает MAC-адрес устройства и в дальнейшем пользователю для аутентификации требуется только нажать на ссылку «Войти в интернет» и немного подождать.
Неудобством такой организации системы является то, что даже если пользователю не нужен браузер, а он, например, хочет залезть в почту или почитать твиттер с помощью специализированного приложения, ему все равно необходимо запустить браузер, попытаться получить доступ к какой-нибудь странице, дождаться переадресации, нажать ссылку, дождаться загрузки страницы приветствия (опционально: посмотреть рекламный ролик) и только после этого он сможет воспользоваться искомым приложением.
Если в метро вы не частый гость, то подобная схема может и не вызвать у вас раздражения, однако при ежедневном использовании она все таки надоедает, поэтому, как говорил один известный и харизматичный политик: «Хватит это терпеть!», сегодня мы будем автоматизировать аутентификацию в Московском метро.
В первую очередь нам понадобится приложение Tasker. Раздобыть его можно тут [1] (за небольшую денежку), ну или где-нибудь тут [2] (на свой страх и риск). Лично я предпочел первый вариант и не пожалел.
Tasker — приложение, которое позволяет в зависимости от определенных условий (дата/время/местоположение/состояние устройств/показания датчиков и т.д.) производить определенные действия (отправку сообщений/вывод уведомлений/включение-отключение устройств/отрисовка простеньких интерфейсов и т.д.). Списки и условий, и действий просто огромны и зависят от версии Android и аппаратного оснащения устройства, так что приводить их полностью смысла нет.
Итак, после запуска Tasker, в первую очередь нужно перевести интерфейс на английский язык, ибо перевод тут хромает на обе ноги: Настройки->Интерфейс->Язык->English и перезапустить приложение. Теперь перед нами есть четыре вкладки:
Перейдем на закладку Tasks и создадим новую задачу, назвав ее Metro Auth:
В открывшемся окне нам необходимо для начала определить несколько переменных. Переменные определяются следующим образом:
Так вот, нам необходимо создать следующие переменные:
Помимо простых действий, Tasker предоставляет нам возможность писать скрипты произвольной сложности с помощью нескольких инструментов. Мы будем использовать простой JavaScript:
Здесь необходимо выставить максимальный Timeout выполнения скрипта — 50 секунд, просто на всякий случай. Галочка Auto Exit отвечает за автоматическое завершения действия после завершения основного потока скрипта. В случае если используются асинхронные запросы (наш случай) или функция setTimeout, эту галочку необходимо снимать, а завершение действия определять самостоятельно с помощью функции exit();.
Сам скрипт я представлю в двух вариантах форматирования: приличное форматирование нужно если хочется рассмотреть скрипт, не сломав глаза, и форматирование под узкий экран позволяет скрипту выглядеть более-менее прилично на узком экране телефона. Изначально скрипт набирался на телефоне в «узком» варианте, и уже потом я переформатировал его для статьи:
function getUrl(url1,url2){
url1=url1.split('?')[0];
return url2.length?
(/^http(s?):///i.test(url2)?url2:
(url2[0]=='/'?url1.split('/').slice(0,3).join('/')+url2:url1.split('/').slice(0,-1).join('/')+'/'+url2)
):url1;
}
function getVars(form,tag){
vars='';
fields=form.getElementsByTagName(tag);
for(i=0;i<fields.length;i++)
vars=vars+(i?'&':'')+fields[i].name+'='+fields[i].value;
return vars;
}
function submit(xhr,request,form){
request.url=getUrl(request.url,form.action);
request.method=form.method;
vars1=getVars(form,'input');
vars2=getVars(form,'textarea');
request.vars=vars1||vars2?(vars1?vars1:'')+(vars1&&vars2?'&':'')+(vars2?vars2:''):null;
getPage(request,processPage,xhr);
}
function processPage(xhr,request){
redir=xhr.getResponseHeader('Location');
if(redir){
if(redir==request.url) finalize('Ошибка: циклическое перенаправление');
else{
log('Перенаправлениеnn');
getPage({'url':redir},processPage,xhr);
}
} else {
forms=local('forms').split(',');
id=null;
for(i=0;i<forms.length;i++)
if(xhr.response.getElementById(forms[i])) id=forms[i];
if(id)submit(xhr,request,xhr.response.getElementById(id));
else if(Number(local('debug'))){
log('Формы на странице:n');
forms=xhr.response.getElementsByTagName('form');
if(forms.length)
for(i=0;i<forms.length;i++)
log((i?', "':'"')+forms[i].id+'"');
else log('отсутствуют');
finalize();
} else finalize('Аутентификация успешна');
}
}
function checkConn(xhr,request){
redir=xhr.getResponseHeader('Location');
if(redir){
log('Перенаправлениеnn');
getPage({'url':redir},processPage,xhr);
} else {
log('Аутентификация не требуется');
finalize();
}
}
function log(txt){
logs=logs+(txt?txt:'');
}
function requestToText(request){
return 'URL: '+request.url+'nMethod: '+request.method+', Vars: '+request.vars+'n';
}
function finalize(txt){
log(txt);
if(Number(local('debug'))) alert(logs);
else if(txt) flashLong(txt);
exit();
}
function getPage(request,func,xhr){
if(!request.method) request.method='GET';
if(!request.vars) request.vars=null;
if(!xhr){
xhr=new XMLHttpRequest();
xhr.responseType="document";
xhr.timeout=20*1000;
}
xhr.open(request.method,request.url,true);
xhr.onload=function(){
if(xhr.status==200 || xhr.status==401){
log (requestToText(request)+'HTTP status: '+xhr.status+' '+xhr.statusText+'n');
func(xhr,request);
} else {
log(requestToText(request));
finalize('Ошибка HTTP: '+xhr.status+' '+xhr.statusText);
}
}
xhr.onerror=function(){
log(requestToText(request));
finalize('Ошибка: отсутствует соединение');
}
xhr.ontimeout=function(){
log(requestToText(request));
finalize('Ошибка: таймаут соединения');
}
xhr.send(request.vars);
}
logs='';
getPage({'url':local('url')},checkConn);
function getUrl(url1,url2){
url1=url1.split('?')[0];
return url2.length?
(/^http(s?):///i.test(url2)?
url2:
(url2[0]=='/'?
url1.split('/').slice(0,3).join('/')+url2:
url1.split('/').slice(0,-1).join('/')+
'/'+url2)
):url1;
}
function getVars(form,tag){
vars='';
fields=form.getElementsByTagName(
tag);
for(i=0;i<fields.length;i++)
vars=vars+(i?'&':'')+fields[i].name+
'='+fields[i].value;
return vars;
}
function submit(xhr,request,form){
request.url=getUrl(request.url,
form.action);
request.method=form.method;
vars1=getVars(form,'input');
vars2=getVars(form,'textarea');
request.vars=vars1||vars2?
(vars1?vars1:'')+
(vars1&&vars2?'&':'')+
(vars2?vars2:'')
:null;
getPage(request,processPage,xhr);
}
function processPage(xhr,request){
redir=xhr.getResponseHeader(
'Location');
if(redir){
if(redir==request.url)
finalize('Ошибка: циклическое '+
'перенаправление');
else{
log('Перенаправлениеnn');
getPage({'url':redir},processPage,
xhr);
}
} else {
forms=local('forms').split(',');
id=null;
for(i=0;i<forms.length;i++)
if(xhr.response.getElementById(
forms[i]))
id=forms[i];
if(id)submit(xhr,request,
xhr.response.getElementById(id));
else if(Number(local('debug'))){
log('Формы на странице:n');
forms=xhr.response.
getElementsByTagName('form');
if(forms.length)
for(i=0;i<forms.length;i++)
log((i?', "':'"')+forms[i].id+'"');
else log('отсутствуют');
finalize();
} else finalize(
'Аутентификация успешна');
}
}
function checkConn(xhr,request){
redir=xhr.getResponseHeader(
'Location');
if(redir){
log('Перенаправлениеnn');
getPage({'url':redir},processPage,
xhr);
} else {
log('Аутентификация не '+
'требуется');
finalize();
}
}
function log(txt){logs=logs+(txt?txt:'');}
function requestToText(request){
return 'URL: '+request.url+
'nMethod: '+request.method+
', Vars: '+request.vars+'n';
}
function finalize(txt){
log(txt);
if(Number(local('debug'))) alert(logs);
else if(txt) flashLong(txt);
exit();
}
function getPage(request,func,xhr){
if(!request.method)
request.method='GET';
if(!request.vars)request.vars=null;
if(!xhr){
xhr=new XMLHttpRequest();
xhr.responseType="document";
xhr.timeout=20*1000;
}
xhr.open(request.method,
request.url,true);
xhr.onload=function(){
if(xhr.status==200 ||
xhr.status==401){
log (requestToText(request)+
'HTTP status: '+xhr.status+' '+
xhr.statusText+'n');
func(xhr,request);
} else {
log(requestToText(request));
finalize('Ошибка HTTP: '+
xhr.status+' '+xhr.statusText);
}
}
xhr.onerror=function(){
log(requestToText(request));
finalize('Ошибка: отсутствует '+
'соединение');
}
xhr.ontimeout=function(){
log(requestToText(request));
finalize('Ошибка: таймаут '+
'соединения');
}
xhr.send(request.vars);
}
logs='';
getPage({'url':local('url')},checkConn);
Набор скрипта с телефонной клавиатуры, к сожалению, не располагает к комментариям, но я вкратце опишу алгоритм:
После завершения ввода скрипта, у нас получилась вот такая задача:
С помощью первой иконки в нижнем ряду можно попробовать ее запустить. Теперь самое время спускаться в метро, чтобы настроить ее!
В метро, подключившись к точке доступа, пробуем запустить задачу. Если нет явных проблем с доступностью серверов, то мы увидим сообщение, похожее на то, что изображено на следующем рисунке слева. Внизу мы видим идентификатор формы, которая есть на последней загруженной странице — auth-form. Эта форма — явно наш клиент, вносим ее название в переменную %forms и запускаем задачу еще раз, получаем примерно то, что изображено на следующем рисунке в центре. Новый идентификатор формы — hidden_form. Добавляем ее в переменную %forms, теперь ее значение будет 'auth-form,hidden_form'. Запускаем задачу еще раз и видим примерно то, что изображено на следующей рисунке справа — либо будет форма без идентификатора, либо пометка «отсутствуют» (зависит от ветки метро). Если теперь запустить браузер, будет понятно, что аутентификацию мы прошли. Присваиваем переменной %debug значение «0», и закрываем задачу — тут мы закончили.
Теперь дело за малым — настроить автоматический запуск задачи при подключении к нужной точке доступа. Переходим на закладку Profiles и создаем новый профиль, который будет активироваться после подключения в точке доступа московского метро. После того, как закончим формировать описание точки доступа, Tasker спросит нас с какой задачей связывать это профиль, выбираем, естественно, Metro Auth.
Еще одни нюанс: хотя и редко, но аутентификация все таки слетает, хотя отключения от точки и не происходило. Если не было отключения, не было и переподключения, а значит Tasker не запустит задачу повторно, поэтому мы настроим Tasker так, чтобы аутентификация автоматически проверялась каждые 2 минуты (минимальный возможный интервал), для этого нам надо долгим нажатием на уже сконфигурированное условие вызвать меню, в котором добавить временное условие, в котором установить интервал.
Ну вот и все. Отныне и до тех пор, пока не придется менять идентификаторы в переменной %forms, алгоритм ваших действий при заходе в вагон следующий:
Автор: Hormiga
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/96485
Ссылки в тексте:
[1] тут: https://play.google.com/store/apps/details?id=net.dinglisch.android.taskerm&hl=ru
[2] где-нибудь тут: https://www.google.ru/#newwindow=1&q=tasker+android+%D1%81%D0%BA%D0%B0%D1%87%D0%B0%D1%82%D1%8C+%D0%B1%D0%B5%D1%81%D0%BF%D0%BB%D0%B0%D1%82%D0%BD%D0%BE+%D0%B1%D0%B5%D0%B7+%D1%80%D0%B5%D0%B3%D0%B8%D1%81%D1%82%D1%80%D0%B0%D1%86%D0%B8%D0%B8+%D0%B8+%D1%81%D0%BC%D1%81
[3] Источник: http://geektimes.ru/post/260580/
Нажмите здесь для печати.