Создание CGI-программ Оглавление COOKIE: Идентификация пользователей

Библиотека ITCGI

Первое, с чего хочу начать этот параграф, так это объяснить, зачем, собственно, нам понадобилось изобретать велосипед и чем наш велосипед лучше других.

Изначально, первые свои CGI-программы для WEB мы писали на языке Perl. Они были не очень сложными. Далее, когда возникли задачи более серьезные, связанные с системным программированием, с базами данных, мы приняли решение перейти на Си/C++. Многие сторонники языка Perl утверждают, что он ни чем не хуже. Может быть, если не знать Си/С++. Конечно, это вопрос очень спорный. Можно долго отстаивать свое мнение, но так ни к чему и не прийти. На нашем сайте http://perl.org.ru в форуме как-то возникла такая дискуссия, но так ничем и не закончилась. Мой хороший знакомый, автор и создатель сайта http://dore.ru, сторонник языка Perl. В скриптовых языках типа Perl, PHP или Unix-shell я вижу только два существенных преимущества.

  1. автоматическое приведение типов
  2. формирование строк на лету
Первое может служить и серьезным недостатком. На мой взгляд, при разработке больших приложений лучше все-таки использовать языки с жесткой типизацией, чтобы не складывать "деньги" с "днями". Самым серьезным недостатком языка Perl является повышенные требования к производительности компьютера. Perl - язык интерпретируемый, поэтому требуется значительно больше памяти и процессорного времени для работы Perl-программы. Мне приходилось наблюдать, как форум, написанный на Perl загружал сервер на 60%. Теперь представьте реальную ситуацию, когда у вас на одном сервере крутится несколько десятков сайтов. Сервер просто может повиснуть. Теперь приведу свои доводы в пользу языков Си\С++.
  1. Системные вещи пишутся на Си. Не будет проблем интеграции с любым API, например, вы можете использовать функции системных библиотек, использовать OpenGL - библиотеку трехмерной графики для создания графических сцен.
  2. Результатом является исполняемый файл, а значит работать будет быстрее. А также в случае, если злоумышленник получит доступ на чтение к файлам в папке cgi-bin, то найти дырку в бинарном файле значительно сложнее, нежели прочитать исходник интерпретируемого языка типа Perl или PHP.
  3. Си имеет гораздо большую сферу применения. Вам не надо изучать синтаксис нового языка, чтобы начать писать игры, приложения баз данных или записную книжку.
  4. Язык С++ хорошо соответствует объектно-ориентированной парадигме.
  5. Си\C++ языки профессионалов.
  6. И решающим фактором был опыт работы, у любого программиста нашей команды более 5 лет программирования на Си\С++.
Вот почему мы выбрали именно этот язык программирования.

Теперь, почему мы решили написать свою библиотеку. Сначала, мы как любые умные люди, попытались воспользоваться уже готовым решением. Под юниксом мы попробовали использовать библиотеки cgiparse и cgihtml. И вот причины, по которым мы пришли к написанию собственной библиотеки.
  1. Перед вызовом функций этих библиотек требуется начальная инициализация, а перед завершением работы, очистка памяти. В программе вы должны объявить переменную llist entries; и вызвать функцию read_cgi_input(&entries); Если начальная инициализация еще не вызывает хлопот, то вызов функции очистки памяти - list_clear(&entries); неприятен тем, что выходов из программы может быть несколько. И перед каждой инструкцией return вам придется писать list_clear(&entries);. В библиотеке ITCGI начальная инициализация выполняется при первом вызове любой функции, а завершающая очистка памяти происходит автоматически при завершении программы.
  2. При получении значения параметра html-формы библиотеки cgiparse и cgihtml возвращают указатель на строку в их внутреннем буфере. Теперь, если вы по этому указателю запишите другое значение, или еще хуже, освободите память под ним, то при следующем запросе значения параметра вы получите уже другое значение - не то, что было в оригинале. Библиотека ITCGI выдает всегда копию строки.
  3. Нам нужно было работать не только под unix, но и под windows. Под Windows мы использовали MFC и класс CString. Библиотека ITCGI имеет одинаковый интерфейс и под Unix и под Windows. В Windows ее реализация написана на С++ с использованием MFC. В Unix мы сделали все на чистом Си, написав свою структуру LString.
  4. Библиотеки cgiparse и cgihtml не имели функций для считывания файла в строку, для замены в html-шаблоне переменных помеченных как %%var%% на их реальные значения, для обработки SSI-директивы include virtual и многих других.
На текущий момент библиотека ITCGI содержит следующие группы функций:
  1. работа с CGI-параметрами
  2. Cookie - параметры на стороне клиента
  3. Работа с параметрами на стороне сервера.
  4. Функции общего назначения.
  5. Работа с HTML.
  6. Функции для работы с картинками GIF & JPEG
Начнем с рассмотрения простой программы, которая выводит список cgi-параметров. Для работы со строками применяется тип LString, который есть ни что иное как char*.
typedef char* LString;
Любая строка создаётся вызовом CreateString(), а удаляется функцией DeleteString(). Вводить свой тип понадобилось для написания логически правильных программ, на наш взгляд. Кто-то может и не согласится. Рассмотрим в качестве примера функцию -
int GetParamByIndex(int index, LString* value);
Эта функция возвращает по индексу параметра его значение. Например, для строки cgi-запроса text=zero&text=zero&list=0&list2=0 нулевым параметром будет text, а его значение - zero. Теперь давайте подумаем о прототипе функции GetParamByIndex. При просмотре некоторых библиотек для разработки CGI-программ мне попадались функции примерно такие:
char* GetParamByIndex(int index);
При этом, в реализации функции GetParamByIndex будет динамически выделятся память под значение возвращаемой строки. Такой интерфейс не годится, т.к. printf("%s", GetParamByIndex(1)); приведет к утечке памяти. На мой взгляд, выделять динамически память и перекладывать заботу о ее освобождение на кого-то является дурным стилем. Кто память выделяет, тот и должен ее освобождать. При использование LString у вас не болит голова о распределение памяти. Ниже приводится исходный код на Си и Makefile, который использовался для сборки программы под операционной системой FreeBSD4.2.
//listcgi.c
#include <itcgi.h>


int main()
{
LString* name = CreateString();  // строка для хранения имени параметра
LString* value = CreateString(); // его значения
int i, count;

count = GetCount();              // получаем общее количество параметров


printf("Content-type: text/html\n\n"); // печатаем заголовок документа

// выводим HTML-код таблицы
printf("<html><table border=\"1\"><caption>Полный список\
 cgi-параметров</caption><tr><td\
 bgcolor=\"E5E5E5\">Индекс<td bgcolor=\"E5E5E5\">Имя<td\
 bgcolor=\"E5E5E5\">Значение");
 
 // в цикле проходим по всем параметрам
for(i=0;i<count;i++)
{
 GetParamByIndex(i, value);    // получаем значение параметра по его индексу
 GetParamNameByIndex(i, name); // получаем имя параметра
 printf("<tr><td>%d <td> %s <td> %s\n", i, *name, *value);
}
printf("</table>");


DeleteString(name); // освобождаем память
DeleteString(value);
return 0;
}

===Makefile===
all: listcgi 

listcgi: listcgi.c itcgi.a
        gcc listcgi.c -L/usr/local/lib/mysql -I/usr/local/include/mysql \
        -L/usr/local/lib -I/usr/local/include \
-o listcgi -lmysqlclient /usr/lib/itcgi.a -Wall -O3 
        strip listcgi
        cp listcgi /www/members/cgi-bin/listcgi



В качестве примера я взял простенькую форму.
text:
password:
checkbox: radio1: radio2: radio3


 

Вот результат ее обработки:
Полный список cgi-параметров
ИндексИмяЗначение
0 text Тестируем
1 password ывфыв
2 checkbox on
3 radio r2
4 textarea Простой текст
5 list 2
6 list2 10
7 list2 1
8 list2 2
9 list2 0
А это строка CGI-парметров:
text=%D2%E5%F1%F2%E8%F0%F3%E5%EC&password=%FB%E2%F4%FB%E2&checkbox=on& radio=r2&textarea=%CF%F0%EE%F1%F2%EE%E9+%F2%E5%EA%F1%F2&list=2&list2=10& list2=1&list2=2&list2=0 Библиотеку ITCGI можно взять на сайте http://itsoft.ru. Далее мы рассмотрим несколько примеров полезных программ.