Сервер Web своими руками. Язык HTML, приложения CGI и ISAPI, установка серверов Web для Windows© Александр Фролов, Григорий ФроловТом 29, М.: Диалог-МИФИ, 1997, 288 стр. Приложение 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 |