/*
 * Copyright (c) 1999-2001 Caucho Technology.  All rights reserved.
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Public License ("the License").  In particular, the following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.

 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL CAUCHO OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 *
 * @author Scott Ferguson
 */

/*
 *  Anh LE(ngoc-anh.le@centraliens.net) : 
 *  IIS5.0 allows ISAPI extension to use IIS authentification 
 *  by introducing SF_NOTIFY_AUTH_COMPLETE event notification for ISAPI filter
/*
 * Copyright (c) 1999-2001 Caucho Technology.  All rights reserved.
 *
 * Caucho Technology permits redistribution, modification and use
 * of this file in source and binary form ("the Software") under the
 * Caucho Public License ("the License").  In particular, the following
 * conditions must be met:
 *
 * 1. Each copy or derived work of the Software must preserve the copyright
 *    notice and this notice unmodified.
 *
 * 2. Redistributions of the Software in source or binary form must include 
 *    an unmodified copy of the License, normally in a plain ASCII text
 *
 * 3. The names "Resin" or "Caucho" are trademarks of Caucho Technology and
 *    may not be used to endorse products derived from this software.
 *    "Resin" or "Caucho" may not appear in the names of products derived
 *    from this software.
 *
 * 4. Caucho Technology requests that attribution be given to Resin
 *    in any manner possible.  We suggest using the "Resin Powered"
 *    button or creating a "powered by Resin(tm)" link to
 *    http://www.caucho.com for each page served by Resin.
 *
 * This Software is provided "AS IS," without a warranty of any kind. 
 * ALL EXPRESS OR IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.

 * CAUCHO TECHNOLOGY AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR
 * DISTRIBUTING SOFTWARE. IN NO EVENT WILL CAUCHO OR ITS LICENSORS BE LIABLE
 * FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE SOFTWARE, EVEN IF HE HAS BEEN ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGES.
 *
 * @author Scott Ferguson
 *
 * $Id: isapi_srun.cpp,v 1.8 2001/06/15 16:54:21 ferg Exp $
 */

/*
 *  Anh LE(ngoc-anh.le@centraliens.net) : 
 *  IIS5.0 allows ISAPI extension to use IIS authentification 
 *  by introducing SF_NOTIFY_AUTH_COMPLETE event notification for ISAPI filter
 *  So we will replace SF_NOTIFY_PREPROC_HEADERS by SF_NOTIFY_AUTH_COMPLETE
 *  for IIS5.0/Windows2000.
 *  Windows 2000 SP1 is strongly recommended because of a bug with this event 
 */
#include <windows.h>
#include <stdio.h>
#include <httpext.h>
#include <httpfilt.h>
#include <tchar.h>
#include "isapi_srun.h"
#include "../../../win32/common/common.h"
#include "iis.h"
extern "C" {
#include "../common/cse.h"
}

#define ISAPI_SCRIPT "/scripts/isapi_srun.dll"

#define URL_SIZE 8192

// the script url size needs to be larger to handle the /script prefix
#define SCRIPT_URL_SIZE (URL_SIZE + 1024)

extern int cse_handle_request(config_t *, EXTENSION_CONTROL_BLOCK *);
extern void log(char *fmt, ...);

static config_t *config;

static HINSTANCE g_hInstance;
static int g_is_iis5 = -1;
static char *g_dll_name;

void die(char *msg, ...) {}

static HINSTANCE AfxGetResourceHandle()
{
	return g_hInstance;
}

static int
parseResinIni(FILE *file, char *resinConf)
{
	char key[1024];

	while (! feof(file)) {
		int ch;
		int i = 0;

		for (ch = fgetc(file); ch > 0 && ! isspace(ch); ch = fgetc(file))
			key[i++] = ch;
		key[i] = 0;
			
		for (; ch == ' ' || ch == '\t'; ch = fgetc(file)) {
		}

		i = 0;
		for (; ch > 0 && ch != '\n'; ch = fgetc(file))
			resinConf[i++] = ch;
		resinConf[i] = 0;
		for (i--; i >= 0 && isspace(resinConf[i]); i--)
			resinConf[i] = 0;

		if (! strcmp(key, "CauchoConfigFile"))
			return 1;
	}

	return 0;
}

static void
findResinConf(char *resinConf, char *pwd)
{
	sprintf(resinConf, "%s/resin.ini", pwd);

	FILE *file = fopen(resinConf, "r");
	if (file) {
		int found = parseResinIni(file, resinConf);
		fclose(file);
		if (found)
			return;
	}

	char *resin_home = get_resin_home(0, pwd);

	if (resin_home) {
		sprintf(resinConf, "%s\\conf\\resin.conf", resin_home);
		return;
	}

	int i;
	for (i = strlen(pwd) - 1; i >= 0 && pwd[i] != '/' && pwd[i] != '\\'; i--) {
	}
	if (i >= 0)
		pwd[i] = 0;

	sprintf(resinConf, "%s/resin.conf", pwd);
}

BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer)
{
	LOG(("start extension\n"));

	// Load description string
	TCHAR sz[HSE_MAX_EXT_DLL_NAME_LEN+1];
	LoadString(AfxGetResourceHandle(), IDS_SERVER, sz, HSE_MAX_EXT_DLL_NAME_LEN);
	_tcscpy(pVer->lpszExtensionDesc, sz);

	char dllName[1024];
	char resinConf[1024];
	GetModuleFileName(AfxGetResourceHandle(), dllName, sizeof(dllName));
	int i;


	if (config)
		return TRUE;

	for (i = strlen(dllName) - 1; i >= 0 && dllName[i] != '/' && dllName[i] != '\\'; i--) {
	}

	for (i--; i >= 0 && dllName[i] != '/' && dllName[i] != '\\'; i--) {
	}

	if (i < 0)
		i = 0;
	dllName[i] = 0;
	findResinConf(resinConf, dllName);

	config = (config_t *) malloc(sizeof (config_t));
	memset(config, 0, sizeof(config_t));
	config->path = strdup(resinConf);

	cse_init_config(config);

	return TRUE;
}

///////////////////////////////////////////////////////////////////////
// CIis_srunExtension command handlers

DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
	if (cse_handle_request(config, pECB))
		return HSE_STATUS_SUCCESS;
	else
		return HSE_STATUS_ERROR;
}

BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
	LOG(("terminate\n"));

	return TRUE;
}


static void 
log_event(char *msg)
{
    char **strings = (char **) malloc(2 * sizeof(char *));
	strings[0] = msg;
	strings[1] = 0;

	HANDLE handle = RegisterEventSource(0, "Application");
	if (! handle)
		return;

	ReportEvent(handle, EVENTLOG_INFORMATION_TYPE,
		      0, 1, 0, 1, 0, 
			  (const char **) strings, 0);

	DeregisterEventSource(handle);
}

BOOL WINAPI GetFilterVersion(HTTP_FILTER_VERSION *pVer)
{
	LOG(("start filter\n"));

	// log_event("start iis_srun filter");

	TCHAR sz[SF_MAX_FILTER_DESC_LEN+1];
	LoadString(AfxGetResourceHandle(), IDS_SERVER, sz, SF_MAX_FILTER_DESC_LEN);
	_tcscpy(pVer->lpszFilterDesc, sz);

	char dllName[1024];
	GetModuleFileName(AfxGetResourceHandle(), dllName, sizeof(dllName));
	int i;

	if (config)
		return TRUE;

	for (i = strlen(dllName) - 1; i >= 0 && dllName[i] != '/' && dllName[i] != '\\'; i--) {
	}

	if (i < 0)
		i = 0;
	dllName[i] = 0;
	char resinConf[4096];

	findResinConf(resinConf, dllName);
	LOG(("loading %s as config %x\n", resinConf, config));

	config = (config_t *) malloc(sizeof (config_t));
	memset(config, 0, sizeof(config_t));
	config->path = strdup(resinConf);

	cse_init_config(config);

	pVer->dwFilterVersion = HTTP_FILTER_REVISION;
	// Anh : Add SF_NOTIFY_AUTH_COMPLETE for IIS5.0
	pVer->dwFlags = (SF_NOTIFY_PREPROC_HEADERS|
			 SF_NOTIFY_LOG|
			 SF_NOTIFY_AUTH_COMPLETE);

	registry_t *node = 0;
	
	if (config && config->registry)
		node = cse_next_link(config->registry->first, "caucho.com");
	if (node)
		node = cse_next_link(node->first, "iis");
	if (node)
		node = cse_next_link(node->first, "priority");

	if (! node || ! node->value)
		pVer->dwFlags |= SF_NOTIFY_ORDER_DEFAULT;
	else if (! stricmp(node->value, "low"))
		pVer->dwFlags |= SF_NOTIFY_ORDER_LOW;
	else if (! stricmp(node->value, "medium"))
		pVer->dwFlags |= SF_NOTIFY_ORDER_MEDIUM;
	else if (! stricmp(node->value, "high"))
		pVer->dwFlags |= SF_NOTIFY_ORDER_HIGH;
	else
		pVer->dwFlags |= SF_NOTIFY_ORDER_DEFAULT;

	return TRUE;
}

DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType,
			    LPVOID pvNotification)
{
	char url[URL_SIZE];
	unsigned long size;

	char query = 0;
	unsigned int query_index = 0;
	HTTP_FILTER_PREPROC_HEADERS *headers=NULL;;
	HTTP_FILTER_AUTH_COMPLETE_INFO *AuthComp=NULL;; 

	if (g_is_iis5 < 0) {
		char version[1024];
		size = sizeof(version);
		g_is_iis5 = 0;

		if (pfc->GetServerVariable(pfc, "SERVER_SOFTWARE", version, &size)) {
			g_is_iis5 = atof(version + strlen("Microsoft-IIS/")) >= 5.0;
		}
		else {
			LOG(("Can't Get SERVER_SOFTWARE %d\n",GetLastError()));
		}
	}
	
	switch (notificationType) {
	case SF_NOTIFY_PREPROC_HEADERS:
		LOG(("HttpFilterProc: SF_NOTIFY_PREPROC_HEADERS\n"));
		if (g_is_iis5)
		  break;
		
		headers = (HTTP_FILTER_PREPROC_HEADERS *) pvNotification;
		if (! pfc->pFilterContext) {
		  pfc->pFilterContext = pfc->AllocMem(pfc, SCRIPT_URL_SIZE, 0);
		  if (! pfc->pFilterContext ) {
		    SetLastError(ERROR_NOT_ENOUGH_MEMORY);
		    return SF_STATUS_REQ_ERROR;
		  }
		}
		((char *) pfc->pFilterContext)[0] = 0;

		size = sizeof(url);
		if (headers->GetHeader(pfc, "URL", url, &size) && size > 0) {
			url[size] = 0;
			for (unsigned int query_index = 0;
			     query_index < size;
			     query_index++) {
				if (url[query_index] == '?') {
					query = url[query_index];
					url[query_index] = 0;
					break;
				}
			}

         DWORD request_time = GetTickCount() / 1000;
         cse_update_config(config, request_time);

			if (cse_match_request(config, "", 0, url, 1) ||
				! config->disable_caucho_status &&
				! strcmp(url, "/caucho-status")) {
				char newurl[SCRIPT_URL_SIZE];
	
				url[query_index] = query;
				strcpy(newurl, ISAPI_SCRIPT);
				strcat(newurl, url);
				headers->SetHeader(pfc, "URL", newurl);  
				strcpy((char *) pfc->pFilterContext, url);
				((char *) pfc->pFilterContext)[query_index] = 0;
			}
		}
      break;

	case SF_NOTIFY_AUTH_COMPLETE:
		LOG(("HttpFilterProc: SF_NOTIFY_AUTH_COMPLETE\n"));
		AuthComp = (HTTP_FILTER_AUTH_COMPLETE_INFO *) pvNotification;
		if (! pfc->pFilterContext)
			pfc->pFilterContext = pfc->AllocMem(pfc, SCRIPT_URL_SIZE, 0);
		if (! pfc->pFilterContext) {
		  SetLastError(ERROR_NOT_ENOUGH_MEMORY);
		  return SF_STATUS_REQ_ERROR;
		}
		((char *) pfc->pFilterContext)[0] = 0;

		size = sizeof(url);
		if (AuthComp->GetHeader(pfc, "URL", url, &size) && size > 0) {
			url[size] = 0;
			for (unsigned int query_index = 0; query_index < size; query_index++) {
				if (url[query_index] == '?') {
					query = url[query_index];
					url[query_index] = 0;
					break;
				}
			}

         DWORD request_time = GetTickCount() / 1000;
         cse_update_config(config, request_time);

			if (cse_match_request(config, "", 0, url, 1) ||
				! config->disable_caucho_status &&
				! strcmp(url, "/caucho-status")) {
				char newurl[SCRIPT_URL_SIZE];
	
				url[query_index] = query;
				strcpy(newurl, ISAPI_SCRIPT);
				strcat(newurl, url);
				AuthComp->SetHeader(pfc, "URL", newurl);  
				strcpy((char *) pfc->pFilterContext, url);
				((char *) pfc->pFilterContext)[query_index] = 0;
			}
		}
      break;
	
	case SF_NOTIFY_LOG:
		if (pfc->pFilterContext && ((char *) pfc->pFilterContext)[0]) {
			char *pch = (char *) pfc->pFilterContext;
			HTTP_FILTER_LOG *pLog = (HTTP_FILTER_LOG *) pvNotification;
			pLog->pszTarget = pch;
		}
		break;
 	}

	return SF_STATUS_REQ_NEXT_NOTIFICATION;
}

BOOL WINAPI DllMain(HINSTANCE hInst, ULONG ulReason, LPVOID lpReserved)
{
	if (ulReason == DLL_PROCESS_ATTACH) {
		g_hInstance = hInst;
	}

	return TRUE;
}
