Руководство часть 7: сессии

$ssh->login([ $user [, $password [, $suppress_shell ] ] ])

Sets the username and password to be used when authenticating with the sshd daemon. The username $user is required for all authentication protocols (to identify yourself to the remote server), but if you don’t supply it the username of the user executing the program is used.

The password $password is needed only for password authentication (it’s not used for passphrases on encrypted RSA/DSA identity files, though perhaps it should be). And if you’re running in an interactive session and you’ve not provided a password, you’ll be prompted for one.

By default, Net::SSH::Perl will open a channel with a shell on it. This is usually what you want. If you are tunneling another protocol over SSH, however, you may want to prevent this behavior. Passing a true value in $suppress_shell will prevent the shell channel from being opened (SSH2 only).

Что пишут на Perl

Если коротко: всё, где нужно автоматизировать работу компьютера или преобразование данных, можно написать на Perl. 

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

CGI-скрипты — любые скрипты для обмена данными между сервером и клиентом

Для Perl неважно, в каком виде данные выходят и поступают обратно, — он переведёт их в нужный вид и отправит из одного сервиса в другой

Объединение сервисов или программ. Если у вас есть две программы, одна из которых выдаёт готовый результат в одном формате, а другой он требуется в другом формате, то Perl поможет вам их соединить. Работает это так: вы пишете скрипт, который запускает первую программу, получает оттуда данные, преобразует их в нужный вид и отправляет во вторую программу. В итоге всё это работает как одно целое.

Ключ -e и -E

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

Ключ -e позволяет выполнить код, который находится сразу после этого ключа.
Например, сложить 2 числа:

Чтобы не писать каждый раз символ для перевода на новую строку «\n», очень
удобно использовать вместо оператора «print» оператор «say» — оператор «say» сам
делает переход на новую строку, а ещё «say» удобнее писать, так как это слово
короче чем слово «print». Но для того, чтобы оператор «say» можно было использовать,
нужно сказать:

Если не указать ‘use feature «say»;’, то мы получим ошибку:

Но писать каждый раз ‘use feature «say»;’ слишком утомительно, поэтому
появился новый ключ ‘-E’. Его можно использовать вместо ‘-e’, и он включит
все новые штуки, которые есть в Perl:

Retrieving Cookies

This is very easy to retrieve all the set cookies. Cookies are stored in CGI environment variable HTTP_COOKIE and they will have following form.

key1=value1;key2=value2;key3=value3....

Here is an example of how to retrieving cookies.

#!/usr/bin/perl
$rcvd_cookies = $ENV{'HTTP_COOKIE'};
@cookies = split /;/, $rcvd_cookies;

foreach $cookie ( @cookies ) {
   ($key, $val) = split(/=/, $cookie); # splits on the first =.
   $key =~ s/^\s+//;
   $val =~ s/^\s+//;
   $key =~ s/\s+$//;
   $val =~ s/\s+$//;
	
   if( $key eq "UserID" ) {
      $user_id = $val;
   } elsif($key eq "Password") {
      $password = $val;
   }
}

print "User ID  = $user_id\n";
print "Password = $password\n";

This will produce following result
User ID = XYZ
Password = XYZ123

Одноразовые данные

Иногда вам нужно сохранить переменную в сессии только для следующего запроса. Вы можете сделать это методом (flash англ. — вспышка — прим. пер.). Сохранённые этим методом данные будут доступны только во время следующего HTTP-запроса, а затем будут удалены. В основном такие данные полезны для кратковременных сообщений о состоянии:

+
5.0

добавлено в

5.0

(08.02.2016)

Для сохранения одноразовых данных в течение большего числа запросов используйте метод , который оставит все эти данные для следующего запроса. А если вам надо хранить только определённые данные, то используйте метод :

+
5.0

добавлено в

5.0

(08.02.2016)

Обновление ID сессии

Обновление ID сессии часто используется для защиты приложения от злоумышленников, применяющих атаку фиксации сессии.

Laravel автоматически обновляет ID сессии во время аутентификации, если вы используете встроенный LoginController; но если вы хотите обновлять ID сессии вручную, используйте метод .

+
5.0

добавлено в

5.0

(08.02.2016)

С сессиями можно работать несколькими способами: с помощью метода HTTP-запросов, с помощью фасада Session, или с помощью функции . При вызове функции без аргументов она возвратит весь объект сессии. Например:

Сохранение переменной в сессии

Добавление элемента к переменной-массиву

Чтение переменной сессии

Чтение переменной или возврат значения по умолчанию

Прочитать переменную и забыть её

Получение всех переменных сессии

Проверка существования переменой

Удаление переменной из сессии

Удаление всех переменных

Присвоение сессии нового идентификатора

PHP: Хранение сессий в защищённых куках +31

  • 04.04.17 10:05


YourChief

#325452

Хабрахабр

10000

Криптография, PHP, Symfony, Разработка веб-сайтов

На некоторой стадии развития веб-проекта возникает одна из следующих ситуаций:

  • backend перестаёт помещаться на одном сервере и требуется хранилище сессий, общее для всех backend-серверов
  • по различным причинам перестаёт устраивать скорость работы встроенных файловых сессий
  • Ruby/Ruby on Rails

HMAC-подпись

Сравнение с классическим подходом

достоинства

  • Возрастает производительность веб-приложения, так как небольшая криптографическая операция дешевле сеанса сетевого обмена или доступа к диску для извлечения данных сессии.
  • Возрастает надёжность веб-приложения, так как оно не зависит от внешнего KV-хранилища. Даже если хранилище сессий обеспечено средствами отказоустойчивости, это не наделяет его абсолютной стабильностью: переключение требует времени, а часть проблем (такие как ухудшение сетевой связности между регионами) и вовсе неискоренимы. Зачастую же сессии и вовсе хранятся на единственном сервере, являющимся единой точкой отказа всего веб-приложения.
  • Экономия ресурсов. Не нужно больше хранить сессии, а значит от этого выиграют и владельцы маленьких сайтов, у которых сократится дисковая активность, и освободят несколько серверов владельцы крупных веб-проектов.

недостатки

  • Возрастает объём данных, передаваемый клиентом
  • Имеется ограничение на размер данных в сессии, связанное с ограничениями на размер кук. Обычно это чуть меньше 4 КБ кодированных данных.
  • Клиент может откатить состояние сессии на любое выданное и подписанное ранее значение, криптоподпись которого ещё действительна в текущий момент времени.

Реализации для PHP

  • Безопасность: отсутствие ошибок при использовании криптографии
  • Актуальная кодовая база: поддержка современных версий PHP, отсутствие deprecated-расширений в зависимостях (таких как mcrypt)
  • Наличие тестов: сессии — это один из фундаментальных механизмов, и в основе реального приложения нельзя использовать незрелый код
  • Возможность шифрования: открытое хранилище сессии на клиенте, читаемое клиентом, не всем подходит.
  • Максимально компактное представление данных — ради минимизации оверхеда и запаса ёмкости сессии
  • Встраиваемость через SessionHandlerInterface
Репозиторий Комментарий
github.com/Coercive/Cookie Фактически не библиотека для работы с сессиями вовсе. Ставит шифрованную куку, не подписывая её.
github.com/stevencorona/SessionHandlerCookie Ближе всего к требованиям, но всё же имеет значительные недостатки:
  • Потенциально уязвима к атакам по времени из-за с образцом
  • Нет шифрования
  • Нет тестов
  • Неэкономная упаковка куки
  • Время истечения куки не хранится со значением и не охвачено подписью. Это значит. что клиент, единожды получив данные в сессии, может воспроизводить их бесконечно.
  • Мелкие баги: read() после write() в рамках одного выполнения скрипта показывает не то, что записано и пр.
github.com/mapkyca/Encrypted-Client-Side-Sessions

вместо

Собственная реализация

packagist.org/packages/snawoot/php-storageless-sessionsgithub.com/Snawoot/php-storageless-sessions

  • Обязательное шифрование. Алгоритм и режим — любой симметричный шифр на выбор, доступный в OpenSSL. По умолчанию: AES-256-CTR.
  • HMAC-подпись куки любым хэш-алгоритмом на выбор из ассортимента криптографического расширения Hash. Он же используется для генерации производных ключей шифрования. По умолчанию: SHA-256.
  • Реализованы контрмеры против атак по времени
  • Помимо основного набора данных и времени истечения, подписью охвачен и ID сессии, что оставляет простор для связывания данных сессии с внешними данными.
  • Реализация представлена в виде класса, совместимого с SessionHandlerInterface, а значит её можно прозрачно использовать практически с любыми PHP-приложениями.
  • Минимальный оверхед хранения, привносимый шифрованием и подписью.

«Лёгкий старт в Perl» от GeekBrains

  • Продолжительность курса: 3 урока = 8,5 часов
  • Формат обучения: записи вебинаров

Описание курса

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

  • Разобраться в истории языка Perl
  • Использовать переменные, функции, модули и классы
  • Настраивать окружение
  • Небольшой курс, направленный на ознакомление с Perl
  • Хорошие отзывы о курсе

Кто проводит курс

GeekBrains

  • Образовательный проект, направленный на обучение программированию, веб-дизайну и маркетингу
  • Является частью IT-холдинга Mail.ru Group
  • Участник инновационного центра «Сколково»
  • Опытный преподавательский состав и гарантированное трудоустройство студентов

Соцсети проекта

  • Facebook:  
  • VK: https://vk.com/geekbrainsru 
  • Instagram: https://www.instagram.com/geekbrains.ru 
  • YouTube: https://www.youtube.com/user/progliveru 
  • Telegram: https://t.me/geekbrains_ru

Perl: пример простого веб-сервиса на Plack

08 Dec 2013

В этой статья я покажу, как написать и запустить работать простейшее PSGI-приложение.

Что такое Plack и PSGI

Plack – это библиотека для Perl, которая позволяет разрабатывать
веб-приложения (в том числе веб-фреймворки, если хочется),
абстрагируясь от конкретного веб-сервера,
в котором будет работать приложение (или фреймворк).

PSGI – это спецификация,
определяющая абстрактный интерфейс веб-сервера,
то есть PSGI – это идея,
реализацией которой является Plack.
А вообще для начала можно не зацикливаться на
терминологической точности, и считать, что
Plack и PSGI – примерно одно и то же ^_^

Зачем нужен Plack и PSGI

Если у вас уже есть какой-нибудь Perl-код, работающий под
mod_perl в Apache (и вы еще не знаете про Plack), вы наверняка используете
класс Apache2::Request для получения параметров запроса,
чтения заголовков и т.п.
И Apache2::Response для управления отдаваемым ответом.
Если ваш проект существует достаточно долго,
велики шансы, что начинали вы с Apache 1.x,
и для переезда на 2.x вам пришлось
немножко переписать код.
А если бы вы захотели иметь возможность
запускать свое веб-приложение и через CGI, и через FastCGI, и через mod_perl,
вам пришлось бы
постараться.

С Plack все это осталось в прошлом.
Вы пишете прикладной код, который
полагается на Plack для получения параметров url,
чтения заголовков запроса, установки заголовков ответа
и т.п., и после этого можете включать
приложение в веб-сервер, используя CGI, FastCGI,
mod_perl под Apache 1.3 и 2.x,
отдельные веб-серверы Starman, Corona, Twiggy
и т.п. – код не надо менять,
чтобы приспособить к очередному интерфейсу
веб-сервера.
Удобно!

Предварительно требуется пакет Plack.

Ubuntu:

FreeBSD:

Простейшее приложение под Plack

Создаем файл first.psgi:

Добавив чуть-чуть логики в вычисление переменной
, можно реализовать, например,
alive-проверку, которя будет рассказывать
о состоянии сервиса (доступность БД, свободное место на диске,
память и т.п.)

Включаем приложение в Apache 2.x

Кладем файл first.psgi там, где его
сможет читать apache, и добавляем в
такие настройки:

Обратите внимание, надо дважды подставить полный путь
к psgi-файлу. Готово: на сайте, который обслуживает этот apache, появился
локейшен /time, показывающий текущее время

Готово: на сайте, который обслуживает этот apache, появился
локейшен /time, показывающий текущее время.

Запускаем приложение из командной строки через plackup

Добавляем обработку параметров

Добавим в наше веб-приложение параметр: формат, в котором хотим получить дату.

Модифицированный first.psgi:

Запускаем:

Смотрим на результат:

  • статья в Pragmatic Perl
  • про PSGI в Википедии
  • Plack
  • Plack::Request
  • Plack::Response
  • Plack::ResponseHelper

Simple URL Example : Get Method

Here is a simple URL which will pass two values to hello_get.cgi program using GET method.

Below is hello_get.cgi script to handle input given by web browser.

#!/usr/bin/perl

local ($buffer, @pairs, $pair, $name, $value, %FORM);
# Read in text
$ENV{'REQUEST_METHOD'} =~ tr/a-z/A-Z/;

if ($ENV{'REQUEST_METHOD'} eq "GET") {
   $buffer = $ENV{'QUERY_STRING'};
}

# Split information into name/value pairs
@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%(..)/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

$first_name = $FORM{first_name};
$last_name  = $FORM{last_name};

print "Content-type:text/html\r\n\r\n";
print "<html>";
print "<head>";
print "<title>Hello - Second CGI Program</title>";
print "</head>";
print "<body>";
print "<h2>Hello $first_name $last_name - Second CGI Program</h2>";
print "</body>";
print "</html>";

1;

Session Configurations Options

session.auto_start: автоматически начинает сессию.

По умолчанию выключена.

session.name: задаёт имя текущей сессии и сессионной куки

По умолчанию PHPSESSID.

Может быть изменено с помощью функции

session.save_path: путь по которому сохраняется информация о сессии

По умолчанию tmp директория сервера.

session.gc_maxlifetime: максимальное время жизни

По умолчанию 1440 секунд (24 минуты).

session.cookie_lifetime: время жизни куки, которая отправляется браузеру. По сути это значение, которое мы добавляем к time() когда
задаём

По умолчанию 0.

session.cookie_path: задаёт

По умолчанию ‘/’.

session.cookie_secure: задаёт

Если включить то куки будут отправляться только по HTTPS.

По умолчанию выключена.

session.use_strict_mode:
если включить то SID которые созданы не сервером будут отклонены.

По умолчанию выключена.

session.cookie_httponly: задаёт

Если включить куки будет доступна только по HTTP (и HTTPS). То есть

JavaScript

или
bbscript не смогут получить к куки доступ

По умолчанию выключена.

session.use_cookies: указывает нужно ли сохранять SID в

cookies

на стороне клиента.

По умолчанию включена.

session.use_only_cookies: заставляет сессию использовать
только

cookie

для хранения SID. Работает совместно с session.use_cookies

По умолчанию включена.

session.use_trans_sid: контролирует использование «прозрачных»
SID

Если включить — SID будет добавляться как параметр прямо в URL. Например:

Эту опцию обычно включают только тогда, когда нет поддержки

cookies

По умолчанию выключена.

Пользуйтесь trans sid с осторожностью так как это может поставить под угрозу безопасность пользователя.

  • Пользователь может отправить URL содержащий активный SID другому человеку по email, irc и т.п.
  • URL содержащая активный SID может быть сохранён на публично доступном компьютере.
  • — Пользователь может заходить на ваш сайт с одним и тем же SID который он сохранил в закладках или
    истории браузера.

session.cache_limiter: указывает способ контроля за кэшем во время сессии.

Доступные варианты:

  • nocache
  • private
  • private_no_expire
  • public

По умолчанию
nocache.

Для сессий с

аутентификацией

нужно, чтобы кэширование в браузере было отключено.

session.cookie_samesite: контролирует доступности куки в кроссдоменных запросах.

Доступные варианты: Lax и Strict

Кросдоменные запросы могу быть не только от злоумышленников. Их может делать ваш сайт с разных поддоменов.
Либо вы можете следить за перемешением пользователя между своими проектами.

Что такое сессии во Flask?

С помощью сессий во Flask пользователь также может сохранять данные конкретных пользователей между запросами. Они работают по принципу, который похож на куки, персонализируя посещение сайтов. Чтобы использовать сессию во Flask, необходимо сначала выполнить настройку специального ключа. Он необходим для того, чтобы защитить данные сессии от редактирования. 

Но прочитать информацию, связанную с конкретной сессией, можно и без ключа. Чтобы это сделать, пользователю необходимо воспользоваться объектом session из пакета flask. В целом, это словарь по принципу работы. Но есть и отличие – в его функционал заложено отслеживание изменений.

Во время использования сессий в фреймворке Flask для Python хранение данных в браузере осуществляется в виде куки. Те куки, которые используются для хранения данных сессии – это куки сессии. Но, в отличие от обычных куки, куки сессии отмечаются криптографически. Это означает, что каждый пользователь может видеть их содержимое, но при этом не может изменять без соответствующего секретного ключа.

После того, как будет осуществлена настройка куки для определенной сессии в браузере, необходимо подтвердить подлинность с использованием секретного ключа. Если этого не получается, тогда контент отклоняется, а браузером получаются новые куки для этой сессии.

Web Browsing

To understand the concept of CGI, lets see what happens when we click a hyper link to browse a particular web page or URL.

  • Your browser contacts the HTTP web server and demand for the URL ie. filename.

  • Web Server will parse the URL and will look for the filename in if it finds that file then sends back to the browser otherwise sends an error message indicating that you have requested a wrong file.

  • Web browser takes response from web server and displays either the received file or error message.

However, it is possible to set up the HTTP server so that whenever a file in a certain directory is requested that file is not sent back; instead it is executed as a program, and whatever that program outputs is sent back for your browser to display. This function is called the Common Gateway Interface or CGI and the programs are called CGI scripts. These CGI programs can be a PERL Script, Shell Script, C or C++ program etc.

Wantarray

В Perl есть такая полезная штука, которая позволяет определить, в каком контексте
вызывается функция. Например, мы хотим, чтобы функция вела себя следующим образом:
когда надо возвращала массив, а иначе — ссылку на массив. Это можно реализовать, и
к тому же очень просто, с помощью wantarray. Напишем простую программу для демонстрации:

Что выведет:

Также хотелось бы напомнить про интересную особенность Perl. %hash = @аrray; В этом случае Perl построит хэш вида
     ($array => $array, $array => $array);

Посему, если применять my %hash = my_cool_sub(), будет использована ветка логики wantarray. И именно по этой причине wanthash нет.

Подпрограммы

Подпрограммы определяются с помощью ключевого слова и вызываются просто путем их именования. Если рассматриваемая подпрограмма еще не была объявлена, для ее вызова требуются скобки после имени функции или амперсанд ( & ) перед ним. Но использование & без круглых скобок также неявно передает аргументы текущей подпрограммы вызываемой, а использование & со скобками обходит прототипы.

# Calling a subroutine

# Parentheses are required here if the subroutine is defined later in the code
foo();
&foo; # (this also works, but has other consequences regarding arguments passed to the subroutine)

# Defining a subroutine
sub foo { … }

foo; # Here parentheses are not required

Список аргументов может быть указан после имени подпрограммы. Аргументы могут быть скалярами, списками или хешами.

foo $x, @y, %z;

Параметры подпрограммы не нужно объявлять ни по номеру, ни по типу; фактически, они могут отличаться от звонка к звонку. Любая проверка параметров должна выполняться явно внутри подпрограммы.

Массивы расширяются до своих элементов; хэши расширяются до списка пар ключ / значение; и весь набор передается в подпрограмму как один плоский список скаляров.

Какие бы аргументы ни передавались, подпрограмма может получить доступ к специальному массиву . Элементы являются ссылками на фактические аргументы; изменение элемента изменяет соответствующий аргумент.

Доступ к элементам можно получить, указав его обычным образом.

$_], $_1

Однако результирующий код может быть трудным для чтения, а параметры имеют семантику , что может быть нежелательным.

Одна распространенная идиома — присвоить список именованных переменных.

my ($x, $y, $z) = @_;

Это обеспечивает мнемонические имена параметров и реализует семантику . Ключевое слово указывает на то, что следующие переменные лексической области видимости вмещающего блока.

Другая идиома — убрать параметры . Это особенно часто встречается, когда подпрограмма принимает только один аргумент или для обработки аргумента в объектно-ориентированных модулях.

my $x = shift;

Подпрограммы могут назначать хэш для имитации именованных аргументов; это рекомендуется в Perl Best Practices для подпрограмм, которые, вероятно, когда-либо будут иметь более трех параметров.

sub function1 {
    my %args = @_;
    print "'x' argument was '$args{x}'\n";
}
function1( x => 23 );

Подпрограммы могут возвращать значения.

return 42, $x, @y, %z;

Если подпрограмма не завершается с помощью оператора, она возвращает последнее выражение, вычисленное в теле подпрограммы. Массивы и хэши в возвращаемом значении расширяются до списков скаляров, как и для аргументов.

Возвращенное выражение оценивается в контексте вызова подпрограммы; это может удивить неосторожных.

sub list { (4, 5, 6) }
sub array { @x = (4, 5, 6); @x }

$x = list; # returns 6 - last element of list
$x = array; # returns 3 - number of elements in list
@x = list; # returns (4, 5, 6)
@x = array; # returns (4, 5, 6)

Подпрограмма может обнаружить контекст своего вызова с помощью функции.

sub either {
    return wantarray ? (1, 2)  'Oranges';
}

$x = either; # returns "Oranges"
@x = either; # returns (1, 2)

Время жизни сессии

По умолчанию
куки сессии существуют, пока пользователь не закроет браузер. Но это поведение
можно изменить и явно указать время жизни сессий в куках. Для этого в функции
представления нужно указать:

session.permanent = True

Тогда время
жизни сессии
устанавливается равным параметру

app.permanent_session_lifetime

который по
умолчанию составляет 31 день. Но, можно определить любое другое значение,
например, так:

app.permanent_session_lifetime = datetime.timedelta(days=10)

Теперь сессии
будут максимум храниться 10 дней в браузере клиента. Давайте посмотрим на
работу этого функционала. Если сейчас произвести обновления страницы

127.0.0.1:5000/session

то увидим
увеличение второго значения списка. Выйдем из браузера, снова зайдем и увидим
последнее установленное значения, т.е. сессия сохраняется в браузере. Далее,
изменим значение:

session.permanent = False

также обновим
страницу, выйдем из браузера и при возвращении увидим начальные значения списка
1, 2, 3, 4, т.е. сессия пропала при закрытии окна браузера. Вот так это
работает.

Видео по теме

Flask #1: Что это такое? Простое WSGI-приложение

Flask #2: Использование шаблонов страниц сайта

Flask #3: Контекст приложения и контекст запроса

Flask #4: Функция url_for и переменные URL-адреса

Flask #5: Подключение внешних ресурсов и работа с формами

Flask #6: Мгновенные сообщения — flash, get_flashed_messages

Flask #7: Декоратор errorhandler, функции redirect и abort

Flask #8: Создание БД, установление и разрыв соединения при запросах

Flask #9: Добавление и отображение статей из БД

Flask #10: Способ представления полноценных HTML-страниц на сервере

Flask #11: Формирование ответа сервера, декораторы перехвата запроса

Flask #12: Порядок работы с cookies (куками)

Flask #13: Порядок работы с сессиями (session)

Flask #14: Регистрация пользователей и шифрование паролей

Flask #15: Авторизация пользователей на сайте через Flask-Login

Flask #16: Улучшение процесса авторизации (Flask-Login)

Flask #17: Загрузка файлов на сервер и сохранение в БД

Flask #18: Применение WTForms для работы с формами сайта

Flask #19: Обработка ошибок во Flask-WTF

Flask #20: Blueprint — что это такое, где и как использовать

Flask #21: Blueprint — подключение к БД и работа с ней

Flask #22: Flask-SQLAlchemy — установка, создание таблиц, добавление записей

Flask #23: Операции с таблицами через Flask-SQLAlchemy

Рейтинг
( Пока оценок нет )
Editor
Editor/ автор статьи

Давно интересуюсь темой. Мне нравится писать о том, в чём разбираюсь.

Понравилась статья? Поделиться с друзьями:
Люкс-хост
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: