
/*
 *  Copyright 1995 Microsoft Corporation. All rights reserved.
 *  Developed by Ataman Software, Inc., ftp://rmii.com/pub2/ataman,
 *       info@ataman.com
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define STRICT
#include <windows.h>
#include <time.h>

#include "rexx.h"

static HKEY GetHKEY(const char *str);

static unsigned char *eLog;

streng *w32_regopenkey(paramboxptr parms) {
	/* hkey = w32regopenkey(hkey, subkeyname) */
	char *subkeyname = NULL;
	HKEY hkey, hkeyRet;
	streng *ret;
	DWORD err;

	checkparam(parms, 1, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	if (parms->next && parms->next->value) {
		parms->next->value = Str_ify(parms->next->value);
		subkeyname = parms->next->value->value;
	}

	if ((err=RegOpenKeyEx(hkey, subkeyname, 0, MAXIMUM_ALLOWED, &hkeyRet)) != ERROR_SUCCESS) {
		return nullstringptr();
	}
	 
	ret = Str_make((sizeof hkeyRet * 2) + 1);
	sprintf(ret->value, "%lx", (unsigned long)hkeyRet);
	ret->len = strlen(ret->value);
	return ret;
}


streng *w32_regcreatekey(paramboxptr parms) {
	/* hkey = w32_regcreatekey(hkey, subkeyname) */
	char *subkeyname = NULL;
	HKEY hkey, hkeyRet;
	streng *ret;
	DWORD err, disp;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	subkeyname = parms->next->value->value;

	if ((err=RegCreateKeyEx(hkey, subkeyname, 0, "", REG_OPTION_NON_VOLATILE,
		MAXIMUM_ALLOWED, NULL, &hkeyRet, &disp)) != ERROR_SUCCESS) {
		return nullstringptr();
	}
	 
	ret = Str_make((sizeof hkeyRet * 2) + 1);
	sprintf(ret->value, "%lx", (unsigned long)hkeyRet);
	ret->len = strlen(ret->value);
	return ret;
}


streng *w32_regconnectregistry(paramboxptr parms) {
	/* hkey = w32_regconnectregistry(hostname, hkey) */
	char *hostname = NULL;
	HKEY hkey, hkeyRet;
	streng *ret;
	DWORD err;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hostname = parms->value->value;

	parms->next->value = Str_ify(parms->next->value);
	hkey = GetHKEY(parms->next->value->value);

	if ((err=RegConnectRegistry(hostname, hkey, &hkeyRet)) != ERROR_SUCCESS) {
		return nullstringptr();
	}
	 
	ret = Str_make((sizeof hkeyRet * 2) + 1);
	sprintf(ret->value, "%lx", (unsigned long)hkeyRet);
	ret->len = strlen(ret->value);
	return ret;
}


streng *w32_regclosekey(paramboxptr parms) {
	/* call w32RegCloseKey hkey */
	HKEY hkey;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);

	sscanf(parms->value->value, "%lx", (unsigned long *)&hkey);

	if (RegCloseKey(hkey) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}



streng *w32_regqueryvalue(paramboxptr parms) {
	/* value = w32regqueryvalue(hkey, valuename) */
	char *valuename = NULL;
	HKEY hkey;
	int lenRet;
	DWORD cbData, dwType;
	char *strRet = NULL;
	streng *ret = NULL;
	unsigned char *byData = NULL;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	valuename = parms->next->value->value;

	if (RegQueryValueEx(hkey, valuename, NULL, &dwType, NULL, &cbData) != ERROR_SUCCESS) {
		goto skipout;
	}

	byData = malloc(cbData);

	if (!byData) {
		goto skipout;
	}

	if (RegQueryValueEx(hkey, valuename, NULL, &dwType, byData, &cbData) != ERROR_SUCCESS) {
		goto skipout;
	}

	switch(dwType) {
	case REG_DWORD:
		{
		strRet = malloc(20);
		if (!strRet) {
			goto skipout;
		}
		sprintf(strRet, "%lu", *((unsigned long *)byData));
		lenRet = strlen(strRet);
		break;
		}
	case REG_EXPAND_SZ:
	case REG_SZ:	
		{
		strRet = malloc(cbData);
		if (!strRet) {
			goto skipout;
		}
		strcpy(strRet, byData);
		lenRet = cbData - 1;
		break;
		}
	default:
		{
		DWORD dw;
		strRet = malloc((cbData*2)+1);
		if (!strRet) {
			goto skipout;
		}
		for (dw = 0; dw < cbData; dw++) {
			sprintf(&strRet[dw*2], "%02x", (unsigned) byData[dw]);
		}
		lenRet = cbData*2;
		break;
		}
	}

	ret = Str_make(lenRet + 1);
	strcpy(ret->value, strRet);
	ret->len = lenRet;

skipout:
	if (strRet) {
		free(strRet);
	}
	if (byData) {
		free(byData);
	}

	if (ret == NULL) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return ret;
}



streng *w32_regqueryvaluetype(paramboxptr parms) {
	/* type = w32regqueryvaluetype(hkey, valuename) */
	char *valuename = NULL;
	HKEY hkey;
	DWORD dwType;
	char *strRet = NULL;
	streng *ret = NULL;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	valuename = parms->next->value->value;

	if (RegQueryValueEx(hkey, valuename, NULL, &dwType, NULL, NULL) != ERROR_SUCCESS) {
		goto skipout;
	}

	switch(dwType) {
	case REG_DWORD:
		strRet = "REG_DWORD";
		break;
	case REG_EXPAND_SZ:
		strRet = "REG_EXPAND_SZ";
		break;
	case REG_SZ:	
		strRet = "REG_SZ";
		break;
	case REG_BINARY:	
		strRet = "REG_BINARY";
		break;
	case REG_DWORD_BIG_ENDIAN:	
		strRet = "REG_DWORD_BIG_ENDIAN";
		break;
	case REG_LINK:	
		strRet = "REG_LINK";
		break;
	case REG_MULTI_SZ:	
		strRet = "REG_MULTI_SZ";
		break;
	case REG_NONE:	
		strRet = "REG_NONE";
		break;
	case REG_RESOURCE_LIST:	
		strRet = "REG_RESOURCE_LIST";
		break;
	default:
		strRet = "UNKNOWN";
		break;
	}

	ret = Str_make(strlen(strRet)+ 1);
	strcpy(ret->value, strRet);
	ret->len = strlen(strRet);

skipout:

	if (ret == NULL) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return ret;
}



streng *w32_regsetvalue(paramboxptr parms) {
	/* call w32regsetvalue hkey, valuename, type, data */
	char *valuename = NULL;
	char *type = NULL;
	char *data = NULL;
	HKEY hkey;
	DWORD dwType, cbData;
	unsigned char *byData = NULL;

	checkparam(parms, 4, 4) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	valuename = parms->next->value->value;

	parms->next->next->value = Str_ify(parms->next->next->value);
	type = parms->next->next->value->value;

	parms->next->next->next->value = Str_ify(parms->next->next->next->value);
	data = parms->next->next->next->value->value;

	if (stricmp(type, "REG_DWORD") == 0) {
		dwType = REG_DWORD;
	} else if (stricmp(type, "REG_EXPAND_SZ") == 0) {
		dwType = REG_EXPAND_SZ;
	} else if (stricmp(type, "REG_SZ") == 0) {	
		dwType = REG_SZ;
	} else if (stricmp(type, "REG_BINARY") == 0) {	
		dwType = REG_BINARY;
	} else if (stricmp(type, "REG_DWORD_BIG_ENDIAN") == 0) {	
		dwType = REG_DWORD_BIG_ENDIAN;
	} else if (stricmp(type, "REG_LINK") == 0) {	
		dwType = REG_LINK;
	} else if (stricmp(type, "REG_MULTI_SZ") == 0) {	
		dwType = REG_MULTI_SZ;
	} else if (stricmp(type, "REG_NONE") == 0) {	
		dwType = REG_NONE;
	} else if (stricmp(type, "REG_RESOURCE_LIST") == 0) {	
		dwType = REG_RESOURCE_LIST;
	} else {
		exiterror(ERR_INCORRECT_CALL);
	}

	switch(dwType) {
	case REG_DWORD:
		{
		byData = malloc(sizeof (DWORD));
		if (!byData) {
			goto skipout;
		}
		sscanf(data, "%lu", (unsigned long *)byData);
		cbData = sizeof (DWORD);
		break;
		}
	case REG_EXPAND_SZ:
	case REG_SZ:	
		{
		cbData = strlen(data)+1;
		byData = malloc(cbData);
		if (!byData) {
			goto skipout;
		}
		strcpy((char *)byData, data);
		break;
		}
	default:
		{
		DWORD dw;
		char *p;

		cbData = strlen(data);
		if ((cbData%2) != 0) {
			exiterror(ERR_INCORRECT_CALL);
		}
		cbData /= 2;

		byData = malloc(cbData);
		if (!byData) {
			goto skipout;
		}
		for (p=data, dw=0; dw<cbData; dw++, p+=2) {
			char buf[3];
			unsigned int x;
			buf[0] = *p;
			buf[1] = *(p+1);
			buf[2] = '\0';
			if (sscanf(buf, "%02x", (unsigned) &x) != 1) {
				exiterror(ERR_INCORRECT_CALL);
			}
			byData[dw] = (unsigned char)x;
		}
		break;
		}
	}

	if (RegSetValueEx(hkey, valuename, 0, dwType, byData, cbData) != ERROR_SUCCESS) {
		goto skipout;
	}

skipout:
	if (byData) {
		free(byData);
	}

	return nullstringptr();
}


streng *w32_expandenvironmentstrings(paramboxptr parms) {
	/* str = w32expandenvironmentstrings(string) */
	DWORD dwSize;
	char *strRet = NULL;
	streng *ret = NULL;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);
	dwSize = ExpandEnvironmentStrings(parms->value->value, NULL, 0);

	strRet = malloc(dwSize);

	if (!strRet) {
		goto skipout;
	}

	/*
		Warning: ExpandEnvironmentStrings is currently returning twice the
		number of characters needed, so we don't count on it's length
		being exact, but rather, at least large enough (including terminating
		NULL character.
	*/
	dwSize = ExpandEnvironmentStrings(parms->value->value, strRet, dwSize);

	ret = Str_make(dwSize);
	strcpy(ret->value, strRet);
	ret->len = strlen(ret->value);

skipout:
	if (strRet) {
		free(strRet);
	}

	if (ret == NULL) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return ret;
}


streng *w32_regdeletekey(paramboxptr parms) {
	/* call w32regdeletekey hkey, subkeyname */
	char *subkeyname = NULL;
	HKEY hkey;
	DWORD err;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	subkeyname = parms->next->value->value;

	if ((err=RegDeleteKey(hkey, subkeyname)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
	return nullstringptr();
}



streng *w32_regdeletevalue(paramboxptr parms) {
	/* call w32regdeletevalue hkey, valuename */
	char *valuename = NULL;
	HKEY hkey;
	DWORD err;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	valuename = parms->next->value->value;

	if ((err=RegDeleteValue(hkey, valuename)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
	return nullstringptr();
}


streng *w32_regenumkey(paramboxptr parms) {
	/* subkeyname = w32regenumkey(hk, index) */
	HKEY hkey;
	DWORD index;
	streng *ret = NULL;
	char keybuf[MAX_PATH+1];

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	if (sscanf(parms->next->value->value, "%lu", &index) != 1) {
		exiterror(ERR_INCORRECT_CALL);
	}

	if (RegEnumKey(hkey, index, keybuf, sizeof keybuf) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}


	ret = Str_make(strlen(keybuf) + 1);
	strcpy(ret->value, keybuf);
	ret->len = strlen(ret->value);

	return ret;
}


streng *w32_regenumvalue(paramboxptr parms) {
	/* valuename = w32regenumvalue(hk, index) */
	HKEY hkey;
	DWORD index;
	streng *ret = NULL;
	char byNameBuf[MAX_PATH+1];
	DWORD cbNameBuf;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	if (sscanf(parms->next->value->value, "%lu", &index) != 1) {
		exiterror(ERR_INCORRECT_CALL);
	}

	cbNameBuf = sizeof byNameBuf;

	if (RegEnumValue(hkey, index, byNameBuf, &cbNameBuf, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}


	ret = Str_make(cbNameBuf+1);
	strcpy(ret->value, byNameBuf);
	ret->len = cbNameBuf;

	return ret;
}


streng *w32_regflushkey(paramboxptr parms) {
	/* call w32regflushkey hk */
	HKEY hkey;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	if (RegFlushKey(hkey) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}


streng *w32_reggetkeysecdesc(paramboxptr parms) {
	/* subkeyname = w32reggetkeysecdesc(hk) */
	HKEY hkey;
	PSECURITY_DESCRIPTOR psd = NULL;
	char *szbuf;
	DWORD cbsd, dw, err;
	streng *ret = NULL;
	SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION |
		GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	cbsd = 0;
	if ((err=RegGetKeySecurity(hkey, si, NULL, &cbsd)) != ERROR_INSUFFICIENT_BUFFER) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	psd = malloc(cbsd);
	if (!psd) {
		goto skipout;
	}

	szbuf = malloc((cbsd*2)+1);
	if (!szbuf) {
		goto skipout;
	}

	if (RegGetKeySecurity(hkey, si, psd, &cbsd) != ERROR_SUCCESS) {
		goto skipout;
	}

	for (dw = 0; dw < cbsd; dw++) {
		sprintf(&szbuf[dw*2], "%02x", (unsigned) ((unsigned char *)psd)[dw]);
	}

	ret = Str_make((cbsd*2)+1);
	strcpy(ret->value, szbuf);
	ret->len = cbsd*2;

skipout:
	if (psd) {
		free(psd);
	}

	if (szbuf) {
		free(szbuf);
	}

	if (ret == NULL) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return ret;
}



streng *w32_regsetkeysecdesc(paramboxptr parms) {
	/* call w32regsetkeysecdesc hkey, secdesc */
	char *secdesc = NULL;
	HKEY hkey;
	PSECURITY_DESCRIPTOR psd;
	DWORD cbpsd;
	DWORD dw;
	char *p;
	SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION |
		GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	secdesc = parms->next->value->value;

	cbpsd = strlen(secdesc);
	if ((cbpsd%2) != 0) {
		exiterror(ERR_INCORRECT_CALL);
	}
	cbpsd /= 2;

	psd = malloc(cbpsd);
	if (!psd) {
		goto skipout;
	}
	for (p=secdesc, dw=0; dw<cbpsd; dw++, p+=2) {
		char buf[3];
		unsigned int x;
		buf[0] = *p;
		buf[1] = *(p+1);
		buf[2] = '\0';
		if (sscanf(buf, "%02x", (unsigned) &x) != 1) {
			exiterror(ERR_INCORRECT_CALL);
		}
		((unsigned char *)psd)[dw] = (unsigned char)x;
	}

	if (RegSetKeySecurity(hkey, si, psd) == ERROR_SUCCESS) {
		return nullstringptr();
	}

skipout:
	if (psd) {
		free(psd);
	}

	exiterror(ERR_SYSTEM_FAILURE);
}


streng *w32_regqueryinfokey(paramboxptr parms) {
	/* info = w32regqueryinfokey(hk, infoname) */
	HKEY hkey;
	char *infoname;
	streng *ret = NULL;
	char numbuf[20];
	DWORD dwNumSubKeys, dwMaxSubKeyName, dwNumValues, dwMaxValueName,
		dwMaxValueData, dwRet, dwDummy, err;
	FILETIME ftDummy;
	char chDummy[MAX_PATH+1];

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	infoname = parms->next->value->value;


	dwDummy = sizeof chDummy;
	if ((err=RegQueryInfoKey(hkey, chDummy, &dwDummy,
		NULL, &dwNumSubKeys, &dwMaxSubKeyName, &dwDummy,
		&dwNumValues, &dwMaxValueName, &dwMaxValueData, &dwDummy, &ftDummy)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if (stricmp(infoname, "NumSubKeys") == 0) {
		dwRet = dwNumSubKeys;
	} else if (stricmp(infoname, "MaxSubKeyName") == 0) {
		dwRet = dwMaxSubKeyName;
	} else if (stricmp(infoname, "NumValues") == 0) {
		dwRet = dwNumValues;
	} else if (stricmp(infoname, "MaxValueName") == 0) {
		dwRet = dwMaxValueName;
	} else if (stricmp(infoname, "MaxValueData") == 0) {
		dwRet = dwMaxValueData;
	} else {
		exiterror(ERR_INCORRECT_CALL);
	}

	sprintf(numbuf, "%lu", dwRet);

	ret = Str_make(strlen(numbuf)+1);
	strcpy(ret->value, numbuf);
	ret->len = strlen(numbuf);

	return ret;
}


streng *w32_regsavekey(paramboxptr parms) {
	/* call w32regsavekey hkey, filename */
	char *filename;
	HKEY hkey;
	DWORD err;
	HANDLE ht;
    LUID luidValue;
    TOKEN_PRIVILEGES tkp;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	filename = parms->next->value->value;

    if (!OpenProcessToken(GetCurrentProcess(),
    	TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &ht)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    if (!LookupPrivilegeValue(NULL, SE_BACKUP_NAME,	&luidValue)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luidValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if ((err=RegSaveKey(hkey, filename, NULL)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

    tkp.Privileges[0].Attributes = 0;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}


streng *w32_regrestorekey(paramboxptr parms) {
	/* call w32regrestorekey hkey, filename */
	char *filename;
	HKEY hkey;
	DWORD err;
	HANDLE ht;
    LUID luidValue;
    TOKEN_PRIVILEGES tkp;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	filename = parms->next->value->value;

    if (!OpenProcessToken(GetCurrentProcess(),
    	TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &ht)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &luidValue)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luidValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if ((err=RegRestoreKey(hkey, filename, 0)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

    tkp.Privileges[0].Attributes = 0;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
	return nullstringptr();
}


streng *w32_regloadkey(paramboxptr parms) {
	/* call w32regloadkey hkey, subkeyname, filename */
	char *filename;
	char *subkeyname;
	HKEY hkey;
	DWORD err;
	HANDLE ht;
    LUID luidValue;
    TOKEN_PRIVILEGES tkp;

	checkparam(parms, 3, 3) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	subkeyname = parms->next->value->value;

	parms->next->next->value = Str_ify(parms->next->next->value);
	filename = parms->next->next->value->value;

    if (!OpenProcessToken(GetCurrentProcess(),
    	TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &ht)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &luidValue)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luidValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if ((err=RegLoadKey(hkey, subkeyname, filename)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
    tkp.Privileges[0].Attributes = 0;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}


streng *w32_regunloadkey(paramboxptr parms) {
	/* call w32regunloadkey hkey, subkeyname */
	char *subkeyname;
	HKEY hkey;
	DWORD err;
	HANDLE ht;
    LUID luidValue;
    TOKEN_PRIVILEGES tkp;

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	hkey = GetHKEY(parms->value->value);

	parms->next->value = Str_ify(parms->next->value);
	subkeyname = parms->next->value->value;

    if (!OpenProcessToken(GetCurrentProcess(),
    	TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &ht)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    if (!LookupPrivilegeValue(NULL, SE_RESTORE_NAME, &luidValue)) {
		exiterror(ERR_SYSTEM_FAILURE);
    }

    tkp.PrivilegeCount = 1;
    tkp.Privileges[0].Luid = luidValue;
    tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if ((err=RegUnLoadKey(hkey, subkeyname)) != ERROR_SUCCESS) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
    tkp.Privileges[0].Attributes = 0;

    if(!AdjustTokenPrivileges(ht, FALSE, &tkp, sizeof tkp, NULL, NULL)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}
	 
	return nullstringptr();
}



static HKEY GetHKEY(const char *str) {
	HKEY hkey;

	if (stricmp(str, "HKEY_LOCAL_MACHINE") == 0) {
		hkey = HKEY_LOCAL_MACHINE;
	} else if (stricmp(str, "HKEY_CLASSES_ROOT") == 0) {
		hkey = HKEY_CLASSES_ROOT;
	} else if (stricmp(str, "HKEY_CURRENT_USER") == 0) {
		hkey = HKEY_CURRENT_USER;
	} else if (stricmp(str, "HKEY_USERS") == 0) {
		hkey = HKEY_USERS;
	} else {
		if (sscanf(str, "%lx", &hkey) != 1) {
			exiterror(ERR_INCORRECT_CALL);
		}
	}
	return hkey;
}


streng *w32_openeventlog(paramboxptr parms) {
	/* handle = w32openeventlog([hostname], sourcename) */
	char *hostname = NULL;
	char *sourcename;
	HANDLE hRet;
	streng *ret;

	if (!parms->next || !parms->next->value || parms->next->next) {
		// First argument is optional, second argument is required.
			exiterror(ERR_INCORRECT_CALL);
	}

	if (parms->value) {
		parms->value = Str_ify(parms->value);
		hostname = parms->value->value;
	}

	parms->next->value = Str_ify(parms->next->value);
	sourcename = parms->next->value->value;

	if ((hRet=OpenEventLog(hostname, sourcename)) == NULL) {
			exiterror(ERR_INCORRECT_CALL);
	}
	 
	ret = Str_make((sizeof hRet * 2) + 1);
	sprintf(ret->value, "%lx", (unsigned long)hRet);
	ret->len = strlen(ret->value);
	return ret;
}


streng *w32_closeeventlog(paramboxptr parms) {
	/* call w32closeeventlog handle */
	HANDLE h;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);

	sscanf(parms->value->value, "%lx", (unsigned long *)&h);

	if (!CloseEventLog(h)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if (eLog) {
		free(eLog);
		eLog = NULL;
	}

	return nullstringptr();
}



streng *w32_getnumberofeventlogrecords(paramboxptr parms) {
	/* num =  w32GetNumberOfEventLogRecords(handle) */
	HANDLE h;
	DWORD num;
	streng *ret;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);

	sscanf(parms->value->value, "%lx", (unsigned long *)&h);

	if (!GetNumberOfEventLogRecords(h, &num)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	ret = Str_make((sizeof num * 2) + 1);
	sprintf(ret->value, "%lu", num);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_openbackupeventlog(paramboxptr parms) {
	/* handle = w32openbackupeventlog([hostname], filename) */
	char *hostname = NULL;
	char *filename;
	HANDLE hRet;
	streng *ret;

	if (!parms->next || !parms->next->value || parms->next->next) {
		// First argument is optional, second argument is required.
			exiterror(ERR_INCORRECT_CALL);
	}

	if (parms->value) {
		parms->value = Str_ify(parms->value);
		hostname = parms->value->value;
	}

	parms->next->value = Str_ify(parms->next->value);
	filename = parms->next->value->value;

	if ((hRet=OpenBackupEventLog(hostname, filename)) == NULL) {
			exiterror(ERR_INCORRECT_CALL);
	}
	 
	ret = Str_make((sizeof hRet * 2) + 1);
	sprintf(ret->value, "%lx", (unsigned long)hRet);
	ret->len = strlen(ret->value);
	return ret;
}


streng *w32_backupeventlog(paramboxptr parms) {
	/* call w32backupeventlog handle, filename */
	char *filename;
	HANDLE h;


	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	sscanf(parms->value->value, "%lx", (unsigned long *)&h);

	parms->next->value = Str_ify(parms->next->value);
	filename = parms->next->value->value;

	if (!BackupEventLog(h, filename)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}


streng *w32_cleareventlog(paramboxptr parms) {
	/* call w32cleareventlog handle, [filename] */
	char *filename = NULL;
	HANDLE h;


	checkparam(parms, 1, 2) ;

	parms->value = Str_ify(parms->value);
	sscanf(parms->value->value, "%lx", (unsigned long *)&h);

	if (parms->next && parms->next->value) {
		parms->next->value = Str_ify(parms->next->value);
		filename = parms->next->value->value;
	}

	if (!ClearEventLog(h, filename)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}


streng *w32_findeventlogentry(paramboxptr parms) {
	/* call w32FindEventLogEntry handle, recnum */
	HANDLE h;
	DWORD recnum;
	DWORD cbRead, needed;
	char chDummy[1];

	checkparam(parms, 2, 2) ;

	parms->value = Str_ify(parms->value);
	sscanf(parms->value->value, "%lx", (unsigned long *)&h);

	parms->next->value = Str_ify(parms->next->value);
	if (sscanf(parms->next->value->value, "%lu", &recnum) != 1 || recnum == 0) {
		exiterror(ERR_INCORRECT_CALL);
	}

	ReadEventLog(h, EVENTLOG_SEEK_READ|EVENTLOG_FORWARDS_READ, recnum,
		&chDummy, 0, &cbRead, &needed);
	if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if (eLog) {
		free(eLog);
	}
	eLog = malloc(needed);
	if (!eLog) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	if (!ReadEventLog(h, EVENTLOG_SEEK_READ|EVENTLOG_FORWARDS_READ, recnum,
		eLog, needed, &cbRead, &needed)) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	return nullstringptr();
}



streng *w32_geteventid(paramboxptr parms) {
	/* id =  w32GetEventID() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	DWORD num;
	streng *ret;

	checkparam(parms, 0, 0) ;

	num = elr->EventID;

	ret = Str_make((sizeof num * 2) + 1);
	sprintf(ret->value, "%lu", num);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventtype(paramboxptr parms) {
	/* id =  w32GetEventType() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	DWORD num;
	streng *ret;

	checkparam(parms, 0, 0) ;

	num = elr->EventType;

	ret = Str_make((sizeof num * 2) + 1);
	sprintf(ret->value, "%lu", num);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventcategory(paramboxptr parms) {
	/* id =  w32GetEventCategory() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	DWORD num;
	streng *ret;

	checkparam(parms, 0, 0) ;

	num = elr->EventCategory;

	ret = Str_make((sizeof num * 2) + 1);
	sprintf(ret->value, "%lu", num);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventnumstrings(paramboxptr parms) {
	/* id =  w32GetEventNumStrings() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	DWORD num;
	streng *ret;

	checkparam(parms, 0, 0) ;

	num = elr->NumStrings;

	ret = Str_make((sizeof num * 2) + 1);
	sprintf(ret->value, "%lu", num);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventtimewritten(paramboxptr parms) {
	/* id =  w32GetEventTimeWritten() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	time_t tim;
	struct tm* tm;
	char *p, *tp;
	streng *ret;

	checkparam(parms, 0, 0) ;

	tim = elr->TimeWritten;

	tm = localtime(&tim);
	p = asctime(tm);
	tp =strrchr(p, '\n');
	*tp = '\0'; 

	ret = Str_make(strlen(p) + 1);
	strcpy(ret->value, p);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventtimegenerated(paramboxptr parms) {
	/* id =  w32GetEventTimeGenerated() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	time_t tim;
	struct tm* tm;
	char *p, *tp;
	streng *ret;

	checkparam(parms, 0, 0) ;

	tim = elr->TimeGenerated;

	tm = localtime(&tim);
	p = asctime(tm);
	tp =strrchr(p, '\n');
	*tp = '\0'; 

	ret = Str_make(strlen(p) + 1);
	strcpy(ret->value, p);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventstring(paramboxptr parms) {
	/* string =  w32GetEventString(index) */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	char *szbuf = NULL;
	DWORD dw;
	DWORD szwanted;
	streng *ret;
	char *p;

	checkparam(parms, 1, 1) ;

	parms->value = Str_ify(parms->value);
	if (sscanf(parms->value->value, "%lu", &szwanted) != 1) {
		exiterror(ERR_INCORRECT_CALL);
	}

  	if (szwanted == 0 || szwanted > elr->NumStrings) {
		exiterror(ERR_INCORRECT_CALL);
	}

	for (p=&eLog[elr->StringOffset], dw=1; dw < szwanted; dw++) {
		while(*p) p++;
		p++;
	}

	ret = Str_make(strlen(p) + 1);
	strcpy(ret->value, p);
	ret->len = strlen(ret->value);
	return ret;
}



streng *w32_geteventdata(paramboxptr parms) {
	/* data =  w32GetEventData() */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	DWORD dw;
	streng *ret;
	char *szbuf;
	unsigned char *p = (unsigned char *)&eLog[elr->DataOffset];

	checkparam(parms, 0, 0) ;

	szbuf = malloc((elr->DataLength*2)+1);
	if (!szbuf) {
		exiterror(ERR_SYSTEM_FAILURE);
	}

	for (dw = 0; dw < elr->DataLength; dw++) {
		sprintf(&szbuf[dw*2], "%02x", (unsigned)p[dw]);
	}

	ret = Str_make(strlen(szbuf) + 1);
	strcpy(ret->value, szbuf);
	ret->len = strlen(ret->value);
	free(szbuf);
	return ret;
}
 


streng *w32_writeeventlog(paramboxptr parms) {
	/* call w32WriteEventLog [hostname], source, [eventtype], [category], [eventid], [data], [string1, ...] */
	/* At least one of data or strings1 must be present */
	EVENTLOGRECORD *elr = (EVENTLOGRECORD *)eLog;
	WORD type;
	HANDLE h = NULL;
	char *hostname = NULL;
	char *source;
	char *data = NULL;
	DWORD datalen = 0;
	WORD category;
	DWORD eventid;
	DWORD dw;
	WORD nStrings = 0;
	paramboxptr ptr;
	char **ppsz = NULL;
	unsigned char *pData = NULL;

	for (ptr=parms, dw=0; ptr; ptr = ptr->next, dw++) {	}

	if (dw < 6 || !parms->next->value) {
		exiterror(ERR_INCORRECT_CALL);
	}

	if (dw > 6) {
		nStrings = (unsigned short)(dw-6);
		ppsz = malloc(nStrings * sizeof (char *));
		if (!ppsz) {
			goto blowup;
		}
	}

	if (parms->value) {
		parms->value = Str_ify(parms->value);
		hostname = parms->value->value;
	}

	parms->next->value = Str_ify(parms->next->value);
	source = parms->next->value->value;

	ptr = parms->next->next;
	if (ptr->value) {
		ptr->value = Str_ify(ptr->value);
		if (sscanf(ptr->value->value, "%hu", (unsigned short *)&type) != 1) {
			goto blowup;
		}
	} else {
		type = EVENTLOG_ERROR_TYPE;
	}

	ptr = ptr->next;
	if (ptr->value) {
		ptr->value = Str_ify(ptr->value);
		if (sscanf(ptr->value->value, "%hu", (unsigned short *)&category) != 1) {
 			goto blowup;
		}
	} else {
		category = 0;
	}

	ptr = ptr->next;
	if (ptr->value) {
		ptr->value = Str_ify(ptr->value);
		if (sscanf(ptr->value->value, "%lu", (unsigned long *)&eventid) != 1) {
 			goto blowup;
		}
	} else {
		eventid = 0;
	}

	ptr = ptr->next;
	if (ptr->value) {
		char *p;
		ptr->value = Str_ify(ptr->value);
		data = ptr->value->value;

		datalen = strlen(data);
		if ((datalen%2) != 0) {
			goto blowup;
		}
		datalen /= 2;

		pData = malloc(datalen);
		if (!pData) {
			goto blowup;
		}
		for (p=data, dw=0; dw<datalen; dw++, p+=2) {
			char buf[3];
			unsigned int x;
			buf[0] = *p;
			buf[1] = *(p+1);
			buf[2] = '\0';
			if (sscanf(buf, "%x", &x) != 1) {
				goto blowup;
			}
			pData[dw] = (unsigned char)x;
		}
	}

	for (dw=0; dw<nStrings; dw++) {
		ptr = ptr->next;
		if (ptr->value) {
			ptr->value = Str_ify(ptr->value);
			ppsz[dw] = ptr->value->value;
		} else {
			ppsz[dw] = "";
		}
	}


	if ((h=RegisterEventSource(hostname, source)) == NULL) {
		goto blowup;
	}

	if (!ReportEvent(h, type, category, eventid, NULL, nStrings, datalen, ppsz, pData)) {
		goto blowup;
	}

	if (!CloseEventLog(h)) {
		goto blowup;
	}

	if (ppsz) {
		free(ppsz);
	}
	if (pData) {
		free(pData);
	}
	return nullstringptr();

blowup:
	if (ppsz) {
		free(ppsz);
	}
	if (pData) {
		free(pData);
	}
	if (h) {
		(void)CloseEventLog(h);
	}
	exiterror(ERR_SYSTEM_FAILURE);
}
