Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

KLSoft company - программирование в Delphi и PHP


Информационный Канал Subscribe.Ru

Навигация по сайту
[Новости]  [Скачать]  [Сделать заказ]  [Добавить сайт в каталог]  [Архив рассылки - 52 номера] 

Навигация по рассылке
[Количество слов в строке]  [Изучаем ассемблер в Delphi]  [Поддержка file upload]  [Сессии]  [Humor]
!!!Новость!!!
Всем привет!
На нашем сайте появился новый раздел <каталог сайтов>.
Вы можете добавить свой сайт! Это повысит его ЦТ на несколько единиц!
Взамен, вы должны будете разместить на своём сайте нашу текстовую ссылку.
[Добавить сайт в каталог]
Количество слов в строке
function Seps(As_Arg: Char): Boolean;
begin
  Seps := As_Arg in
    [#0..#$1F, ' ', '.', ',', '?', ':', ';', '(', ')', '/', '\'];
end;

function WordCount(CText: string): Longint;
var
  Ix: Word;
  Work_Count: Longint;
begin
  Work_Count := 0;
  Ix := 1;
  while Ix <= Length(CText) do
  begin
    while (Ix <= Length(CText)) and (Seps(CText[Ix])) do
      Inc(Ix);
    if Ix <= Length(CText) then
    begin
      Inc(Work_Count);

      while (Ix <= Length(CText)) and (not Seps(CText[Ix])) do
        Inc(Ix);
    end;
  end;
  Word_Count := Work_Count;
end;
Изучаем ассемблер в Delphi
Основное предназначение этой статьи, заполнить пробелы в оригинальной документации по Borland Delphi Developer, при этом весь программный код, а так же теория, полность совместимы со всеми версиями Delphi.

Основное направление статьи, это познакомиться с использованием ассемблера в Object Pascal. Однако, не будем пропускать и те аспекты программирования, которые будут требовать пояснения для конкретных примеров, приведённых в этой статье.

Использование Ассемблера в Борландовком Delphi
Перед тем, как начать, хотелось бы определиться с уровнем знаний, необходимых для нормального усвоения данного материала. Необходимо быть знакомым со встроенными средствами отладки в Delphi. Так же необходимо иметь представление о таких терминах как тип реализации (instantiation), null pointer и распределение памяти. Если в чём-то из вышеупомянутого Вы сомневаетесь, то постарайтесь быть очень внимательны и осторожны при воплощении данного материала на практике. Кроме того, будет обсуждаться только 32-битный код, так что понадобится компилятор не ниже Delphi 2.0.

Зачем использовать Ассемблер?
На мой взгляд, Object Pascal, это инструмент, позволяющий генерировать быстрый и эффективный код, однако использование ассемблера в некоторых случаях позволяет решать некоторые задачи более эффективно. За всю работу с Delphi, я пришёл к выводу, что использование низкоуровневого кода необходимо в двух случая.

(1) Обработка большого количества данных. Nb. В данный случай не входит ситуация, когда используется язык запроса данных.

(2) В высокоскоростных подпрограммах работы с дисплеем. Nb. Имеется ввиду использование простых процедур на чистом паскале, но никак не внешних библиотек и DirectX.

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

Что такое Ассемблер?
Надеюсь, что Все читатели этой статьи имеют как минимум поверхностное представление о работе процессора. Грубо говоря, это калькулятор с большим объёмом памяти. Память, это не более чем упорядоченная последовательнось двоичных цифр. Каждая такая цифра является байтом. Каждый байт может содержать в себе значение от 0 до 255, а так же имеет свой уникальный адрес, при помощи которого процессор находит нужные значения в памяти. Процессор так же имеет набор регистров (это можно расценить как глобальные переменные). Например eax,ebx,ecx и edx, это универсальные 32-битные регистры. Это значит, что самое большое число, которое мы можем записать в регистр eax, это 2 в степени 32 минус 1, или 4294967295.

Как мы уже выяснили, процессор манипулирует значениями регистров. Машинный код операции прибавления 10 к значению регистра eax будет выглядеть следующим образом
05/0a/00/00/00
Однако, такая запись абсолютно не читабельна и, как следствие, не пригодна при отладке программы. Так вот Ассемблер, это простое представление машинных команд в более удобном виде. Теперь давайте посмотрим, как будет выглядеть прибавление 10 к eax в ассемблерном представлении:
add eax,10 {a := a + 10}
А вот так выглядит вычитаение значения ebx из eax
sub eax,ebx {a := a - b }
Чтобы сохранить значние, можно просто поместить его в другой регистр
mov eax,ecx {a := c }
или даже лучше, сохранить значение по определённому адресу в памяти
mov [1536],eax {сохраняет значение eax по адресу 1536}
и конечно же взять его от туда
mov eax,[1536]

Однако, тут есть важный момент, про который забывать не желательно. Так как регистр 32-битный(4 байта), то его значение будет записано сразу в четыре ячейки памяти 1536, 1537, 1538 и 1539.

А теперь давайте посмотрим, как компилятор преобразует действия с переменными в машинный код. Допустим у нас есть строка
Count := 0;
Для компилятора это означает, что надо просто запомнить значение. Следовательно, компилятор генерирует код, который сохраняет значение в памяти по определённому адресу и следит, чтобы не произошло никаких накладок, и обзывает этот адрес как 'Count'. Вот как выглядит такой код

mov eax,0
mov Count,eax

Компилятор не может использовать строку типа

mov Count,0
из-за того, что как минимум один параметр инструкции должен являться регистром.
Если посмотреть на строку
Count := Count + 1;
то её ассемблерное представление будет выглядеть как
mov eax,Count
add eax,1
mov Count,eax
Для переменных, тип которых отличается от целого, всё усложняется. Однако, рассмотрим эту тему немного позже, а сейчас предлагаю закрепить теорию практическими примерами.

Итак, рассмотрим первый пример. Сразу извинюсь за тривиальность, но с чего-то надо начинать.

function Sum(X,Y:integer):integer;
begin
 Result := X+Y;
end;

А вот так будет выглядеть оперция сложения двух целых чисел на ассемблере:

function Sum(X,Y:integer):integer;
begin
 asm
  mov eax,X
  add eax,Y
  mov Result,eax
 end;
end;

Этот код прекрасно работает, однако он не даёт нам преимущества в скорости, а так же потерялось восприятие кода. Но не стоит огорчаться, так как те немногие знания, которые Вы почерпнули из этого материала, можно использовать с большей пользой. Допустим, нам необходимо преобразовать явные значения Red,Green, и Blue в цвета типа TColor, подходящие для использования в Delphi. Тип TColor описан как 24-битный True Colour хранящийся в формате целого числа, то есть четыре байта, старший из которых равен нулю, а далее по порядку красный, зелёный, синий.

function GetColour(Red,Green,Blue:integer):TColor;
begin
 asm
{ecx будет содержать значение TColor}
  mov ecx,0
{начинаем с красной компоненты}
  mov eax,Red
{необходимо убедиться, что красный находится в диапазоне 0<=Red<=255}
  and eax,255
{сдвигаем значение красного в правильное положение}
  shl eax,16
{выравниваем значение TColor}
  xor ecx,eax
{проделываем тоже самое с зелёным}
  mov eax,Green
  and eax,255
  shl eax,8
  xor ecx,eax
{и тоже самое с синим}
  mov eax,Blue
  and eax,255
  xor ecx,eax
  mov Result, ecx
 end;
end;

Заметьте, что я использовал несколько бинарных операций. Эти операции также определены непосредственно в Object Pascal.
Поддержка file upload
PHP может принимать файлы, загруженные из любого браузера, отвечающего стандартам RFC-1867 (которыми являются, например, Netscape Navigator 3 или cтарше, Microsoft Internet Explorer 3 с исправлениями от Microsoft, или cтарше). Эта возможность позволяет людям загружать файлы. С PHP-аутификацией и функциями манипулирования файлами, вы имеете полный контроль над тем, кому позволять загружать файлы и что должно быть выполнено с файлом, если он был загружен.
Экран загрузки файла может быть организован созданием специальной формы, которая выглядит примерно так:

Пример 2-3. Форма загрузки файла

<FORM ENCTYPE="multipart/form-data" ACTION="_URL_" METHOD=POST>
<INPUT TYPE="hidden" name="MAX_FILE_SIZE" value="1000">
Send this file:<INPUT NAME="userfile" TYPE="file">
<INPUT TYPE="submit" VALUE="Send File">
</FORM>

_URL_ должен указать на php html файл. Cкрытое поле MAX_FILE_SIZE должно предшествовать полю ввода файла и означает максимально допустимый размер файла. Значение определяется в байтах. Для этого файла при успешной загрузке будут определены следующие переменные :
$userfile - Временное имя файла под которым загруженный файл загружается в машину сервера.

$userfile_name - Исходное имя файла в системе отправителя.

$userfile_size - Размер загруженного файла в байтах.

$userfile_type - Тип MIME файла, если броузер предоставил эту информацию. Например может быть "image/gif". Обратите внимание, что компонент вышеупомянутых переменных "$userfile" - это любое значение поля Name тега INPUT c TYPE=file обозначенное в форме загрузки. В приведенном выше примере формы загрузки мы назвали его "userfile".

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

Скрипт PHP, который получает загруженный файл, должен определить, что должно быть выполнено с загруженным файлом. Вы можете, например, использовать переменную $file_size, чтобы отбросить любые файлы, которые являются или слишком маленькими или слишком большими. Вы могли бы использовать переменную $file_type, чтобы отбросить любые файлы, которые не соответствуют некоторым критериям типа. В любом случае, вы должны или удалить файл из временного каталога или переместить эго в другое место.
Файл будет удален из временного каталога в конце запроса, если он не перемещен или переименован.

Пожалуйста обратите внимание, что вебсервер CERN httpd, кажется, удаляет все начинающееся с первого пробела в заголовке content-type mime, полученном от клиента. Пока дело обстоит так, CERN httpd не будет поддерживать возможность загрузки файла.
Сессии
С самого начала PHP все приняли на ура, но как только на этом языке стали создавать достаточно крупные проекты, разработчики столкнулись с новой проблемой – в PHP отсутствовало понятие глобальных переменных! То есть, выполнялся некий скрипт, посылал сгенерированную страницу клиенту, и все ресурсы, используемые этим скриптом уничтожались. Попробую проиллюстрировать: предположим есть две страницы одного сайта, index.php и dothings.php. Исходники к этим страницам выглядят так:

- index.php -
<?php
  $a = “Меня задали на index.php”;
?>
<html><body>
<?php
  echo $a;
?>
</body></html>

- dothings.php -
<html><body>
<?php
  echo $a;
?>
</body></html>

Если выполнить эти два скрипта, то на первой странице мы увидим надпись “Меня задали на index.php”, а вторая страница будет пустой.

Разработчики web-сайтов, недолго думая, стали использовать cookie для хранения глобальных переменных на стороне клиента. Процесс выглядел примерно так: пользователь приходит на главную страницу сайта, делает какие-то действия, и вся информация, связанная с этим пользователем, которая может потребоваться на других страницах сайта, будет храниться у него в браузере в виде cookie. Этот метод меет довольно серьезные минусы, из-за которых от PHP в своё время отвернулось немало разработчиков. Например, нам нужно авторизовать пользователя, чтобы разрешить ему доступ к закрытым (или принадлежащим только ему) разделам сайта. Придёться «кидать» пользователю cookie, который будет служит его последующим идентификатором на сайте. Такой подход становится очень громоздким и не удобным, как только сайт начинает собирать всё больше и больше сведений о поведении пользователя, ведь всю информацию, посылаемую пользователю, желательно кодировать, чтобы её нельзя было подделать. Ещё совсем недавно подделкой cookie можно было «пова
лить» не один чат, а порой и пробраться в чужую почту. К тому же есть ещё на свете странные люди, у которых браузер cookie не поддерживает.

При использовании сессий вся информация хранится не на стороне клиента, а на стороне сервера, и потому лучше защищена от манипуляций злоумышленников. Да и работать с сессиями куда проще и удобнее, так как все данные автоматически проходят через алгоритмы криптографии модуля PHP. В броузере клиента, лишь хранится уникальный идентификатор номера сессии, либо в форме cookie, либо в виде переменной в адресной строке броузера, какой из двух способов использовать для передачи идентификатора сессии между страницами интерпретатор PHPвыбирает сам. Это на 100 безопасно, так как идентификатор сессии уникален, и подделать его практически невозможно (об этом чуть далее, в разделе о безопасности сессий).

Я не буду вдаваться в технологические вопросы устройства механизма работы сессий, а только опишу, как правильно работать с сессиями в PHP.

Как работать с сессиями?
Если вы будете тестировать примеры из статьи (или ваши скрипты) на каком-либо коммерческом хостинге, проблем с работой с сессиями быть не должно. Если же вы сами настраивали ваш сервер (будь то реальный сервер, или эмулятор), могут появляться ошибки примерно такого содержания:

“Warning: open(/var/state/php/sess_6f71d1dbb52fa88481e752af7f384db0, O_RDWR) failed: No such file or directory (2)”.

Это значит всего лишь, что у вас неправильно настроен PHP. Решить эту проблему можно, прописав правильный путь (на существующую директорию) для сохранения сессий в файле php.ini и перезапустить сервер.

Любой скрипт, который будет использовать переменные (данные) из сессий, должен содержать следующую строчку:

session_start();

Эта команда говорит серверу, что данная страница нуждается во всех переменных, которые связаны с данным пользователем (браузером). Сервер берёт эти перемнные (из файла, либо из БД) и делает их доступными. Очень важно открыть сессию до того, как какие-либо данные будут посылаться пользователю; на практике это значит, что функцию session_start() желательно вызывать в самом начале страницы, например так:

<?php
  session_start();
?>
<html>
 <head>
 </head>
 ...

После начала сессии можно задавать глобальные переменные. Это элементарно: вызываем функцию session_register(‘var_name’); и переменная $var_name становится доступной на всех страницах, использующих сессию. Для примера поковыряем программку, приведенную в начале статьи:

- index.php -
<?php
  // открываем сессию
  session_start();
  // задаём значение переменной
  $a = “Меня задали на index.php”;
  // регистрируем переменную с открытой сессией
  // важно: названия переменных передаются функции session_register()
  // без знака $
  session_register(“a”);
?>
<html>
 <body>
  Всё ОК. Сессию загрузили!
  Пройдём, посмотрим что <a href=”dothings.php>там…</a>
 </body>
</html>

- dothings.php -
<?php
  // открываем сессию
  session_start();
?>
<html>
 <body>
 <?php
   echo $a;
 ?>
 </body>
</html>

При запуске этих файлов (в логической последовательности конечно), первый скрипт (index.php) выдаст следующий результат:

Всё ОК. Сессию загрузили! Пройдём, посмотрим что там…
А второй (dothings.php) вот это:

Меня задали на index.php
Переменная $a теперь доступна на всех страницах данного сайта, которые запустили сессии.

Другие полезные функции для работы с сессиями:

· session_unregister(string) – сессия «забывает» значение заданной глобальной переменной;

· session_destroy() – сессия уничтожается (например, если пользователь покинул систему, нажав кнопку «выход»);

· session_set_cookie_params(int lifetime [, string path [, string domain]])–с помощью этой функции можно установить, как долго будет «жить» сессия, задав unix_timestampопределяющий время «смерти» сессии. По умолчанию, сессия «живёт» до тех пор, пока клиент не закроет окно браузера.

Примеры
Теперь обратимся к практическому применению механизма сессий. Давайте рассмотрим пару довольно простых и в то же время полезных примеров.

Авторизация Пользователя
Вопросы по авторизации пользователей с помощью PHP-сессий постоянно задаются в конференциях по web-программированию. Механизм авторизации пользователей в системе с помощью сессий довольно хорош с точки зрения безопасности (см. раздел «Безопасность» ниже).

Наш пример будет состоять из трёх файлов: index.php, authorize.php и secretplace.php. Файл index.php содержит форму, где пользователь введёт свой логин и пароль. Эта форма передаст данные файлу authorize.php, который в случае успешной авторизации допустит пользователя к файлу secretplace.php, а в противном случае выдаст сообщение об ошибке.


Приступим: - index.php -
<html>
 <head>
  <title>Введи пароль, смертный</title>
 </head>
 <body>
  <form action=”authorize.php” method=”post”>
   Логин:<input type=”text” name=”user_name”><br>
   Пароль:<input type=”password” name=”user_pass”><br>
   <input type=”submit” name=”Submit”>
  </form>
 </body>
</html>

- authorize.php -
<?php
  // открываем сессию
  session_start();
  // данные были отправлены формой?
  if($Submit){
    // проверяем данные на правильность... в данном случае я
    // вписал имя пользователя и пароль прямо в код, целесообразней
    // было бы проверить логин/пароль в базе данных и при сов-
    // падении дать доступ пользователю...
    if(($user_name==”cleo”)&&($user_pass==”password”)){
      $logged_user = $user_name;
      // запоминаем имя пользователя
      session_register(“logged_user”);
      // и переправляем его на «секретную» страницу...
      header(“Location: secretplace.php”);
      exit;
    }
  }
  // если что-то было не так, то пользователь получит сообщение об ошибке.
?>
<html><body>
Вы ввели неверный пароль!
</body></html>

- secretplace.php -
<?php
  // открываем сессию
  session_start();
  /*
    просто зайти на эту страницу нельзя... если
    имя пользователя не зарегистрировано, то
    перенаправляем его на страницу index.php
    для ввода логина и пароля... тут на самом деле
    можно много чего сделать, например запомнить
    IP пользователя, и после третьей попытки получить
    доступ к файлам, его закрыть.
  */
  if(!isset($logged_user)){
    header(“Location: index.php”);
    exit;
  }
?>
<html>
 <body>
  Привет, <?php echo $logged_user; ?>, ты на секретной странице!!! :)
 </body>
</html>

Безопасность
Итак, мы умеем передавать идентификатор от одной страницы (PHP-скрипта) к другой (до следующего вызова с нашего сайта), а значит мы можем различать всех посетителей сайта. Так как идентификатор сессии – это очень большое число (128 бит), шансов, что его удастся подобрать перебором, практически нет. Поэтому злоумышленнику остаются следующие возможности:

· на компьютере пользователя стоит «троян», который ворует номера сессий;

· злоумышленник отлавливает трафик между компьютером пользователя и сервером. Конечно, есть защищенный (зашифрованный) протокол SSL, но им пользуются не все;

· к компьютеру нашего пользователя подошел сосед и стащил номер сессии.

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

Впрочем, PHP очень часто можно «обмануть». Давайте рассмотрим возможные точки взлома в программе авторизации пользователя:

· Файл authorize.php – попытка подбора пароля с помощью стороннего скрипта;

· Файл secretplace.php – попытка обмануть программу путём вписывания значений переменной $logged_user в адресной строке браузера, например так:
http://www.yoursite.ru/secretplace.php?logged_user=hacker

Итак, в нашей программе явно видны две «дыры», одна маленькая и не особо заметная, а вот вторая - просто огромная, через которую большинство хакеров и лезет туда, куда не надо.

Как «залатать» дыру номер 1?
Не будем писать тонны кода по блокировке IP-адреса и т.п., а просто проверим, откуда приходит запрос, а точнее с какой страницы пришёл запрос, если это будет любая страница с нашего сайта, то всё нормально, а во всех остальных случаях пускать не будем. Подкорректируем файл authorize.php:

- authorize.php V2 -
<?php
  // открываем сессию
  session_start();
  
  // полный путь к корневой директории где расположены скрипты
  $SERVER_ROOT = “http://localhost/test1/”;
  
  // если пользователь пришёл с любой страницы нашего сайта
  // то он вроде наш...
  // Переменная $HTTP_REFERER всегда доступна по умолчанию
  // и содержит полный адрес ссылающейся страницы...
  // функция eregi() проверяет, начинается ли адрес ссылающейся страницы
  // со значения в переменной $SERVER_ROOT
  
  if(eregi("^$SERVER_ROOT",$HTTP_REFERER)){
    // данные были отправлены формой?
    if($Submit){
      // далее все как раньше
      if(($user_name==”cleo”)&&($user_pass==”password”)){
        $logged_user = $user_name;
        // запоминаем имя пользователя
        session_register(“logged_user”);
        // и переправляем его на «секретную» страницу...
        header(“Location: secretplace.php”);
        exit;
      }
    }
  }
?>
<html><body>
  Вы ввели неверный пароль!
</body></html>

Как избавиться от «дыры» номер 2?
Предположим, у вас есть сайт, где каждый смертный может зарегистрироваться чтобы добавлять сообщения в форум. Естественно, в форуме у некоторых пользователей (админов, модераторов), возможностей больше чем у других, они, например, могут удалять сообщения других пользователей. Уровень доступа пользователя вы храните в сессии, в переменной $user_status, где $user_status = 10 соответствует полному доступу к системе. Пришедшему на сайт злоумышленнику достаточно зарегистрироваться штатным образом, а потом дописать в адресной строке браузера ?user_status=10. Вот и завёлся у вас на форуме новый админ!

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

- secretplace.php V2 -
<?php
  // убираем всё лишнее из адресной строки
  // функция unset() «освобождает» переменную
  unset($logged_user);
  
  // открываем сессию
  session_start();
  
  // и корректируем испорченные перменные.
  // Важно: в этом случае, переменная регистрируется не как новая
  // переменная, а как уже существующая, а потому знак $ не опускается
  session_register($logged_user);
  
  /*
    просто зайти на эту страницу нельзя... если
    имя пользователя не зарегистрировано, то
    перенаправляем его на страницу index.php
    для ввода логина и пароля... тут на самом деле
    можно много чего сделать, например запомнить
    IP пользователя, и после третьей попытки получить
    доступ к файлам, его перекрыть.
  */
  if(!isset($logged_user)){
    header(“Location: index.php”);
    exit;
  }
?>
<html>
 <body>
  Привет, <?php echo $logged_user; ?>, ты на секретной странице!!! :)
 </body>
</html>

Итоги
Механизм сессий – довольно удачная особенность языка PHP. Сессии росты, очень гибки в использовании. Кстати, есть одна, мало где документированная возможность сессий PHP (доступна начиная с версии 4.0.3) – в сессиях можно хранить не только переменные, но и объекты.

Добавление от 14.12.2001
С выходом в свет PHP 4.1.0 – работа с сессиями значительно облегчилась. Все переменные сессий стали доступны из глобального массива _SESSION[‘var_name’]. Самое приятное наверное в том, что при присвоении какого-либо значения любому полю массива, переменная с таким же именем автоматически регистрируется, как переменная сессии, на пр:

<?
  $_SESSION[‘counter’] = 12;
  echo $counter;
?>

выведет на экран броузера число 12.
Humor
- Как жизнь?
- Слоями...
Adobe Photoshop


Cегодня вот... оживленно треплюсь с парнем, сидящим за соседним компом...
и от возбуждения разговором слегка теряю равновесие и почти падаю на него :))..
Он мне говорит: "отойди от меня..."
"отойти на 20 пунктов?" - несмело интересуюсь я...
"Нет", - задумчиво продолжает он... - "отойди на 30 пикселей"...


Едут в поезде 2 программера и 2 юзера, в разных купе.
У программеров 1 билет на двоих, у юзеров по билету на каждого.
Когда проходит контроль, программеры бегут в сортир, там запираются.
Когда контролер стучит в дверь, они ему через окошко билет просовывают.
Алгоритм ясен. Едут обратно тем же составом.
У юзеров 1 билет, у программеров ни одного.
Когда проходит контроль, юзеры бегут в сортир, там запираются.
Программисты стучатся в дверь, оттуда высовывается билет, после чего программисты дружной толпою бегут в другой сортир.
Дальше схема ясна.
Мораль такова: не каждый алгритм, написанный программером, будет првильно применен юзером.
Хотите увидеть здесь свои анекдоты или историю из жизни?
Если да то пишите и отправляёте! Так же через эту форму Вы можете отправлять нам свои вопросы, пожелания, замечания и т.п.


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: comp.soft.prog.program
Архив рассылки
Отписаться
Вспомнить пароль

В избранное