Укрощаем мобилу. Пишем mail-forwarder

Дмитрий Докучаев aka Forb

Xakep, номер #050, стр. 090-093

forb@real.xakep.ru

В нашей современной жизни на первом плане стоят вещи, без которых человек уже не способен обходиться. Одна из таких вещей - мобила. Cегодня народ уже не помнит, что когда-то мобила была в диковинку и имелась лишь у избранных. Да, ценности постепенно теряют свою актуальность и дешевеют, а взгляды людей на вещи меняются.

Мобилы бывают разные

Стоимость их колеблется от 50 до 2000$, в зависимости от наличия различных функциональных возможностей. Одна из таких возможностей - снятие мыла через мобилу, некое подобие встроенного Outlook’а. Соответственно, цена на такой телефон будет довольно высокой. А что же делать простым смертным, у которых дешевые телефоны, а тратиться на дорогой не хочется? Здесь тебе поможет смекалка и умение программировать. Программирование, как известно, может пригодиться во многих отраслях наук, в том числе и в мобильной телефонии.

В чем же фишка всего вышесказанного? Начнем по порядку. Каждый уважающий себя провайдер мобильной связи предоставляет такую услугу, как SMS через Internet. Используя эту услугу, можно обмениваться SMS-сообщениями по e-mail как с телефона, так и на телефон. За каждым клиентом закрепляется собственный ящик для SMS, например, +79028863231@sms.uraltel.ru. Пришедшее на него мыло автоматически скидывается клиенту на телефон (не думай, что я буду описывать DoS по SMS :)). Провайдер выделяет также номер, с которого пришедшие на него SMS в формате "mail@host.ru ТЕКСТ СООБЩЕНИЯ" автоматически уходят на заданный e-mail. Вот весь спектр услуг, которые может юзать клиент с любой мобилы (где есть возможность отправлять SMS, естественно). Наша задача состоит в том, чтобы заставить почту приходить с твоего любимого ящика к тебе на телефон в виде SMS.

Итак, что нам потребуется для заветной цели? Во-первых, шелл, где есть поддержка crontab, Perl и работа с сокетами. Именно на таком шелле мы будет обкатывать свой супер-скрипт. Во-вторых, активная услуга SMS через интернет, так как, если она не будет активизирована, ты не сможешь пользоваться SMS через e-mail. В-третьих, зарегистрированный почтовый ящик с любым названием (я опишу, для чего он нужен) с POP3-авторизацией для снятия с него e-mail. И, наконец, прямые руки и стремление к цели.

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

Погружение в кодинг

Теперь пора заняться созданием самого алгоритма. Для проверки сообщений я использовал стандартный Perl-модуль Net/POP3.pm. Он позволяет гибко работать с pop3-сервером без лишнего кода, который имел бы место при использовании IO/Socket.pm. Для отправки сообщений используется обычный sendmail. Ах да, забыл упомянуть, что на шелле он тоже должен быть в рабочем состоянии :). Еще несколько особенностей, чтобы не было путаницы в приведенном ниже коде. У скрипта существуют три фичи. Кратко остановимся на каждой из них.

1. Debug - вывод пошаговой информации или, иными словами, печать того, что делает скрипт в данный момент.

2. Режим удаления сообщений. Тебе решать, что делать со стянутой со своего ящика мессагой - удалять или оставлять ее на сервере. По умолчанию удаление выключено.

3. Указание максимального размера сообщения. Дело в том, что если длина SMS с интернета превышает 160 символов, сервер оператора разбивает сообщение на 2,3 или более сообщений. Я не уверен, что тебе было бы приятно получить по SMS всего «Евгения Онегина», поэтому, если размер сообщения превышает заданное число, то SMS урезается.

А теперь приведу сам скрипт с подробными комментариями. Ты можешь скачать его с http://kamensk.net.ru/forb/1/mail.tar.gz.

Итак, поехали.

#!/usr/bin/perl
### Sms-Forwarder for Mobiles any versions ;]
### By Forb <forb@real.xakep.ru>, ICQ: 304211
use Net::POP3; ## Подгружаем модуль POP3.pm
$debug=0;
$delete=0;
$maxsize=600;
## Фичи скрипта, их назначение смотри выше
$mail='myname@secureserver.ru@mysmallpassword'; ## Данные на ящик, с которого будем перенаправлять почту
$cmail='sms_for_me@mail.ru@smska'; ## Данные на ящик, на который будем отсылать почту с мобильника
$mcenter='me@shell.ru'; ## Адрес отправителя SMS (можно указать твой логин на шелле и хост самого шелла)
$smsmail='+79028863231@sms.uraltel.ru'; ## Почтовый ящик, который предоставляет оператор
my($res)=mconnect($cmail); ## Первоначальная стадия - проверка мыла на реквестовом ящике
$res=$res == 1 ? print "Got SMS. Trying to get new mailn" : exit print "No such
SMSn" if $debug; ## Обработка результата, если выставлен Debug
$step=1; ## Установка глобальной переменной step - далее станет ясно, зачем она нужна
my($res)=mconnect($mail); ## Еще один вызов процедуры mconnect, уже с параметром ящика для перенаправления
$res=$res == 1 ? print "Getting mail. Sending to mobilen" : exit print "No mess
agesn" if $debug; ## Обработка результатов
sendmessage(@send); ## Отправление сообщения на мобилу, если скрипт доживет до этого места
## Вот все тело скрипта. Далее идут две компактные процедуры mconnect() и sendmessage()
sub mconnect {
my($info)=shift; ## Берем параметр в переменную $info
my($stop)=0; ## Обнуляем локальную переменную $stop
my($popuser,$pophost,$poppass)=split('@',$info); ## Получаем имя пользователя, адрес сервера и пароль на ящик
my($pobject)=Net::POP3->new("$pophost", Timeout => 60) || die "Cannot create objectn"; ## Вызываем конструктор New, с указанием хоста и поля Timeout со значением 1 минута
$messages=$pobject->login("$popuser","$poppass"); ## Логинимся на сервер
if ($messages ne '0E0' && $step == 1) { ## Если есть сообщения и шаг второй (проверка второго по счету ящика):
$msg=$pobject->get($messages); ## Стягиваем сообщение
for ($i=1;$i<=$#$msg;$i++) {
my($mess)=$msg->[$i];
chomp($mess);
push(@send,$mess) if $stop; ## Записываем его тело в массив @send
$stop = 1 if ($mess eq '');
break if ($mess eq '.');
}
}
if ($messages eq '0E0') { ## Если сообщений нет, вернем 0 при дебаге, либо просто завершим скрипт
retu 0 if $debug;
exit
}
$pobject->delete(1) if (($delete && $step) || (!$step)); ## Удалим сообщение, если стоит опция $delete, либо если проверяем реквестовый ящик
$pobject->quit;
retu 1; ## Успешно выходим...
}
sub sendmessage {
my(@what)=@_;
my($scal)=0; ## Получаем тело сообщения и обнуляем переменную для получения размера
open(MAIL,"|/usr/sbin/sendmail -t") || die "cant send email $!n"; ## Открываем sendmail для записи данных в него
print MAIL << "EOF";
From: Center <$mcenter>
To: $smsmail
Content-type: text/plain
EOF
## Пишем в него заголовок сообщения: адрес отправителя и ящик адресата
foreach (@what) {
$scal+=length($_); ## Получаем размер каждой строки тела сообщения, суммируя его в $scal
break if ($scal > $maxsize); ## Прерываем цикл при достижении максимального размера
print MAIL "$_n" ## Пишем тело сообщения в дескриптор sendmail'а
}
close(MAIL); ## Закрываем поток
}

Как видишь, все просто. Суть скрипта - это вызов mconnect() 2 раза для проверки обоих ящиков и в случае, когда на обоих есть сообщения, пересылка последнего сообщения на мобилу. Метод $self->login() возвращает количество сообщений на ящике, либо 0E0, когда сообщений нет. Затем происходит тонкая работа с элементами массива, ссылка на который возвращается методом $self->get(). Переменная $step нужна, чтобы не переписывать одну и ту же процедуру несколько раз. Как видишь, у меня это получилось =).

А теперь немного расскажу, как юзать эту штуковину. После успешного создания скрипта на всякий случай проверь его синтаксис командой perl -c mail.pl, где mail.pl - твой скрипт. Затем запусти редактор кронтаба командой crontab -e. В нем войди в режим ввода текста (ESC, i) и забивай туда примерно следующее:

0,10,20,30,40,50 * * * * /usr/bin/perl /home/login/work/mail.pl >/dev/null 2>&1 &

, где 0,10,20,30,40,50 - время в минутах для запуска скрипта (в моем случае скрипт будет запускаться каждые 10 минут). Все сообщения, которые он будет выводить, уйдут в /dev/null (если бы этого не было, они бы ушли тебе на почтовый ящик).

Вариации на тему

Ты можешь модифицировать этот скрипт, добавив, например, туда чистку от html-сообщений, либо создав проверку более одного реквестового ящика, по которой можно судить, какой ящик следует проверять и с какого перенаправлять =). Таким образом ты создашь мультипользовательскую среду в одном скрипте. Фантазия тут безгранична. Я же ограничился вышеизложенным вариантом и пользуюсь этой фичей уже больше недели.

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

Содержание
ttfb: 3.6859512329102 ms