Приложение ISFORM
Приложение ISFORM демонстрирует способ получения и обработки данных, полученных от формы, расширением ISAPI. Аналогичные действия выполняла программа CGI с именем CONTROLS, описанная нами в предыдущей главе.
Исходный текст документа HTML, содержащий форму, представлен в листинге 8.4. Эта форма уже использовалась нами ранее в предыдущей главе (рис. 7.2), поэтому мы не будем показывать ее внешний вид снова для экономии места.
Листинг 8.4. Файл chap8\isform\isform.htm
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>Органы управления в формах</TITLE>
</HEAD>
<BODY BGCOLOR=#FFFFFF>
<FORM METHOD=POST ACTION="http://frolov/scripts/isform.dll?Param1|Param2|Param3">
<TABLE>
<TR>
<TD VALIGN=TOP>Текстовое поле TEXT</TD>
<TD><INPUT TYPE=text NAME="text1" VALUE="Sample of text1" SIZE=30></TD>
</TR>
<TR>
<TD VALIGN=TOP>Текстовое поле PASSWORD</TD>
<TD><INPUT TYPE=password NAME="pwd" VALUE="Sample of password"></TD>
</TR>
<TR>
<TD VALIGN=TOP>Текстовое поле TEXTAREA</TD>
<TD><TEXTAREA NAME="text2" ROWS=4 COLS=30>Sample of text</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="First Option">First Option</OPTION>
<OPTION Value="Second Option">Second Option</OPTION>
<OPTION Value="None">None Selected</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">
<INPUT TYPE=reset VALUE="Reset">
<P><INPUT TYPE=IMAGE SRC="send.gif" BORDER=0>
</FORM>
</BODY>
</HTML>
Вызов расширения ISAPI выполняется в форме с помощью параметра ACTION оператора <FORM>, как это показано ниже:
ACTION="http://frolov/scripts/isform.dll?Param1|Param2|Param3">
После разделительного символа “?” расширению передается строка параметров Param1|Param2|Param3.
Результат обработки формы показан на рис. 8.2.

Рис. 8.2. Результат обработки формы расширением ISAPI с именем isform.dll
Обратите внимание, что поля TotalBytes и Available содержат одинаковые значения. Следовательно, все принятые данные поместились в буфере предварительной загрузки. И это не удивительно - форма передала всего 127 байт данных.
Исходный текст расширения isform.dll показан в листинге 8.5.
Листинг 8.5. Файл chap8\isform\isform.c
// ===============================================
// Расширение ISAPI isform.c
// Обработка данных, полученных от формы,
// при помощи расширения ISAPI
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW: http://www.glasnet.ru/~frolov
// или
// http://www.dials.ccas.ru/frolov
// ===============================================
#include <windows.h>
#include <httpext.h>
// Прототипы функций перекодировки
void DecodeStr(char *szString);
char DecodeHex(char *str);
// =============================================================
// Функция GetExtensionVersion
// Запись версии интерфейса ISAPI и
// строки описания расширения
// =============================================================
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
// Записываем версию интерфейса ISAPI
pVer->dwExtensionVersion =
MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );
// Записываем строку описания расширения
lstrcpyn(pVer->lpszExtensionDesc,
"Form Parser ISAPI DLL", HSE_MAX_EXT_DLL_NAME_LEN);
return TRUE;
}
// =============================================================
// Функция HttpExtensionProc
// =============================================================
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)
{
CHAR szBuff[4096];
CHAR szTempBuf[4096];
char * szPtr;
char * szParam;
// Нулевой код состояния - признак успешного выполнения
lpECB->dwHttpStatusCode = 0;
// Записываем в буфер заголовок HTTP и начальный
// фрагмент формируемого динамически документа HTML
wsprintf(szBuff,
"Content-Type: text/html\r\n\r\n"
"<HTML><HEAD><TITLE>Simple ISAPI Extension</TITLE></HEAD>\n"
"<BODY BGCOLOR=#FFFFFF><H2>Information from ECB</H2>\n");
// Добавляем версию интерфейса ISAPI
wsprintf(szTempBuf, "<P>Extension Version: %d.%d",
HIWORD(lpECB->dwVersion), LOWORD(lpECB->dwVersion));
strcat(szBuff, szTempBuf);
// Название метода передачи данных
wsprintf(szTempBuf, "<BR>Method: %s", lpECB->lpszMethod);
strcat(szBuff, szTempBuf);
// Строка параметров запуска расширения ISAPI
wsprintf(szTempBuf, "<BR>QueryString: %s",
lpECB->lpszQueryString);
strcat(szBuff, szTempBuf);
// Физический путь к программному файлу расширения ISAPI
wsprintf(szTempBuf, "<BR>PathTranslated: %s",
lpECB->lpszPathTranslated);
strcat(szBuff, szTempBuf);
// Полный размер данных, которые нужно получить
wsprintf(szTempBuf, "<BR>TotalBytes: %d",
lpECB->cbTotalBytes);
strcat(szBuff, szTempBuf);
// Сколько доступно предварительно прочитанных данных
wsprintf(szTempBuf, "<BR>Available: %d",
lpECB->cbAvailable);
strcat(szBuff, szTempBuf);
// Тип данных
wsprintf(szTempBuf, "<BR>ContentType: %s",
lpECB->lpszContentType);
strcat(szBuff, szTempBuf);
lstrcpyn(szTempBuf, lpECB->lpbData, lpECB->cbAvailable + 1);
szTempBuf[lpECB->cbAvailable + 1] = '\0';
strcat(szBuff, "<H2>Принятые данные</H2>");
strcat(szBuff, szTempBuf);
// Перекодируем данные и отображаем результат
// перекодировки
DecodeStr(szTempBuf);
strcat(szBuff, "<H2>Данные после перекодировки</H2>");
strcat(szBuff, szTempBuf);
// Выводим в документ список значений полей формы
strcat(szBuff, "<H2>Список значений полей</H2>");
szTempBuf[lpECB->cbAvailable] = '&';
szTempBuf[lpECB->cbAvailable + 1] = '\0';
for(szParam = szTempBuf;;)
{
szPtr = strchr(szParam, '&');
if(szPtr != NULL)
{
*szPtr = '\0';
DecodeStr(szParam);
strcat(szBuff, szParam);
strcat(szBuff, "<BR>");
szParam = szPtr + 1;
if(szParam >= (szTempBuf + lpECB->cbAvailable))
break;
}
else
break;
}
// Конечный фрагмент документа HTML
strcat(szBuff, "</BODY></HTML>");
// Посылаем содержимое буфера удаленному пользователю
if(!lpECB->ServerSupportFunction(lpECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL,
(LPDWORD)szBuff))
{
// Если послать данные не удалось,
// завершаем работу нашего расширения ISAPI
// с кодом ошибки
return HSE_STATUS_ERROR;
}
// Записываем код успешного завершения
lpECB->dwHttpStatusCode = 200;
// Возвращаем принак успешного завершения
return HSE_STATUS_SUCCESS;
}
// ------------------------------------------------
// Функция DecodeStr
// Раскодирование строки из кодировки URL
// ------------------------------------------------
void DecodeStr(char *szString)
{
int src;
int dst;
char ch;
// Цикл по строке
for(src=0, dst=0; szString[src]; src++, dst++)
{
// Получаем очередной символ перекодируемой строки
ch = szString[src];
// Заменяем символ "+" на пробел
ch = (ch == '+') ? ' ' : ch;
// Сохраняем результат
szString[dst] = ch;
// Обработка шестнадцатеричных кодов вида "%xx"
if(ch == '%')
{
// Выполняем преобразование строки "%xx"
// в код символа
szString[dst] = DecodeHex(&szString[src + 1]);
src += 2;
}
}
// Закрываем строку двоичным нулем
szString[dst] = '\0';
}
// ------------------------------------------------
// Функция DecodeHex
// Раскодирование строки "%xx"
// ------------------------------------------------
char DecodeHex(char *str)
{
char ch;
// Обрабатываем старший разряд
if(str[0] >= 'A')
ch = ((str[0] & 0xdf) - 'A') + 10;
else
ch = str[0] - '0';
// Сдвигаем его влево на 4 бита
ch <<= 4;
// Обрабатываем младший разряд и складываем
// его со старшим
if(str[1] >= 'A')
ch += ((str[1] & 0xdf) - 'A') + 10;
else
ch += str[1] - '0';
// Возвращаем результат перекодировки
return ch;
}
Для перекодирования принятых данных из кодировки URL мы использовали здесь функции DecodeStr и DecodeHex, описанные нами в разделе “Программа CONTROLS” предыдущей главы.
Перед тем как выполнить перекодировку принятых данных, расширение копирует эти данные во временный буфер szTempBuf, и закрывает его двоичным нулем, превращая в строку. Такая операция допустима, если передаются только текстовые данные (а это как раз наш случай). После копирования адрес буфера передается функции DecodeStr:
lstrcpyn(szTempBuf, lpECB->lpbData, lpECB->cbAvailable + 1);
szTempBuf[lpECB->cbAvailable + 1] = '\0';
DecodeStr(szTempBuf);
Сканирование и вывод значений отдельных полей формы выполняется аналогично тому, как это делалось в программе CGI с именем CONTROLS. Однако в отличие от указанной программы, значения отдельных полей не выводятся в стандартный поток STDOUT, а дописываются в конец буфера szBuff функцией strcat.
Файл определения модуля библиотеки DLL приложения приведен в листинге 8.6.
Листинг 8.6. Файл chap8\isform\isform.def
LIBRARY isform
DESCRIPTION 'Form Parser ISAPI DLL'
EXPORTS
GetExtensionVersion
HttpExtensionProc