Как и любая программа, программа CGI шлюза должна получить данные, обработать их и вывести результат работы в наглядной форме.
Весь процесс получения данных от веб-сервера можно представить следуюшим образом:
Если (REQUEST_METHOD="GET") то
Взять данные из переменной окружения QUERY_STRING
Иначе
Если (REQUEST_METHOD="POST") то
Проанализировать переменную QUERY_STRING
Получить длинну данных из CONTENT_LENGTH
Если (CONTENT_LENGTH>0) то
Считать CONTENT_LENGTH байт из sdtin как данные.
Иначе
Выдать сообщение об ошибке и выйти.
С анализом переменной REQUEST_METHOD думаю все ясно, а вот ,что значит в методе POST проанализировать QUERY_STRING, наверно, не все ясно. При передаче методом POST сервер посылает данные через стандартный поток ввода, но это не значит, что пользователь не пользовался URL'ом для передачи данных. Примером может служить многопользовательский шлюз, в котором для идентификации пользователя используется URL, а для передачи данных stdin:
http://.../cgi-bin/guestbook.cgi?user=bob&rec=0
В этом случае шлюзу гостевой книги (guestbook.cgi) сообщается два параметра user и rec, с помощью которых она может узнать "куда записывать" или "как обрабатывать" данные поступающие через поток ввода.
Считывание данных через поток sdtin должно осуществляться в динамическую память, или же во временный файл, если размер памяти ограничен или данные слишком велики для полного размещения в ОЗУ. С чем это связано ? Это связано с тем, что при использовании статичестких буферов может произойти его переполнение.
Пример:
char cgi_data[1000];
...
long content_length = atol(getenv("CONTENT_LENGTH"));
fread(cgi_data, content_length, 1,stdin);
...
Надеюсь все сразу видно :-)). Если content_length будет больше 1000, то произойдет переполнение cgi_data. Переполнение буферов это излюбленный метод атаки хакеров. Вместо этого лучше выделять память динамически:
char *cgi_data;
// ...
long content_length = atol(getenv("CONTENT_LENGTH"));
cgi_data = (char *)malloc(content_length);
if (cgi_data != NULL)
fread(cgi_data, content_length, 1, stdin);
// ...
После получения данных от сервера их надо еще декодировать. Можно это сделать сразу, а можно по мере надобности. Если Вы будете это делать сразу, то Вам также придется их разбить на куски, так как при декодировании могут появиться лишние знаки "&" и "=", которые больше не позволят вам отделять пары "имя=значение" друг от друга.
Вот пример процедуры, которая декодирует данные из буфера:
// Возвращает верхний регистр символа
char upperchar(char ch) {
if ((ch >= 'a') && (ch <= 'z')) {
ch = 'A' + (ch - 'a');
return ch;
} else return ch;
};
// Переводит из Hex в Dec
char gethex(char ch) {
ch = upperchar(ch);
if ((ch >= '0') && (ch <= '9')) return (ch - '0');
if ((ch >= 'A') && (ch <= 'F')) return (ch - 'A' + 10);
};
/*
Ищет и возвращает параметр с именем name, в buffer.
Если параметр name не найден, возвращает NULL.
Пример : message = getparam(post_buffer,"message=");
Замечание : символ "=" после имени параметра не удаляется
и входит в возвращаемый результат, поэтому рекомендуется
искать параметр вместе с символом "=".
*/
char *getparam(char *buffer, char *name)
{
if (buffer==NULL) return NULL;
char *pos;
long leng = 512, i=0, j=0;
char h1, h2, Hex;
char *p = (char *)malloc(leng);
if (p == NULL) return NULL;
pos = strstr(buffer, name);
if (pos == NULL) return NULL;
if ((pos != buffer) && (*(pos - 1) != '&')) return NULL;
pos += strlen(name);
while ((*(pos+i) != '&') && ( *(pos + i) != '\0')) {
if ( *(pos+i) == '%') {
i++;
h1 = gethex(*(pos + i));
i++;
h2 = gethex(*(pos + i));
h1 = h1 << 4;
*(p + j) = h1 + h2;
} else {
if (*(pos+i)!='+')
*(p + j) = *(pos + i);
else
*(p + j) = ' ';
};
i++;
j++;
if (j >= leng) p = (char*)realloc(p, leng + 20);
leng += 20;
};
if (j < leng) p = (char*)realloc(p, j + 1);
*(p + j)='\0';
return p;
};
Теперь используя функцию getparam Вы сможете в любое время получить какой-нибудь параметр. Конечно эта процедура еще далека от совершенства, так как использует стандартный realloc и не обрабатывает ошибку нехватки памяти, но всеже ее можно использовать для данных не особо больших размеров.
Кстати надо еще сказать, что нецелесообразно размещать поступающие данные размером >64 Кб. в памяти, если, например, скрипт предназначен для закачки файлов, то можно (а вернее нужно) напрямую организовать запись в файл, иначе файл размером в пару мегабайт ваш скрипт не обработает :-)).