(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Интеграция шифрования в Календарь Google при помощи расширений Firefox

Источник: ibm
Натан Харрингтон

Сегодня Web-приложения предоставляют множество возможностей для обмена файлами, совместного доступа и работы. Хотя некоторые из этих приложений поддерживают шифрование пользовательской информации, таких программ - меньшинство. В этой статье обсуждаются средства и программный код, необходимые для поддержки базового шифрования в одном из наиболее популярных онлайновых органайзеров. В статье рассказано, как благодаря удивительной гибкости расширений Firefox и Gnu Privacy Guard можно хранить события Календаря Google зашифрованными, отображая их в виде простого текста только обладателям соответствующих ключей шифрования.

Мы воспользуемся великолепным расширением Элиаса Торреса (Elias Torres) "Google Calendar Quick Add", рассмотрим выбор, изменение и вставку различных компонентов для реализации шифрования событий, без использования безопасного канала данных TLS. На рисунке 1 ниже показан пример того, как будет выглядеть приложение для оператора сервера (слева) и для Интернет-пользователя (справа) после выполнения всех инструкций в данной статье.

Рисунок 1. Зашифрованный Календарь Google

 
Предварительные требования

Оборудование

Любое оборудование, поддерживающее Web-технологии, появившиеся после 2002 года, будет способно работать с кодом, приведенным в данной статье. Алгоритмы шифрования активно используют процессор, поэтому, если на каждой странице вам необходимо дешифровать несколько десятков или сотен событий календаря, для комфортной работы вам понадобится быстрый процессор.

Программное обеспечение

Требуется браузер Firefox 1.5 или выше, а также программа GnuPG - Gnu Privacy Guard. Также пригодятся инструменты разработки расширений Firefox. В разделе Ресурсы приведены ссылки на соответствующие программы.

Хотя эта статья создавалась с использованием ОС Ubuntu 7.10, все основные идеи легко переносятся на множество других операционных систем. Перед началом работы убедитесь в том, что ваша ОС поддерживает Perl, GnuPG и Firefox.

Общий подход

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

Для работы рекомендуется создать новый профиль Firefox. Все функции шифрования данного расширения будет обеспечивать GnuPG. Это избавляет от неэффективного использования JavaScript, одновременно обеспечивая надежное кроссплатформенное управление ключами. Вам понадобится полная установка GnuPG с возможностью выбора удобных для вас ключей шифрования. Также понадобится программа gpg-agent (обычно входит в пакет GnuPG) для обслуживания временного хранилища и удаления истекших паролей.

Имея в наличии рабочую среду разработки создания расширений и GnuPG для выполнения шифрования, мы модифицируем интерфейс, взяв за основу существующее расширение. Расширение Google Quick Add Элиаса Торреса представляет собой хорошую отправную точку для реализации поддержки шифрования. На этапе шифрования вводимый текст сохраняется на диске и шифруется при помощи внешней программы, после чего зашифрованный файл считывается и загружается на серверы Google. При дешифровании каждое событие записывается на диск, расшифровывается и выводится пользователю в виде дешифрованного текста. Альтернативой могло бы быть написание новых протоколов для Firefox. Хотя такой подход может обеспечить более надежный канал данных, для этого вида расширений более простым является организация взаимодействия программ в форме вывода, обработки и чтения.

Прежде чем переходить к коду, приведенному в данной статье, убедитесь в том, что вы располагаете работоспособными программами Firefox, GnuPG и gpg-agent.

Доработка расширения Google Calendar Quick

Расширение Google Calendar Quick Add позволяет добавлять события в ваш календарь с любой страницы, используя API-интерфейс SOAP. Для записи в календарь шифрованных событий мы перехватываем передаваемую информацию и шифруем ее. Можно было бы просто вводить в Календарь Google шифрованные записи в формате ASCII, но предлагаемый подход позволяет автоматизировать этот процесс.

Для начала при помощи команды mkdir ~/calendarEncrypt создадим каталог для хранения программного кода и каталогов расширения. Перейдем в этот каталог и загрузим расширение Quick Google Calendar Quick Add в формате xpi.

Распакуйте расширение при помощи команды unzip quickgooglecal.xpi. Перейдите в созданный каталог chrome и выполните команду unzip quickgooglecal.jar. У вас должно получиться дерево каталогов, приведенное в листинге 1.

Листинг 1. Дерево каталогов Google Calendar Quick Add

calendarEncrypt/chrome.manifest
calendarEncrypt/readme.txt
calendarEncrypt/chrome
calendarEncrypt/chrome/content
calendarEncrypt/chrome/content/hello.xul
calendarEncrypt/chrome/content/overlay.xul
calendarEncrypt/chrome/content/overlay.js
calendarEncrypt/chrome/skin
calendarEncrypt/chrome/skin/overlay.css
calendarEncrypt/chrome/quickgooglecal.jar
calendarEncrypt/chrome/locale
calendarEncrypt/chrome/locale/en-US
calendarEncrypt/chrome/locale/en-US/hello.dtd
calendarEncrypt/chrome/locale/en-US/overlay.dtd
calendarEncrypt/install.rdf
calendarEncrypt/quickgooglecal.xpi

Перейдите в каталог ~/calendarEncrypt и отредактируйте файл install.rdf. Измените значения идентификатора и автора, как показано в листинге 2.

Листинг 2. Изменение значений идентификатора и автора в файле install.rdf
Измените идентификатор:

  <em:id>{E31AE5B1-3E5B-4927-9B48-76C0A701F105}</em:id>

на:

  <em:id>calendarEncrypt@devWorks_IBM.com</em:id>

Также измените имя автора:

    <em:creator>Elias Torres</em:creator>

  на:

    <em:creator>Elias Torres с изменениями devWorks</em:creator>

Отредактируйте файл chrome.manifest, чтобы изменить пакет расширения на основе jar на более удобную для разработчика структуру каталогов. Необходимые изменения показаны в листинге 3.

Листинг 3. Редактирование файла chrome.manifest для изменения формата из jar в структуру каталогов

Исходный chrome.manifest

content quickgooglecal  jar:chrome/quickgooglecal.jar!/content/
overlay chrome://browser/content/browser.xul chrome://quickgooglecal/content/overlay.xul

locale  quickgooglecal  en-US   jar:chrome/quickgooglecal.jar!/locale/en-US/

skin    quickgooglecal  classic/1.0     jar:chrome/quickgooglecal.jar!/skin/
style   chrome://global/content/customizeToolbar.xul
chrome://quickgooglecal/skin/overlay.css

Измененный chrome.manifest:

content quickgooglecal  chrome/content/

overlay chrome://browser/content/browser.xul chrome://quickgooglecal/content/overlay.xul

locale  quickgooglecal  en-US   chrome/locale/en-US/

skin    quickgooglecal  classic/1.0     chrome/skin/
style   chrome://global/content/customizeToolbar.xul
chrome://quickgooglecal/skin/overlay.css

Теперь добавим ссылку на каталог расширения в ваш тестовый профиль Firefox. Например, если ваш профиль расположен в ~/.mozilla/firefox/b2w2sglj.development, создайте ссылку на ~/.mozilla/firefox/b2w2sglj.development/extensions/ под названием calendarEncrypt@devWorks_IBM.com. Добавьте путь к каталогу с Google Quick Add в файл calendarEncrypt@devWorks_IBM.com; в нашем случае это /home/username/calendarEncrypt.

Войдите в Календарь Google и нажмите ctrl+, чтобы активировать расширение Google Calendar Quick Add. Убедитесь в том, что события добавляются как положено, введя событие Test unencrypted event tomorrow 15:30. Убедитесь, что событие было добавлено правильно.

Теперь мы можем вносить изменения в расширение Google Calendar Quick Add для поддержки шифрования и дешифрования событий.

Модификация расширения Quick Add для поддержки прозрачного шифрования

Отправка шифрованных событий

Для автоматизации добавления шифрованных событий в календарь в этой статье используется перехват и шифрование данных расширения Quick Add. Изменение готового расширения для поддержки перехвата показано ниже начиная с листинга 4. Отредактируйте файл chrome/content/hello.xul и удалите строку 69 var quickAddText = number_html(document.getElementById("quickText").value);. Вставьте в строку 69 следующий код.

Листинг 4. Переменные шифрования, извлечение даты и времени.
var words = document.getElementById("quickText").value.split(' ');
   var dayVal = words[words.length-2];
   var timeVal = words[words.length-1];
   var elementData = "";

   for( var n=0; n < words.length-2; n++ ){
     elementData = elementData + " " + words[n];
   }

Код, приведенный выше, предполагает, что дата и время всегда являются двумя последними словами в тексте заголовка события. Чтобы убедиться в корректном добавлении в календарь, все последующие заголовки событий должны иметь формат "текст сообщения дата время", где дата - это "пятница/понедельник/завтра" и т.п., а время - "05:30/10:30" и т.п.

После того как мы отделили текст описания события от даты и времени, нужно записать текст события на диск и зашифровать его. Добавьте в строку 77 код, приведенный ниже в листинге 5.

Листинг 5. Запись на диск, вызов программы шифрования

                
 
   // Записываем текст события на диск
   var fileOut = Components.classes["@mozilla.org/file/local;1"]
                       .createInstance(Components.interfaces.nsILocalFile);
   fileOut.initWithPath("/tmp/calendarEvent");
   var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                           .createInstance(Components.interfaces.nsIFileOutputStream);
   foStream.init(fileOut, 0x02 / 0x08 / 0x20, 0666, 0);
   foStream.write(elementData, elementData.length);
   foStream.close();


   // Запускаем внешнюю программу шифрования
   var fileExe = Components.classes["@mozilla.org/file/local;1"]
                       .createInstance(Components.interfaces.nsILocalFile);
   fileExe.initWithPath("/tmp/CalendarCrypt.pl");
   var process = Components.classes["@mozilla.org/process/util;1"]
                           .createInstance(Components.interfaces.nsIProcess);
   process.init(fileExe);
   var args = ["encrypt"];
   process.run(true, args, args.length);

Первая часть кода, приведенного выше в листинге 5, указывает использовать файл "/tmp/calendarEvent" для записи перехваченного текста событий на диск. Этот файл содержит данные в виде простого текста, но он будет удален и затерт после завершения шифрования. Первая часть кода в листинге 5 указывает использовать файл для рекомендуемого компонента nsIProcess. За шифрование, дешифрование и безопасное удаление отвечает программа на языке Perl /tmp/CalendarCrypt.pl , которая будет описана ниже. Когда шифрование данных будет завершено, код, приведенный ниже в листинге 6, считывает зашифрованный текст события обратно. Добавьте следующий код в строку 98 файла chrome/content/hello.xul.

Листинг 6. Чтение зашифрованного файла, создание текста события

                
   // Считываем обратно зашифрованный текст
   var fileIn = Components.classes["@mozilla.org/file/local;1"]
                       .createInstance(Components.interfaces.nsILocalFile);
   fileIn.initWithPath("/tmp/calendarEvent.asc");
   var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                           .createInstance(Components.interfaces.nsIFileInputStream);
   istream.init(fileIn, 0x01, 0444, 0);
   istream.QueryInterface(Components.interfaces.nsILineInputStream);
 
   var data = "";
   var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                           .createInstance(Components.interfaces.nsIFileInputStream);
  var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
                           .createInstance(Components.                           interfaces.nsIScriptableInputStream);
   // перед строками, разделенными \ для форматирования, не вставляйте разрыв строк

   fstream.init(fileIn, -1, 0, 0);
   sstream.init(fstream);
 
   var str = sstream.read(4096);
   while (str.length > 0) {
     data += str;
     str = sstream.read(4096);
   }
 
   sstream.close();
   fstream.close();
   
   quickAddText = data + "  " + dayVal + " " + timeVal;

Код в листинге 5 выполняет запись текста события в файл и запуск процесса шифрования. Код в листинге 6 выполняет чтение зашифрованного ASCII-текста из файла, добавление информации о дате и времени и возобновление работы расширения quick-add.

Теперь каждое событие, добавляемое при помощи Google Calendar Quick Add, будет перехватываться, шифроваться и размещаться на серверах Google в зашифрованном виде.

Изменение файла overlay.js для чтения зашифрованных сообщений

После того как мы разобрались с кодом в chrome/content/hello.xul, отвечающим за шифрование, добавьте код для дешифрования событий в chrome/content/overlay.js. Вставьте строки, показанные в листинге 7, в конец файла.

Листинг 7. Дешифрование события, определение компонентов

                
window.addEventListener("load", function() { calendarDecryptExtension.init(); }, false);

var calendarDecryptExtension = {
  init: function() {
    var appcontent = document.getElementById("appcontent");   // браузер
    if(appcontent)
      appcontent.addEventListener("DOMContentLoaded", this.onPageLoad, true);
    var messagepane = document.getElementById("messagepane"); // почта
   if(messagepane)
      messagepane.addEventListener("load", function ()       { calendarDecryptExtension.onPageLoad(); }, false);
    // перед строками, разделенными \ для форматирования, не вставляйте разрыв строк
},

Каждый раз при загрузке страницы с событием выполняется вызов функции calendarDecryptExtension при помощи addEventListener. За функцией calendarDecryptionExtension следуют различные определения и перехваты, которые обеспечивают запуск кода при каждой загрузке страницы и передачу в функцию соответствующих данных. Добавьте код, приведенный в листинге 8, вслед за строками из листинга 7, для продолжения дешифрования документа.

Листинг 8. Функция onPageLoad, запись событий

                
  onPageLoad: function(aEvent) {
    dump("pre elemenet \n");

    var elementData = "NODATA";
    var allSpans = content.document.getElementsByTagName("span");

    for (var n = 0; n < allSpans.length; n++){
      if( allSpans[n].innerHTML.indexOf("BEGIN PGP") > -1 ){

        // выходной файл для шифрования текста события
        elementData = allSpans[n].innerHTML;
        var fileOut = Components.classes["@mozilla.org/file/local;1"]
                        .createInstance(Components.interfaces.nsILocalFile);
        fileOut.initWithPath("/tmp/calendarEvent.temp");
        var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
                                .createInstance(Components.                                 interfaces.nsIFileOutputStream);
        // перед строками, разделенными \ для форматирования, не вставляйте разрыв строк
        foStream.init(fileOut, 0x02 / 0x08 / 0x20, 0666, 0);
        foStream.write(elementData, elementData.length);
        foStream.close();       
Все записи календаря заключены в HTML-элементы span. После задания временных переменных производится обработка всех элементов span в цикле for, и если их текст зашифрован, их содержимое выгружается. В листинге 9 показан вызов программы CalendarCrypt.pl с параметром "decrypt" для дешифрования текста события.

Листинг 9. Запуск скрипта шифрования.

                

        // создаем исполняемый файл nsILocalFile
        var fileExe = Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsILocalFile);
        fileExe.initWithPath("/tmp/CalendarCrypt.pl");
        var process = Components.classes["@mozilla.org/process/util;1"]
                                .createInstance(Components.interfaces.nsIProcess);
        process.init(fileExe);
        var args = ["decrypt"];
        process.run(true, args, args.length);
Зашифрованный текст записывается на диск и запускается процесс дешифрования, который создает файл с расшифрованным текстом. Добавьте код, приведенный в листинге 10, для обратного считывания данных и изменения выводимой информации.

Листинг 10. Чтение дешифрованного текста

                

        // входной файл для дешифрования текста события 
        var data = "";
        var fileIn = Components.classes["@mozilla.org/file/local;1"]
                            .createInstance(Components.interfaces.nsILocalFile);
        fileIn.initWithPath("/tmp/calendarEvent.decrypted");
        var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                                .createInstance(Components.interfaces.nsIFileInputStream);
        istream.init(fileIn, 0x01, 0444, 0);
        istream.QueryInterface(Components.interfaces.nsILineInputStream);
        var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
                                .createInstance(Components.interfaces.nsIFileInputStream);
        var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
                                .createInstance(Components.                                 interfaces.nsIScriptableInputStream);
        // перед строками, разделенными \ для форматирования, не вставляйте разрыв строк
        fstream.init(fileIn, -1, 0, 0);
        sstream.init(fstream);

        var str = sstream.read(4096);
        while (str.length > 0) {
          data += str;
          str = sstream.read(4096);
        }

        sstream.close();
        fstream.close();

        allSpans[n].innerHTML = data;
Чтение из файла производится аналогично процессу шифрования. Обратите внимание на последнюю строку в листинге 10, которая подставляет в текущий элемент span расшифрованный текст вместо "BEGIN PGP..." исходного текста. Добавьте код, приведенный в листинге 11, для завершения процесса дешифрования.

Листинг 11. Стираем текст на диске, конец цикла

              

        // очищаем данные с диска
        args = ["shred"];
        process.run(true, args, args.length);

      }//если содержимое span зашифровано

    }//для каждого элемента span

  }//при загрузке страницы
}//calendarDecryptExtension

Изменение параметра на "shred" и повторный запуск компонента nsiProcess гарантируют надежное удаление дешифрованного текста из временного хранилища на локальном диске. В следующем разделе рассматривается программа CalendarCrypt.pl, вызов которой был показан выше.

Программа CalendarCrypt.pl

Завершает данный обзор программа CalendarCrypt.pl , показанная в следующем листинге. Обратите внимание на то, что приведенная реализация содержит несколько допущений относительно типичных ситуаций шифрования/дешифрования для обычного пользователя GnuPG. В случае необходимости поддерживается замена вызова внешней программы и настроек другими параметрами, включающимися при помощи GnuPG::Encrypt. Например, если вам необходимо использовать несколько ключей или ваша конфигурация несовместима с gpg-agent, модуль Perl GnuPG::Encrypt содержит множество параметров, позволяющих встроить программу в вашу рабочую среду. Начните с создания в каталоге /tmp скрипта Perl calendarEncrypt.pl с содержанием, приведенным в листинге 12.

Листинг 12. CalendarEncrypt.pl - заголовок и шифрование
#!/usr/bin/perl -w
# calendarEncrypt.pl - шифрование/дешифрование/удаление файлов
use strict;

die "specify a mode " unless @ARGV == 1;
my $mode = $ARGV[0];

chomp(my $userName = `whoami`);

if( $mode eq "encrypt" )
{
  my $res = `gpg --yes --armor --encrypt -r $userName /tmp/calendarEvent`;
  $res = `shred /tmp/calendarEvent; rm /tmp/calendarEvent`;

После проверки параметров и задания имени пользователя по умолчанию выполняется шифрование файла /tmp/calendarEvent . Очистка и удаление исходного файла с незашифрованным текстом гарантирует, что на диске не останется конфиденциальных данных. В листинге 13 показан режим дешифрования.

Листинг 13. Обработка файла, дешифрование

}elsif( $mode eq "decrypt" )
{

  open(INFILE,"/tmp/calendarEvent.temp") or die "no in file";
  open(OUTFILE,"> /tmp/calendarEvent.encrypted" ) or die "no file out";

    while(my $line =<INFILE>)
    {
      my $begin = substr( $line, 0, 23 );
      print OUTFILE "-----$begin\n";

      my $version = substr( $line, 23, 34 );
      print OUTFILE "$version\n";

      print OUTFILE "\n";

      my $body = substr( $line, 57 );
      $body = substr($body, 0, length($body)-26);

      my @parts = split " ", $body;
      for my $piece( @parts )
      {
        print OUTFILE "$piece\n";
      }

      print OUTFILE "-----END PGP MESSAGE-----\n";
    }#while each line

  close(OUTFILE);
  close(INFILE);

  my $cmd  = qq{ gpg --yes --decrypt /tmp/calendarEvent.encrypted };
     $cmd .= qq{ > /tmp/calendarEvent.decrypted };
  my $res = `$cmd`;

В процессе загрузки, обработки или отображения событий календаря могут быть утеряны некоторые элементы исходного форматирования. В частности, удаляются префикс "-----" в сообщении "BEGIN PGP", а также переносы строки, удаляются. Функции обработки строк в листинге 13 восстанавливают потерянное форматирование перед вызовом команды дешифрования. Наконец, код в листинге 14 выполняет безопасное удаление расшифрованного текстового файла, сохраненного на диске.

Листинг 14. Уничтожение расшифрованных текстовых файлов
elsif( $mode eq "shred" )
{
  my $res = `shred /tmp/calendarEvent.decrypted`;
  $res    = `rm /tmp/calendarEvent.decrypted`;
}

# EOF

После сохранения файла /tmp/CalendarCrypt.pl убедитесь, что файл является исполняемым, выполнив команду chmod a+x /tmp/CalendarCrypt.pl.

Примеры использования

Теперь каждое событие, добавляемое при помощи Google Calendar Quick Add, будет перехватываться, шифроваться и загружаться на серверы Google в зашифрованном виде. Перезагрузите все события из каталога chrome при помощи расширения Extension Developer's Extension или перезапустите Firefox. Используйте сочетание клавиш Ctrl+; и добавьте событие с текстом "Визит к врачу завтра 16:30". Откройте Календарь Google в "обычном" режиме; вы должны увидеть событие, подобное показанному слева на рисунке 1.

Чтобы просмотреть событие в расшифрованном виде, откройте ссылку http://www.google.com/calendar/htmlembed?src=<yourCalendarName>%40gmail.com где <yourCalendarName> это имя вашей учетной записи, например "developer.works" или "bob_smith". При загрузке страницы вы должны увидеть всплывающее окно gpg-agent с запросом вашего пароля. Введите пароль для пользователя, указанного в параметре "uname" команды из раздела 1 программы CalendarCrypt.pl, и ваши события будут расшифрованы и отображены в вашем календаре.

Заключение, дополнительные примеры

При помощи инструментов и кода, описанных в этой статье, вы можете хранить весь текст Календаря Google в зашифрованном виде. Благодаря модификации расширения Firefox Google Calendar Quick Add от Элиаса Торреса каждое добавляемое или просматриваемое событие будет прозрачно шифроваться или дешифроваться. В результате вы получаете возможность лучше контролировать свои личные данные, пользуясь при этом лучшими возможностями приложений Web 2.0.

Можно добавить дополнительный уровень шифрования для изменения сохраняемого времени событий, чтобы снизить эффективность анализа трафика. Создайте собственную программу, использующую интерфейсы SOAP API Google Calendar, для обработки, шифрования и хранения всех прошлых и будущих событий календаря. Попробуйте создать расширение для прозрачного шифрования стандартного интерфейса Календаря Google, используя все возможности Ajax.

Ссылки по теме


 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 10.12.2008 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
IBM RATIONAL Clearcase Floating User From Rational Clearcase Lt Floating User Trade Up License + Sw Subscription & Support 12 Months
Panda Mobile Security - ESD версия - на 1 устройство - (лицензия на 1 год)
SmartBear LoadComplete - Node-Locked License Subscription w/ 250 Virtual Users (includes 1 year of Maintenance)
Allround Automation PL/SQL Developer Single user license
GFI WebMonitor Plus Edition - подписка на 1 год
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Программирование в AutoCAD
СУБД Oracle "с нуля"
OS Linux для начинающих. Новости + статьи + обзоры + ссылки
Новые материалы
Работа в Windows и новости компании Microsoft
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100