// ===============================================
// Расширение ISAPI fileupl.c
// Загрузка файла из локального компьютера
// на диск удаленного сервера WWW
// из документа HTML
//
// (C) Фролов А.В., 1997
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//         или
//         http://www.dials.ccas.ru/frolov
// ===============================================

#include <windows.h>
#include <httpext.h>

// Прототипы функций, определенных в приложении
LPVOID ReadClientMIME(EXTENSION_CONTROL_BLOCK *lpECB, 
                      int *nStatus);
BOOL GetMIMEBoundary(LPVOID lpDataMIME, LPSTR lpBuffer, 
                     DWORD dwBufSize);

// =============================================================
// Функция GetExtensionVersion
// Запись версии интерфейса ISAPI и
// строки описания расширения
// =============================================================
BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
  // Записываем версию интерфейса ISAPI
  pVer->dwExtensionVersion = 
    MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR );

  // Записываем строку описания расширения
  lstrcpyn(pVer->lpszExtensionDesc,
    "Remote File Upload", HSE_MAX_EXT_DLL_NAME_LEN);

  return TRUE;
}

// =============================================================
// Функция HttpExtensionProc
// =============================================================
DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB)
{
  CHAR   szBuff[4096];
  HANDLE hOutFile;
  DWORD  dwWritten;
  LPVOID lpDataMIME;
  int    nStatus;

  // Нулевой код состояния - признак успешного выполнения
  lpECB->dwHttpStatusCode = 0;

  // Получаем данные от навигатора в кодировке MIME
  lpDataMIME = ReadClientMIME(lpECB, &nStatus);

  if(lpDataMIME != NULL)
  {
    // Создаем файл для записи принятых данных
    hOutFile = CreateFile("e:\\InetPub\\scripts\\uploaded.dat", 
      GENERIC_WRITE, FILE_SHARE_READ, NULL,
      CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	  
	  if(hOutFile != INVALID_HANDLE_VALUE)
    {
	    // Выполняем запись данных в файл
      WriteFile(hOutFile, lpDataMIME, 
        lpECB->cbTotalBytes, &dwWritten, NULL);
	    
      // Закрываем файл
      CloseHandle(hOutFile);
    }
      
    // Освобождаем память, заказанную для принятых
    // данных функцией ReadClientMIME
    LocalFree(lpDataMIME);
  }
  
  // Создаем документ HTML с сообщением об
  // успешной загрузке файла
  wsprintf(szBuff,
    "Content-Type: text/html\r\n\r\n"
    "<HTML><HEAD><TITLE>Remote File Upload</TITLE></HEAD>\n"
    "<BODY BGCOLOR=#FFFFFF><H1>Make your file upload!</H1>\n"
    "<HR>\n");

  strcat(szBuff, "<P>Upload finished");
  strcat(szBuff, "</BODY></HTML>");  

  // Отправляем созданный документ HTML
  if(!lpECB->ServerSupportFunction(lpECB->ConnID,
    HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff))
  {
    return HSE_STATUS_ERROR;
  }

  lpECB->dwHttpStatusCode = 200;
  return HSE_STATUS_SUCCESS;
}

// =============================================================
// Функция ReadClientMIME
// Получение данных в кодировке MIME
// =============================================================
LPVOID ReadClientMIME(
	EXTENSION_CONTROL_BLOCK *lpECB, 
	int *nStatus)
{
  DWORD cbReaded;
  DWORD nBufferPos;
  DWORD nBytesToCopy;

  LPVOID lpTemp = NULL;
  
  // Код завершения
  *nStatus = 0;

  // Определяем, есть ли данные для чтения
  if(lpECB->cbTotalBytes != 0)
  {
    // Заказываем буфер памяти для чтения принятых данных
    if(!(lpTemp = (LPVOID)LocalAlloc(LPTR, 
      lpECB->cbTotalBytes)))
	  {
      // Если памяти не хватает, завершаем работу
      // с установкой кода ошибки и возвращаем
      // значение NULL
      *nStatus = HSE_STATUS_ERROR;
      return NULL;
    }

    // Копируем в буфер предварительно считанные данные
    memcpy(lpTemp, lpECB->lpbData, lpECB->cbAvailable);
    
    // Устанавливаем указатель текущей позиции
    // в буфере после скопированных данных
    nBufferPos = lpECB->cbAvailable;
 
	  // Определяем, сколько данных нужно считать
    // дополнительно с помощью функции ReadClient
    nBytesToCopy = lpECB->cbTotalBytes - lpECB->cbAvailable;
	  
    // cbReaded = nBytesToCopy;

    // Если не все данные находятся в буфере предварительного 
    // чтения, запускаем цикл копирования оставшихся данных
    if(nBytesToCopy > 0)
    {
      while(1)
	    {
        // Читаем очередную порцию данных в текущую
        // позицию буфера
        lpECB->ReadClient(lpECB->ConnID, 
			    (LPVOID)((LPSTR)lpTemp + nBufferPos), &cbReaded);
		
		    // Уменьшаем содержимое переменной nBytesToCopy,
        // в которой находится размер непрочитанных данных
        nBytesToCopy -= cbReaded;

        // Продвигаем указатель текущей позиции в буфере
        // на количество прочитанных байт данных
        nBufferPos   += cbReaded;
		
        // Когда копировать больше нечего, прерываем цикл
        if(nBytesToCopy <= 0l)
		      break;
	    }
	  }

	  // В случае успешного копирования возвращаем
    // адрес буфера с прочитанными данными
    return lpTemp;
  }
  
  // Если данных для чтения нет, завершаем
  // работу с установкой кода ошибки
  else
  {
    *nStatus = HSE_STATUS_ERROR;

    // В случае ошибки вместо адреса буфера
    // с прочитанными данными возвращается
    // значение NULL
    return NULL;
  }
}

// =============================================================
// Функция GetMIMEBoundary
// Поиск разделителя в буфере.
// Параметры:
//   lpDataMIME - адрес буфера с данными MIME
//   lpBuffer   - адрес буфера для записи разделителя
//   dwBufSize  - размер буфера с данными MIME
// =============================================================
BOOL GetMIMEBoundary(LPVOID lpDataMIME, LPSTR lpBuffer, DWORD dwBufSize)
{
  LPSTR lpCurrent;
  DWORD dwOffset;
  BOOL  fFound;
  
  // Устанавливаем признак успешного поиска
  fFound = TRUE;

  // Ищем конец первой строки
  for(lpCurrent = lpDataMIME, 
    dwOffset = 0;;lpCurrent++, dwOffset++)
  {
    // Сравниваем с концом строки
    if(!memcmp(lpCurrent,"\r\n",2))
      break;
    
    // Если достигнут конец буфера,
    // сбрасываем признак успешного поиска
    // и прерываем работу цикла
    if(dwOffset >= dwBufSize)
    {
      fFound = FALSE;
      break;
    }

    // копируем очередной символ разделителя
    *(lpBuffer + dwOffset) = *lpCurrent;
  }
  
  // Если разделитель найден, закрываем строку
  // разделителя двоичным нулем
  if(fFound)
    *lpBuffer = '\0';  

  // Возвращаем признак успешного или
  // неуспешного поиска
  return fFound;
}







