Программизм
 
Программизм
На главную | Графомания | Программизм | Книги | Всячина | Скачать | Ъ?  

Скачивание почты через web-интерфейс

Многие из тех, кто работает в крупных компаниях, предпочитают получать личную почту не в рабочий ящик (типа vassily_pouppkine@bigcompany.com), а в специальный маленький личный ящичек, заведённый на одной из бесплатных почтовых служб.

Одно из главных достоинств этих служб в том, что для чтения почты совсем не обязательно иметь специальную почтовую программу — достаточно зайти на web-сайт почтовой службы броузером. Такой подход хорош, когда приходится читать почту из разных случайных мест — интернет-кафе, компьютеров знакомых и т.п. Если же почту читать из одного места, то пользоваться почтовой программой (например, The Bat!) гораздо удобнее.

Казалось бы, в чём проблема — все почтовые службы предоставляют доступ к ящикам по протоколам pop3/smtp! но я не случайно заговорил о «сотрудниках крупных компаний» — зачастую у них все сервисы, кроме http, перекрыты. Именно им призван помочь описанный здесь скрипт, сохраняющий письма с бесплатных ящиков на локальный диск.

Первое, что приходит на ум, — написать программу на Perl, открывающую http-соединение, скачивающую файл... Но много ли найдётся людей, у которых установлен Perl? Поэтому, чтобы программа была универсальной и доступной, писать её следует на чём-нибудь общедоступном. Например, на WSH.

Windows Scripting Host — революционная технология Microsoft, которая... На самом деле, это всего лишь интерфейс для интерпретаторов командных языков и их расширений. Входит в Windows 98/ME/2000/XP, а также в NT Option Pack. В комплект поставки входят интерпретаторы двух языков — JScript И VBScript. Первый — Microsoft’овская версия JavaScript, второй — нечто очень похожее с синтаксисом, напоминающим Visual Basic. Отличия этих языков крайне незначительны. Учебников по ним в сети море, поэтому на синтаксисе останавливаться не имеет смысла. Расширяются возможности языка при помощи ActiveX-объектов. Найти информацию о расширениях можно в MSDN или на сайте ScriptInfo. Искать дополнительные источники нужно, т.к. документация в MSDN не совсем полная.

Избрав в качестве исходного языка JScript, мы сразу убиваем ещё одного зайца: вместо формирования http-запросов будем использовать ActiveX-компонент Internet Explorer, который сам позаботится о том, чтобы сохранять cookies и данные форм.

Сразу предупреждаю, что приведённая здесь программа — первая, написанная мной на JScript. Возможно, на некоторых вещях я остановлюсь более подробно, чем надо — не судите строго. Возможно, некоторые вещи сделаны не оптимально — комментарии приветствуются.

Рассмотрим сценарий чтения почты:

  1. Открыть заглавную страницу почтовой службы.
  2. Ввести имя и пароль, отправить эту информацию на сервер.
  3. Перейти в папку «Входящие».
  4. Последовательно перебрать все письма в этой папке.
  5. Сохранить письма на диск.
  6. Удалить письма с сервера.

Чтобы письмо было удобнее читать, желательно сохранить его без рекламы спонсора, баннеров и прочего мусора. Для этого на Яndex’е, например, в каждом письме есть ссылка «версия для печати», а в web-интерфейсе почтовой службы net.ru — ссылка «undecoded letter», то есть готовое письмо в виде, пригодном для импорта в почтовую программу. Скрипт должен пройти по этой ссылке и сохранить письмо без мусора.

Итак, вот обещанный скрипт, а потом будут комментарии:

// константы для Internet Explorer
var navOpenInNewWindow = 1;
var navNoHistory = 2;
var navNoReadFromCache = 4;
var navNoWriteToCache = 8 ;

var READYSTATE_UNINITIALIZED = 0;
var READYSTATE_LOADING = 1;
var READYSTATE_LOADED = 2;
var READYSTATE_INTERACTIVE = 3;
var READYSTATE_COMPLETE = 4;


// hardsign - для проектов с хостингом на net.ru
var USER_NAME = "hardsign@hardsign.com";
var PASSWORD = "********";
var START_URL = "http://mail.hardsign.com/";
var C_USER_NAME = "Username";
var C_PASSWORD = "Password";
var INBOX = "InBox";
var SUBMIT = "login";
var MESSAGE = "Message\\.wssp\\?Mailbox=INBOX&MSG=[0-9]+$";
var PLAIN = "MessagePart/INBOX/[0-9]+-P\\.txt";
var DELETE = "href";
var DELETE_NAME = "Mailbox.wssp\\?Mailbox=INBOX&MSG=[0-9]+&Delete=";

// Яndex-почта
var USER_NAME = "hardsign";
var PASSWORD = "********";
var START_URL = "http://mail.yandex.ru/login";
var C_USER_NAME = "login";
var C_PASSWORD = "passwd";
var INBOX = "Входящие";
var SUBMIT = "submit";
var MESSAGE = "msg\\?mesid=[0-9]+&folder=[0-9]+";
var PLAIN = "msg\\?mesid=[0-9]+&printit=yes";
var DELETE = "button";
var DELETE_NAME = "delete";

// mail.ru
var USER_NAME = "hardsign";
var PASSWORD = "********";
var START_URL = "http://www.mail.ru/";
var C_USER_NAME = "Login";
var C_PASSWORD = "Password";
var INBOX = "ВХОДЯЩИЕ";
var SUBMIT = "";
var MESSAGE = "readmsg\\?id=[0-9]+";
var PLAIN = "readmsg\\?id=[0-9]+&template=printmsg\\.tmpl";
var DELETE = "href";
var DELETE_NAME = "movemsg\\?remove&id=[0-9]+";


var ie = WScript.CreateObject("InternetExplorer.Application");
ie.Visible = true;


function GoTo(url) {
  with (ie) {
    if (url!="") Navigate(url);
    while (ReadyState==READYSTATE_COMPLETE) 
      WScript.Sleep(10);
    while (ReadyState!=READYSTATE_COMPLETE) 
      WScript.Sleep(10);
  }
}


WScript.Echo("Logging in...");
GoTo(START_URL);
with (ie.Document) {
  all[C_USER_NAME].value = USER_NAME;
  all[C_PASSWORD].value = PASSWORD;
  if (SUBMIT!="") all[SUBMIT].click(); 
  else forms(0).submit();
}
GoTo("");

WScript.Echo("Searching for inbox...");
var href = "";
with (ie.Document) {
  for (i = 0; i<links.length; i++) {
    if (links(i).innerText==INBOX) {
      href = links(i).href;
      break;
    }
  }
}
if (href=="") {
  WScript.Echo("ERROR: inbox not found!");
  ie.Quit();
  WScript.Quit();
}
GoTo(href);

WScript.Echo("Reading messages...");
var regexp = new RegExp();
regexp.compile(MESSAGE.toUpperCase());
var letters = new Array();
with (ie.Document) {
  for (i = 0; i<links.length; i++) {
    if (regexp.test(links[i].href.toUpperCase())) {
      var found = false;
      for (j = 0; j<letters.length; j++)
        if (letters[j]==links[i].href) { 
          found = true; 
          break; 
        }
      if (!found) letters[letters.length] = links[i].href;
    }
  }
}


WScript.Echo(letters.length+" messages found!");
regexp.compile(PLAIN);
var fs = new ActiveXObject("Scripting.FileSystemObject");
var curDate = new Date();
regexp.compile(PLAIN.toUpperCase());
for (msg = 0; msg<letters.length; msg++) {
  WScript.Echo("  Transferring message #"+msg);
  GoTo(letters[msg]);
  with (ie.Document) {
    for (i = 0; i<links.length; i++) {
      if (regexp.test(links[i].href.toUpperCase())) {
        GoTo(links[i].href);
        if (charset!="windows-1251") {
          charset = "windows-1251";
          ie.Refresh();
          WScript.Sleep(100);
          while (readyState!="complete") 
            WScript.Sleep(100);
        }
        file = fs.OpenTextFile(
          msg+"-"+curDate.getTime()+".msg",
          2,
          true
        );
        try {
          file.Write(body.innerText);
          file.WriteLine();
          GoTo(letters[msg]); // ie.GoBack(); GoTo("");
          if (DELETE=="href") {
            var dregexp = new RegExp();
            dregexp.compile(DELETE_NAME.toUpperCase());
            for (j = 0; j<links.length; j++) 
              if (dregexp.test(links[j].href.toUpperCase())) {
                GoTo(links[j].href);
                break;
              }
          } else if (DELETE=="button") {
            var btn = all[DELETE_NAME];
            if (btn.length!=0) btn = btn[0];
            btn.click();
            GoTo("");
          }
        } catch (E) {
          WScript.Echo("  EXCEPTION: "+E.message);
        }
        break;
      }
    }
  }
}

ie.Quit();
WScript.Echo("Done!");

Чтобы начать работу, выберите один из блоков переменных, где задаются настройки скрипта — для mail.ru, Яndex’а или net.ru и впишите туда название своего почтового ящика и пароль. Если вы пытаетесь адаптировать скрипт для другой почтовой службы, то вот что надо записать в переменные:

  • USER_NAME — название вашего почтового ящика;
  • PASSWORD — пароль к вашему почтовому ящику;
  • START_URL — адрес страницы, с которой начинается работа с почтой;
  • C_USER_NAME — название элемента input, куда вводится имя пользователя;
  • C_PASSWORD — название элемента input, куда вводится пароль;
  • SUBMIT — название кнопки, на которую нужно нажать, чтобы отправить регистрационную форму на сервер. Если название оставить пустым, будет вызван метод forms(0).submit();
  • INBOX — название ящика с входящими сообщениями. Регистрозависимое!
  • MESSAGE — регулярное выражение, которому должны удовлетворять ссылки из ящика на письма;
  • PLAIN — регулярное выражение, которому должны удовлетворять ссылки из писем на их версии для печати.
  • DELETE — если для того, чтобы удалить письмо, надо из письма пройти по ссылке, впишите сюда href, если надать на кнопку — button. Если не хотите удалять письма с сервера, вставьте пустую строку.
  • DELETE_NAME — регулярное выражение, которому должны удовлетворять ссылки из писем на их удаление или имя кнопки, которую надо нажать.

Первым делом надо создать объект InternetExplorer.Application. Кстати, если не устанавливать ему свойство visible в true, то вся работа будет происходить незаметно для пользователя. Однако делать этого я бы не советовал.

Функция GoTo(), как несложно догадаться, открывает переданный ей URL. Сначала она ждёт, чтобы броузер начал скачивать документ, а затем ждёт полного окончания загрузки. Кстати, функция Sleep() в MSDN не описана, но, тем не менее, она есть и работает. Ставить задержку больше 10 миллисекунд не стоит, т.к. иногда переход происходит практически мгновенно. Но и совсем выкидывать Sleep() тоже не нужно — процессор надо освобождать.

Затем, как несложно догадаться, заполняем поля login и password, загружаем страницу с перечнем папок и ищем на ней папку с входящими сообщениями.

Ну, а дальше всё совсем просто. Собираем все ссылки, ведущие на письма, проходимся по ним, находим ссылку на версию для печати, переходим туда и сохраняем полученный текст.

Обратите внимание, что ссылки на письма проверяются на наличие дубликатов — в почтовой системе net.ru из ящика к письму ведут две ссылки. Естественно, сохраняется всё в несортированный массив, а поиск используется линейный — не те объёмы, чтобы огород городить.

В файл записывается body.innerText, а не body.innerHTML, ибо то, что получается при сохранении HTML, в почтовую программу импортировать невозможно. Впрочем, вам может больше понравиться именно HTML.

Если кодировка определена неправильно, то возможно, что Internet Explorer не сможет корректно сохранить текст в файл. В этом случае будет брошено исключение. Чтобы поймать его, применяется конструкция try .. catch, которая хорошо знакома программистам на Java, но в справочнике по JScript в MSDN не описана. Правда, в большинстве случаев изменение свойства Document.charset спасает ситуацию.

После сохранения письма скрипт возвращается на предыдущую страницу и удаляет письмо. Обратите внимание, что кнопка кликается не сама по себе — берётся первый элемент массива. Это помогает в тех случаях, когда на странице две кнопки с одинаковым именем, как, например, на Яndex’е.

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

Кстати, вероятно, похожими скриптами пользуются спаммеры, массово регистрирующие ящики на бесплатных службах типа nldftwkod@mail.ru и рассылающие с них всякий мусор. Однако против них придумали достаточно эффективную защиту — при регистрации надо ввести текст, нарисованный на картинке. Пример такой защиты можно найти на Hotmail и Bee Online.

22.01.2003

Бесплатные почтовые службы* (ссылки открываются в новом окне)


* В лучших традициях Домашней Страницы Васи Пупкина™, но удержаться я не мог, к тому же здесь это вроде как в тему...
Поиск
См. также

Пользователи всех клонов *nix давно уже могут писать командные файлы на любом языке. Для этого надо лишь указать в первой строке имя интерпретатора... »»»

Иногда мне приходится слышать, что Oracle очень сложен в настройке, «вот ××× — совсем другое дело». Перестать обращать внимание на подобные заявления мне помог случай... »»»

Очевидно, что char поместится в int. Во многих моделях памяти размер указателя также равен размеру int. Следовательно, структуру можно заменить массивом... »»»

Рекомендую

e.g.Orius’
Игорь Иртеньев
Вячеслав Шевченко

Copyright notice

ъ) Все материалы, размещённые на странице, являются неотъемлемой собственностью автора с вытекающими отсюда правами, как ©, так и (ъ). Некоммерческое их распространение всячески приветствуется, разумеется, при условии сохранения ссылки на оригинал. Что касается коммерческого использования — пишите письма, договориться можно всегда.

Удивительное рядом

lj userhardsign
Закладки Карта Королёва

Пишите письма

Счётчики

XPEHOMETP™ Рейтинг@Mail.ru