#define	KEY_CODE
/*
 Module Name: Key
Programed By: Lynden Sherriff
Created Date: 8/1/99
	 Version: 1.0c

Modified by Matt to compile as stand-alone adt.
#define OLD_KEY_ADT to make compatible with original version
*/

#ifdef __XCODE__
#include "xcode_quiet.h"
#endif

#include <stdlib.h>

#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <time.h>
#include "lib.h"
#define DMID 14
#include "debug.h"
#ifdef unix
#include <netdb.h>
#endif
#include "emsg.h"

#include "key.h"
#ifdef OLD_KEY_ADT
#include "nlib.h"
#include "net_fns.h"
#endif

#ifndef OLD_KEY_ADT
/*
Define some functions and values so we can compile without all the extra files
*/

#define LBUFSZ 1000

#include "defines.h"

#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE (1)
#endif

#define s_strcmp key_s_strcmp
#define s_strlwr key_s_strlwr
int s_strcmp( char *string1, char *string2 );

char *lcpy(char *dst, char *src, int len)
{
	if (dst && src) {
		char *ptr = dst;

		len--;
		while (len > 0 && *src) {
			*ptr++ = *src++;
			len--;
		}
		*ptr = '\0';
	}
	return dst;
}


char *s_strlwr( char *string )
{
	if (string) {
#		ifdef unix
			char *ptr;
			ptr = string;

			while (*ptr != '\0') {
				*ptr = tolower(*ptr);
				ptr++;
			}
#		else
			return strlwr(string);
#		endif
	}
	return string;
}



#ifdef WIN32
#	define KEY_COMPUTER_NAME "SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName"

#	include <windows.h>
#	include <winnt.h>

	char *nt_pcname(void)
	{
		HKEY MyKey;
		static    char Line[64];
		int  linelen = 64;
		DWORD type_of_entry, result;

		RegCreateKey(HKEY_LOCAL_MACHINE, KEY_COMPUTER_NAME, &MyKey);
		result = RegQueryValueEx(MyKey, "ComputerName", 0, &type_of_entry,
								 (unsigned char *)Line, &linelen);
		RegCloseKey(MyKey);
		if (result == ERROR_SUCCESS && type_of_entry == REG_SZ)
			return Line;

		return "NONAME";
	}

#endif

char *oldhost_details(void)
{
#ifdef WIN32
	return nt_pcname();
#else

    static char bf[BFSZ]="";
	char addr[20];
	int i;
	struct hostent *hent;

	if (lib_strlen(bf)!=0) if (s_strcmp(bf,"NONAME")!=0){
		return bf;
	}

	if (0 != gethostname(bf,BFSZ-1)){
		emsg("host_details: gethostname failed returning NONAME\n");
		strcpy(bf,"NONAME");
		return bf;
	}
	if ((hent = (struct hostent *)gethostbyname(bf))==NULL){
		dmsg("host_details: Error on gethostbyname");
		return bf;
	}
/*	sprintf(bf,"%s", hent->h_name);*/
	for(i=0; hent->h_addr_list[i]; i++);
	if (i==0) return bf;
	i--;
	sprintf(addr,":%u.%u.%u.%u",
		(unsigned char) hent->h_addr_list[i][0],
		(unsigned char) hent->h_addr_list[i][1],
		(unsigned char) hent->h_addr_list[i][2],
		(unsigned char) hent->h_addr_list[i][3]);
	strcat(bf,addr);

	return bf;

#endif
}


/* generic get host details to give name:addr */
char *host_details(void)
{
    static char bf[BFSZ]="";
	if (lib_strlen(bf)!=0) if (s_strcmp(bf,"NONAME")!=0)
		return bf;

	lcpy(bf,oldhost_details(),BFSZ);
	return bf;
}


#endif /* of ndef OLD_KEY_ADY  */


/* Internal Functions */
void swap_nib(char *bb1, char *bb2);
void swap_nibs(char *s, int len);
char *chunk(char *in);
char *despace(char *in);

void long_to_char(long in, char *out);
void int_to_char(int in, char *out);
long char_to_long(char *inn);
int char_to_int(char *inn);

int new_crc(unsigned char *x, int len);
int crc(unsigned char *x, int len);

long lnew_crc(unsigned char *x, int len);
long new_hash(char *x, int flags);
long new_hash_lwr(char *x, int flags);

#define xfail(xx,yy) {isok = FALSE; strcpy(key->expires,xx);key->key_info |= yy;}
static char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};


/*************************************************************/
/*************************************************************/
#ifdef LYNDENS_REGO

#include "new_log.h"

int check_rego(pINI_VIRTUAL ini, char *file, int product_id, char *version)
{
/**********************/
/* Check Registration */
/**********************/
	STATIC_LIST list;
	KEY		rego;
	char	*skey;
	int		value;

	if (ini) {
		list = vini_multi_value(ini,"key");

	/* Check all ini Keys first */
		static_start(&list);
		skey = (char *)static_get(&list);
		while (skey) {

			value = key_check(&rego,skey, NULL, NULL,version, product_id);
			if (value & KEY_POSSIBLE) {
				if (value & KEY_ERROR_MASK)
					f_error(ini,"INI Key Failure - %s\n",key_get_expires(&rego));
				else {
				/* Key GOOD accept */
				/* Save Correct Key to File */
					key_save_file(skey, host_details(), vini_value(ini,"workarea",TRUE), file);
					if (value & KEY_TEMPORARY) {
						vini_add(ini,"trial_finishes",rego.expires);
						return -TRUE;
					}
					return TRUE;
				}
			}

			skey = (char *)static_get(&list);
		}

	/* Check 'wfile.dat' */
		value = key_check(&rego,NULL, vini_value(ini,"workarea",TRUE), file ,version, product_id);

	/* Failed Key check */
		if ( !(value & KEY_POSSIBLE) ) {
		/* create temp key */
			skey = key_create_temp(&rego, 0, product_id, VERSION_SPECIFIC, version);
			vini_add(ini, "key", skey);

			value = key_check(&rego,skey, vini_value(ini,"workarea",TRUE), file , version, product_id);
			if ((value & KEY_ERROR_MASK) || !(value & KEY_POSSIBLE)) {
				f_error(ini,"Key Failure - %s\n",key_get_expires(&rego));
				return FALSE;
			}
			imsg("Using Temporary Key\n");
		}
		else if (value & KEY_ERROR_MASK) {
			f_error(ini,"Key Failure - %s\n",key_get_expires(&rego));
			return FALSE;
		}

		if (value & KEY_TEMPORARY) {
			vini_add(ini,"trial_finishes",rego.expires);
			return -TRUE;
		}
		return TRUE;
	}
	return FALSE;
}
#endif

/*************************************************************/
/*************************************************************/

int x_rotate(int ch, int x)
{
	ch = (ch+x);
	ch += 127*1000;
	return ch % 127;
}

int hextobin(char *s)
{
 	int t,i;
	if (sisdigit(*s)) i = *s-'0';
	else i = *s-'a'+10;
	t = i*16; s++;
	if (sisdigit(*s)) i = *s-'0';
	else i = *s-'a'+10;
	t += i;
	return t;
}

char *bintostr(char *s, int len)
{
	static char bf[LBUFSZ];
	char tmp[LBUFSZ];
	int xlen;
	int i;
	strcpy(bf,"");
 	for (i=0; i<len; i++,s++) {
	 	sprintf(tmp,"%02x",(unsigned int) *s);
		xlen = lib_strlen(tmp);
		if (xlen>=2) xlen -=2;
		strcat(bf,tmp+xlen);
	}
	return bf;
}

char *strtobin(char *s, int *xlen)
{
	static unsigned char bf[LBUFSZ];
 	int i,len;
	unsigned char *out = bf;

	len = lib_strlen(s);
	for (i=0;i<len;i+=2,s+=2) {
		*out++ = hextobin(s);
	}
	*xlen = len/2;
	return (char *) bf;
}

int pc_encode(char *pcname)
{
	char bf[LBUFSZ];
	int pc=0;
	int i;
	strncpy(bf,pcname,8); bf[8] = 0;
 	s_strlwr(bf);
	if (strcmp(bf,"*")==0) return 0; /* Wild card */
 	for (i=0;bf[i]!=0; i++) {
		pc += bf[i]*i;
	}
	return pc % 32000;
}

#define NXLATE (256*256)
static unsigned int xlate[NXLATE];
static unsigned int xlate2[NXLATE];

void xlate_init(unsigned int key)
{
	int i;
	int j;
	int a;
	for (i=0; i<NXLATE; i++) xlate[i] = i;
	for (i=0; i<NXLATE; i++) {
		j = (key * i) % NXLATE;
		a = xlate[i];
		xlate[i] = xlate[j];
		xlate[j] = a;
	}
	for (i=0; i<NXLATE; i++) {
		xlate2[xlate[i]] = i;
	}
}

char *text_encrypt(char *s, int key)
{
	int len;
	xlate_init(key);
 	s = bin_encrypt(s,&len);
	s = bintostr(s,len);
	return s;
}

char *bin_encrypt(char *xs, int *nout)
{
	unsigned char bf[LBUFSZ];
	int w;
	unsigned char *s = bf;
	unsigned char *out;
	static unsigned char bf2[LBUFSZ];
	strcpy((char *) &bf[0],xs);
	bf[lib_strlen((char *) bf)+1] = 0;
	out = bf2;
	for (;*s!=0;s+=2) {
		w = s[0] + s[1] * 256;
		w = xlate[w];
		*out++ = w & 255;
		*out++ = (w >> 8) & 255;
	}
	*nout = (int)(out-bf2);
	return (char *) bf2;
}

char *x_hide(char *key, char *pass, int day)
{
	if (key && pass) {
		static char result[BFSZ];
		unsigned char table[BFSZ];
		int i;
		char *s;
		char *r;
		/* day = 12; */
		memset(table,0,100);
		strcpy((char *)table,key);
		for (i=0; i<20; i++) {
			table[i] += day;
			day += table[i];
		}
		r = result;
		for (i=0,s = pass; *s!=0; s++,i++) {
			*r++ = x_rotate(*s,table[i]);
		}
		*r++ = 0;
		return bintostr(result,lib_strlen(pass)+1);
	}
	return NULL;
}

char *x_unhide(char *key, char *hex, int day)
{
	static char result[BFSZ];
	static char *pass;
	int i;
	unsigned char table[BFSZ];
	char *r;
	char *s;
	int n;

	/* day = 12; */
	pass = strtobin(hex,&n);
	memset(table,0,100);
	strcpy(table,key);
	for (i=0; i<20; i++) {
		table[i] += day;
		day += table[i];
	}
	r = result;
	for (i=0,s = pass; i<(n-1); s++,i++) {
		*r++ = x_rotate(*s,-table[i]);
	}
	*r++ = 0;
	return result;
}



// MW: A threadsafe version of the x_hide/x_unhide functions in key.c - Logic not reviewed but 
// buffers no longer static
char *bintostr_2(char *s, int len, char *bf)
{
	char tmp[LBUFSZ];
	int xlen;
	int i;
	strcpy(bf,"");
 	for (i=0; i<len; i++,s++) {
	 	sprintf(tmp,"%02x",(unsigned int) *s);
		xlen = lib_strlen(tmp);
		if (xlen>=2) xlen -=2;
		strcat(bf,tmp+xlen);
	}
	return bf;
}

char *strtobin_2(char *s, int *xlen, char *bf)
{
 	int i,len;
	unsigned char *out = bf;

	len = lib_strlen(s);
	for (i=0;i<len;i+=2,s+=2) {
		*out++ = hextobin(s);
	}
	*xlen = len/2;
	return bf;
}
char *lib_hide(char *key, char *pass, int day, char *output)
{
	char result[BFSZ];
	unsigned char table[BFSZ];
	int i,keylen;
	int len;
	char *s;
	char *r;
	if (!key || !pass) return NULL;
	/* day = 12; */
	memset(table,0,100);
	ncpy((char *)table,key,BFSZ-1);
	keylen=lib_strlen(table);
	for (i=0; i<20; i++) {
		table[i] += day;
		day += table[i];
	}
	r = result;
	for (i=0,s = pass; *s!=0; s++,i++) {
		*r++ = x_rotate(*s,table[i%keylen]);
		if (i>BFSZ-10) break;
	}
	*r++ = 0;
	len = lib_strlen(pass);
	if (len>(BFSZ/2-5)) len=BFSZ/2-5;
	return bintostr_2(result,len+1, output);
}

char *lib_unhide(char *key, char *hex, int day, char *result)
{
	char pass[BFSZ];
	int i,keylen;
	unsigned char table[BFSZ];
	char *r;
	char *s;
	int n;

	/* day = 12; */
	strtobin_2(hex,&n,pass);
	memset(table,0,100);
	strcpy(table,key);
	keylen=lib_strlen(table);
	for (i=0; i<20; i++) {
		table[i] += day;
		day += table[i];
	}
	r = result;	
	for (i=0,s = pass; i<(n-1); s++,i++) {
		*r++ = x_rotate(*s,-table[i%keylen]);
	}
	*r++ = 0;
	return result;
}

/*************************************************************/
/*************************************************************/

/* ?file.dat */

#ifdef OLD_KEY_ADT
int key_save_file(char *key, char *domain, char *path, char *filename)
{
	if (path && key) {
		STATIC_LIST list;
		FILE *stream;
		char *loc, *str, *ptr;
		char bf[BFSZ];

		if (domain)
			sprintf(bf,"%s %s",domain,key);
		else
			sprintf(bf,"%s",key);
		list = key_load_file(path, NULL, filename);

	/* Remove current key */
		static_start(&list);
		str = (char *)static_get(&list);
		while (str) {
			ptr = strchr(str,' ');
			if (ptr) {
				*ptr = '\0';
				if ( !s_strcmp(domain, str) ) {
					if (!s_strcmp(&ptr[1], key)) {
						static_clear_dealloc(&list);
						return TRUE;
					}
					ptr = static_remove(&list);
					myfree(ptr);
				}
				if (ptr)
					*ptr = ' ';
			}
			else if (!s_strcmp(str, key)) {
				ptr = static_remove(&list);
				myfree(ptr);
			}
			str = (char *)static_get(&list);
		}
		mystrdup(str,bf);
		static_add(&list, str);

		loc = attach_path(path,filename);
		stream = myfopen(loc,"wb");
		if (stream) {
			static_start(&list);
			str = (char *)static_get(&list);
			while (str) {
				fprintf(stream,"%s%s",str,NEXT_LINE);
				str = (char *)static_get(&list);
			}
			myfclose(stream);
			static_clear_dealloc(&list);
			return TRUE;
		}
		static_clear_dealloc(&list);
	}
	return FALSE;
}

STATIC_LIST key_load_file(char *path, char *domain, char *file)
{
	STATIC_LIST list;
	char bf[BFSZ];
	char *key, *str;

	static_init(&list, NULL);
	if (path) {
		FILE *stream;
		char *loc, *ptr;

		loc = attach_path(path,file);

		stream = myfopen(loc,"rb");
		if (stream) {
			while (!feof(stream)) {
				if (!fgets(bf,BFSZ-1,stream))
					break;

				key = bf;
				while (*key == ' ' || *key == '\t') key++;
				if (*key == '#' || *key == '\r' || *key == '\n')
					continue;

				str = key + lib_strlen(key) -1;
				while (*str == ' ' || *str == '\r' || *str == '\n' || *str == '\t')
					*str-- = '\0';

				if (domain) {
					ptr = strchr(key,' ');
					if (ptr) {
						*ptr = '\0';
						if ( !s_strcmp(domain, key) ) {
							*ptr = ' ';
							mystrdup(str,key);
							static_add(&list,str);
						}
					}
					else {
						mystrdup(str,key);
						static_add(&list,str);
					}
				}
				else {
					mystrdup(str,key);
					static_add(&list,str);
				}
			}
			myfclose(stream);
		}
	}
	return list;
}

/*************************************************************/
/*************************************************************/

/* check key */
int key_check(pKEY key, char *skey, char *path, char *filename, char *version, int product_id)
{
	char *valid_key = NULL, *ptr;
	int ret;

	memset(key,0,sizeof(KEY));
	strcpy(key->expires,"No Key Entered");
	ret = KEY_NONE_ENTERED;

	if (!key || product_id >= ID_LAST)
		return KEY_NONE_ENTERED;

	if (skey) {
		if (lib_strlen(skey) > 0) {
		/* checkit */
			myfree(valid_key);
			mystrdup(valid_key,skey);

			if (!key_decode(key, skey, host_details(),version)) {
				if (!key_decode(key, skey, oldhost_details(),version))
					ret = key->key_info;
			}

			if (product[product_id].id != ID_NONE) {
				if (key->product_id) {
					if (key->product_id != product_id) {
						strcpy(key->expires,"Incorrect Product");
						ret |= KEY_INVALID_TYPE;
					}
					else
						ret |= KEY_POSSIBLE;
				}
				else {
					if (!(key->flags & product[product_id].old_key_mask)) {
						strcpy(key->expires,"Incorrect Product");
						ret |= KEY_INVALID_TYPE;
					}
					else
						ret |= KEY_POSSIBLE;
				}
			}
			else
				ret |= KEY_POSSIBLE;

			if (key->key_info & KEY_TEMPORARY)
				ret |= KEY_TEMPORARY;
/*
			if (ret != GOOD_KEY) {
				dmsg("Incorrect Key Registration\n");
				dmsg("  - %s\n",key->expires);
				dmsg("Using Old Key\n");
			}
*/
		}
	}

	if ((ret & KEY_ERROR_MASK) || !(ret & KEY_POSSIBLE)) {
		STATIC_LIST list;

		ret = KEY_NONE_ENTERED;

	/* check jfile.dat if failed */
		list = key_load_file(path, host_details(), filename);

	/* Check all ini Keys first */
		static_start(&list);
		ptr = (char *)static_get(&list);
		while ( ptr &&
				!( (ret & KEY_POSSIBLE) && !(ret & KEY_ERROR_MASK) ) ) {
			if (lib_strlen(ptr) > 0) {
				int check = FALSE;

				skey = strchr(ptr, ' ');
				if (skey)	skey++;
				else		skey = ptr;

				if (ptr) {
				/* checkit */
					myfree(valid_key);
					mystrdup(valid_key,skey);

					if (!key_decode(key, skey, host_details(), version)) {
						key_decode(key, skey, oldhost_details(), version );
					}

					ret |= KEY_FROM_FILE | key->key_info;

					if (product[product_id].id != ID_NONE) {

						if (key->product_id) {
							if (key->product_id != product_id) {
								strcpy(key->expires,"Incorrect Product");
								ret |= KEY_INVALID_TYPE;
							}
							else
								ret |= KEY_POSSIBLE;
						}
						else {
							if (!(key->flags & product[product_id].old_key_mask)) {
								strcpy(key->expires,"Incorrect Product");
								ret |= KEY_INVALID_TYPE;
							}
							else
								ret |= KEY_POSSIBLE;
						}

					}
					else
						ret |= KEY_POSSIBLE;

					if (key->key_info & KEY_TEMPORARY)
						ret |= KEY_TEMPORARY;
				}
			}

			ptr = (char *)static_get(&list);
		}

		static_clear_dealloc(&list);
	}

	key->key_info = ret;
	if ( (ret & KEY_POSSIBLE) && !(ret & KEY_ERROR_MASK) )
		key_save_file(valid_key, host_details(), path, filename);

	myfree(valid_key);
	return ret;
}
#endif /* old ifdef OLD_KEY_ADT*/

char *key_get_expires(pKEY key)
{
	return key->expires;
}

int key_get_users(pKEY key)
{
	return key->users;
}

int key_get_flags(pKEY key)
{
	return key->flags;
}

int key_get_product_id(pKEY key)
{
	return key->product_id;
}

int key_get_reg_num(pKEY key)
{
	return key->reg_num;
}

int key_recheck_key(pKEY key)
{
	if (!(key->key_info & KEY_ERROR_MASK))
		return 0;
	else
		return key->key_info;
}

/*************************************************************/
/*************************************************************/

int key_decode(pKEY key, char *skey, char *host,char *version)
{
 	int i,len,vrsn;
 	char *k = strtobin(despace(skey),&len);
 	int m,mon,year,xhost,xcrc,rcrc;
	int isok = TRUE;
	time_t t;
	struct tm *tt;
	struct tm mytt;
	static char rslt[200];

/*printf("key_decode(%s) len %d host(%s)\n",skey,len,host);*/
	key->key_info = KEY_NONE_ENTERED;
	strcpy(key->expires,"");
	if (len<8) xfail("Bad Key",KEY_INVALID);

	swap_nibs(k,len);
	time(&t);
	tt = safe_localtime(&t,&mytt);
	vrsn = k[0] & 3;
	m = (k[0]>>2) & 63;
	key->flags = (unsigned char)k[7]  + ( ((unsigned int)k[3] & 0x0F0) << 4);
	if (key->flags & FTEMP_KEY)
		key->key_info = KEY_TEMPORARY;

	key->users = (unsigned char) (k[1]-3);
	if (key->users < 100)
		key->users *= 10;
	else
		key->users = (key->users-100) * 1000;
	year = k[2];
	mon = k[3] & 0x0F;
	xhost = char_to_long(k+4);
	xcrc = char_to_int(k+8);
	rcrc = new_crc((unsigned char *)k,8);

	if (len>=14) {
		int a,b;
		a = k[13];
		b = (char) crc((unsigned char *)k,13);
		if (a != b) xfail("Bad Bit Length2",KEY_INVALID);
		key->product_id = (unsigned int)k[12];
		key->reg_num = char_to_int(k+10);
	}
	else {
		key->product_id = key->reg_num = 0;
		i = 1;

	/* Try and determine the product id type */
		while (product[i].id != ID_LAST) {
			if (product[i].old_key_mask & key->flags) {
				key->product_id = product[i].id;
				break;
			}
			i++;
		}
	}

	if (key->flags & VERSION_SPECIFIC ) {
		if (version)
			if ((version[0] & 3) != vrsn) xfail("Key for wrong version",KEY_WRONG_VERSION);
	}
	if (tt->tm_year < year) xfail("Futuristic Key Year",KEY_INVALID_TIME);
	if (tt->tm_year == year)
	  if (tt->tm_mon < mon) xfail("Futuristic Key Month",KEY_INVALID_TIME);

	if (isok) {
		if (m>0) {
			mon += m;
			for (i=0;i<25;i++) {
				if (mon>11) {
					year++; mon -= 11;
				}
			}
			if (mon>0 && mon<12){
				sprintf(key->expires,"Temporary license expires after 31-%s-%d",months[mon],1900+year);
				/*** only flag sets it now ---  *key_info = TEMPORARY_KEY; */
				key->key_info = KEY_TEMPORARY;
			}
			if (year < tt->tm_year){
				xfail("Key expired last year",KEY_EXPIRED);
			}
			if (year == tt->tm_year)
				if (mon < tt->tm_mon) xfail("Key expired last month",KEY_EXPIRED);
		}
		if (xcrc != rcrc) xfail("Bad Bit Length",KEY_INVALID);
		if (xhost!=0) {
			if ((xhost != new_hash(host,key->flags)) && (xhost != new_hash_lwr(host,key->flags))) {
				xfail("Key for the wrong computer {",KEY_WRONG_PC);
				strcat(key->expires,host_details());
				strcat(key->expires,"}");
			}
		}
	}
	return isok;
}

/*************************************************************/
/*************************************************************/

char *key_create_temp(pKEY key, int users, int product_id, int flags, char *vrsn)
{
	return key_make(key, 1, users, product_id, 0, (flags | FTEMP_KEY), host_details(), vrsn);
}

/* modified dnews 4.0 public key functions */
char *key_make(pKEY key, int months, int users, int product_id, int reg_num, int flags, char *host, char *vrsn)
{
	char xxx[20];
	time_t t;
	struct tm *tt;
	struct tm mytt;
	static char rslt[200];

	if (product_id >= ID_LAST)
		return NULL;

/* Ensure the key mask if needed */
	if (product_id) {
		flags &= NO_IPNUM | VERSION_SPECIFIC | DEALER_KEY | FTEMP_KEY;
		flags |= product[product_id].old_key_mask;
	}

	key->users = users;
	key->flags = flags;

	if (users<1000)
		users /= 10;
	else
		users = users/1000 + 100;

	time(&t);
	tt = safe_localtime(&t,&mytt);
	xxx[7] = flags & 0xFF;
	xxx[0] = (months<<2) | (vrsn[0]&3);
	xxx[1] = (users+3) % 256;
	xxx[2] = tt->tm_year;
	xxx[3] = tt->tm_mon + ((flags & 0xF00) >> 4);
	long_to_char(new_hash(host,flags),xxx+4);
	int_to_char(new_crc((unsigned char *)xxx,8),xxx+8);
	int_to_char(reg_num, &xxx[10]);
	if (product_id) {
		xxx[12]  = product_id & 255;
		xxx[13] = crc((unsigned char *)xxx,13);
		swap_nibs(xxx,14);
		strcpy(rslt,chunk(bintostr(xxx,14)));
	}
	else {
		swap_nibs(xxx,10);
		strcpy(rslt,chunk(bintostr(xxx,10)));
	}

	return rslt;
}

/*************************************************************/
/*************************************************************/
/*************************************************************/
/*************************************************************/

/* dnews 4 private key functions */
void swap_nib(char *bb1, char *bb2)
{
	char b1 = *bb1;
	char b2 = *bb2;
	*bb1 = (b1 & 0x55) | (b2 & 0xaa);
	*bb2 = (b2 & 0x55) | (b1 & 0xaa);
}

void swap_nibs(char *s, int len)
{
	int i;
	for (i=0; i<len; i+=2,s+=2) swap_nib(s,s+1);
}

char *chunk(char *in)
{
	static char bf[BFSZ];
	char *p1=bf,*p2=in;
	int i=0;
	for(; *p2 != 0; i++){
		if(i>=4){
			i=0;
			*p1++ = '-';
		}
		*p1++= *p2++;
	}
	return (char *) bf;
}

char *despace(char *in)
{
	static char bf[BFSZ];
	char *p1=bf,*p2=in;
	for(; *p2 != 0; p2++){
		if(*p2 != '-') *p1++= *p2;
	}
	*p1 = 0;
	return (char *) bf;
}

void long_to_char(long in, char *out){
 	*out++ = in & 255;
	in = in >> 8;
 	*out++ = in & 255;
	in = in >> 8;
 	*out++ = in & 255;
}

void int_to_char(int in, char *out){
 	*out++ = in & 255;
	in = in >> 8;
 	*out++ = in & 255;
}

long char_to_long(char *inn){
	unsigned char *in = (unsigned char *)inn;
	long r = 0;
	r = in[2];
	r = r << 8;
	r += in[1];
	r = r << 8;
	r += in[0];
	return r;
}

int char_to_int(char *inn){
	unsigned char *in = (unsigned char *)inn;
	int r = 0;
	r = in[1];
	r = r << 8;
	r += in[0];
	return r;
}

int new_crc(unsigned char *x, int len){
	int crc=0;
	int i;
 	for (i=0; i<len; i++) {
	 	crc = (crc << 1) + x[i]*i;
		crc = crc % 32000;
	}
	return crc % 32000;
}

int crc(unsigned char *x, int len){
	int crc=0;
	int i;
 	for (i=0; i<len; i++) {
	 	crc = (crc << 1) + x[i]*i;
		crc = crc % 255;
	}
	return crc % 255;
}


long lnew_crc(unsigned char *x, int len){
	int crc=0;
	int i;
 	for (i=0; i<len; i++) {
	 	crc = (crc << 1) + x[i]*i;
		crc = crc % (32000*256);
	}
	return crc % (256*32000);
}

long new_hash(char *x, int flags){
	char *s;
	int n;
	static char bf[BFSZ];
	if (s_strcmp(x,"*")==0) return 0;
    strcpy(bf,x);
	s = strchr(bf,':');
	if (s!=NULL) {
		if (flags & NO_IPNUM)
			*s = 0;
		else {
			for (n=0;*s!=0;s++) {
		 		if (*s=='.') n++;
				if (n>=2) {*s=0; break;}
			}
		}
	}
	return lnew_crc((unsigned char *)bf,lib_strlen(bf));
}

long new_hash_lwr(char *x, int flags)
{
	static char bf[800];
	strcpy(bf,x);
	s_strlwr(bf);
	return new_hash(bf,flags);
}
int s_strcmp( char *string1, char *string2 )
{
	if (string1 && string2) {
		if (*string1 == *string2)
			return strcmp(string1,string2);
		else
			return (*string1 - *string2);
	}
	return TRUE;
}
