Давайте теперь подведем черту под тем, что было сделано в прошлых шагах. Все эти классы были предназначены для одного самого главного CGIApp.
Вот его описание:
class CGIApp{
public:
CGIParam *Param;
int Method;
char *Query_String;
long Content_Length;
CGIApp();
~CGIApp();
int Init();
CGIContent *FindParam(char *name);
};
Все просто как всегда, Param хранит полученные данные, Method содержит данные о методе передачи данных, который определяется следующими константами:
#define METHOD_GET 1 #define METHOD_POST 2Переменные Query_String и Content_Length содержат значения полученные из соответствующих переменных окружения.
В конструкторе и деструкторе класса также все по старому:
CGIApp::CGIApp(){
Param=new CGIParam();
Query_String=NULL;
Method=0;
Content_Length=0;
};
CGIApp::~CGIApp(){
delete Param;
if (Query_String!=NULL) free(Query_String);
};
Особую "ценность" содержит в себе метод Init(). Эта процедура инициализирует все данные необходимые для работы приложения CGI, т.е. получает и декодирует все полученные от клиента данные. Сначала приведу текст этого метода:
int CGIApp::Init(){
char *Buffer;
char *Content_Len=NULL;
char *Query_Str=NULL;
char *ParamName=NULL;
long Buffer_Len=0;
long Buffer_Size=0;
char Mode=0, ToMode=0;
long i=0;
char ch=0;
CGIContent *temp=NULL;
Buffer=getenv("REQUEST_METHOD");//получаем метод передачи данных
if (Buffer==NULL) return 0;//если нет такой переменной то ошибка
Query_Str=getenv("QUERY_STRING");//строка в URL после "?"
Content_Len=getenv("CONTENT_LENGTH");//длинна передаваемых данных
if (strcmp(Buffer,"GET")==0){
if (Query_Str!=NULL) Content_Length=strlen(Query_Str);
else Content_Length=0;
Method=METHOD_GET;
} else {
if (strcmp(Buffer,"POST")==0){
Method=METHOD_POST;
if (Content_Len!=NULL) Content_Length=atol(Content_Len);
else Content_Len=0;
} else goto proc_exit;
};
Buffer=NULL;
Buffer_Size=10000;
//инициализируем буффер для работы
while (Buffer==NULL){
Buffer=(char *)malloc(Buffer_Size);
if (Buffer==NULL){
if ((Buffer_Size-=1000)<0) return 0;
};
};
//основной цикл
while (i<Content_Length){
//получаем следующий байт данных в
//зависимости от метода передачи
switch (Method){
case METHOD_GET:{
ch=*(Query_Str+i);
break;
};
case METHOD_POST:{
fread(&ch,1,1,stdin);
break;
};
};//switch;
switch (Mode){
case 0:{//режим накопления имени параметра
if (ch!='='){
if (ch=='+') ch=' '; else
if (ch=='%'){
ToMode=0;
Mode=2;
break;
};
*(Buffer+Buffer_Len)=ch;
Buffer_Len++;
} else {
Mode=1;
*(Buffer+Buffer_Len)=0;
if (ParamName!=NULL) free(ParamName);
ParamName=strdup(Buffer);
Buffer_Len=0;
};
break;
};//case 0;
case 1:{//режим накопления содержимого атрибута
if (ch!='&'){
if (ch=='+') ch=' '; else
if (ch=='%'){
ToMode=1;
Mode=2;
break;
};
*(Buffer+Buffer_Len)=ch;
if ((Buffer_Len+=1)>=Buffer_Size){
*(Buffer+Buffer_Len)=0;
if (ParamName!=NULL){
temp=Param->Add(ParamName,Buffer);
free(ParamName);
ParamName=NULL;
} else {
temp->Add(Buffer);
};
Buffer_Len=0;
};
} else {//если найден символ &, то добавляется параметр и значение
*(Buffer+Buffer_Len)=0;
if (ParamName!=NULL)
temp=Param->Add(ParamName,Buffer);
else
temp->Add(Buffer);
Buffer_Len=0;
Mode=0;
};
break;
};//case 1;
case 2:{//режим преобразования из HEX первого символа после %
*(Buffer+Buffer_Len)=(gethex(ch)<<4);
Mode=3;
break;
};//case
case 3:{//второго символа HEX
*(Buffer+Buffer_Len)+=gethex(ch);
Buffer_Len++;
Mode=ToMode;
break;
};
};//switch (Mode)
i++;
};
//если осталось что-то после работы то добавляем
if (Buffer_Len>0){
*(Buffer+Buffer_Len)=0;
if (ParamName!=NULL)
temp=Param->Add(ParamName,Buffer);
else
temp->Add(Buffer);
};
proc_exit:
if ((Method==METHOD_GET)&&(Query_Str!=NULL))
Query_String=strdup(Query_Str);
return 0;
};
Этот метод получает все переменные окружения необходимые для считывания приходящих данных. После чего инициализирует буффер и считывает их в зависимости от метода передачи.
В зависимости от считанного символа происходит смена режима и символ добавляется либо в имя параметра, либо в содержимое, либо декодируется.
Последний на этот момент необходимый метод поиска:
CGIContent *CGIApp::FindParam(char *name){
CGIContent *temp=Param->Find(name);
return temp;
};
Стоило бы заметить о некоторых особенностях работы класса на разных платформах. Изначально писал я его в среде Borland C++ 3.1 и в среде Windows все скрипты просто "летали". При отладке в Linux начались большие проблемы. После поиска ошибок, которых по идее не было, в течении получаса оказалось, что нельзя использовать процедуру free(*char), если указатель был получен с помощью getenv(). Т.е. я предполагаю в Линуксе процедура getenv() не копирует содержимое переменной окружения в отдельную строку, а возвращает адрес, в котором она содержится. В Досе таких ошибок не возникало, правда тут нельзя утверждать, что getenv() создает отдельную строку для этого, вероятно процедура free() просто не вызывает того аварийного завершения, которое получается в Линуксе.
Вобщем, что не система, то свои проблемы, да и что вообще там система... Компиляторы чего только стоят :-) Отладка CGI написанных на языке С++ всегда будет иметь некоторую зависимость от платформы или реализации компилятора, хотя считается, что все везде работает по одному алгоритму. Оказывается, что не все и не всегда.