Давайте теперь подведем черту под тем, что было сделано в прошлых шагах. Все эти классы были предназначены для одного самого главного 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 написанных на языке С++ всегда будет иметь некоторую зависимость от платформы или реализации компилятора, хотя считается, что все везде работает по одному алгоритму. Оказывается, что не все и не всегда.