Глава 21. CGI: многопользовательские чаты, теневые посылки (cookies) и игры
Коротко
В этой главе мы собираемся рассмотреть несколько мощных примеров CGI-сценариев. В этот список входят: приложение, обеспечивающее одновременную беседу нескольких пользователей, сценарий, предоставляющий возможность устанавливать и читать теневые посылки (cookies), и настоящая интерактивная игра — возможно, вы даже получите удовольствие, поиграв в нее.
Подсказка. Имейте в виду, что эти сценарии предназначены лишь для демонстрационных целей. Если вы соберетесь установить их на Web-сервере, следует усилить такие аспекты, как проверка ошибок и защитные функции, и после переделки сценариев под свои нужды проверить, действительно ли они работают так, как ожидалось.
Приложение для поддержки многопользовательской беседы (chat)
Приложения для поддержки многопользовательской беседы предназначены для одновременной работы нескольких пользователей. То, что вводит один, сразу становится видно остальным — таким образом, можно обсуждать что-либо прямо в Интернете. В принципе, такие приложения не очень сложны. То, что присылают пользователи, записывается в один общедоступный файл, а, кроме того, программа проверяет, показывает ли каждый из броузеров обновленный текст. Фактически же существует несколько подводных камней — например, поскольку к файлу будут пытаться получить одновременный доступ несколько пользователей, следует блокировать его во избежание конфликтов. Я написал простой, но впол
не работоспособный пример многопользовательской беседы. Это приложение демонстрирует некоторые проблемы реального программирования для CGI и способы их решения.
Теневые посылки (cookies)
Создание и чтение теневой посылки (английское cookie, на современном новоязе именуемая также «кука») стало популярным в Интернете — по крайней мере, среди Web-программистов. Под этим именем подразумевается использование протокола HTTP для хранения информации, полученной от сервера, на машине клиента и обмен этой информацией между компьютерами и программами-броу зерами незаметно от пользователя, то есть в теневом режиме.
Некоторые пользователи протестуют против получения и обработки теневых посылок на своих компьютерах и по возможности запрещают этот режим для своих броузеров, поскольку компьютерный хулиган может с их помощью доставить серьезные неприятности. Поэтому приведенный в этой главе пример не активизирует их до тех пор, пока пользователь сам не введет данные для работы теневой посылки.
Наш сценарий, работающий с теневыми посылками, сохраняет имя и день рождения пользователя, чтобы в дальнейшем приветствовать его каждый раз при посещении ссылки на сценарий и даже поздравлять с днем рождения, если оно совпало с днем визита. Этот сценарий хранит данные в хэше, поэтому вы легко подстроите его для использования в ваших собственных сценариях.
Игра
Сценарий игры, приведенный в этой главе, является полной версией широко известной «Виселицы» — игры, в которой надо угадать слово по буквам. Интерфейс данной версии игры вполне защищен, поскольку не воспринимает текст, непосредственно введенный пользователем, — игрок выбирает букву, щелкая по кнопке выбора. Если игрок с восьми попыток не угадал, игра сообщает ему, какое слово было загадано. Сценарий позволяет использовать (не обязательно) графику. Таким образом, при каждой ошибке игрока она дорисовывает страшную картинку с висельником. (Сценарий достаточно интеллектуален, чтобы игнорировать картинки, если броузер их не поддерживает.)
Это был краткий обзор сценариев этой главы — перейдем к коду.
Подсказка. При установке этих сценариев помните: они используют пакет CGI.pm и требуют Perl пятой версии или старше. На некоторых машинах еще стоит Perl самой ранней, первой версии, поэтому, если вы пользуетесь Unix-системой, вам может понадобиться сменить строку #/usr/lib/perl на что-то вроде #/usr/lib/perl5.
Непосредственные решения
Создаем приложение для многопользовательской беседы
Приложение для многопользовательской беседы позволит поддерживать беседы в Интернете без помощи апплетов Java, JavaScript, дополнительных модулей к броузеру и других приспособлений, причем оно будет работать с большинством существующих программ просмотра. Сценарий поддерживает некоторое количество пользователей, печатающих одновременно, при этом то, что ввел один, становится видно всем остальным. Поэтому приложение для бесед поддерживает общение в реальном времени.
ВНИМАНИЕ! Имейте в виду, что такое приложение может существенно увеличить количество посещений вашей страницы, поскольку во время работы оно постоянно передает обновленные данные броузеру каждого из пользователей. Провайдер может быть недоволен клиентом, который так загружает канал. Один из путей решения этой проблемы — увеличение временного интервала между обновлениями страницы; о том, как это сделать, можно прочесть в разделе «Устанавливаем период обновления HTML».
Написанное мною приложение «Chat room» выглядит так, как показано на рис. 21.1. Как видите, пользователь вводит свое имя и комментарии по ходу беседы на Web-странице. После нажатия на кнопку Send text напечатанный им текст отправляется сценарию и появляется вместе с именем пользователя в окнах броузеров всех подключившихся к обсуждению пользователей.
Рис. 21.1. Пример многопользовательской беседы
Все, что нужно для подключения к разговору, — это Web-броузер, способный работать с метакомандой обновления. Впрочем, это поддерживают практически все современные программы просмотра. Все, что пользователь должен сделать, — это открыть Web-страницу chat.htm. Броузер и мое приложение «Chat room» сделают все остальное (см. рис. 21.1).
Проблемы защиты в системе со многими пользователями
Следует обсудить еще несколько проблем — что если кто-то начнет вводить в качестве комментария код HTML? Этот сценарий обрабатывает HTML, введенный как в поле комментария, так и в поле имени, заменяя символы < на <. Благодаря этому приему, вместо того чтобы интерпретироваться как метки HTML, они появляются в окне броузера в виде символов "<".
Также, поскольку многие пользователи пытаются получить доступ к файлу одновременно, во избежание конфликтов на время чтения или записи, сценарий блокирует файл функцией flock. Выбран вариант монопольной блокировки (в отличие от разделяемой блокировки), даже на чтение, поскольку он оказался наиболее живучим в различных системах (как показала практика, некоторые из них просто не поддерживают разделяемую блокировку). Если flock задает монопольную блокировку, ни одна другая программа не сможет использовать файл до тех пор, пока он не будет разблокирован. Это не означает, что остальные программы не смогут использовать его (в Unix, например, смогут), — это просто обозначает, что они не смогут получить от функции flock значение истина. Этот сценарий использует flock для координации доступа к файлу многих пользователей, перед началом работы с файлом ожидая, пока flock не вернет значение истина.
Если же в состоянии ожидания файла с беседой сценарий заблокируется, он сделает еще десять попыток доступа с интервалом от одной до пяти секунд. Если это не помогает, значит, что-то не так и пользователю выводится сообщение «Server too busy».
Обработка атак типа «отказ от обслуживания»
Атаки типа «отказ от обслуживания» (denial of service) делают именно то, что следует из их названия, — отказывают пользователям в обслуживании. Одна из наиболее распространенных форм атаки — это перегрузка системы. Пакет CGI.pm чувствителен к отправке и получению больших объемов информации. Чтобы удерживать такие атаки в неких рамках, можно установить переменную $CGI::POST_MAX в неотрицательное целое. Эта переменная указывает верхний предел размера посылки в байтах.
ВНИМАНИЕ! Имейте в виду, что приложение для чата не было рассчитано на глобальное запрещение доступа к нему — если вам это нужно, добавьте проверку пароля.
Болтаем из броузера
Вот как работает приложение: пользователь переходит к chat.htm, которое создает две формы. Верхняя выводит текущий текст беседы с помощью сценария chat1.cgi,
Создаем приложение для многопользовательской беседы
а в нижней сценарий chat2.cgi генерирует текстовую область, в которой пользователь может ввести текст, и кнопку подтверждения для его отправки. Верхняя форма использует тег <META>, чтобы указать броузеру на необходимость регулярно го (каждые 5 секунд) обновления формы.
Для установки приложения вам необходимо разместить chat.htm, chat1.cgi и chat2.cgi, а также два файла данных, chat1.dat и chat2.dat, в одном и том же каталоге.
Вы найдете chat.htm в листинге 21.1, chat1.cgi в листинге 21.2 и chat2.cgi в листинге 21.3. Файлы данных (chat1.dat и chat2.dat) заполняются автоматически — достаточно создать файлы с такими именами, поместив в них какой-либо текст для примера и установив права доступа к ним достаточно низкими, чтобы CGI-сценарии chat1.cgi и chat2.cgi могли открыть их для чтения и записи. Чтобы начать беседу, пользователь просто открывает chat.htm.
Приложение использует два файла данных для хранения двух последних реплик. (Я использую отдельные текстовые файлы для каждой реплики, чтобы сделать хранилище текстов более защищенным с точки зрения блокировки файлов, а приложение — более надежным.) При желании можно изменить код и выводить на экран больше реплик.
Устанавливаем период обновления HTML
Наверняка вам захочется изменить по крайней мере одно — это пятисекундный период обновления, используемый данным приложением. Для этого в строке файла chat1.cgi; просто впишите требуемое количество секунд:
"<meta HTTP-EQUIV=\"refresh\" CONTENT=\"5\">",
Очищаем обновленные элементы HTML
Надо сделать еще одно замечание. Оно касается CGI.pm. Когда пользователь посылает форму, элементы управления которой содержат данные, а ваш сценарий возвращает ее без изменений, CGI.pm копирует данные из старых элементов управления в новые. Другими словами, предположим, что форма включает текстовую область:
$co->textarea(
-name=>'textarea',
-default=>'',
-rows=>4,
-columns=>40)
Если пользователь вводит текст, а затем передает его сценарию, тот может прочитать данные стандартными CGI-методами. Однако, когда вы возвращаете Web-страницу с аналогичной формой, CGI.pm восстанавливает в текстовой области исходный текст (даже если в качестве текста по умолчанию задана пустая строка). В приложении результат будет таков: когда пользователь отсылает набранный текст, он воспринимается сценарием, но не исчезает из текстовой области. Чтобы CGI.pm обновлял элементы управления значениями по умолчанию, атрибут -override нужно установить в значение истина:
$co->textarea(
-name=>'textarea',
-default=>'',
-override=>1,
-rows=>4,
-columns=>40
)
Теперь текстовая область будет очищаться после прочтения комментария, к чему вы и стремились.
Листинг 21.1. chat.htm
<HTML>
<HEAD>
<TITLE>Chat</TITLE>
<FRAMESET ROWS="150,*">
<NOFRAMES>Sorry, you need frames to use chat.</NOFRAMES>
<FRAME NAME="_display" SCR="chat1.cgi">
<FRAME NAME="_data" SRC="chat2.cgi">
</FRAMESET>
</HTML>
Листинг 21.2. chat1.cgi
#!/usr/lib/perl
use CGI;
use Fcntl;
$co = new CGI;
open (DATA1, "<chat1.dat") or die "Could not open data
file.";
lockfile(DATA1);
$text1 = < DATA1>;
unlockfile(DATA1);
close DATA1;
open (DATA2, "<chat2.dat") or die "Could not open datafile.";
lockfile(DATA2);
$text1 = < DATA2>;
unlockfile(DATA2);
close DATA2;
$co->header,
"<meta HTTP-EQUIV=\"refresh\" CONTENT=\"5\">",
$co->start_html(
-title=>'Chat Example',
-author=>'Steve',
-target=>'_display',
Создаем приложение для многопользовательской беседы
-BGCOLOR=>'white',
-LINK=>'red'
),
$co->center($co->h1('Multi-User Chat')),
$co->p,
$co->p,
$co->center($text1),
$co->p,
$co->center($text2),
$co->end_html;
exit;
sub lockfile
{
my $count = 0;
my $handle = shift;
until (flock($handle, 2)) {
sleep .10;
if(++$count > 50) {
$co->header,
"<meta HTTP-EQUIV=\"refresh\" CONTENT=\"5\">",
$co->start_html(
-title=>'Chat Example',
-author=>'Steve',
-target=>'_display',
-BGCOLOR=>'white',
-LINK=>'red'
),
$co->center($co->h1('Server too busy')),
$co->end_html;
exit;
}
}
}
sub unlockfile
{
my $handle = shift;
flock($handle, 8);
}
Листинг 21.3. chat2.cgi
#!/usr/lib/perl
use CGI;
use Fcntl;
$co = new CGI;
if($co->param()) {
продолжение И
$name =$co->param('username');
$name =~ s/</</;
$text =$co->param('textarea');
$text =~ s/</</;
if ($text) {
my $oldtext; open (OLDDATA, "<chat2.dat" or die "Could not open
data file."
lockfile(OLDDATA);
$oldtext= <OLDDATA>;
unlockfile(OLDDATA);
close OLDDATA;
open (DATA, "<chat1.dat" or die "Could not open
data file."
lockfile(DATA);
print DATA $oldtext;
unlockfile(DATA);
close DATA;
open (NEWDATA, "<chat2.dat" or die "Could not open
data file."
lockfile(NEWDATA);
print NEWDATA "<B>", $name, ": ", "</B>", $text;
unlockfile(NEWDATA);
close NEWDATA;
}
}
&printpage
sub printpage
{
$co->header,
$co->start_html(
-title=>'Chat Example',
-author=>'Steve',
-BGCOLOR=>'white',
-LINK=>'red'
),
$co->startform,
$co->center(
$co->textarea(
-name=>'textarea',
-default=>'',
-override=>1,
-rows=>4,
-columns=>40
Читаем и записываем теневые посылки (cookies)
)
),
$co->center
( $co->submit(-value=>'Send text'),
$co->reset, ),
$co->hidden(-name=>'hiddendata'),
$co->endform,
$co->end_html;
}
sub lockfile
{my $count = 0;
my $handle = shift;
until (flock($handle, 2)) {
sleep .10;
if(++$count > 50)
{ &printpage;
exit; }
}
}
sub unlockfile
{ my $handle = shift;
flock($handle, 8); }
Читаем и записываем теневые посылки (cookies)
Этот параграф посвящен записи и использованию теневых посылок, которые, как известно пользователям Интернета, позволяют сохранять информацию сервера на машине пользователя. Но прежде чем обратиться к этой возможности, учтите, что единодушного мнения относительно теневых посылок нет и далеко не все одобряют их.
Использование теневых посылок
Теневые посылки и любимы, и ненавидимы. Многие пользователи терпеть не могут, когда на их компьютерах сохраняются мегабайты информации подобного рода. Мне приходилось видеть Web-страницу, на которой было более 70 теневых посылок. (Это не столь безобидно, как кажется. В большинстве броузеров верхний предел теневых посылок — число порядка 200.) Поскольку теневые посылки позволяют отслеживать передвижение пользователей по разделам, а также делать заказы при помощи «магазинной карты», то теплые чувства к теневым посылкам иногда все же преобладают над раздражением.
Сценарий, приведенный в листинге 21.4, позволяет посетителю изменить страницу так, чтобы при следующих визитах она бы приветствовала его по имени, а в день рождения еще и поздравляла бы. Этот сценарий вполне корректен — он не устанавливает никаких теневых посылок до тех пор, пока пользователь сам не предоставит или не обновит необходимую информацию. Сценарий проверяет данные, полученные от пользователя, чтобы убедиться, что день рождения вве
ден в формате месяц/день (mm/dd) (и содержит лишь цифры, а единственным символ / находится в нужном месте) и удаляет теги HTML, которые он мог ввести в строку для имени.
Когда пользователь впервые открывает сценарий hellocookie.cgi, он видит то, что изображено на рис. 21.2. Чтобы настроить эту страницу, он может ввести имя и дату рождения в формате mm/dd. После нажатия на кнопку подтверждения сценарий записывает информацию под именем greetings, сохраняя имя и день рождения, на компьютере клиента.
Когда пользователь снова открывает hellocookie.cgi, сценарий проверяет, нет ли у посетителя теневой посылки greetings, и, если она есть, выводит приветствие (рис. 21.3), включая при необходимости поздравление с днем рождения. Вот и все.
Рис. 21.2. Устанавливаем теневую посылку
Рис. 21.3. Читаем теневую посылку
Читаем и записываем теневые посылки (cookies)
Как записать теневую посылку
Записать теневую посылку с помощью CGI.pm несложно. В нашем примере она будет называться "greetings" и хранить информацию в хэше %greetings, уничтожая ее по истечении года:
$co = new CGI;
$greetingcookie = $co->cookie(
-name=>'greetings',
-value=>\%greetings,
-expires=>'+365d'
);
$co->header(-cookie=>$greetingcookie);
Заметьте, что для создания теневой посылки вы передаете ее в качестве именованного параметра CGI-методу header.
Как прочитать теневую посылку
Для чтения теневой посылки используется обычный CGI-метод, получающий в качестве параметра имя посылки. После этой операции можно использовать данные хэша %greetings:
$co = new CGI;
%greetings = $co->cookie('greeting');
print $greetings{'name'}
Вот и вся работа с теневыми посылками. Но имейте в виду, что многие пользователи не желают, чтобы программы хранили какие бы то ни было данные на их машинах.
Листинг 21.4. hellocookie.cgi
#!/usr/bin/perl
use CGI;
$co = new CGI;
%greetings = $co->cookie('greeting');
if ($co->param('name')) {$greetings{'name'} =
$co->param{'name'}}
if ($co->param('birthday') =~ m/\d\d\/\d\d/) {
$greetings{'birthday'} = $co->param('birthday');
}
($date, $month, $year) = (lockaltime)[3, 4, 5];
$date = join (".", $month + 1, $day);
if(exists($greetings{'name'})) {
$greetingstring = "Hello " , $greetings{'name'};
$greetingstring .= ", happy birthday!" if
($date eq $greetings{'birthday'});
$greetingstring =~ s/</</;
$promt = "If you want to change this page's settings,
just enter new data below.";
} else {
$promt = "To have this page greet you next time,
enter your data below.";
}
$greetingcookie = $co->cookie(
-name=>'greetings',
-value=>\%greetings,
-expires=>'+365d'
);
if ($co->param('name') || $co-param{'birthday')) {
print $co->header(-cookie=>$greetingcookie);
} else {
print $co->header;
}
$co->start_html(
-title=>"Cookie Example",),
$co->center(
$co->h1("Cookie Example"),
$co->p,
$co->h1($greetingsstring"),
$prompt,
$co->startform,
"Your name: ",
$co-textfield(
-name=>'name',
-default=>'',
-override=>1 ),
$co->p,
"Your birthday (mm/dd): ",
$co->textfield(
-name=>'birthday',
-default=>'',
-override=>1
),
$co->p,
$co->submit (-value=>'Submit'),
$co->reset,
$co->endform,
),
$co->end_html;
Создаем игру
Создаем игру
Наша книга завершается разделом об игре, game.cgi. Это Интернет-версия широко известной «Виселицы». Она интерактивна и неплохо защищена, поскольку пользователь работает лишь с переключателями, кнопками для подтверждения и отмены и гиперссылками. И в итоге получается очень неплохо — содержимое окна Netscape Navigator в процессе игры показано на рис. 21.4.
Рисунок 21.4 изображает начальный экран игры. Пользователь может угадывать буквы с помощью переключателя и кнопки Submit. Если он угадает слово до того, как сделает 8 неверных попыток, то увидит страничку с поздравлением, в противном случае сценарий откроет ответ и предложит попробовать еще раз. Также пользователь может в любой момент начать новую игру, выбрав соответствую щий переключатель и нажав кнопку Submit.
Рис. 21.4. Устанавливаем теневую посылку
Хранение данных между вызовами сценария в Web-страницах
Этот сценарий — хороший пример того, как сохранять данные в Web-страницах между вызовами сценария. Все промахи и попадания, сделанные пользовате лем, — и даже сам ответ — хранятся в скрытых полях формы. Это означает, что отслеживать действия пользователей между вызовами сценария нет необходимо сти, — заполненная форма даст вам всю необходимую информацию. Конечно же, пользователь может «передергивать карты», изменяя код HTML-страницы, но в конце концов это всего лишь игра. Однако, если это для вас важно, вы можете зашифровать информацию (см. всеобъемлющий архив, посвященный языку Perl, по адресу www.cpan.prg/CPAN.html).
Настраиваем игру
Для установки игры понадобится файл game.cgi, приведенный в листинге 21.5. Помимо прочего, нужен файл со словами, answers.dat, который игра использует при загадывании слов. Я постарался сделать это необходимое требование настолько гибким, насколько возможно — в файле answers.dat может храниться произволь ное количество слов любой длины. Достаточно записать по одному слову (строчными буквами) на строчку файла answers.dat без запятых, пробелов и других разделителей. Сценарий написан так, чтобы принимать текстовые файлы с переводами строк в стиле Unix (\n) и DOS (\r\n), так что вы можете создать файл ответов на своей машине, а затем переслать его провайдеру (только не забудьте установить достаточно низкий — например, 644 в Unix — уровень доступа, чтобы game.cgi мог читать данные). Вот несколько записей из файла answers.dat:
instruction
history
attempt
harpsicord
flower
person
pajamas
Поскольку игра вполне наглядна, сценарий автоматически умеет выводить (если они есть) картинки вроде той, что показана на рис. 21.4. (Ничего страшного, если картинок нет, — сценарий проверяет наличие графических файлов до их использования.) Картинки хранятся в одном каталоге с game.cgi, в файлах hang1.gif, hang2.gif и т. д. до hang8.gif. Это, соответственно, рисунок для заставки и остальная графика, причем число файлов равняется количеству попыток пользователя. Файл hang1.gif хранит изображение виселицы, файл hang2.gif — виселицы с головой приговоренного и так шаг за шагом, до рисунка «следующий — последний» в hang8.gif. Если пользователь ошибается еще раз, игра выводит картинку hang9.gif (если она существует) и правильный ответ; если же пользователь выигрывает, то выводится (если есть) hang10.gif и страница с поздравлением. Наконец, это просто хорошо настраиваемый сценарий — несомненно, он вам понравится!
Листинг 21.5. game.cgi
#!/usr/lib/perl
use CGI;
$co = new CGI;
if($co->param('newgame') eq "yes" ||
!$co->paraml('newgame')){
newgame();
} else {
if($co->param('newgameyesno') eq "yes"){
newgame();
} else {
Создаем игру
$theanswer = $co->param('answer');
$theguess = getguess();
if($theguess iq "-"){
$thehits = $co->param('hits');
$themisses = $co->param('misses');
displayresult();
} else {
$thehits = gethits();
if (index($thehits, "-" eq -1){
youwin();
} else {
$themisses = getmisses();
if(length($themisses) >= 9){
youlose();
} else {
desplayresult();
}
}
}
}
}
sub newgame
{
$datafile = "answers.dat"
open ANSWERDATA, $datafile;
@answers = <ANSWERDATA>;
close (ANSWERDATA);
srand(time ^ $$);
$index1 = $#answers * rand;
$theanswer = $answers[$index];
chomp($theanswer);
$themisses = "-";
$thehits = "";
for($loopindex = 0; $loopindex < length($theanswer);
$loopindex++){
$thehits .= "-";
}
displayresult();
}
sub getguess
{
theguess = "-";
if ($co->param('letters')){
$theguess = lc($co->param('letters'));
}
}
sub displayresults
{
$co->header,
$start_html(-tetle=>'Word Game', -author=>'Steve',
-bgcolor=>'black, -text=>'#ffff00', -link=>'#ff0000',
-alink=>'#ffffff', -vlink'#ffff00'),
$co->center(
"<front color = #ffff00>",
$co->h1('World Game!'),
$co->hr
);
$len = length($themisses);
if (-e "hang${len}.gif") {
print $co->img({-src=>"hang${len}.gif",
-align=>left, -vspace=>10, -hspace=>1});
}
$co->center(
$co->h1($thehits),
"<cont color = #ffff00>",
$co->h2(Misses (8 max); " . substr($themisses, 1)),
$co->startform,
$co->hidden(-name=>'newgame', -default=>'no',
-override=>1),
$co->hidden(-name=>'answer', -default=>'$theanswer',
-override=>1),
$co->hidden(-name=>'hits', -default=>'$thehits',
-override=>1),
$co->hidden(-name=>'misses', -default=>'$themisses',
-override=>1),
$co->br,
"Guess a letter:",
$co->br,
),
"<center>",
"A<input type = radio name = \"letters\" value = \"A\"
checked>";
for ($loopindex = ord('B'); $loopindex <= ord('M');
$loopindex++) {
$c = chr($loopinsex);
print "${c}<input type = radio name = \"letters\"
value = \"${c}\" >";
}
print $co->br,
for ($loopindex = ord('N'); $loopindex <= ord('Z');
$loopindex++) {
$c = chr($loopinsex);
print "${c}<input type = radio name = \"letters\"
value = \"${c}\" >";
Создаем игру
}
print $co->br,
"Then submit your guess by clicking ",
$co->submit(-value=>'this button'),
$co->br, $co->br,
"Start new game?"
"<input type = radio name = \"newgameyesno\" value =
\"yes\"> Yes"
"<input type = radio name = \"newgameyesno\" value =
\"no\" checked> No"
"</center>",
$co->endform,
"</form>",
$co->end_html;
}
sub dethits
{
$temphits = $co->param('hits');
$thehits = "";
for($loopindex = 0; $loopindex < length($theanswer);
$loopindex++){
$thechar = substr($temphits, $loopindex, 1);
$theanswerchar = substr($temphits, $loopindex, 1);
if($theguess eq $theanswerchar){
$thechar = $theguess;
}
$thehits .= $thechar;
}
return $thehits;
}
sub getmisses
{
$themisses = $co->param('misses');
if(index($theaswer, $theguess) eq -1){
if(index($themisses, $theguess) eq -1){
$themisses, .= $theguess;
}
}
return $themisses;
}
sub youwin
{
$co->header,
$start_html(-tetle=>'Word Game', -author=>'Steve',
-bgcolor=>'black, -text=>'#ffff00', -link=>'#ff0000',
-alink=>'#ffffff', -vlink'#ffff00'),
"<center>",
"<front color = #ffff00>",
$co->h1('World Game!'),
$co->hr
$co->br
"</font>"
"<front color = #ffffff>",
$len = length($themisses);
if (-e "hang10.gif") {
print $co->img({-src=>"hang10.gif",
-align=>left, -vspace=>10, -hspace=>1});
}
$co->h1("You got it: ", $theanswer),
$co->h1("You win!"),
$co->br, $co->br,
$co->startform,
$co->hidden(-name=>'newgame', -default=>"yes",
-override=>1),
$co->br, $co->br,
$co->submit(-value=>'New Game'),
$co-endform,
"</font>",
"</center>",
$co->end_html;
}
sub youlose
{
$co->header,
$start_html(-tetle=>'Word Game', -author=>'Steve',
-bgcolor=>'black, -text=>'#ffff00', -link=>'#ff0000',
-alink=>'#ffffff', -vlink'#ffff00'),
"<center>",
"<front color = #ffff00>",
$co->h1('World Game!'),
$co->hr
$co->br
"</font>"
"<front color = #ffffff>",
$len = length($themisses);
if (-e "hang9.gif") {
print $co->img({-src=>"hang9.gif",
-align=>left, -vspace=>10, -hspace=>1});
}
$co->h1("The answer: ", $theanswer),
$co->h1("Sorry, too many guesses taken!"),
$co->br, "Better luck next time.",
Создаем игру
$co->br,$co->br,
$co->startform,
$co->hidden(-name=>'newgame', -default=>"yes",
-override=>1),
$co->br, $co->br,
$co->submit(-value=>'New Game'),
$co-endform,
"</font>",
"</center>",
$co->end_html;
}