// ===============================================
// Программа CGI controls.c
// Получение данных от формы и их 
// отображение в документе HTML,
// создаваемом динамически
//
// (C) Фролов А.В., 1997, 1998
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Прототипы функций перекодировки
void DecodeStr(char *szString);
char DecodeHex(char *str);

// ------------------------------------------------
// Функция main
// Точка входа программы CGI
// ------------------------------------------------
void main(int argc, char *argv[])
{
  int lSize;
  FILE * fileReceived;
  char * szMethod;
  char szBuf[8196];
  char szSrcBuf[8196];
  char * szPtr;
  char * szParam;

  // Вывод заголовка HTTP и разделительной строки
  printf("Content-type: text/html\n\n");

  // Вывод начального форагмента документа HTML,
  // формируемого динамически
  printf("<!DOCTYPE HTML PUBLIC"
    " \"-//W3C//DTD HTML 3.2//EN\">");
  printf("<HTML><HEAD><TITLE>User Registration"
    "</TITLE></HEAD><BODY BGCOLOR=#FFFFFF>");

  // Определяем метод передачи данных
  szMethod = getenv("REQUEST_METHOD");

  // Обработка метода POST
  if(!strcmp(szMethod, "POST"))
  {
    // Определяем размер данных, полученных от навигатора
    // при передаче данных из полей формы
    lSize = atoi(getenv("CONTENT_LENGTH"));

    // Читаем эти данные в буфер szBuf из
    // стандартного потока ввода STDIN
    fread(szBuf, lSize, 1, stdin);

    // Создаем файл, в который будут записаны
    // принятые данные
    fileReceived = fopen("received.dat", "w");

    // Выполняем запись принятых данных
    fwrite(szBuf, lSize, 1, fileReceived);

    // Закрываем файл принятых данных
    fclose(fileReceived);

    // Отображаем значения некоторых переменных среды
    printf("<H2>Переменные среды</H2>");
    
    // Метод доступа
    printf("REQUEST_METHOD = %s", getenv("REQUEST_METHOD"));

    // Размер полученных данных в байтах
    printf("<BR>CONTENT_LENGTH = %ld", lSize);

    // Тип полученных данных
    printf("<BR>CONTENT_TYPE = %s", getenv("CONTENT_TYPE"));

    // Закрываем буфер данных двоичным нулем, 
    // превращая его таким образом в строку
    szBuf[lSize] = '\0';

    // Делаем копию принятых данных в буфер szSrcBuf
    strcpy(szSrcBuf, szBuf);

    // Отображаем принятые данные без обработки
    printf("<H2>Принятые данные</H2>");
    printf("<P>%s", szSrcBuf);

    // Выполняем перекодировку принятых данных
    DecodeStr(szSrcBuf);

    // Отображаем результат перекодировки
    printf("<H2>Данные после перекодировки</H2>");
    printf("<P>%s", szSrcBuf);

    // Выводим список значений полей формы
    printf("<H2>Список значений полей</H2>");
    
    // Дописываем в конец буфера принятых данных
    // символ "&", который используется в качестве
    // разделителя значений полей
    szBuf[lSize] = '&';
    szBuf[lSize + 1] = '\0';

    // Цикл по полям формы
    for(szParam = szBuf;;)
    {
      // Ищем очередной разделитель
      szPtr = strchr(szParam, '&');
      
      // Если он найден, раскодируем строку параметров
      if(szPtr != NULL)
      {
        *szPtr = '\0';
        DecodeStr(szParam);
      
        // Выводим в документ значение параметра
        printf("%s<BR>", szParam);

        // Переходим к следующему параметру
        szParam = szPtr + 1;

        // Если достигнут конец буфера, завершаем цикл
        if(szParam >= (szBuf + lSize))
          break;
      }
      else
        break;
    }
    
    // Выводим завершающий фрагмент документа HTML
    printf("</BODY></HTML>");
    return;
  }
}

// ------------------------------------------------
// Функция 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;
}
