Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых
[Назад] [Содержание] [Дальше]

Практика применения Perl, PHP, Apache, MySQL для активных Web-сайтов

(С) Александр Фролов, Григорий Фролов, 2002

5. Создание программ CGI на языке Perl

5. Создание программ CGI на языке Perl.. 1

Установка интерпретатора Perl. 2

Выполнение установки в RedHat Linux. 3

Выполнение установки в Microsoft Windows. 3

Установка дополнительных модулей Perl 7

Загрузка дистрибутива с Web-узла CPAN.. 8

Утилита PPM для Microsoft Windows. 9

Утилита CPAN для Linux. 10

Настройка конфигурации Apache. 11

Создание виртуального каталога программ CGI 12

Определение виртуального каталога программ CGI 12

Права доступа к каталогу программ CGI 12

Права доступа к файлам программ CGI 13

Первая программа CGI 14

Параметры виртуального Web-узла. 14

Документ HTML со ссылкой на программу CGI 14

Исходный текст программы CGI 16

Просмотр переменных окружения.. 16

Обработка формы... 19

Извлечение содержимого текстовых полей формы.. 23

Обработка флажков с независимой фиксацией. 23

Обработка флажков с зависимой фиксацией. 23

Обработка списка. 24

Обработка данных от графической кнопки. 24

Программа AREF. 25

Применение шаблонов HTML. 26

Шаблоны переменных. 27

Циклические шаблоны.. 29

Условные шаблоны.. 31

Работа с Cookie в программах CGI 34

Заголовок HTTP для создания Cookie. 35

Запись Cookie программой CGI 36

Получение значения Cookie. 37

Изменение значения параметра Cookie. 37

Удаление Cookie. 37

Ограничения на использование Cookie. 37

Обмен данными с сервером электронной почты... 38

Протокол SMTP.. 38

Протокол POP3. 40

Протокол IMAP.. 43

Внутренняя структура электронного сообщения. 43

Заголовок сообщения. 43

Тело сообщения. 45

Наборы символов и кодировка сообщения. 46

Присоединенный файл. 47

Отправка данных из формы по электронной почте. 48

Исходный текст программы urgent_mail.pl 49

Функция win2koi 50

Функция send_mail 50

Обработка формы HTML. 51

Получение электронной почты.. 53

Использование модуля Net::POP3. 53

Использование модуля Mail::POP3Client 57

 

Как мы уже говорили, программы CGI можно создавать с применением различных языков программирования. Язык Perl, на наш взгляд, наиболее удобен для этого. Так как Perl — интерпретируемый язык, то в некоторых случаях можно добиться более высокой скорости работы программ CGI, если составлять их на С или С++. Однако большинство Web-приложений, в том числе такие сложные как Интернет-мгазины, могут быть с успехом реализованы на Perl.

В чем преимущества применения Perl для создания программ CGI?

Вот только некоторые из них:

·         удобные средства извлечения данных из полей форм;

·         мощные функции для работы с текстовыми строками и датами;

·         удобные средства интеграции программ CGI с базами данных;

·         способность программ CGI, составленных на языке Perl, работать на различных компьютерных платформах под управлением самых разных операционных систем;

·         огромное количество готовых модулей Perl, предназначенных для решения самых разных задач программирования, от общесистемных до специализированных, характерных только для Web-приложений;

·         применяя готовые модули, можно значительно сократить размер листингов программ CGI, составленных на Perl, по сравнению с аналогичными программами, но написанными на С или С++;

·         открытость платформы — все модули Perl доступны с исходными текстами и могут быть положены в основу Ваших собственных разработок.

Изучение языка Perl может стать предметом отдельной книги или даже несколько книг. Если Вы никогда не использовали этот язык в своей работе, то мы рекомендуем Вам обратиться к литературе, список которой приведен в библиографическом указателе нашей книги. Перед тем как продолжить работу с нашей книгой, Вы, например, можете начать изучение Perl с книги [12] или с любой другой книги, ориентированной на начинающего программиста Perl. При изложении материала мы, тем не менее, будем рассказывать о тех или иных возможностях Perl, имеющих отношение к обсуждаемой теме.

Установка интерпретатора Perl

Если при установке операционной системы Red Hat Linux 7.1, описанной нами в главе 2 этой книге, Вы следовали нашим рекомендациям, то интерпретатор Perl уже установлен. Что же касается ОС Microsoft Windows, то для нее придется выполнять установку отдельно.

Прежде пытаться работать с Perl в Red Hat Linux, проверьте, были ли установлен Perl в процессе установки ОС или нет. Проще всего это сделать, если ввести в системном приглашении следующую команду:

perl -v

Если в ответ появится показанное ниже сообщение с номером версии Perl, и этот номер не ниже 5.6, можно продолжать работу:

# perl -v

This is perl, v5.6.0 built for i386-linux

Copyright 1987-2000, Larry Wall

Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5.0 source kit.

Complete documentation for Perl, including FAQ lists, should be found on this system using `man perl' or `perldoc perl'.  If you have access to the Internet, point your browser at http://www.perl.com/, the Perl Home Page.

В том случае, когда на экране появляется только сообщение об ошибке «perl: command not found», необходимо установить Perl.

Мы рассмотрим процедуру установки Perl в RedHat Linux и в Microsoft Windows.

Выполнение установки в RedHat Linux

Интерпретатор Perl практически для всех распространенных компьютерных платформ можно загрузить с Web-узла http://www.perl.com.

Для операционной системы RedHat Linux можно загрузить RPM-дистрибутив интерпретатора Perl с Web-узла компании ActiveState. Этот узел расположен в Интернете по адресу http://www/activestate.com (рис. 5-1).

Дистрибутив хранится в файле ActivePerl-5.6.1.629-i686-linux-thread-multi.rpm и его можно установить при помощи утилиты RPM. Вот команда установки, которую следует ввести в системном приглашении:

rpm -i ActivePerl-5.6.1.629-i686-linux-thread-multi.rpm

Рис. 5-1. Web-узел компании ActiveState

Завершив установку, снова введите команду «perlv» для проверки правильности установки интерпретатора Perl.

Выполнение установки в Microsoft Windows

Для установки интерпретатора Perl в операционной системе Microsoft Windows 2000 загрузите с Web-узла компании Active State файл ActivePerl-5.6.1.629-MSWin32-x86-multi-thread.msi (или файл более новой версии) и запустите его на выполнение. Чтобы установить данную версию дистрибутива в ОС Microsoft Windows NT, необходимо дополнительно загрузить и установить программу Microsoft Windows Installer версии 1.1. Ссылку на эту программу можно найти на странице загрузки дистрибутива Perl.

Итак, начнем установку.

Запустите на выполнение загруженный файл ActivePerl-5.6.1.629-MSWin32-x86-multi-thread.msi. На экране появится первое окно мастера установки, показанное на рис. 5-2.

Рис. 5-2. Мастер установки ActivePerl

Щелкните кнопку Next чтобы продолжить работу мастера установки. Вы увидите окно с текстом лицензионного соглашения на использование интерпретатора Perl. Прочитав его, отметьте флажок I accept the terms of the License Agreement (рис. 5-3) и щелкните кнопку Next.

Рис. 5-3. Просмотр текста лицензионного соглашения

Обратите внимание, что хотя Perl и бесплатен, лицензионное соглашение разрешает Вам распространять его дистрибутив только с письменного разрешения компании Active State.

Далее Вам нужно выбрать устанавливаемые компоненты (рис. 5-4).

Рис. 5-4. Выбор устанавливаемых компонентов

Если на данном компьютере не планируется установка Microsoft Internet Information Server, а роль Web-сервера будет играть Apache, компонент Perl ISAPI можно не устанавливать. Рабочий сервер также не нуждается в установке примеров программ и документации, но на компьютер разработчика Web-приложений мы рекомендуем установить эти компоненты.

В следующем окне нужно указать параметры установки Perl (рис. 5-5).

Рис. 5-5. Выбор параметров установки

Если Perl будет использован с Microsoft IIS, отметьте все флажки, расположенные в этом окне. В том случае, если на сервере будет установлен Apache, два нижних флажка отмечать не нужно.

Указав параметры установки, щелкните кнопку Next. На экране появится сообщение о том, что мастер установки готов к копированию дистрибутивных файлов (рис. 5-6).

Рис. 5-6. Запуск процедура копирования дистрибутивных файлов

Для запуска процесса копирования щелкните кнопку Install. Ход копирования будет отображаться в окне, показанном на рис. 5-7.

Рис. 5-7. Идет копирование дистрибутивных файлов

После завершения процесса копирования Вы увидите сообщение о завершении установки (рис. 5-8).

Рис. 5-8. Работа мастера установки завершена

Щелкните кнопку Finish и перезагрузите компьютер. На этом установку Perl можно считать оконченной.

Теперь в ответ на команду «perlv», введенную в системном приглашении, Вы должны увидеть следующее сообщение:

D:\>perl -v
This is perl, v5.6.1 built for MSWin32-x86-multi-thread
(with 1 registered patch, see perl -V for more detail)

Copyright 1987-2001, Larry Wall

Binary build 629 provided by ActiveState Tool Corp. http://www.ActiveState.com
Built 12:27:04 Aug 20 2001

Perl may be copied only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5 source kit.

Complete documentation for Perl, including FAQ lists, should be found on this system using `man perl' or `perldoc perl'.  If you have access to the Internet, point your browser at http://www.perl.com/, the Perl Home Page.

Установка дополнительных модулей Perl

Как мы уже говорили, удобство применения Perl для создания программ CGI во многом определяется наличием большого количества готовых модулей, решающих типичные задачи CGI-программирования. Часть этих модулей устанавливается вместе с дистрибутивом Perl, другие нужно устанавливать дополнительно.

Модули Perl можно бесплатно установить из сети архива Comprehensive Perl Archive Network (CPAN), расположенного в Интернете по адресу http://www.perl.com/CPAN-local/README.html (рис. 5-9).

Рис. 5-9. Главная страница сети CPAN

У Вас есть две возможности установить дополнительный модуль Perl — загрузив его дистрибутив с Web-узла архива CPAN и запустив процедуру инсталляции, или установив его непосредственно через Интернет с помощью специальных утилит установки. Второй способ, очевидно, подходит только в том случае, если Ваш компьютер подключен к Интернету.

Сравнивая различные методы установки, заметим, что если Вы пользуетесь первым методом, то в Вашем распоряжении оказывается дистрибутив модуля. При помощи этого дистрибутива Вы всегда сможете установить модуль, даже если компьютер не подключен к Интернету. Далее, Вы можете включить дистрибутивный файл модулей Perl, необходимых для работы Вашего Web-приложения, в общий дистрибутивный архив этого Web-приложения. Это может в дальнейшем упростить установку Web-приложения на другой компьютер или его переустановку. Дистрибутив модуля необходим, если Ваше Web-приложение разрабатывается для интрасети, не подключенной к Интернету.

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

Рассмотрим использование обоих способов в ОС Linux и Microsoft Windows.

Загрузка дистрибутива с Web-узла CPAN

Каждый модуль Perl идентифицируется по имени. Например, в наших примерах широко используется модуль HTML:Template, значительно упрощающий создание Web-приложений. Это происходит благодаря разделению процессов разработки дизайна Web-узла и программирования активных компонентов. Мы также будем использовать модули драйвера СУБД MySQL с именем DBD:MySQL и некоторый другие.

Чтобы найти нужный модуль, воспользуйтесь ссылкой modules, показанной на рис. 5-9. Далее в появившейся странице выберите ссылку Modules by name. В результате Вы получите доступ к архиву модулей, сгруппированных по именам. На рис. 5-10 мы показали страницу, с которой можно загрузить упомянутый выше модуль HTML:Template. Обратите внимание, что здесь имеется несколько версий модуля, каждый из которых снабжен файлом документации с именем HTML-Template-x.x.readme. Вам нужно выбрать самую новую версию модуля, загрузив архив tar.gz.

Рис. 5-10. Выбор модуля по имени

Загрузив архивный файл, распакуйте его во временном каталоге. В среде ОС Microsoft Windows архив можно распаковать при помощи широко распространенной утилиты WinZip или бесплатной программы Win-GZ (ее можно найти на http://www.download.com).

Что же касается ОС Linux, то там эту операцию можно выполнить либо с помощью утилит gunzip и tar, либо программой Midnight Commander.

Вот пример применения утилит gunzip и tar:

# gunzip HTML-Template-2.3.tar.gz
# tar xf HTML-Template-2.3.tar

В результате во временном каталоге будет создан каталог с именем HTML-Template-2.3. Сделайте его текущим и введите следующие команды в системном приглашении ОС Linux:

# perl Makefile.pl
# make
# make test
# make install

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

Установка модуля из временного каталога в среде Microsoft Windows выполняется аналогично, однако вместо утилиты make необходимо использовать утилиту nmake. Она входит, например, в комплект Microsoft Visual Studio.

Утилита PPM для Microsoft Windows

Если Вам нужно установить дополнительные модули для Active Perl на компьютер, имеющий соединение через Интернет, проще всего воспользоваться утилитой ppm, которая устанавливается вместе с интерпретатором Perl.

Запустите эту утилиту в командном приглашении Microsoft Windows:

I:\>ppm
PPM interactive shell (2.1.5) - type 'help' for available commands.

PPM>

Далее введите команду поиска нужного Вам модуля:

PPM> search HTML-Template
Packages available from http://ppm.ActiveState.com/cgibin/PPM/ppmserver.pl?urn:/
PPMServer:
HTML-Template [1.8]
PPM>

Если модуль найден, установите его при помощи команды install:

PPM> install HTML-Template
Install package 'HTML-Template?' (y/N): y
Installing package 'HTML-Template'...
Bytes transferred: 40203
Installing H:\Perl\html\site\lib\HTML\Template.html
Installing H:\Perl\site\lib\HTML\Template.pm
Writing H:\Perl\site\lib\auto\HTML\Template\.packlist

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

Утилита CPAN для Linux

Для установки дополнительных модулей Perl на компьютер с операционной системой Linux, подключенный к Интернету, воспользуйтесь утилитой cpan.

Запустите эту утилиту с помощью интерпретатора perl, введя в системном приглашении следующую команду:

# perl -MCPAN -e shell

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

We have to reconfigure CPAN.pm due to following uninitialized parameters:

cpan_home, keep_source_where, build_dir, build_cache, scan_cache, index_expire, gzip, tar, unzip, make, pager, makepl_arg, make_arg, make_install_arg, urllist, inhibit_startup_message, ftp_proxy, http_proxy, no_proxy, prerequisites_policy

/usr/lib/perl5/5.6.0/CPAN/Config.pm initialized.

CPAN is the world-wide archive of perl resources. It consists of about 100 sites that all replicate the same contents all around the globe. Many countries have at least one CPAN site already. The resources found on CPAN are easily accessible with the CPAN.pm module. If you want to use CPAN.pm, you have to configure it properly.

If you do not want to enter a dialog now, you can answer 'no' to this
question and I'll try to autoconfigure. (Note: you can revisit this
dialog anytime later by typing 'o conf init' at the cpan prompt.)

Are you ready for manual configuration?
[yes]

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

Когда конфигурирование будет закончено, Вы увидите приглашение утилиты cpan. Введите в нем команду поиска нужного Вам модуля:

cpan> i /HTML-Template/

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

Distribution    S/SA/SAMTREGAR/HTML-Template-2.4.tar.gz
Distribution    S/SA/SAMTREGAR/HTML-Template-Expr-0.01.tar.gz
Module          HTML::Template  (S/SA/SAMTREGAR/HTML-Template-2.4.tar.gz)
Module          HTML::Template::Expr (S/SA/SAMTREGAR/HTML-Template-Expr-0.01.tar.gz)
cpan>

Теперь для установки модуля введите команду install, передав ей в качестве параметра имя устанавливаемого модуля:

cpan:>install HTML::Template

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

. . .
/usr/bin/make test -- OK
Running make install
Installing /usr/lib/perl5/site_perl/5.6.0/HTML/Template.pm
Installing /usr/share/man/man3/HTML::Template.3pm
Writing /usr/lib/perl5/site_perl/5.6.0/i386-linux/auto/HTML/Template/.packlist
Appending installation info to /usr/lib/perl5/5.6.0/i386-linux/perllocal.pod
/usr/bin/make install  -- OK
cpan>

На этом установка модуля считается завершенной.

Настройка конфигурации Apache

Одно из преимуществ сервера Apache заключается в том, что он не допускает запуск никаких программ, если это не разрешено явным образом в конфигурационном файле httpd.conf.

Для того чтобы файлы с расширением имени cgi распознавались сервером Apache как программы CGI, нужно вставить в файл httpd.conf команду следующего вида:

AddHandler cgi-script .cgi

Если сервер Apache работает в операционной системе Microsoft Windows, для разрешения работы программ Perl необходимо снять символ комментария со следующей команды, расположенной в конфигурационном файле httpd.conf:

ScriptInterpreterSource registry

При этом Apache будет распознавать программы CGI, написанные на языке Perl, по расширению имени файла pl.

Заметим, что без выполнения описанной выше настройки файла httpd.conf все попытки запуска программ CGI будут отвергнуты сервером Apache. В ответ на каждую такую попытку в файл журнала ошибок будет записываться следующее сообщение:

[Wed Oct 17 13:26:28 2001] [error] [client 154.100.100.31] couldn't spawn child process: e:/www.cgi-test.at-home/cgi/login.pl

В данном случае оно говорит о невозможности запуска программы login.pl.

Дополнительно в файле журнала доступа появится запись об отказе в доступе с кодом 500:

154.100.100.31 - - [17/Oct/2001:13:26:28 +0400] "GET /cgiprg/login.pl HTTP/1.1" 500 619

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

Создание виртуального каталога программ CGI

Итак, Вы установили интерпретатор Perl и познакомились с тем, как найти и установить для этого интерпретатора дополнительный модули. Теперь мы расскажем о том, как создать для Web-узла виртуальный каталог, в котором будут находиться программы CGI, написанные на языке Perl.

Как мы уже говорили в главе 3, посвященной установке и настройке Web-сервера Apache, каталоги, предназначенные для программ CGI, нужно создавать и описывать в файле конфигурации httpd.conf специальным образом. В частности, необходимо создать псевдоним физического каталога программ CGI, называемый также виртуальным каталогом программ CGI.

Записав файлы программ CGI в предназначенный для них каталог, необходимо соответствующим образом настроить доступ пользователей компьютера к этим файлам. Доступ нужно настроить так, чтобы никто, кроме администратора, не мог читать или изменять содержимое файлов. В то же время посетители Web-узла должны обладать правами на запуск программ CGI.

Определение виртуального каталога программ CGI

Как мы уже говорили в главе 3, посвященной установке и настройке Web-сервера Apache, каталоги с программами CGI настраиваются специальным образом.

Обычно Web-приложения Apache, ссылаясь на файлы программ CGI, используют не физический путь к этим файлам, а виртуальный, построенный с использованием команды ScriptAlias. Данная команда задает псевдоним (алиас) для физического каталога. В дальнейшем Web-приложения ссылаются на этот псевдоним, а не на реальный каталог с программами CGI.

Вот пример использования команды ScriptAlias в файле конфигурации Apache для операционной системы Microsoft Windows:

ScriptAlias /cgiprg/ "e:/Admin123Trudogolik/cgi/"

Эта команда определяет виртуальный каталог программ CGI с именем cgiprg для физического каталога e:/Admin123Trudogolik/cgi/.

Что же касается Apache для Linux, то здесь команда ScriptAlias используется аналогичным образом:

ScriptAlias /cgiprg/ "/var/web_projects/www_trudogolik/cgiprg/"

В данном случае команда определяет виртуальный каталог программ CGI с именем cgiprg для физического каталога /var/web_projects/www_trudogolik/cgiprg.

Приведем пример адресации каталога программ CGI с применением описанных выше псевдонимов. Если для Web-узла http://www.datarecovery.ru был определен псевдоним (виртуальный каталог) cgi-bin, отображаемый на физический каталог  /var/www/cgi-bin/, то правильный адрес URL каталога программ CGI будет http://www.datarecovery.ru/cgi-bin.

Права доступа к каталогу программ CGI

Для каждого каталога программ CGI нужно определить права доступа с помощью команд <Directory> и </Directory>.  В частности, для этих каталогов необходимо запретить интерпретацию файла управления доступом .htaccess и разрешить исполнение программ CGI.

Ниже мы привели пример для сервера Apache, работающего под управлением ОС Microsoft Windows:

<Directory "e:/Admin123Trudogolik/cgi/">
  AllowOverride None
  Order allow,deny
  Allow from all
</Directory>

Здесь интерпретация файла управления доступом .htaccess запрещается командой AllowOverride с аргументом None.

Команды Order и Allow разрешают доступ к каталогу программ CGI всем посетителям независимо от адреса IP их рабочих станций.

Определение прав доступа к каталогу программ CGI для сервера Apache, установленного в Linux, выглядит следующим образом:

<Directory "/var/web_projects/www_trudogolik/cgiprg/">
  Options ExecCGI
  AllowOverride none
  Order allow,deny
  Allow from all
</Directory>

Здесь дополнительно используется команда Options с аргументом ExecCGI, разрешающая запуск программ CGI из данного каталога. Данную команду можно применять и для сервера Apache, работающего в среде ОС Microsoft Windows.

Права доступа к файлам программ CGI

Записав файлы программ CGI в предназначенный для них каталог, необходимо определить для пользователей сервера права доступа к этим файлам.

В операционной системе Linux для этого можно использовать команду chmod с параметром 755:

chmod 755 my_cgi.pl

Если Вы загружаете программы CGI на Web-сервер при помощи клиентов FTP, то эти же программы могут помочь Вам установить необходимые атрибуты доступа. Например, выделив нужный файл в программе FAR, нажмите клавишу F9 и выберите из меню Files строку File attributes. На экране появится окно Set attributes (рис. 5-11).

Рис. 5-11. Установка прав доступа к файлу в программе FAR

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

Если сервер Apache работает в операционной системе Microsoft Windows, то Вы можете усилить защиту программ CGI, запретив их изменение и чтение всем пользователям, кроме администратора. При этом не забудьте разрешить всем пользователям выполнение этих программ.

Первая программа CGI

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

Параметры виртуального Web-узла

Для отладки этой и других учебных программ CGI, приведенных в нашей книге, мы будем использовать виртуальный Web-узел с доменным именем www.cgi-test.at-home. Заметим, что это доменное имя не может существовать в Интернете, так как не существует корневой зоны at-home.

Для использования имени www.cgi-test.at-home мы должны настроить соответствующим образом локальный сервер DNS. В нашем случае в зоне cgi-test.at-home необходимо описать узел www с адресом IP, равным 154.100.100.31. Вы, разумеется, можете использовать другие доменные имена и адреса IP.

Ниже мы привели фрагмент файла httpd.conf для операционной системы Microsoft Windows, в котором определен наш виртуальный Web-узел  www.cgi-test.at-home:

<VirtualHost 154.100.100.31:80>
  ServerAdmin alexandre@frolov.pp.ru
  DocumentRoot "e:/www.cgi-test.at-home/root"
  ServerName www.cgi-test.at-home

  <Directory "e:/www.cgi-test.at-home/cgi/">
    Options +ExecCGI
    Order allow,deny
    Allow from all
    AllowOverride none
  </Directory>

  ScriptAlias /cgiprg/ "e:/www.cgi-test.at-home/cgi/"
</VirtualHost>

Обратите внимание, что для программ CGI мы создали физический каталог e:/www.cgi-test.at-home/cgi. В нем мы разрешили выполнение программ CGI, указав команду Options с аргументом +ExecCGI. Дополнительно с помощью команд Order, Allow и AllowOverride мы разрешили всем посетителям доступ к этому каталогу и запретили использование файла .htaccess для управления доступом в данном каталоге.

Чтобы исключить возможность прямого доступа посетителей к физическому каталогу программ CGI, мы создали псевдоним cgiprg, использовав для этого команду ScriptAlias.

Документ HTML со ссылкой на программу CGI

Программы CGI можно запускать из командного приглашения операционной системы, однако для нас важнее научиться запускать их из документов HTML, расположенных в каталогах Web-сервера. Как Вы, наверное, знаете, программы CGI можно запускать при помощи форм и ссылок, созданных с применением тега <A>.

В нашем первом примере мы воспользуемся именно этим, вторым способом. Для запуска программы CGI, расположенной в файле с названием login.pl, мы создали документ HTML со ссылкой Запустить! (рис. 5-12).

Рис. 5-12. Документ HTML со ссылкой для запуска программы CGI

Если щелкнуть эту ссылку, в окне браузера появится динамически созданный документ HTML, показанный на рис. 5-13.

Рис. 5-13. Документ HTML, созданный динамически программой CGI

Рассмотрим исходный текст документа HTML со ссылкой (листинг 5-1).

Листинг 5-1 Вы найдете в файле chap05\cgi-1\www.cgi-test.at-home\root\index.html на прилагаемом к книге компакт-диске.

<html>
<head>
<title>Запуск программы CGI</title>
</head>
<body>
<H1>Запуск программы CGI</H1>
<P><A href="/cgiprg/login.pl">Запустить!</A></P>
</body>
</html>

Как видите, в нем с помощью тега <A> определена ссылка на программу CGI следующего вида:

<A href="/cgiprg/login.pl">Запустить!</A>

Обратите внимание, что здесь мы ссылаемся не на физический каталог cgi, а на псевдоним cgiprg, определенный нами в файле httpd.conf. Имя файла программы CGI (login.pl) выбрано нами произвольно и в настоящий момент не несет никакой смысловой нагрузки. В дальнейшем мы наделим эту программу функцией аутентификации посетителей, и тогда использование данного имени станет оправданным.

Исходный текст программы CGI

Теперь настало время изучить исходный текст нашей первой программы CGI, составленной на языке Perl. Вы найдете его в листинге 5-2.

Листинг 5-2 Вы найдете в файле chap05\cgi-1\www.cgi-test.at-home\cgi\login.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI;
use strict;

print "Content-Type: text/html\n\n";
print "<html><head><title>Динамический документ HTML</title></head><body>";
print "<h1>Создан динамический документ</h1>";
print "<p>Текст этой страницы был создан динамически сценарием CGI.</p>";
print "</body></html>";

Первая строка исходного текста программ Perl всегда выглядит одинаково:

#!/usr/bin/perl -w

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

Оператор use с параметром CGI подключает модуль CGI, значительно облегчающий создание CGI-программ:

use CGI;

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

Следующая строка программы записывает в стандартный поток вывода текстовую строку заголовка HTTP:

print "Content-Type: text/html\n\n";

В этой сроке определяется тип документа, который будет отправлен браузеру. В нашем случае это текстовый документ HTML. Обратите внимание, что в конце строки заголовка мы пометили два символа конца строки \n. Это необходимо для отделения заголовка HTTP от текста документа HTML пустой строкой.

Далее в стандартный поток вывода записывается текст документа HTML:

print "<html><head><title>Динамический документ HTML</title></head><body>";
print "<h1>Создан динамический документ</h1>";
print "<p>Текст этой страницы был создан динамически сценарием CGI.</p>";
print "</body></html>";

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

Просмотр переменных окружения

В этом и следующем разделе мы покажем примеры несложных программ CGI, демонстрирующих динамическое создание документов HTML, просмотр переменных окружения и обработку данных, введенных при помощи форм.

Предыдущая программа CGI создавала новый документ HTML, не анализируя никаких передаваемых ей данных. Однако большинство программ CGI извлекает данные из форм, а также из так называемых переменных среды Web-сервера (Web-server environment variables).

Переменные среды формируемых Web-сервером на основе заголовков HTTP, полученных от браузера. Они были перечислены в разделе «Переменные среды для программы CGI» главы 3. Это такие переменные, как AUTH_TYPE, GATEWAY_INTERFACE, HTTP_ACCEPT и другие.

Когда программа CGI, написанная на языке Perl, получает управление, глобальный хеш с именем %ENV записываются все переменные среды. Вы можете извлекать отдельные значения переменных по имени, такому как HTTP_HOST, или выводить их в цикле, перебирая все элементы хеша. В нашей программе (листинг 5-3) мы используем оба способа.

Листинг 5-3 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\http-header.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI;
use strict;

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";

print "<html><head><title>
Переменные окружения</title></head><body>";
print "<h1>
Узел $ENV{'HTTP_HOST'}</h1>";

print "<table border=1><tr><th width=150 align=left>
Переменная</th><th align=left>Значение</th></tr>";

my $env_var;
foreach $env_var (keys %ENV)
{
  my $val=$ENV{$env_var};
  if ($val eq "")
  {
    $val = " [
не определено] ";
  }
  print "<tr><td width=150>$env_var</td><td>$val</td></tr>\n";
}

print "</table>";
print "</body></html>";

Прежде всего, обратите внимание, что к динамически формируемому документу HTML мы добавили новый заголовок HTTP, указывающий примененную в документе кодировку символов:

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";

В данном случае применена кодировка символов кириллицы, принятая в ОС Microsoft Windows. Обратите внимание на пустую строку, отделяющую заголовок HTTP от текста документа.

Далее наша программа формирует верхнюю часть заголовка и тела документа HTML, отображая в заголовке доменное имя Web-узла, полученное из переменой среды HTTP_HOST:

print "<h1>Узел $ENV{'HTTP_HOST'}</h1>";

Здесь в фигурных скобках мы указали имя нужной нам переменой среды.

Вслед за заголовком в документ HTML выводится таблица имен и значений переменных среды (рис. 5‑14).

Рис. 5-14. Просмотр переменных окружения

Ниже мы привели цикл формирования строк таблицы:

my $env_var;
foreach $env_var (keys %ENV)
{
  my $val=$ENV{$env_var};
  if ($val eq "")
  {
    $val = " [не определено] ";
  }
  print "<tr><td width=150>$env_var</td><td>$val</td></tr>\n";
}

Цикл проходит по всем ключам хеша %ENV, записывая в переменную $env_var поочередно имена всех переменных окружения. Далее значение каждой переменной окружения сохраняется в переменой $val:

my $val=$ENV{$env_var};

Если значение переменной среды не пустое, оно используется при формировании строки таблицы. В том случае, если переменная окружения определена с пустым значением, мы записываем в таблицу значение в виде строки «[не определено]».

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

Взглянув на рис. 5-14, можно определить, что адрес IP посетителя REMOTE_ADDR равен 154.100.100.31, что браузер посетителя способен отображать страницы, написанные на английском и русском языках (эта информация хранится в переменной среды HTTP_ACCEPT_LANGUAGE). Можно определить тип браузера (переменная HTTP_USER_AGENT), а также тип кодировки документов, воспринимаемой браузером (переменная HTTP_ACCEPT_ENCODING) .

Что же касается информации о Web-сервере, то исходя из анализа значения переменной среды SERVER_SIGNATURE, можно сделать вывод о том, что в качестве программного обеспечения сервера используется Apache версии 1.3.20. Из переменной GATEWAY_INTERFACE можно узнать версию протокола CGI, с которой работает данный сервер. Программа CGI может узнать доменное имя Web-сервера, с которого она была запущена (переменная HTTP_REFERER), имя программы (переменная SCRIPT_NAME), физический путь к файлу программы CGI (переменная SCRIPT_FILENAME) и к каталогу документов HTML (переменная DOCUMENT_ROOT), а также другую информацию.

Программа CGI может использовать данную информацию, например, для оптимизации своей работы и для получения статистической информации о посетителях. Заметим еще раз, что набор переменных среды зависит от типа и версии программного обеспечения Web-сервера, поэтому не следует ожидать, что Вы получите одинаковый набор переменных для разных Web-серверов и для разных операционных систем.

Обработка формы

В разделе «Подготовка форм HTML» предыдущей главы мы рассказывали Вам о том, как создавать формы. Тогда же мы привели пример формы, содержащей элементы управления различных типов (на рис. 5-15 эта форма показана в заполненном виде). Немного измененный исходный текст формы на языке HTML приведен в листинге 5-4.

Рис. 5-15. Форма с заполненными полями

Теперь мы расскажем Вам о том, как «оживить» эту форму, обеспечив обработку полученных от нее данных в программе CGI. В результате обработки создается динамический документ HTML с таблицей, строки которой отражают содержимое полей формы (рис. 5-16).

Рис. 5-16. Данные из полей формы

Последние две строки таблицы появляются только в том случае, если для отправки данных формы была использована графическая кнопка Send it!, а не обычная кнопка Send.

Листинг 5-4 Вы найдете в файле chap05\www.cgi-test.at-home\root\form.html на прилагаемом к книге компакт-диске.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
 <TITLE>Элементы управления в формах</TITLE>
</HEAD>
<BODY BGCOLOR=#FFFFFF>
<FORM METHOD=POST ACTION="/cgiprg/form.pl">
  <TABLE>
    <TR>
      <TD VALIGN=TOP>
Текстовое поле TEXT</TD>
      <TD><input type="text" name="TEXT1" value="
Текст" size="30"></TD>
    </TR>
    <TR>
      <TD VALIGN=TOP>
Текстовое поле PASSWORD</TD>
      <TD><INPUT TYPE=password NAME="PWD" VALUE="
Пароль"></TD>
    </TR>
    <TR>
      <TD VALIGN=TOP>
Текстовое поле TEXTAREA</TD>
      <TD><TEXTAREA NAME="TEXT2" ROWS=4 COLS=30>
Это
  многострочный
    текст
    </TEXTAREA></TD>
   
</TR>
    <TR>
      <TD VALIGN=TOP>Флажки с независимой фиксацией типа CHECKBOX</TD>
      <TD>
        <INPUT TYPE=CHECKBOX NAME="chk1" VALUE="on" CHECKED>
Первый<BR>
        <INPUT TYPE=CHECKBOX NAME="chk2" VALUE="on">
Второй<BR>
        <INPUT TYPE=CHECKBOX NAME="chk3" VALUE="on" CHECKED>
Третий<BR>
      </TD>
    </TR>
    <TR>
     
<TD VALIGN=TOP>Флажки с зависимой фиксацией типа RADIO</TD>
      <TD>
        <INPUT TYPE=RADIO NAME="rad" VALUE="on1" CHECKED>
Первый<BR>
        <INPUT TYPE=RADIO NAME="rad" VALUE="on2">
Второй<BR>
        <INPUT TYPE=RADIO NAME="rad" VALUE="on3">
Третий<BR>
      </TD>
    </TR>
    <TR>
      <TD VALIGN=TOP>
Список</TD>
      <TD>
        <SELECT NAME="sel" SIZE="1">
         
<OPTION Value="Первая строка">Первая строка</OPTION>
          <OPTION Value="Вторая строка">Вторая строка</OPTION>
          <OPTION Value="Ничего не выбрано">Ничего не выбрано</OPTION>
        </SELECT>
      </TD>
    </TR>
   
<TR>
      <TD VALIGN=TOP>Скрытый элемент управления</TD>
      <TD><INPUT TYPE=HIDDEN NAME="hid" VALUE="Hidden"></TD>
    </TR>
  </TABLE>
<BR><INPUT TYPE=submit VALUE="Send">&nbsp;
<INPUT TYPE=reset VALUE="Reset">
<P><INPUT TYPE=IMAGE NAME="graf" SRC="send.gif" BORDER=0>
</FORM>

</BODY>
</HTML>

Рассмотрим исходный текст программы form.pl, обрабатывающей данные нашей формы (листинг 5-5).

Листинг 5-5 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\form.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use strict;

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";

print "<html><head><title>Результаты обработки формы</title></head><body>";
print "<h1>Результаты обработки формы</h1>";

print "<table border=1 width='100%'><tr><th align=left>Поле формы</th><th align=left>Значение</th></tr>";
print "<tr><td width=150>text1</td><td>".param('TEXT1')."</td></tr>";
print "<tr><td width=150>pwd</td><td>".param('PWD')."</td></tr>";
print "<tr><td width=150>text2</td><td>".param('TEXT2')."</td></tr>";

print "<tr><td width=150>Отмеченные флажки CHECKBOX</td><td>";
if(param('chk1') ne '') { print "Первый"; }
if(param('chk2') ne '') { print " Второй"; }
if(param('chk3') ne '') { print " Третий"; }
print "</td></tr>";

print "<tr><td width=150>Отмеченный флажок RADIO</td><td>";
if(param('rad') eq 'on1') { print "Первый"; }
elsif(param('rad') eq 'on2') { print "Второй"; }
elsif(param('rad') eq 'on3') { print "Третий"; }
print "</td></tr>";

print "<tr><td width=150>Выбранная строка списка</td><td>".param('sel')."</td></tr>";
print "<tr><td width=150>Скрытый элемент управления</td><td>".param('hid')."</td></tr>";

if(param('graf.x') ne '')
{
  print "<tr><td width=150>Графическая кнопка (координата X)</td><td>".param('graf.x')."</td></tr>";
  print "<tr><td width=150>Графическая кнопка (координата Y)</td><td>".param('graf.y')."</td></tr>";


print "</table>";
print "</body></html>";

В отличие от предыдущей программы, программа form.pl пользуется модулем CGI. Для обработки формы мы загружаем не все функции этого модуля, а только стандартные:

use CGI qw(:standard);

Далее наша программа записывает в стандартный поток вывода заголовок HTTP создаваемого документа HTML:

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";

Для получения содержимого полей формы мы используем функцию param, определенную в модуле CGI. В качестве параметра этой функции нужно передать имя поля, определенного при помощи параметра NAME соответствующего тега формы.

Извлечение содержимого текстовых полей формы

В следующей строке мы извлекаем содержимое текстового поля с именем TEXT1 и вставляем его в строку таблицы, формируемой динамически:

print "<tr><td width=150>text1</td><td>".param('TEXT1')."</td></tr>";

Соответствующее поле ввода определено в форме так:

<input type="text" name="TEXT1" value="Текст" size="30">

Для успешного извлечения содержимого поля Вы должны передать функции param имя поля в точно таком же виде, как оно было определено в форме. В данном случае это имя TEXT1.

Обработка текстового поля, предназначенного для ввода паролей, а также многострочного поля выполняется аналогично:

print "<tr><td width=150>pwd</td><td>".param('PWD')."</td></tr>";
print "<tr><td width=150>text2</td><td>".param('TEXT2')."</td></tr>";

Здесь мы передаем функции param имена соответствующих полей, получая от нее содержимое поля.

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

print "<tr><td width=150>Скрытый элемент управления</td>
<td>".param('hid')."</td></tr>";

Обработка флажков с независимой фиксацией

Как мы уже говорили, состояние флажков с независимой фиксацией можно изменять индивидуально.

В нашей форме определено три таких флажка с именами chk1, chk2 и chk3:

<INPUT TYPE=CHECKBOX NAME="chk1" VALUE="on" CHECKED>Первый<BR>
<INPUT TYPE=CHECKBOX NAME="chk2" VALUE="on">
Второй<BR>
<INPUT TYPE=CHECKBOX NAME="chk3" VALUE="on" CHECKED>
Третий<BR>

Сразу после загрузки формы первый и третий флажок отображаются в отмеченном состоянии.

Ниже мы привели фрагмент программы, извлекающий состояние флажков и формирующий соответствующую строку таблицы:

print "<tr><td width=150>Отмеченные флажки CHECKBOX</td><td>";
if(param('chk1') ne '') { print "Первый"; }
if(param('chk2') ne '') { print " Второй"; }
if(param('chk3') ne '') { print " Третий"; }
print "</td></tr>";

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

Обработка флажков с зависимой фиксацией

Что касается флажков с зависимой фиксацией, то из них может быть отмечен только один. Все связанные между собой флажки определяются с одним и тем же именем. В нашем случае это имя rad:

<INPUT TYPE=RADIO NAME="rad" VALUE="on1" CHECKED>Первый<BR>
<INPUT TYPE=RADIO NAME="rad" VALUE="on2">
Второй<BR>
<INPUT TYPE=RADIO NAME="rad" VALUE="on3">
Третий<BR>

Для того чтобы программа CGI могла их различать, каждому флажку мы присвоили свое индивидуальное значение, воспользовавшись для этого параметром VALUE тега <INPUT>.

Фрагмент программы, определяющий отмеченную кнопку, приведен ниже:

print "<tr><td width=150>Отмеченный флажок RADIO</td><td>";
if(param('rad') eq 'on1') { print "Первый"; }
elsif(param('rad') eq 'on2') { print "Второй"; }
elsif(param('rad') eq 'on3') { print "Третий"; }
print "</td></tr>";

Здесь при помощи функции param программа извлекает значение параметра VALUE и в зависимости от этого значения записывает ту или иную строку в ячейку таблицы.

Обработка списка

Наш список содержит три строки, определенных при помощи тега <OPTION>:

<SELECT NAME="sel" SIZE="1">
  <OPTION Value="Первая строка">Первая строка</OPTION>
  <OPTION Value="Вторая строка">Вторая строка</OPTION>
  <OPTION Value="Ничего не выбрано">Ничего не выбрано</OPTION>
</SELECT>

С каждой строкой при помощи оператора VALUE связывается то или иное значение, доступное программе CGI.

Вот как мы извлекаем значение, присвоенное строке, которую выбрал посетитель:

print "<tr><td width=150>Выбранная строка списка</td>
<td>".param('sel')."</td></tr>";

Как видите, здесь мы используем все ту же функцию param.

Обработка данных от графической кнопки

Если посетитель отправляет данные формы на Web-сервер при помощи графической кнопки, программа CGI может извлечь координаты курсора (в пикселах), отсчитываемые относительно верхнего левого угла графического изображения кнопки.

В нашей форме графическая кнопка определена следующим образом:

<INPUT TYPE=IMAGE NAME="graf" SRC="send.gif" BORDER=0>

Когда посетитель щелкает такую кнопку, форма отправляет программе CGI два параметра. Имена этих кнопок образуются путем добавления к имени кнопки (заданному параметром NAME) суффиксов «.x» и «.y». Первый такой параметр задает координату X, а второй — координату Y курсора мыши в момент щелчка кнопки.

В том случае, когда посетитель отправил данные формы на Web-сервер, щелкнув обычную кнопку типа SUBMIT, указанные выше параметры не передаются.

Вот как наша программа получает и обрабатывает данные, полученные от графической кнопки:

if(param('graf.x') ne '')
{
  print "<tr><td width=150>Графическая кнопка (координата X)</td><td>".param('graf.x')."</td></tr>";
  print "<tr><td width=150>Графическая кнопка (координата Y)</td><td>".param('graf.y')."</td></tr>";
}

Вначале программа проверяет, что посетитель щелкнул графическую кнопку, а не обычную, анализируя содержимое переменной graf.x (можно было использовать и переменную graf.y). Если содержимое этой переменной не пустое, программа получает координаты курсора мыши, вызывая для этого функцию param.

Что программа CGI может сделать с этими координатами?

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

Программа AREF

В примерах, приведенных выше, мы использовали программы CGI только для обработки данных из полей форм. При этом адрес URL загрузочного файла программы указывался в параметре ACTION тега <FORM>.

Однако есть и другая возможность вызова программ CGI: указать их адрес в параметре HREF тега ссылки <A>. В этом случае Вы можете передать программе CGI параметры, указав их после имени файла загрузочного модуля через разделительный символ «?». Программа CGI получит строку параметров методом GET и сможет извлечь ее из переменной среды с именем QUERY_STRING или, что проще, с помощью функции param, описанной в предыдущем разделе.

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

Пример документа HTML, в котором демонстрируется вызов программы CGI указанным выше способом, показан в листинге 5-6.

Листинг 5-6 Вы найдете в файле chap05\www.cgi-test.at-home\root\aref.html на прилагаемом к книге компакт-диске.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
 <TITLE>Ссылки на разные серверы Web</TITLE>
</HEAD>
<BODY BGCOLOR=#FFFFFF>

<A HREF="/cgiprg/aref.pl?page=1">Издательство "Русская редакция"</A><BR>
<A HREF="/cgiprg/aref.pl?page=2">Microsoft</A><BR>
<A HREF="/cgiprg/aref.pl?page=3">
Служба восстановления данных DataRecovery.Ru</A><BR>

</BODY>
</HTML>

В этом документе есть три ссылки на программу CGI с именем aref.exe, причем каждый раз ей передаются разные значения параметра page:

<A HREF="/cgiprg/aref.pl?page=1">Издательство "Русская редакция"</A><BR>
<A HREF="/cgiprg/aref.pl?page=2">Microsoft</A><BR>
<A HREF="/cgiprg/aref.pl?page=3">Служба восстановления данных DataRecovery.Ru</A><BR>

Программа CGI принимает параметр и в зависимости от его значения отображает один из документов HTML. Например, при выборе первой строки в окне браузера отображается главная страница сервера Web издательства «Русская Редакция», а при выборе последней — главная страница Web-узла службы восстановления данных DataRecovery.Ru.

Исходный текст программы AREF приведен в листинге 5-7.

Листинг 5-7 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\aref.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use strict;

if(param('page') eq '1')
{
  print "Location: http://www.rusedit.ru/\n\n";
}
elsif(param('page') eq '2')
{
  print "Location: http://www.microsoft.com/\n\n";
}
 
elsif(param('page') eq '3')
{
  print "Location: http://www.datarecovery.ru\n\n";


else
{
  print "Location: error.html\n\n";

Программа получает значение параметра page при помощи функции param. Далее она сравнивает значение параметра со строками «1», «2» и «3». При совпадении программа возвращает браузеру адрес URL соответствующего документа HTML, формируя заголовок HTTP специального вида:

Location: [Адрес URL]\n\n

Когда браузер получает от сервера Web такой заголовок, он отображает документ или файл графического изображения, адрес URL которого указан в заголовке. В случае ошибки посетителю отправляется документ с именем error.html.

Таким образом, программа CGI анализирует параметры, поступающие от браузера через ссылку или поля формы, а затем не только динамически формирует документ HTML для отображения в окне браузера, но и возвращает ссылки на уже существующие документы в виде их адресов URL.

Эта возможность пригодится Вам, например, для организации ссылок на документы через списки, создаваемые тегом <SELECT>, находящимся в форме. Программа CGI определит, какая строка выбрана в списке в момент посылки заполненной формы серверу Web, и в зависимости от этого, либо возвратит ссылку на тот или иной существующий документ, либо сформирует новый документ динамически.

Применение шаблонов HTML

В предыдущих примерах программы CGI самостоятельно формировали заголовки HTTP и текст динамически создаваемого документа HTML при помощи функции записи в стандартный поток вывода print:

print "<h1>Результаты обработки формы</h1>";

Несмотря на простоту данного способа, он обладает одним существенным недостатком — для работы над дизайном формируемой страницы Web-мастер должен редактировать исходный текст программы CGI. Этот метод пригоден, если создается относительно простой Web-узел, когда и программированием, и дизайном занимается один и тот же человек.

Однако более или менее сложные Web-узлы обычно не создают в одиночку. Дизайн создается Web-дизайнерами, а программы CGI, базы данных и другие программные модули — Web-программистами. Технология создания сложных Web-узлов и перечень задач, встающих перед разработчиками, Вы найдете в нашей книге [1].

При этом возникает проблема — дизайнеры могут не владеть языками и технологиями программирования, а программисты не в состоянии самостоятельно разработать и реализовать хороший дизайн Web-узла.

Механизм шаблонов, реализованный в модуле HTML::Templates, решает эту проблему для программ CGI, написанных на языке Perl. При этом дизайнер создает шаблон страницы в виде обычного документа HTML, пользуясь привычным для него инструментарием. Помимо обычных тегов HTML шаблон содержит специальные теги, позволяющие программе CGI динамически изменять содержимое шаблона при отправке посетителю.

 Существуют три вида шаблонов: шаблоны переменных, циклические и условные шаблоны. Расскажем о них подробнее.

Шаблоны переменных

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

<TMPL_VAR NAME=[Имя переменной шаблона]>

В качестве параметра NAME тега <TMPL_VAR> подставляется так называемое имя переменной шаблона, которая будет использована программой CGI для подстановки.

Чтобы это было понятнее, сразу перейдем к практическому примеру. Пусть нам нужно написать программу CGI, которая создает динамическую страницу HTML с информацией об адресе IP и браузере посетителя. В листинге 5-8 показан исходный текст шаблона, подготовленного нами для такой страницы.

Листинг 5-8 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\Template\templ_sample1.html на прилагаемом к книге компакт-диске.

<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<meta http-equiv="Content-Language" content="ru">
<TITLE>Использование шаблонов HTML::Template</TITLE>
</HEAD>
<BODY>
<H1>Сведения о Вашем компьютере и браузере</H1>
<table border=1>
<tr><td width=150>Адрес IP Вашей рабочей станции</td><td><TMPL_VAR NAME=REMOTE_ADDR></td></tr>
<tr><td width=150>Тип Вашего браузера</td><td><TMPL_VAR NAME=HTTP_USER_AGENT></td></tr>
<tr><td width=150>Языки, с которыми может работать Ваш браузер</td><td><TMPL_VAR NAME=HTTP_ACCEPT_LANGUAGE></td></tr>
</table>
</BODY>
</HTML>

Обратите внимание, что помимо обычных тегов HTML в этом шаблоне определено три тега <TMPL_VAR> с именами переменных шаблона REMOTE_ADDR, HTTP_USER_AGENT и HTTP_ACCEPT_LANGUAGE (эти имена выбраны нами произвольно, но так, чтобы отражать смысл передаваемой через них информации).

Если просмотреть файл шаблона в браузере, то ячейки таблицы с тегами шаблона <TMPL_VAR> будут показаны пустыми (рис. 5-17). Именно в таком виде шаблон может создаваться и редактироваться Web-дизайнером, не знакомым с программированием вообще и с языком Perl в частности.

Рис. 5-17. Просмотр шаблона страницы в браузере

Заметим, что файл шаблона не предназначен для непосредственной загрузки в браузер посредством указания его адреса URL. Вместо этого отображение шаблона выполняет программа Perl, обращаясь для этого к функциям, определенным в модуле HTML::Template.

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

Листинг 5-9 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\template1.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use HTML::Template;
use strict;

my $template = HTML::Template->new(filename => 'template/templ_sample1.html');

$template->param(REMOTE_ADDR => $ENV{'REMOTE_ADDR'});
$template->param(HTTP_USER_AGENT => $ENV{'HTTP_USER_AGENT'});
$template->param(HTTP_ACCEPT_LANGUAGE => $ENV{'HTTP_ACCEPT_LANGUAGE'});

print header (-charset=>'windows-1251'); 
print $template->output;

Для того чтобы программа могла обращаться к модулю HTML::Template, его нужно подключить явным образом при помощи оператора use:

use HTML::Template;

Заметим, что модуль HTML::Template не устанавливается вместе с интерпретатором Perl. Его нужно загрузить из библиотеки CPAN и установить, как это было описано в разделе «Установка дополнительных модулей Perl» этой главы.

Для работы с модулем HTML::Template необходимо использовать объектно-ориентированные возможности языка Perl.

Прежде всего, необходимо создать объект шаблона:

my $template = HTML::Template->new(filename => 'template/templ_sample1.html');

Здесь объект создается при помощи метода new, причем ссылка на созданный объект записывается в переменную $template. Методу new мы указываем относительный путь к файлу шаблона, который отсчитывается от каталога программ CGI. Можно, разумеется, указать и абсолютный путь, однако при этом файлы шаблона будут жестко привязаны к физическому расположению содержащего их каталога.

Далее наша программа заполняет три переменных шаблона с именами REMOTE_ADDR, HTTP_USER_AGENT и HTTP_ACCEPT_LANGUAGE:

$template->param(REMOTE_ADDR => $ENV{'REMOTE_ADDR'});
$template->param(HTTP_USER_AGENT => $ENV{'HTTP_USER_AGENT'});
$template->param(HTTP_ACCEPT_LANGUAGE => $ENV{'HTTP_ACCEPT_LANGUAGE'});

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

После того как программа определила значения всех переменных шаблона, можно записывать заголовок протокола HTTP и шаблон в стандартный поток вывода:

print header (-charset=>'windows-1251'); 
print $template->output;

Обратите внимание, что для записи заголовка HTTP мы применили здесь функцию header, определенную в модуле CGI. При этом мы указали параметр charset, задающий кодировку символов документа HTML. Вместо этого можно было воспользоваться и старым методом формирования заголовка, использованным нами в предыдущих программах:

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";
print $template->output;

На рис. 5-18 мы показали результат работы нашей программы. Теперь, как видите, все ячейки таблицы заполнены.

Рис. 5-18. Страница, сформированная с использованием шаблона

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

Циклические шаблоны

Для формирования периодически повторяющихся фрагментов документов HTML, таких, например, как строки таблицы, очень удобно использовать так называемые циклические шаблоны. Циклические шаблоны создаются при помощи тегов <TMPL_LOOP> и <TMPL_VAR>.

В следующем примере мы покажем использование шаблонов HTML:Template для формирования таблицы значений переменных окружения. Ранее мы уже решали эту задачу, записывая текст документа HTML в стандартный поток вывода (см. раздел «Просмотр переменных окружения» этой главы).

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

Листинг 5-10 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\Template\templ_sample2.html на прилагаемом к книге компакт-диске.

<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<meta http-equiv="Content-Language" content="ru">
<TITLE>
Использование шаблонов HTML::Template</TITLE>
</HEAD>
<BODY>
<H1>
Переменные окружения</H1>

<table border=1>
<tr><th width=150 align=left>
Переменная</th><th align=left>Значение</th></tr>

<TMPL_LOOP NAME="THIS_LOOP">
<tr>
<td width=150><TMPL_VAR NAME=ENV_NAME></td><td><TMPL_VAR NAME=ENV_VALUE></td>
</tr>
</TMPL_LOOP>

</table>
</BODY>
</HTML>

Повторяющиеся строки таблицы мы описали с помощью тегов <TMPL_LOOP> и </TMPL_LOOP>:

<TMPL_LOOP NAME="THIS_LOOP">
<tr>
<td width=150><TMPL_VAR NAME=ENV_NAME></td><td><TMPL_VAR NAME=ENV_VALUE></td>
</tr>
</TMPL_LOOP>

Обратите внимание, что тег <TMPL_LOOP> снабжен именем, определенным при помощи параметра NAME. Таким образом, в одном документе HTML можно определять несколько циклических шаблонов, задавая для каждого из них свое имя.

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

Исходный текст программы, заполняющей наш циклический шаблон, представлен в листинге 5-11.

Листинг 5-11 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\template2.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use HTML::Template;
use strict;

my $template = HTML::Template->new(filename => 'template/templ_sample2.html');

@::loop = ();
my $env_var;

foreach $env_var (keys %ENV)
{
  my $val=$ENV{$env_var};
  if ($val eq "")
  {
    $val = " [
не определено] ";
  }
  my %row = (ENV_NAME => $env_var, ENV_VALUE => $val);
  push(@::loop, \%row);
}

$template->param(THIS_LOOP => \@::loop);

print header (-charset=>'windows-1251'); 
print $template->output;

После создания объекта модуля HTML::Template, наша программа определяет массив loop для хранения данных строк таблицы:

@::loop = ();

Далее программа в цикле записывает в этот массив хеши с именами переменных окружения и их значениями:

my %row = (ENV_NAME => $env_var, ENV_VALUE => $val);
push(@::loop, \%row);

После этого заполненный массив используется для инициализации циклического шаблона:

$template->param(THIS_LOOP => \@::loop);

Заголовок HTTP и циклический шаблон отображаются в окне браузера посетителя точно таким же образом, что и в предыдущем примере:

print header (-charset=>'windows-1251'); 
print $template->output;

В результате посетитель увидит страницу, показанную на рис. 5-14.

Условные шаблоны

Условные шаблоны позволяют управлять внешним видом страницы или отдельных ее фрагментов, просто устанавливая значения переменных шаблона.

Для условных шаблонов применяются теги <TMPL_IF>, <TMPL_ELSE> и </TMPL_IF>, ограничивающие фрагменты шаблона, которые должны использоваться при выполнении тех или иных условий. В нашей книге мы ограничимся использованием только этих тегов. Документация, загруженная Вами вместе с дистрибутивом модуля HTML::Template, содержит детальное описание этих и некоторых других тегов, применяющихся при создании шаблонов.

Для демонстрации возможностей условных шаблонов модифицируем предыдущий пример программы, отображающей значения переменных окружения Web-сервера. Новая программа будет отображать таблицу значений два раза. В первый раз мы используем простой дизайн таблицы, а во второй — немного улучшенный (рис. 5-19).

Рис. 5-19. Выбор дизайна таблицы при помощи условного шаблона

Исходный текст шаблона, примененного нами в этой программе, представлен в листинге 5-12.

Листинг 5-12 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\Template\templ_sample3.html на прилагаемом к книге компакт-диске.

<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<meta http-equiv="Content-Language" content="ru">
<TITLE>
Использование шаблонов HTML::Template</TITLE>
</HEAD>
<BODY>
<H1>
Переменные окружения</H1>

<TMPL_IF NAME="SMART_LAYOUT">

<table border="0" cellspacing="1" cellpadding="3">
<tr bgcolor="#808080"><th width=150 align=left><font color="#FFFFFF">
Переменная</th>
<th align=left><font color="#FFFFFF">
Значение</font></th></tr>
<TMPL_LOOP NAME="THIS_LOOP">
<tr bgcolor="#C5C5C5"><td width=150><TMPL_VAR NAME=ENV_NAME></td><td><TMPL_VAR NAME=ENV_VALUE></td></tr>
</TMPL_LOOP>
</table>

<TMPL_ELSE>

<table border=1>
<tr><th width=150 align=left>
Переменная</th><th align=left>Значение</th></tr>
<TMPL_LOOP NAME="THIS_LOOP">
<tr><td width=150><TMPL_VAR NAME=ENV_NAME></td><td><TMPL_VAR NAME=ENV_VALUE></td></tr>
</TMPL_LOOP>
</table>

</TMPL_IF>

</BODY>
</HTML>

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

Первый, более привлекательный дизайн будет использован для формирования страницы в том случае, когда программа CGI определит значение переменной шаблона с именем SMART_LAYOUT:

<TMPL_IF NAME="SMART_LAYOUT">

<table border="0" cellspacing="1" cellpadding="3">
<tr bgcolor="#808080"><th width=150 align=left><font color="#FFFFFF">
Переменная</th>
<th align=left><font color="#FFFFFF">
Значение</font></th></tr>
<TMPL_LOOP NAME="THIS_LOOP">
<tr bgcolor="#C5C5C5"><td width=150><TMPL_VAR NAME=ENV_NAME></td><td><TMPL_VAR NAME=ENV_VALUE></td></tr>
</TMPL_LOOP>
</table>

<TMPL_ELSE>
. . .
</TMPL_IF>

В противном случае будет использован более простой способ оформления таблицы.

Заметим, что указанным образом можно изменять оформление не только целых таблиц, но и любых отдельных фрагментов документа HTML — ячеек и строк таблицы, отдельных слов, символов, графических изображений и т.д. В одном шаблоне можно задействовать несколько тегов <TMPL_IF>, определив для них разные или одинаковые имена. В первом случае будет выполняться проверка нескольких условий.

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

Листинг 5-13 Вы найдете в файле chap05\www.cgi-test.at-home\cgi\template3.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use HTML::Template;
use strict;

my $template = HTML::Template->new(filename => 'template/templ_sample3.html');

@::loop = ();
my $env_var;

foreach $env_var (keys %ENV)
{
  my $val=$ENV{$env_var};
  if ($val eq "")
  {
    $val = " [
не определено] ";
  }
  my %row = (ENV_NAME => $env_var, ENV_VALUE => $val);
  push(@::loop, \%row);
}

$template->param(THIS_LOOP => \@::loop);

print header (-charset=>'windows-1251'); 
print $template->output;

$template->param(SMART_LAYOUT => 1);
print $template->output;

Обратите внимание, что после однократной выдачи заголовка HTTP наша программа два раза выводит данные шаблона:

print header (-charset=>'windows-1251'); 
print $template->output;

$template->param(SMART_LAYOUT => 1);
print $template->output;

Перед вторым выводом программа устанавливает значение переменной шаблона  SMART_LAYOUT равным единице. В результате во второй раз будет отображена таблица с улучшенным дизайном.

Работа с Cookie в программах CGI

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

Что такое Cookie?

Скажем сразу, что с кулинарным искусством это связано мало, хотя переводится с английского языка как «печенье» или «булочка». Говоря кратко, Cookie представляет собой свойство документа HTML. Данные Cookie физически хранятся локально на компьютере пользователя, загрузившего этот документ, в виде специальных файлов. Средствами Cookie пользователь может настроить, или «приготовить», по собственному вкусу документ HTML, если для него предусмотрена такая настройка.

Проще всего представить себе Cookie как набор строковых параметров, каждый из которых имеет имя и значение. Клиентский сценарий может создавать Cookie для документа HTML, определяя в нем произвольное количество параметров и задавая для них произвольные значения. Далее такой набор параметров становится принадлежностью данного конкретного документа HTML и его может быть проанализирован, изменен или удален сценарием.

Как мы уже говорили, основное, для чего нужен Cookie, — так это для того, чтобы предоставить пользователю возможность настроить «под себя» интерфейс активных документов HTML. Эти параметры могут анализироваться или не анализироваться сервером Web, но в любом случае они хранятся у пользователя. И, разумеется, пропадут, если пользователь, скажем, отформатирует свой жесткий диск. После этого для документа HTML их придется задавать заново.

Конечно, задачу индивидуальной настройки параметров страниц удается решить и другими способами. Это можно сделать, например, при помощи технологии PHP, активных серверных страниц ASP или расширений сервера Web — программ CGI и приложений ISAPI. Для этого на сервере Web надо установить базу данных, хранящую параметры для всех зарегистрированных в ней пользователей. В этом случае расширение сервера Web способно динамически создавать настраиваемые документы HTML, используя для определения внешнего вида страниц параметры, хранящиеся в базе данных.

Возлагая задачу хранения параметров отдельных документов HTML на браузер посетителя, Вы сильно упрощаете задачу организации настройки диалогового интерфейса. А для этого как раз нужны Cookie.

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

Среди других применений Cookie можно отметить сетевые игры. В Cookie может хранится, например, текущее состояние игры или другие параметры.

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

Далее мы рассмотрим основные операции с Cookie, например создание Cookie, получение и изменение значений параметров Cookie, а также удаление Cookie.

Существуют два способа создания Cookie, первый из которых используется расширениями сервера Web, а второй — клиентскими сценариями. Мы рассмотрим только первый из них в приложении к программам CGI. Детальное описание второго способа и примеры приложений Вы найдете в [1]. Там же рассказано о приемах работы с Cookie в серверных сценариях ASP.

Заголовок HTTP для создания Cookie

Для того чтобы создать Cookie первым способом, программа CGI или расширение Web-сервера другого типа обычно добавляет в заголовок HTTP динамически создаваемого документа HTML поле с именем Set-Cookie. В нем определяются имена и значения параметров Cookie.

Когда программа CGI вызывается из документа HTML, имеющего Cookie, параметры Cookie предаются этому расширению через поле Cookie заголовка HTTP и могут быть проанализированы.

Заголовок HTTP, предназначенный для создания Cookie, выглядит следующим образом:

Set-Cookie: Имя=Значение; expires=Дата_GMT;
path=Адрес_URL; domain=Домен; secure

Описание отдельных полей заголовка Set-Cookie приведено в табл. 5-1.

Таблица 5-1. Поля заголовка Set-Cookie

Поле

Описание

Имя

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

Значение

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

expires=Дата_GMT

Дата по Гринвичу автоматического удаления Cookie. Если эта дата не указана, а параметр expires отсутствует, Cookie будет удален сразу после того, как браузер закончит сеанс связи с сервером Web

domain=Домен

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

path=Адрес_URL

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

secure

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

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

Дата должна быть записана в формате День_недели, ДД-Мес-ГГ ЧЧ:ММ:СС GMT, где:

·         День_недели — английское трехбуквенное сокращение названия дня недели (например, Mon);

·         ДД — номер дня недели;

·         Мес — английское трехбуквенное сокращение названия месяца (например, Jun);

·         ГГ — две последние цифры года;

·         ЧЧ — часы;

·         ММ — минуты;

·         СС — секунды.

Например, дату можно указать так:

Mon, 07-Jun-93 14:45:00 GMT

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

Когда браузер загружает документ HTML с сервера Web и среди заголовков HTTP этого документа присутствует заголовок Set-Cookie, он проверяет возможность установки Cookie. В процессе проверки анализируется адрес URL, откуда был загружен этот документ, а также содержимое полей domain и path.

Если эти поля не указаны, то по умолчанию считаются, что они соответствуют адресу URL, по которому находится загруженный документ HTML. В этом случае выполняется установка Cookie.

Когда же указано поле domain, установка Cookie выполняется, только если документ загружен с сервера Web, принадлежащего данному домену.

Средствами параметра path можно установить ограничение на адреса URL в рамках домена, для которых выполняется установка Cookie. При этом значение «/» соответствует всем адресам данного домена.

Одновременно сервер Web способен создать несколько параметров Cookie, включив в заголовок документа HTML несколько заголовков Set-Cookie.

Запись Cookie программой CGI

Когда сценарий CGI создает страницу, он может записать Cookie в заголовке HTTP с помощью обычной функции print.

Вот фрагмент программы CGI, записывающий Cookie подобным образом:

print "Content-Type: text/html\n";
print "Charset: windows-1251\n";
print "Set-Cookie: ".$cookie_name."=".$cookie_value."\n\n";
my $template = HTML::Template->new(filename => $template_path);
print $template->output;

Здесь записывается Cookie с именем $cookie_name и значением $cookie_value. После записи Cookie программа выводит шаблон, путь к которому находится в переменной $template_path.

Пользуясь информацией из предыдущего раздела Вы можете формировать Cookie подобным образом, задавая различные поля, описанные в табл. 5-1.

Модуль Perl с названием CGI предоставляет в распоряжение программиста другие средства записи Cookie. Вот, например, как можно записать сразу два Cookie:

use CGI qw/:standard/;
use CGI::Cookie;
$cookie1 = new CGI::Cookie(-name=>'UserName',-value=>’
Иванов’);
$cookie2 = new CGI::Cookie(-name=>'UserID',  -value=>’3649762’);
print header(-cookie=>[$cookie1, $cookie2]);

Заметим, что если Вы не указали дату автоматического удаления Cookie, то созданные подобным образом Cookie будут автоматически удалены при завершении работы браузера.

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

Практическое применение Cookie мы рассмотрим в главе 8 «Виртуальное кадровое агентство Трудоголик.Ру» при описании Web-узла администрирования виртуального кадрового агентства http://www.trudogolik.ru.

Получение значения Cookie

Итак, мы рассказали, как создавать Cookie в клиентских сценариях. Теперь попробуем решить другую задачу — определение значения параметров Cookie.

Проще всего это сделать при помощи функции cookie, определенной в модуле Perl с названием CGI:

my $user_id_cookie=cookie('trudogolik_ru_user_id');

Здесь мы получаем значение Cookie с именем trudogolik_ru_user_id и затем сохраняем его в переменной $user_id_cookie для дальнейшего использования.

Изменение значения параметра Cookie

Для изменения значения параметра Cookie с заданным именем Вы можете просто записать новое значение с использованием одной из двух технологий, описанных нами ранее в разделе «Запись Cookie программой CGI» этой главы.

Удаление Cookie

Самый простой способ удалить Cookie — установить для него такую дату автоматического удаления, которая уже прошла:

print "Content-Type: text/html\n";
print "Charset: windows-1251\n";
print "Set-Cookie: ".$cookie_name."=".$cookie_value.; Expires: Thu Jan  1 01:01:01 1970"\n\n";
my $template = HTML::Template->new(filename => $template_path);
print $template->output;

Здесь мы задали уже прошедшую дату автоматического удаления: 1 января 1970 года. Разумеется, программа Perl может определить текущую дату, немного уменьшить ее и затем использовать полученный результат для формирования даты автоматического удаления.

Модуль CGI позволяет указывать относительную дату автоматического удаления:

use CGI qw/:standard/;
use CGI::Cookie;
$cookie1 = new CGI::Cookie(-name=>'UserName',-value=>’
Иванов’, ‑expires => ’-5d’);
$cookie2 = new CGI::Cookie(-name=>'UserID',  -value=>’3649762’, ‑expires => ’-5d’);
print header(-cookie=>[$cookie1, $cookie2]);

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

Ограничения на использование Cookie

На использование Cookie накладываются определенные ограничения, перечисленные ниже:

·         всего разрешается создать не более 300 Cookie;

·         необходимо, чтобы размер каждого Cookie не превышал 4 кб;

·         для каждого домена может быть создано не более 20 Cookie

Данные ограничения зависят от типа браузера и приведены только для ориентировки. Если указанные ограничения нарушаются, браузер может удалить самые старые Cookie или обрезать значения параметров Cookie.

Заметим, что технологию Cookie нужно использовать только в тех случаях, когда она действительно необходима. Разработчик Web-приложения не может полагаться на то, что браузер посетителя принимает Cookie или что данные Cookie хранятся какое-то определенное время. Поэтому неправильное использование Cookie может привести к ненадежной работе Web-приложения. Там, где требуется высокая надежность работы и максимальная независимость работоспособности Web-приложения от типа браузера, установленного у посетителя, Cookie использовать не рекомендуется. Рассмотрите другие альтернативы хранения персональных данных посетителей, например, хранение этих данных в СУБД, установленной на сервере.

Обмен данными с сервером электронной почты

Большинство приложений Web тем или иным способом интегрируется с почтовыми серверами. Например, сервер Web Интернет-магазина может выполнять ночную рассылку сообщений ленты новостей по электронной почте или отправлять почтовые сообщения из форм, расположенных на его страницах. В этом разделе мы расскажем Вам об основных протоколах электронной почты и научим отправлять почтовые сообщения при помощи программ CGI, составленных на языке программирования Perl. Если Вы создаете ASP-приложения для операционной системы Microsoft Windows, мы рекомендуем Вам прочитать в [1] описание элемента управления ActiveX с названием MTASend. Этот элемент может быть использован для отправки электронной почты серверными сценариями ASP.

Как Вы, наверное, знаете, электронная почта появилась в Интернете еще до возникновения серверов Web вместе с операционной системой UNIX. Работать с электронной почтой в локальных и глобальных компьютерных сетях, выполненных на базе UNIX, позволял протокол копирования UNIX-UNIX (UNIX to UNIX Copy, UUCP). При использовании этого протокола почта передавалась последовательно с одного узла на другой, в результате чего она иногда долго путешествовала по сети, прежде чем достигала адресата.

Сегодня в Интернете протокол UUCP, хотя и применяется, но гораздо реже: его постепенно заменяют современные протоколы:

·         простой протокол передачи почты (Simple Mail Transfer Protocol, SMTP);

·         протокол почтового отделения (Post Office Protocol, POP);

·         протокол доступа к сообщениям Интернета (Internet Message Access Protocol, IMAP)

Эти протоколы надежнее и быстрее, чем UUCP. Все они описаны в документах RFC.

О том, что представляют собой эти документы, мы подробно рассказывали в главе 1.

Если Вас заинтересовал какой-либо протокол, отыщите его описание в документе RFC, отыскав тот по номеру и загрузив его, например, c Web-сервера http://www.rfc-editor.org.

Протокол SMTP

Простой протокол передачи почты Simple Mail Transfer Protocol (SMTP) предназначен только для передачи почты. Обычно его используют в паре с протоколами POP3 и IMAP, обеспечивающими прием почтовых сообщений.

Работа протокола SMTP основана на передаче текстовых строк. После того как почтовая программа установит соединение с почтовым сервером провайдера, между ней и сервером начинается обмен текстовыми строками.

Чуть позже мы покажем примерный сценарий такого обмена.

Например, почтовая программа установила соединение с почтовым сервером smtp.galsnet.ru. Это доменный адрес сервера SMTP компании провайдера Гласнет. Если Вы подключены к другому провайдеру, то и адрес сервера SMTP будет иным.

Сразу после установки соединения с использованием порта 25 почтовый сервер посылает почтовой программе сообщение следующего вида:

220-hawk.glas.apc.org Smail-3.2.0.109 (#4 1999-Nov-10) ready at Sun, 26 Dec 1999
20:42:55 +0300 (MSK)
220 ESMTP supported

Для другого сервера внешний вид приглашения будет отличаться от приведенного выше.

В ответ почтовая программа отправляет серверу SMTP команду подключения HELO, передавая ей в качестве параметра доменный адрес сервера:

HELO smtp.glasnet.ru

Если подключение выполнено без ошибок, сервер должен ответить сообщением с кодом 250:

250 hawk.glas.apc.org Hello smtp.glasnet.ru (smtp.glasnet.ru from address [195.178.200.74]).

Далее почтовая программа продолжит диалог с сервером. Теперь она отправит серверу команду MAIL FROM с адресом отправителя:

MAIL FROM: alexandre@frolov.pp.ru

Здесь в качестве адреса отправителя используется наш электронный почтовый адрес alexandre@frolov.pp.ru, так как мы отправляли сообщение именно снего.

В ответ почтовая программа получает следующее сообщение с кодом 250:

250 2.1.0 alexandre@frolov.pp.ru Sender Okay.

Теперь она должна сообщить серверу адрес получателя. Например:

RCPT TO: a_frolov@hotmail.com

Здесь указан наш второй адрес электронной почты на сервере Hotmail. Об этом сервере мы Вам уже рассказывали в главе 5.

В ответ сервер посылает почтовой программе подтверждение следующего вида:

250 2.1.0 'a_frolov@hotmail.com' Recipient Okay.

Далее почтовая программа передает серверу команду, свидетельствующую о готовности к началу передачи данных (то есть тела сообщения):

DATA

В ответ она получает приглашение для ввода текста сообщения с кодом 354:

354 Enter mail, end with "." on a line by itself...

Далее она передает серверу строки заголовка сообщения, начиная с Subject:

SUBJECT:Test

И затем — пустую строку и строки тела сообщения. Завершает ввод строка, состоящая из одной точки в первой позиции:

This is test
.

В нашем послании тело сообщения состоит только из одной строки (что вполне допустимо).

Сообщение будет отправлено, а почтовая программа получит извещение об этом в следующем виде:

250 2.6.0 Mail accepted, queue ID m122Hir-001oz7n.

Как видите, протокол SMTP действительно оправдывает свое название — он весьма прост: описание основ его работы занимает всего лишь около страницы книги (мы опустили некоторые несущественные детали, не меняющие сути дела).

Обратите внимание, что текст сообщения передается в открытом виде, а для соединения с почтовым сервером не нужно указывать идентификатор пользователя и пароль. Из этого следует, что протокол SMTP не обеспечивает никакой, даже самой слабой защиты передаваемой информации. Ее легко перехватить или подделать.

Чтобы хоть как-то контролировать процесс отправки почты, провайдеры требуют установки прямого соединения между компьютером, отправляющим почту, и почтовым сервером SMTP провайдера (строго говоря, они должны находиться в сети с одним адресом IP). При этом, дабы вычислить пользователя, который, например, занимается массовой рассылкой рекламной почты, провайдеру достаточно сопоставить параметры отправленного сообщения и статистические данные о работе своих клиентов.

В табл. 5-2 мы перечислили основные команды протокола SMTP.

Таблица 5-2. Команды SMTP

Команда

Параметр

Описание

HELO

Доменное имя сервера

Создание канала связи с сервером SMTP

QUIT

Нет

Закрытие канала связи с сервером SMTP

MAIL

FROM: обратный адрес

Запуск передачи почты

RCPT

TO: адрес получателя

Сообщение серверу адреса получателя

DATA

Нет

Запуск передачи данных

VRFY

имя пользователя

Проверка существования почтового ящика пользователя с заданным идентификатором. Из соображений безопасности эта команда обычно блокируется администратором почтового сервера

EXPN

Имя списка рассылки

Проверка существования списка рассылки и пополнение его новыми данными

HELP

Нет

Запрос от сервера списка допустимых команд

NOOP

Нет

Пустая операция (в ответ на эту команду почтовый сервер не выполняет никаких операций)

RSET

Нет

Повторная инициализация сеанса связи с сервером

Более подробно протокол SMTP и все его команды описаны в документе RFC 821.

Протокол POP3

Протокол почтового отделения Post Office Protocol версии 3 — POP3 — предназначен для получения почты с сервера и обычно применяется вместе с только что описанным протоколом SMTP.

Для того чтобы подключится к серверу POP3 (имеющему, как и сервер SMTP, свой доменный адрес), почтовая программа должна сообщить ему идентификатор и пароль пользователя. Протокол POP3 обеспечивает некоторую защиту. Во всяком случае, никто не сможет забрать Ваши почтовые сообщения, не зная Вашего идентификатора и пароля.

Когда почтовая программа загружает сообщение с сервера POP3, она может либо удалить оригинал сообщения, либо оставить его на постоянное или временное хранение.

Строго говоря, протокол POP3, в отличие от своего предшественника POP2, способен не только принимать, но и передавать сообщения. Поэтому при его использовании протокол SMTP не требуется. Однако многие почтовые программы все же используют оба протокола — и SMTP, и POP3.

Чтобы Вы получили некоторое представление о том, что представляет собой протокол POP3, расскажем, как с его помощью почтовые программы получают информацию о содержимом почтового ящика пользователя и загружают сообщения. В качестве примера мы рассмотрим сервер POP3 с вымышленным именем pop3.goods.ru (не пытайтесь обращаться к нему; если хотите потренироваться, воспользуйтесь сервером своего провайдера).

После того как почтовая программа установит соединение с сервером POP3 с использованием порта 110, она получит от него сообщение следующего вида:

+OK QPOP (version 2.53) at pop3.goods.ru starting.

В ответ она посылает серверу команду подключения USER, указывая в качестве параметра идентификатор пользователя username:

USER username

Сервер отправляет почтовой программе сообщение о необходимости предоставления пароля пользователя с идентификатором username:

+OK Password required for username.

Почтовая программа пересылает его с помощью команды PASS:

PASS *******

Далее сервер «докладывает» почтовой программе, сколько сообщений хранится в почтовом ящике пользователя и сколько они занимают места на диске сервера:

+OK username has 6 messages (27973 octets).

Эту же информацию можно получить посредством команды STAT:

STAT
+OK 6 27973

Команда LIST позволяет почтовой программе дополнительно получить список сообщений, хранящихся в почтовом ящике пользователя:

LIST
+OK 6 messages (27973 octets)
1 3084
2 6132
3 1026
4 2735
5 7453
6 7543
.

Для каждого сообщения в списке указан его размер в количестве символов. Список завершается строкой, состоящей из одной точки.

Если команде LIST указать номер сообщения, сервер выдаст информацию только о заданном сообщении:

LIST 1
+OK 1 3084

Чтобы извлечь полный текст сообщения с заданным номером, почтовая программа должна отправить серверу POP3 команду RETR, указав номер сообщения в качестве параметра:

RETR 4

Командой TOP можно извлечь только часть сообщения. Команде нужно передать два параметра — номер сообщения и количество извлекаемых строк сообщения:

TOP 4 10

Вот что вернул сервер в ответ на эту команду:

+OK 2735 octets
Return-path: <alexandre@frolov.pp.ru>
Envelope-to: alexandre@frolov.pp.ru
Delivery-date: Thu, 11 May 2000 22:28:04 +0400
Received: from [193.124.5.42] (helo=nnfalcon.glasnet.ru)
 by pop3.goods.ru with esmtp (Exim 3.10 #1) id 12pxgq-00018O-00
 for alexandre@frolov.pp.ru; Thu, 11 May 2000 22:28:04 +0400
Received: from hawk.glasnet.ru([193.124.5.50]) (2123 bytes) by nnfalcon.glasnet.ru
 via sendmail with P:esmtp/R:inet_hosts/T:inet_zone_smtp
 (sender: <alexandre@frolov.pp.ru>)
 id <m12pxYP-0015w9n@nnfalcon.glasnet.ru>
 for <alexandre@frolov.pp.ru>; Thu, 11 May 2000 22:19:21 +0400 (MSD)
 (Smail-3.2.0.111 2000-Feb-17 #1 built 2000-Mar-3)
Received: from saturn(ppp1283.glas.apc.org[194.154.81.229]) (1782 bytes) by hawk.glasnet.ru
 via sendmail with P:smtp/R:smart_host/T:smtp
 (sender: <alexandre@frolov.pp.ru>)
 id <m12pxYN-001ozkn@hawk.glasnet.ru>
 for <alexandre@frolov.pp.ru>; Thu, 11 May 2000 22:19:19 +0400 (MSD)
 (Smail-3.2.0.111 2000-Feb-17 #1 built 2000-Mar-3)
Message-ID: <002401bfbb75$b1f31990$140115ac@saturn>
Reply-To: "Alexandre Frolov" <alexandre@frolov.pp.ru>
From: "Alexandre Frolov" <alexandre@frolov.pp.ru>
To: <alexandre@frolov.pp.ru>
Subject: Test message
Date: Thu, 11 May 2000 21:43:41 +0400
MIME-Version: 1.0
Content-Type: multipart/alternative;
        boundary="----=_NextPart_000_001D_01BFBB91.F92D4990"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 5.00.2919.6600
X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2919.6600
X-UIDL: 04ed453aebf416737f367b9af906d5a0
Status: RO

This is a multi-part message in MIME format.

------=_NextPart_000_001D_01BFBB91.F92D4990
Content-Type: text/plain;
        charset="koi8-r"
Content-Transfer-Encoding: quoted-printable

This is test message
.

Почтовая программа анализирует ответ сервера POP3, извлекает из него поля заголовка, текст сообщения и отображает все это в окне просмотра сообщения.

В табл. 5-3 мы кратко описали некоторые команды протокола POP3. Полную информацию о протоколе POP3 и его командах Вы найдете в документе RFC 1939.

Таблица 5-3. Команды POP3

Команда

Параметры

Описание

USER

идентификатор пользователя

Передает серверу POP3 идентификатор пользователя

PASS

пароль пользователя

Передает серверу POP3 пароль пользователя

STAT

нет

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

LIST

[номер сообщения]

Если команда использована без параметра, возвращается список сообщений с номерами, если указан номер сообщения, то команда передает информацию о данном сообщении (номер и размер сообщения)

RETR

номер сообщения

Сервер передает почтовой программе полный текст сообщения с заданным номером

TOP

номер сообщения, количество извлекаемых строк

Сервер передает почтовой программе заданное количество первых строк сообщения с заданным номером

DELE

номер сообщения

Удаление сообщения с заданным номером

RSET

-

Отмена операции удаления сообщений, выполненных командой DELE

NOOP

-

Пустая операция (в ответ на эту команду почтовый сервер не выполняет никаких операций)

QUIT

-

Завершение сеанса связи с сервером

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

Протокол IMAP

Пара протоколов SMTP и POP3 очень удобна для обмена почтой в пакетном режиме. При этом за относительно короткий промежуток времени, когда Ваш компьютер подключен к серверу провайдера, Вы отправляете сообщения по протоколу SMTP и получаете по протоколу POP3. Когда обмен почтой завершен, можно отключаться от Интернета и работать с полученными сообщениями в автономном режиме.

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

Тем не менее, в последнее время все популярнее становится такая услуга, как постоянное подключение к Интернету: Вы платите фиксированную сумму в месяц и получаете неограниченный доступ в глобальную сеть. При этом для работы с электронной почтой удобнее использовать современный протокол доступа к сообщениям Интернета Internet Message Access Protocol (IMAP). Он описан в документе RFC 2060 и используется совместно с протоколом SMTP.

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

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

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

Заметим, что протокол POP3 тоже позволяет отказаться от загрузки некоторых сообщений, параметры которых задаются в специальном фильтре. Например, от сообщений, пришедших с каких-то конкретных адресов или содержащих в своем теле или в строке Тема (Subject) какие-то конкретные слова. Однако в этом режиме нельзя просмотреть заголовки отвергнутой почты, так как она сразу по поступлении удаляется с сервера.

Режим постоянного подключения к Интернету или тарифный план без ограничения времени подключения позволяет, не торопясь, просматривать сообщения и загружать их с почтового сервера с помощью протокола IMAP. Но если Вы выбрали поминутный режим оплаты сеанса связи с Интернетом, лучше воспользоваться протоколами SMTP и POP3.

Внутренняя структура электронного сообщения

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

Заголовок содержит адресную и служебную информацию, тело сообщения — текст электронного письма, а присоединенные файлы — любые данные. Заголовок и тело сообщения неотъемлемая часть сообщения, а вот присоединенные файлы Вы вставляете в послание сами, если Вам это требуется. Фактически присоединенные файлы можно рассматривать как необязательную часть тела сообщения.

Заголовок сообщения

Заголовок почтового сообщения состоит из нескольких полей. Некоторые из них Вы должны заполнить сами (это поля To, Cc, Bcc и Subject), чтобы отправить новое сообщение. Эти поля расположены в окне нового сообщения, которое Вы открываете в почтовой программе. Остальные почтовая программа заполнит автоматически.

Вот заголовок письма, который мы сами отправили на наш же электронный адрес:

From: "Alexandre Frolov" <alexandre@frolov.pp.ru>
To: <alexandre@frolov.pp.ru>
Cc: <a_frolov@hotmail.ru>
Bcc: "usa.net" <a_frolov@usa.net>
Subject: This is a test
Date: Thu, 16 Mar 2000 15:01:36 +0300
MIME-Version: 1.0
Content-Type: multipart/alternative;
  boundary="----=_NextPart_000_001C_01BF8F58.8645AB30"
X-Priority: 3
X-MSMail-Priority: Normal
X-Mailer: Microsoft Outlook Express 5.00.2314.1300
X-MimeOLE: Produced By Microsoft MimeOLE V5.00.2314.1300

Этот заголовок можно увидеть, например, в почтовой программе Microsoft Outlook Express. Для этого в окне просмотра сообщения из меню File нужно выбрать строку Properties. Далее на вкладке Detailes щелкните кнопку Message Source. На экране появится окно с полным текстом сообщения, включающим все заголовки.

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

Рассмотрим некоторые поля заголовка по отдельности.

From. В этом поле, заполняющемся автоматически почтовой программой, указывается имя и электронный адрес отправителя сообщения. В данном случае сообщение было отправлено Александром Фроловым (Alexandre Frolov) с адреса alexandre@frolov.pp.ru.

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

Cc. Имя поля образовано как сокращение от словосочетания Carbon Copy, которое можно перевести как «копия». Это поле Вам понадобится, когда Вы захотите разослать одно сообщение сразу по нескольким адресам. Надо указать в этом поле список электронных адресов, и сообщение будет отправлено по ним всем.

Bcc. Названия этого поля образовано от Blind Carbon Copy —  «слепая», или «блокированная», копия. По своему назначению это поле аналогично полю Cc, однако между ними есть одно существенное отличие. Поле Bcc позволяет скрыть от получателей список рассылки: имена других адресатов сообщения останутся неизвестны Вашим корреспондентам.

Subject. Здесь Вам надо кратко описать содержимое почтового сообщения. Когда адресат получит Ваше послание, по описанию в поле Subject он поймет, о чем речь в сообщении.

Date. Это поле заполняется автоматически почтовой программой. Оно содержит дату создания сообщения. В конце строки даты указано значение смещения часового пояса (разница между местным временем и временем по Гринвичу), для Москвы оно равно +3 часам.

MIME-Version. Электронные сообщения передаются в закодированном виде. В поле MIME-Version почтовая программа указывает номер версии стандарта многоцелевых расширений почты Интернета (Multipurpose Internet Mail Extension, MIME), использованной для кодирования данного сообщения.

Content-Type. В соответствии со стандартом MIME сообщение может содержать данные различного типа — как текстовые, так и двоичные. Причем в заголовке сообщения обязательно находится строка описания типа данных для каждого конкретного сообщения.

Тип данных описывается в поле заголовка Content-Type (данное поле автоматически заполняется почтовой программой), например:

Content-Type: multipart/alternative;
  boundary="----=_NextPart_000_001C_01BF8F58.8645AB30"

В данном случае тип multipart означает, что в сообщении возможно несколько фрагментов данных. При этом параметр boundary определяет строку, разделяющую отдельные фрагменты сообщения. В данном случае в качестве разделителя выступает строка «----=_NextPart_000_001C_01BF8F58.8645AB30».

X-Priority и X-MSMail-Priority. Здесь определяется так называемый приоритет почтового сообщения.

Что это такое?

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

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

X-Mailer. Здесь почтовая программа записывает свое собственное название и версию. Анализируя содержимое данного поля, нетрудно узнать, какой почтовой программой пользуется Ваш корреспондент. Например, взглянув на показанный выше заголовок, можно установить, что для подготовки сообщения использовалась почтовая программа Microsoft Outlook Express версии 5.00.2314.1300 (цифры 2314.1300 означают номер модификации для версии 5.0).

X-MimeOLE. Здесь указывается название и версию программной компоненты, выполняющей кодирование данных в стандарте MIME.

Тело сообщения

Чтобы показать, как выглядит тело сообщения, мы подготовили с помощью почтовой программы Microsoft Outlook Express небольшое электронное письмо, состоящее из одной строки «Test message». Заголовок этого сообщения показан в предыдущем разделе.

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

This is a multi-part message in MIME format.

------=_NextPart_000_001C_01BF8F58.8645AB30
Content-Type: text/plain;
  charset="koi8-r"
Content-Transfer-Encoding: quoted-printable

Test message

------=_NextPart_000_001C_01BF8F58.8645AB30
Content-Type: text/html;
  charset="koi8-r"
Content-Transfer-Encoding: quoted-printable

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META content=3D"text/html; charset=3Dkoi8-r" http-equiv=3DContent-Type>
<META content=3D"MSHTML 5.00.2314.1000" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Test message</FONT></DIV></BODY></HTML>

------=_NextPart_000_001C_01BF8F58.8645AB30--

Как видите, собственно строка сообщения составляет лишь малую часть тела сообщения.

Почтовая программа Microsoft Outlook создала два фрагмента для двух вариантов сообщения, один из которых текстовый, а другой подготовлен с применением языка разметки гипертекста HTML, применяемого для создания страниц серверов Web.

Каждый фрагмент имеет свой заголовок, начинающийся со строки разделителя. Вы видите, что первый фрагмент имеет тип text/plain, а второй — text/html. Этот тип определяется содержимым поля Content-Type.

Кроме типа данных, в заголовке фрагмента указано название набора символов сообщения charset и тип кодирования Content-Transfer-Encoding. В данном случае для текста использован набор символов koi8-r, а для кодирования этого текста при передаче в теле сообщения используется формат quoted-printable. О наборах символов и кодировке текста сообщения мы расскажем в следующем разделе этой главы.

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

Для чего почтовая программа поместила в тело сообщения два фрагмента?

Первый из них предназначен для устаревших почтовых программ, которые не способны отображать сообщения в формате HTML. Такие программы проигнорируют фрагмент типа text/html.

Что же касается новых программ, то они способны реализовать богатые возможности языка HTML, применяя их для шрифтового и стилевого оформления почтовых сообщений, а также для размещения в таких сообщениях графических изображений и активных объектов. Сообщения, подготовленные с применением языка разметки HTML, выглядят более привлекательно, чем обычный неформатированный текст.

Наборы символов и кодировка сообщения

Остановимся подробнее на названии набора символов, указанного в поле charset.

Как Вы, наверное, знаете, серверы Интернета создаются на базе различных операционных систем, однако большинство почтовых серверов и серверов Web работают в среде Unix. На компьютерах пользователей Интернета, однако, может быть установлена не только операционная система Windows, но и Unix, IBM OS/2 и даже DOS. Так как в разных операционных системах применяются различные несовместимые между собой наборы символов, появляются, в частности, сложности с отображением кириллицы.

Обычно почтовые сообщения с символами кириллицы передаются через Интернет с применением набора символов KOI8-R, принятого по умолчанию в операционной системе Unix, однако возможны и другие наборы. В табл. 5-4 мы перечислили названия стандартных наборов символов кириллицы для некоторых наиболее распространенных операционных систем.

Таблица 5-4. Наборы символов кириллицы в разных операционных системах

Операционная система

Набор символов кириллицы

Microsoft Windows 95/98,
Microsoft Windows NT,
Microsoft Windows 2000

Windows-1251

Unix, Linux, FreeBCD, Sun OS и другие версии Unix

KOI8-R, ISO-8859-5

IBM OS/2, DOS

DOS CP866

Помимо этого существует и другая проблема при передаче сообщений по каналам электронной почты. Она связана с тем, что некоторые почтовые серверы способны пропускать только 7-битные потоки данных, а для представления полного набора символов (т. е. букв, цифр, знаков пунктуации) нужно 8 бит.

Чтобы решить эту проблему, применяется дополнительное кодирование текста передаваемых сообщений.

На заре развития электронной почты для кодирования использовался метод «кодировка UNIX-UNIX» (UNIX to UNIX Encode, UUEncode): двоичные 8-битные данные представлялись 7-битным эквивалентом в виде символов латинского алфавита. Имейте в виду, что латинские символы кодируются числами в диапазоне от 0 до 127 и могут быть представлены только посредством 7 бит.

Как мы уже говорили, сегодня кодирование почтовых сообщений выполняется в соответствии со стандартом многоцелевых расширений почты Интернета (Multipurpose Internet Mail Extension, MIME). При этом в заголовке фрагмента помимо названия набора символов сообщения charset указан и тип кодирования Content-Transfer-Encoding.

А теперь мы перечислим различные типы кодирования (примеры использования некоторых из них показаны в следующем разделе):

·         binary — означает отсутствие кодирования. Символы сообщения передаются в исходном виде, даже если для их представления используется 8 бит. Передаваемые строки сообщения могут иметь большую длину;

·         8-bit — аналогично предыдущему, но передаются строки небольшого размера;

·         7-bit — аналогично предыдущему, но для представления символов передаваемого сообщения используется только 7 бит;

·         quoted-printable — применяется для кодирования текста, большинство символов которого могут быть представлены посредством 7 бит. Это латинские символы. Если же для представления символа необходимо 8 бит, то такой символ кодируется с использованием 7-битных символов;

·         base-64 — в результате кодирования данного типа выполняется преобразование групп из трех 8-битных символов в группы по четыре 6-битных символа. В результате такой кодировки размер сообщения увеличивается примерно на треть;

·         x-token — если указан этот тип кодирования, то отправляющая и принимающая программа сами договариваются между собой об использовании той или иной кодировки символов.

Присоединенный файл

Как мы уже говорили, в электронное письмо разрешается вставить присоединенные файлы. В зависимости от содержимого такого файла применяется тот или иной метод кодирования.

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

------=_NextPart_000_0019_01BFE386.575BA590
Content-Type: text/plain;
  name="Text Document.txt"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
  filename="Text Document.txt"

Hello, Dear Friend!
I send to You this plain text file.
Alexandre Frolov

Email: alexandre@frolov.pp.ru
Web:   http://www.frolov.pp.ru
------=_NextPart_000_0019_01BFE386.575BA590--

Мы добавили к сообщению файл с именем Text Document.txt.

Как видите, в заголовке фрагмента указан тип данных application/octet-stream, имя файла и 7-битная кодировка. Далее следует пустая строка и данные файла до строки разделителя.

Если к сообщению присоединяется двоичный файл, он кодируется по-другому:

------=_NextPart_000_004A_01BF8F69.E9392E40
Content-Type: application/octet-stream;
  name="RK.COM"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
  filename="RK.COM"

6RkqKEMpIDE5ODkgIEEuU3RyYWtob3YsIEFjYWRlbXlTb2Z0AAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAASQ9CD00PTB9hH3QfUk91T3NPAehfAw4HuQYAv1wAvtwB9sNAdAO+0AHzpbQD
uAARuwAOuQABugAAvmAAxCzNEFpZtAG3AM0QtAK3AM0Q6DYDwwAAAAAAAAAAAAAAABwF
HBsAAP7LeAd0CP7LdBvDjMjDoCAELoQGQAC0AHUDgMwCqCB0A4DMAcMuoCAEitj2x4B1
AXQDgMsg9scCdQOAy0AuiB4gBDLDJEB0A+hS/8P7LvYGigECdQqA/BF0dfbEf3QFLv8u
HmwAWOiQAiR/M9uOwyaKJoUEvlwAJjoGSQR0BOiQAs+AJooB/jwDdxuADooBAbcIgPwO
DnQFvmQAtxDpjwAzyTwHdN88E3fOsQiA/A58aL5gALEOdGG+ZACxEOta6Cz/zzxVdPg8
IwK+XAA8AnRdPBJ0WTwQdH20CDwgdDs8IXQsPCN0KL5gALQOPAF0NTwRdDE8InQXvmQA
. . .
Lv8ubAC46AH/0M9QUx4zwI7Y9sNAuBwJjMt0Brg0Ers0EqN8AIkefgAfW1jDAAAAAAAA

------=_NextPart_000_004A_01BF8F69.E9392E40--

Ради экономии места мы пропустили часть листинга.

Как видите, для двоичного файла с именем RK.COM применена кодировка base64. В результате 8-битовый поток данных удалось представить в виде 7-битовых символов ANSI.

Кодировка ANSI

Американский национальный институт стандартов American National Standards Institute (ANSI) разработал стандарт для представления символов. Согласно этому стандарту, каждому из 256 символов соответствует одно 8-битное значение (т. е. один символ кодируется одним байтом).

При этом первые 128 значений (с кодами в пределах от 0 до 127) соответствуют латинским буквам и символам. Значения от 128 до 255 предназначены для представления специальных символов и букв национальных алфавитов. Используя 7-битную кодировку, можно представить только латинские буквы и символы.

Вы можете познакомиться со стандартами ANSI на сервере Web http://www.ansi.org.

Отправка данных из формы по электронной почте

Рассмотрим программу CGI, составленную на Perl и используемую на нашем узле Web службы восстановления данных DataRecovery.Ru для отправки срочной заявки на выполнение аварийных работ. Соответствующая страница узла http://www.datarecovery.ru показана на рис. 5‑20.

Рис. 5-20. Форма для отправки срочного сообщения

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

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

<form method="POST" action="http://www.datarecovery.ru/urgent_mail.pl">
<table border="0" width="100%" cellspacing="5" cellpadding="0">
<tr>
<td width="148"><input type="text" name="PHONE" size="20" class="table"></td>
<td><FONT CLASS="table">
Номер
Вашего телефона в Москве</FONT></td>
</tr>
<tr>
<td width="148"><input type="text" name="NAME" size="20" class="table"></td>
<td><font class="table">
Ваше
имя и фамилия</font></td>
</tr>
<tr>
<td width="148">
<select size="1" name="FS" CLASS="table">
  <option selected value="NTFS">NTFS</option>
  <option value="FAT">FAT</option>
  <option value="OTHR">
Другая
</option>
  <option value="UNKN">
Не
знаю</option>
</select></td>
<td><FONT CLASS="table">
Файловая
система</FONT></td>
</tr>
<tr>
<td width="148">
<select size="1" name="OS" CLASS="table">
  <option selected value="NT">Windows NT</option>
  <option value="2K">Windows 2000</option>
  <option value="98">Windows 95/98/ME</option>
  <option value="OTHR">
Другая
</option>
  <option value="UNKN">
Не
знаю</option>
  </select></td>
<td><FONT CLASS="table">
Операционная
система</FONT></td>
</tr>
</table>
<p><input type="submit" value="
Экстренный
вызов" name="B1" CLASS="table"></p>
</form>

Как видите, атрибут ACTION тега FORM задает путь к программе обработки данных формы как http://www.datarecovery.ru/urgent_mail.pl. Файл urgent_mail.pl представляет собой программу CGI, составленную на языке программирования Perl (листинг 11-19).

Поля формы с именами PHONE и NAME предназначены соответственно для ввода номера контактного телефона, а также имени контактного лица. Список FS позволяет выбрать тип файловой системы, а список OS — тип операционной системы.

Исходный текст программы urgent_mail.pl

Рассмотрим исходный текст программы urgent_mail.pl (листинг 11-19).

Листинг 5-14 Вы найдете в файле chap5\urgentmail\urgentmail.pl на прилагаемом к книге компакт-диске.

Начальный фрагмент программы характерен для всех программ CGI, составленных на языке Perl:

#!/usr/bin/perl -w

Как мы уже говорили, в первой строке мы указываем путь к интерпретатору Perl. Если программа выполняется под управлением операционной системы Microsoft Windows NT или Microsoft Windows 2000, указанный в этой строке путь /usr/bin/perl не используется. Параметр –w указывает на то, что интерпретатор должен выводить все предупреждающие сообщения об ошибках.

Вторая строка заставляет интерпретатор Perl выполнять строгую проверку использования необъявленных и неинициализированных переменных:

use strict;

Далее мы подключаем к программе два дополнительных модуля CGI и Net (вторая строка направляет сообщения об ошибках программы в окно браузера, что облегчает отладку):

use CGI qw(:all);
use CGI::Carp qw(fatalsToBrowser);
use Net::SMTP;

Модуль CGI необходим для использования функций, выполняемых программами CGI. Параметр all обеспечивает возможность использования любых функция модуля. Что же касается модуля Net, то он нужен для отправки сообщений электронной почты с применением протокола SMTP. Заметим, что в отличие от модуля HTML::Template модуль Net устанавливается вместе с интерпретатором Perl, поэтому Вам не придется загружать и устанавливать его отдельно.

В программе urgent_mail.pl определены две функции с именами win2koi и send_mail.

Функция win2koi

Функция win2koi перекодирует текстовую строку из кодировки Windows-1251 в КОИ-8, что необходимо для передачи символов кириллицы, введенных в ОС Microsoft Windows, через электронную почту:

sub win2koi
{
  my($from)=@_;
  $_=$from;
  tr/\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61\62\63\64\65\66\67\70\71\72\73\74\75\76\77\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377/\0\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17\20\21\22\23\24\25\26\27\30\31\32\33\34\35\36\37\40\41\42\43\44\45\46\47\50\51\52\53\54\55\56\57\60\61\62\63\64\65\66\67\70\71\72\73\74\75\76\77\100\101\102\103\104\105\106\107\110\111\112\113\114\115\116\117\120\121\122\123\124\125\126\127\130\131\132\133\134\135\136\137\140\141\142\143\144\145\146\147\150\151\152\153\154\155\156\157\160\161\162\163\164\165\166\167\170\171\172\173\174\175\176\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\341\342\367\347\344\345\366\372\351\352\353\354\355\356\357\360\362\363\364\365\346\350\343\376\373\375\377\371\370\374\340\361\301\302\327\307\304\305\326\332\311\312\313\314\315\316\317\320\322\323\324\325\306\310\303\336\333\335\337\331\330\334\300\321/;
  return $_;
}

Записав входной параметр в служебную переменную с именем $_, эта функция перекодирует полученную текстовую строку при помощи оператора tr. При этом оператору tr передается полная таблица перекодировки.

Функция win2koi возвращает результат перекодировки при помощи оператора return.

Функция send_mail

Для отправки почты используется функция с именем send_mail, исходный текст которой показан ниже:

my $relay="mail.domain.ru";
sub send_mail
{
  my($to, $from, $subject, @body)=@_;

  my $smtp = Net::SMTP->new($relay);
  return 1  if (! defined $smtp);
 
  $smtp->mail($from);
  $smtp->to($to);
  $smtp->data();
  $smtp->datasend("To: $to\n");
  $smtp->datasend("From: $from\n");
  $smtp->datasend("Subject: $subject\n");
  $smtp->datasend("\n");
  foreach(@body)
  {
    $smtp->datasend("$_\n");
  }
  $smtp->dataend();
  $smtp->quit;
  return 0;
}

Функции send_mail передаются четыре параметра, которые сохраняются в переменных $to, $from, $subject и @body.

Переменные $to и $from задают соответственно адрес электронной почты получателя и отправителя сообщения. В переменную $subject записывается тело сообщения, а в переменную @body — текст сообщения.

Первое, что делает функция send_mail после обработки входных параметров, создает новый объект — канал передачи данных с почтовым сервером SMTP:

my $smtp = Net::SMTP->new($relay);
return 1  if (! defined $smtp);

Здесь в переменной $relay хранится доменное имя почтового сервера (имя mail.domain.ru мы указали только для примера, Вы должны заменить его именем своего почтового сервера).

Если канал создать не удалось, функция возвращает значение 1 и завершает свою работу. В противном случае функция заполняет поля заголовка сообщения и отправляет их на сервер при помощи метода datasend.

Перед тем как вернуть управление, функция завершает процесс передачи данных на почтовый сервер методом dataend и закрывает канал связи с почтовым сервером, вызывая метод quit.

Обработка формы HTML

Модуль CGI содержит чрезвычайно удобные средства для обработки данных форм HTML, поступающих от браузера. В своей программе мы использовали функцию param, возвращающую содержимое поля, имя которой передается этой функции в качестве параметра.

Вот как мы извлекаем из формы номер телефона, а также имя контактного лица:

my $phone=param("PHONE");
my $name=param("NAME");

Как видите, для этого нам потребовалось написать в программе всего две строки.

Чтобы не отправлять сообщение в тех случаях, когда посетитель забыл указать номер телефона или имя, мы выполняем простую проверку:

if(length($phone) < 7 || length($name) == 0)
{
 print header(-charset=>'windows-1251');
 open (INCOMPLETE, "mail_incomplete.htm") || die;
 while(<INCOMPLETE>) { print $_; }
 close (INCOMPLETE);
}
else
{
  . . .
 
# Отправка сообщения
  . . .
}

В номере телефона, хранящемся в переменной $phone, должно быть не менее 7 символов. Кроме того, длина имени контактного лица $name должна отличаться от нуля.

Если посетитель ошибся при заполнении формы, наша программа отправляет ему содержимое документа HTML с сообщением об ошибке. Этот документ хранится в файле с именем mail_incomplete.htm.

Для этого вначале программа динамически формирует заголовок документа, указывая в нем кодировку символов Windows-1251:

print header(-charset=>'windows-1251');

Далее программа открывает файл mail_incomplete.htm и копирует его содержимое в выходной поток, а затем закрывает файл. Если файла нет, программа завершает свою работу аварийно посредством команды die с выдачей соответствующего сообщения в окно браузера.

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


$rc=send_mail('@'.$relay.':alexandre@frolov.pp.ru', 'web@dataRecovery.ru', 'Urgent!', param("PHONE")." ".win2koi(param("NAME"))." ".param("FS")." ".param("OS")." ");

$rc1=send_mail('@'.$relay.':datarecovery@sms_gate.ru',  'web@dataRecovery.ru', 'Urgent!', param("PHONE")." ".win2koi(param("NAME"))." ".param("FS")." ".param("OS")."
");

В первый раз сообщение отправляется по адресу alexandre@frolov.pp.ru, а во второй — на адрес шлюза с системой передачи сообщений SMS на мобильный телефон или пэйджер (адрес datarecovery@sms_gate.ru указан только для примера, в своих программах Вы должны заменить его).

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

Тема сообщения указана как «Urgent!» («Срочно!»). Текст сообщения комбинируется из содержимого полей формы, заполненной посетителем узла Web службы восстановления данных.

Отправив сообщения, программа проверяет содержимое переменных $rc и $rc1, содержащие коды завершения функции send_mail:

if($rc != 0 || $rc1 != 0)
{
 print header(-charset=>'windows-1251');
 open (MAIL_ERROR, "mail_error.htm") || die;
 while(<MAIL_ERROR>) { print $_; }
 close (MAIL_ERROR);
}
else
{
 print header(-charset=>'windows-1251');
 open (MAIL_OK, "mail_ok.htm") || die;
 while(<MAIL_OK>) { print $_; }
 close (MAIL_OK);
}

Если оба сообщения отправлены без ошибок, посетителю отправляется содержимое файла mail_ok.htm, а в противном случае — содержимое файла mail_error.htm.

Получение электронной почты

Итак, теперь Вы умеете создавать программы CGI, отправляющие сообщения электронной почты. Однако иногда возникает обратная задача — нужно «научить» Web-сервер принимать почту с использованием протокола POP3.

Для чего Web-серверу принимать почту?

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

Пользуясь сведениями о работе с электронной почтой, приведенными в этой главе, Вы даже сможете создать свою почтовую систему с Web-интерфейсом, напоминающую по своим функциональным возможностям таким системам, как Microsoft Hotmail. Заметим, однако, что программы CGI, составленные с использованием языка Perl, не подойдут для обработки огромного количества запросов в единицу времени, характерных для известных бесплатных систем электронной почты. Однако корпоративную почтовую систему с Web-интерфейсом вполне можно создать при помощи программ CGI, написанных на языке Perl.

Мы расскажем Вам о двух модулях Perl, предназначенных для получения электронной почты посредством протокола POP3. Это модули Net::POP3 и Mail::POP3Client. Оба этих модуля можно найти в архиве CPAN (http://www.cpan.org). Там, кстати, имеются модули и для создания почтовых серверов, однако их рассмотрение выходит за рамки нашей книги.

Использование модуля Net::POP3

Модуль Net::POP3, имеющий объектно-ориентированный интерфейс, содержит все необходимые методы для подключения к серверу электронной почты, извлечения списка сообщений, хранящихся в почтовом ящике, а также самих сообщений и связанной с ними информацией.

В листинге 5-15 мы привели исходный текст несложной программы CGI, проверяющей содержимое почтового ящика и отображающей в окне браузера обнаруженные в нем сообщения электронной почты.

Листинг 5-15 Вы найдете в файле chap5\www.cgi-test.at-home\cgi\read_mail.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use Net::POP3;
use HTML::Entities ();
use strict;

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";
print "<html><head><title>
Чтение почты</title></head><body>";
print "<h1>
Список сообщений</h1>";

my $ServerName = "YourMailServer";
my $pop3 = Net::POP3->new($ServerName, Debug => 0, Timeout => 120);

if(!$pop3)
{
  print "<p>
Ошибка при получении почты</h1>";
}
else
{
  my $UserName = "YourName";
  my $Password = "YourPassword";
  my $Num_Messages = $pop3->login($UserName, $Password);

  my $Messages;
  my $msg_id;
  my $uidl;

  $Messages = $pop3->list();

  foreach $msg_id (keys(%$Messages))
  {
    my $MsgContent = $pop3->get($msg_id);
    my $MsgUidl = $pop3->uidl($msg_id);
    PrintMessage($MsgUidl, \@$MsgContent);
  }

  $pop3->quit();
  print "</body></html>";
}

sub PrintMessage
{
  my ($msguidl, $lines) = @_;
  my ($from, $line, $subject, $full_msg, @lines);
  foreach $line (@$lines)
  {
    $full_msg=$full_msg.HTML::Entities::encode($line)."<br>";
    if($line =~ m/^From: (.*)/)
    {
      $from = $1;
      $from =~ s/"|<.*>//g;
    }
    elsif( $line =~ m/^Subject: (.*)/)
    {
      $subject = $1;
    }
  }
  print "<P><hr><b>From:</b> ".HTML::Entities::encode($from)."<br>";
  print "<b>Subject:</b> ".HTML::Entities::encode($subject)."<br>";
  print "<b>UIDL:</b> ".$msguidl."<hr>";
  print $full_msg;
}

Для того чтобы программа могла работать с модулем Net::POP3, его необходимо подключить при помощи оператора use:

use Net::POP3;

Дополнительно в нашей программе мы будем работать с модулем HTML::Entities:

use HTML::Entities ();

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

Первое, что должна сделать наша программа, это создание объекта Net::POP3. Соответствующему конструктору необходимо передать доменное имя почтового сервера $ServerName, значение флага отладки Debug и максимального времени ожидания Timeout:

my $ServerName = "YourMailServer";
my $pop3 = Net::POP3->new($ServerName, Debug => 0, Timeout => 120);

if(!$pop3)
{
  print "<p>
Ошибка при получении почты</h1>";
}
else
{
  . . .
}

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

На следующем этапе программа устанавливает соединение с почтовым сервером, передавая методу login идентификатор пользователя $UserName и пароль пользователя $Password:

my $UserName = "YourName";
my $Password = "YourPassword";
my $Num_Messages = $pop3->login($UserName, $Password);

Если идентификатор и пароль указаны правильно, метод login возвращает количество сообщений, хранящихся в почтовом ящике пользователя, или неопределенное значение undef, если при указании идентификатора или пароля были допущены ошибки.

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

my $Messages;
$Messages = $pop3->list();

Обработка всех элементов хеша $Messages выполняется в цикле:

foreach $msg_id (keys(%$Messages))
{
  my $MsgContent = $pop3->get($msg_id);
  my $MsgUidl = $pop3->uidl($msg_id);
  PrintMessage($MsgUidl, \@$MsgContent);
}

Здесь для каждого сообщения, имеющего текущий номер $msg_id, мы получаем его содержимое (вызывая метод get), а также уникальный идентификатор сообщения UIDL (при помощи метода uidl).

Метод get возвращает строки сообщения в виде массива. Ссылку на этот массив, а также значение UIDL текущего сообщения мы передаем функции PrintMessage. Данная функция записывает эти строки в выходной поток, создавая таким способом динамический документ HTML (рис. 5-21).

Рис. 5-21. Просмотр полученных сообщений (первый вариант)

Перед тем как завершить свою работу, наша программа закрывает соединение с почтовым сервером, вызывая для этого метод quit:

  $pop3->quit();

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

Получив управление, функция PrintMessage сохраняет значения своих параметров в переменных $msguidl и $lines:

my ($msguidl, $lines) = @_;

В первую из этих переменных записывается идентификатор сообщения, а во вторую — массив строк сообщения.

Обработка массива строк выполняется в цикле:

my ($from, $line, $subject, $full_msg, @lines);
foreach $line (@$lines)
{
  $full_msg=$full_msg.HTML::Entities::encode($line)."<br>";
  if($line =~ m/^From: (.*)/)
  {
    $from = $1;
    $from =~ s/"|<.*>//g;
  }
  elsif( $line =~ m/^Subject: (.*)/)
 
{
    $subject = $1;
  }
}

Полный текст тела сообщения мы накапливаем построчно в переменной $full_msg, копируя в конец этой переменной текущую строку $line. Перед копированием эта строка преобразуется методом HTML::Entities::encode таким образом, что вместо специальных симвов HTML (таких, как <>&”) преобразуются в символьные объекты (&lt; &gt; &amp; и &quot;).

Далее, мы ищем в строках сообщения поля From и Subject, записывая их содержимое в переменные $from и $subject.

Перед завершением своей работы функция записывает в выходной поток содержимое этих двух переменных, переменной $msguidl идентификатора сообщения, а также переменной $full_msg с полным текстом сообщения, включающим в себя все заголовки:

print "<P><hr><b>From:</b> ".HTML::Entities::encode($from)."<br>";
print "<b>Subject:</b> ".HTML::Entities::encode($subject)."<br>";
print "<b>UIDL:</b> ".$msguidl."<hr>";
print $full_msg;

При этом вызывается метод HTML::Entities::encode. Этот метод выполняет преобразование полей From и Subject, необходимое для записи их содержимого в документ HTML.

Заметим, что в модуле Net::POP3 предусмотрены и другие методы. Прежде всего, заслуживает упоминание метод delete, с помощью которого можно удалить из почтового ящика сообщение с заданным номером. Обычно такая операция выполняется после прочтения сообщения. Полное описание методов модуля Net::POP3 Вы можете найти в документации на модуль, поставляющейся вместе с интерпретатором Perl, а также на Web-узле http://www.cpan.org.

Использование модуля Mail::POP3Client

Аналогичными возможностями работы с серверами POP3 обладает модуль Mail::POP3Client (по сравнению с только что описанным модулем Net::POP3).

Об использовании модуля Mail::POP3Client мы расскажем на примере программы CGI, исходный текст которой представлен в листинге 5-16.

Листинг 5-16 Вы найдете в файле chap5\www.cgi-test.at-home\cgi\read_mail1.pl на прилагаемом к книге компакт-диске.

#!/usr/bin/perl -w
use CGI qw(:standard);
use Mail::POP3Client;
use HTML::Entities ();
use strict;

print "Content-Type: text/html\n";
print "Charset: windows-1251\n\n";
print "<html><head><title>
Чтение почты</title></head><body>";
print "<h1>
Список сообщений</h1>";

my $pop = new Mail::POP3Client(
  USER     => "YourName",
  PASSWORD => "YourPassword",
  HOST     => "YourMailServer");

if(!$pop->Alive())
{
  print "<p>
Ошибка при чтении почты</h1>";
}
else
{
  my ($from, $subject);

  for(my $i = 1; $i <= $pop->Count(); $i++)
  {
    print "<hr>";
    my ($line, $from, $subject);
 
    foreach $line ($pop->Head($i))
    {
      if($line =~ m/^From: (.*)/)
      { 
        $from = $1;
        $from =~ s/"|<.*>//g;
      }
      elsif( $line =~ m/^Subject: (.*)/)
      {
        $subject = $1;
      }
    }

    my $MsgUidl = $pop->Uidl($i);
 
    print "<b>From: </b>".HTML::Entities::encode($from), "<br>";
    print "<b>Subject: </b>".HTML::Entities::encode($subject), "<br>";
    print "<b>Uidl: </b>".$MsgUidl, "<br>";
    print "<hr>";
 
    foreach ( $pop->Body($i))
    {
      print HTML::Entities::encode($_), "<br>";
    } 
    print "<br>";
  }
  $pop->Close();
}

print "</body></html>";

Прежде всего, нам необходимо подключить модуль Mail::POP3Client с помощью оператора use:

use Mail::POP3Client;

Далее при конструировании объекта модуля Mail::POP3Client мы подключаемся к почтовому серверу. При этом мы передаем конструктору идентификатор пользователя USER, пароль PASSWORD и доменное имя почтового сервера HOST:

my $pop = new Mail::POP3Client(
  USER     => "YourName",
  PASSWORD => "YourPassword",
  HOST     => "YourMailServer");

if(!$pop->Alive())
{
  print "<p>
Ошибка при чтении почты</h1>";
}
else
{
  . . .
}

После этого мы вызываем метод Alive, проверяя, было ли успешным установление соединения с почтовым сервером. В случае возникновения ошибки программа отображает сообщение и завершает свою работу.

Выборка сообщений выполняется в цикле:

for(my $i = 1; $i <= $pop->Count(); $i++)
{
  . . .
}

Для определения количества сообщений, имеющихся в почтовом ящике пользователя, применяется метод Count.

Далее для каждого сообщения наша программа получает заголовки, вызывая метод Head:

foreach $line ($pop->Head($i))
{
  if($line =~ m/^From: (.*)/)
  { 
    $from = $1;
    $from =~ s/"|<.*>//g;
  }
  elsif( $line =~ m/^Subject: (.*)/)
 
{
    $subject = $1;
  }
}

Строки заголовков анализируются в цикле. При этом из них извлекаются значения полей From и Subject, которые затем сохраняются в переменных $from и $subject, соответственно.

Уникальный идентификатор сообщений мы получаем методом Uidl, передавая ему номер сообщения:

my $MsgUidl = $pop->Uidl($i);

Перед тем, как приступить к извлечению тела сообщения, программа записывает в стандартный поток вывода значения полей From и Subject, а также идентификатор сообщения:

 

print "<b>From: </b>".HTML::Entities::encode($from), "<br>";
print "<b>Subject: </b>".HTML::Entities::encode($subject), "<br>";
print "<b>Uidl: </b>".$MsgUidl, "<br>";
print "<hr>";

Тело сообщения мы извлекаем методом Body и записываем в выходной поток построчно:

foreach ( $pop->Body($i))
{
  print HTML::Entities::encode($_), "<br>";
}

При этом применяется перекодирование при помощи функции HTML::Entities::encode.

Перед завершением своей работы программа закрывает соединение с почтовым сервером, вызывая метод Close:

$pop->Close();

Внешний вид выходного документа HTML с содержимым сообщений показан на рис. 5‑22.

Рис. 5-22. Просмотр полученных сообщений (второй вариант)

Как видите, с помощью методов Head и Body нам удалось выделить тело сообщения и заголовки «в чистом виде». В этом отношении модуль Mail::POP3Client удобнее по сравнению с модулем Net::POP3.

Помимо методов, использованных в нашей программе, модуль Mail::POP3Client экспортирует и другие модули. Например, при помощи метода Delete можно удалить сообщение с заданным номером, а при помощи метода ListArray — получить массив размеров сообщений. Более подробную информацию о модуле Mail::POP3Client ищите в документации или на Web-узле http://www.cpan.org.

[Назад] [Содержание] [Дальше]