/*
	NOTE .... See keylib.h for instructions on implementing in your application
	SEE KEYLIB.H !!!!
	Should encode required parts of tcplib.c,h into keylib.c for ease of use
	compile keylib.c then goback to cgilib.c...
	Must use FALLBACK host so it will work if we loose host name or site is down.
	Higher level functions:

__DATE__
  --------------------------------------------------------------------------
New key system:
	Key includes:
		reg number
		code
	Install product:
		Product asks for email address of customer etc
	 	Product connects to main site to request temp key and reg number
		If fails then offer
			Browser option (paste this data in to get key)
			Email option.
			Email fails option
		Main site sends reminders as per schedule.
	Customer wants to register,
		goto web address
		fill out form (defaults already from product)
		enter visa details - processed online
		records updated.
	Product connects and finds it now has a valid key
		It stores details and unique information used to identify system
		to prevent key re-use
	.or. Customer enters 'key' and reg info, and it records details.

	Product connects randomly, once a month, and checks key is valid, if
	told it's not valid, then product goes into 'failure' mode.

Things to be sure we deal with:
	Generated keys ?
	Out of touch systems ?
	Tracking from download to sale?
	....

------- Download/Temp Key ----------------------------------
User clicks on 'download' and enters email address to get 'key'
User installs application, and enters registration number from email.
Application connects to server and asks for key. If none then asks user for email and where he heard from?
(option to 'buy now' given)
If can't connect then send as email, if can't then create message to be sent.
Response comes in email.
--- Purchase Steps -------------
Click on web - fill in extra details (address company name, fax, phone, credit card)
Verify cc details online (or just pretend is ok but revert user in database later)
Give key to user directly and in an email.
--- Changing computer name ---------
Just re-enter registration key
--- Key stealing detection ---------
Once every month server connects and asks if it's registration key is valid
If it isn't then it is given a reason and advice to give to user.
And failure level -  temporary, warning, fail
Can be blocked by ip address, or registration number
If can't connect then assume valid local key is correct
--- Reseller tracking ---------------------
Put link on their web page to our download web page with token so we record as their sale.
Pulls up order page and enters a new customer order.
(we could do cgi to track token from a user)
--- Advertising tracking ----------------------
Advert points to site with a ?track=nnn setting, which is then maintained
through site. - same as reseller tracking.
--- tracking ----
We alias key input files index.html, download.htm, etc, through a cgi that
records the users ip address, and issues a cookie, then it matches up the
ip address and cookie later to find where this user came from.
(or just search the web log files to find which input file they hit today ?
maybe add apache cookie session id's to log so it works better  ? )
-----------------------------------------------------------------
Also search for file in distribution which we can set for each
download site we upload to ?


*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#ifndef WIN32
#include <dirent.h>
#endif

#define IS_DNEWS 1
#ifndef IS_DNEWS
#include "safelib.h"
#ifdef WIN32
#define FD_SETSIZE 2048
#endif
#else
#include "safelib.h"
#define BFSZ 600
#ifndef SYSTEM_TYPE
#define SYSTEM_TYPE "WINDOWS"
#endif
#endif

#define IS_KEYLIB_C 1
#include "keylib.h"

#define lprintf kkk_lprintf
#define ncpy kkk_ncpy
#define strcmpnc kkk_strcmpnc
#define mystrtok kkk_mystrtok
#define lib_str_hash kkk_lib_str_hash
int strcmpnc(char *s1, char *s2) ;
char *ncpy(char *dst, char *src, int len);
int lib_str_hash(char *s, int max);
char *mystrtok(char *start, char *toks, char **upto);
int lprintf( char *buffer, int bfsize, char *format, ... );


#ifndef IS_DNEWS
#include "defines.h"
#include "debug.h"
#else 
//#define BFSZ 1000
#endif

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

/* now in defines.h
#ifdef WIN32
#ifndef SYSTEM_TYPE
#define SYSTEM_TYPE "WIN32_DFLT"
#endif
#endif
*/

static int extend;
static int kreason[BFSZ];
static int isok=TRUE;
//static Knames knames[]=
static int (*imsg)(const char *arg,...);
int keylib_days(time_t t);
int keylib_imsg(int (*ximsg)(const char *arg,...))
{
	imsg = ximsg;
	return TRUE;
}
static int s_istemp;
static int flags;
static int s_ok;
static char s_reason[BFSZ];
static char s_email[BFSZ];
static char fail_reason[BFSZ];
static int s_ulimit;
static int s_done;
static int s_regid;
static int hostid;
static int sdone=FALSE;
static char s_product[BFSZ];
int keylib_del_fail(char *kfile);
static int s_flags;
static char s_key[BFSZ];
int keylib_idle(char *kfile, char *reason) ;
#define strlwr kkk_strlwr
char *strlwr(char *s);

static char *months[]={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};

static int s_expired;
int keylib_cur_key(char *bf)
{
	ncpy(bf,s_key,BFSZ-1);
	return TRUE;
}
int keylib_isflag(int x)
{
	if (s_flags!=0) if ((s_flags & x)!=0) return TRUE;
	return FALSE;
}
int keylib_expired(void)
{
	return FALSE;
	return s_expired;
}
char *keylib_email(void)
{
	return s_email;
}
char *keylib_url(void)
{
	static char bf[BFSZ];
	if (!s_expired) if (!s_istemp) return "";
	if (strcmpnc(s_product,"surgemail")==0) {
		char p[BFSZ];
		ncpy(p,s_product,BFSZ-1); strlwr(p);
		lprintf(bf,200,"http://netwinsite.com/%s_order.htm",p);
	} else {
		lprintf(bf,200,"https://netwinsite.com/cgi-bin/keycgi.exe?cmd=buy&email=%s&regid=n%d",s_email,s_regid);
	}
//	lprintf(bf,200,"https://netwinsite.com/cgi-bin/keycgi.exe?cmd=buy&email=%s&regid=n%d",s_email,s_regid);
	return bf;
}
int keylib_regid(void)
{
	return s_regid;
}
char *keylib_regids(void)
{
	static char bf[BFSZ];
	lprintf(bf,100,"N%d",s_regid);
	return bf;
}
int keylib_mon_find(char *mon)
{
	int i;
	char bf[BFSZ];
	for (i=0; i<12; i++) {
		strcpy(bf,months[i]);
		strlwr(bf);
		if (strcmp(bf,mon)==0) return i;
	}
	return 0;
}
int keylib_build_date(void) /* Returns build date of source, used to find if license key is good */
{
	/* Date of build */
	//Apr 15 2002
	static struct tm tm;
	char bf[BFSZ];
	ncpy(bf,__DATE__,100);
	strlwr(bf);
	tm.tm_mday = atoi(bf+4);
	bf[3] = 0;
	tm.tm_mon = keylib_mon_find(bf);
	tm.tm_year = atoi(bf+7) - 1900;
	return (int) mktime(&tm);
}
int keylib_nossl(void)
{
	return FALSE;
	if ((flags & KF_NOSSL)>0) return TRUE;
	return FALSE;
}
int keylib_domlimit(void)
{
	return FALSE;
	if ((flags & KF_DOMLIMIT)>0) return TRUE;
	return FALSE;
}
int keylib_free(void)
{
	return FALSE;
	if ((flags & KF_FREE)>0) return TRUE;
	return FALSE;
}


void keylib_extend(int x)
{
	extend = x;
}
char *keylib_nice(char *product)
{
	int i;
	char less[BFSZ];
	char *s;
	for (i=0; products[i].name!=NULL; i++) {
		if (strcmpnc(products[i].name,product)==0) return products[i].name;
	}
	ncpy(less,product,BFSZ-1);
	s = strchr(less,'-');
	if (s!=NULL) *s = 0;

	for (i=0; products[i].name!=NULL; i++) {
		if (strcmpnc(products[i].name,less)==0) return products[i].name;
	}
	return "PRODUCT_NOT_FOUND";
}
int keylib_prodid(char *xprod)
{
	int i;
	char *s;
	char prod[BFSZ];
	ncpy(prod,xprod,BFSZ-1);
	s = strchr(prod,'-');
	if (s!=NULL) *s = 0;
	for (i=0; knames[i].name!=NULL; i++) {
		if (strcmpnc(knames[i].name,prod)==0) return knames[i].id;
	}
	return -1;
}
int keylib_new(char *key, int regid, int prodid, int months, char *host, int xtime, int ulimit, int flags)
{
	unsigned char keybin[BFSZ];
	int len;
	Akey k;
	strcpy(key,"");
	k.flags = flags;
	k.host = keylib_host(host);
	k.prodid = prodid;
	k.regid = regid;
	k.ulimit = ulimit;
	k.regdate = keylib_todate(xtime,months);
	len = keylib_pack(&k,keybin);
	keylib_bintohex(keybin,len,key,100);
	return TRUE;
}
char *keylib_host_trim(char *host, char *tmp)
{
	char *s;
	ncpy(tmp,host,BFSZ-1);
	s = strchr(tmp,'.');
	if (s!=NULL) *s = 0;
	return tmp;
}
char *keylib_host_new(char *host, char *tmp)
{
	ncpy(tmp,host,BFSZ-1);
	strcat(tmp,":$new$");
	return tmp;
}
static time_t regdate,s_regdate;
time_t keylib_regdate(void)
{
	return regdate;
}
int keylib_check(char *key, int *ulimit, char *host, int nusers, int *regid, int *flags, char *product, char *reason)
{
	Akey k;
	char tmp[BFSZ];
	char tmp2[BFSZ];
	int badcrc;
	time_t reg_time;
	time_t expire_time;
	int prodid = keylib_prodid(product);

// chrisp hack to disable key.
	lprintf(reason,200,"Key is good");
	*ulimit = 0;
	expire_time = 0;
	return TRUE;


	keylib_decode(key,&k,&reg_time,&expire_time,&badcrc);
	regdate = reg_time;
	if (badcrc) {
		lprintf(reason,200,"Bad crc in key for host=(%s) key=(%s)",host,key);
		return FALSE;
	}
	if (k.host!=0) if ((keylib_host(host)!=k.host) && (keylib_host(keylib_host_trim(host,tmp2))!=k.host)) {
		lprintf(reason,200,"Key for wrong host=(%s) key=(%s), try activate again",host,key);
		return FALSE;
	}
	if (k,prodid!=0) if (k.prodid!=prodid) {
		lprintf(reason,200,"Key for wrong product %d!=%d (%s) (%s) (%s)",prodid,k.prodid,host,key,product);
		return FALSE;
	}
	*ulimit = k.ulimit;
	if (k.ulimit < nusers) { /* Maybe check at next layer for news anyway */
		lprintf(reason,200,"User limit exceeded %d > %d",nusers,k.ulimit);
		return FALSE;
	}
	*regid = k.regid;
	*flags = k.flags;
	if (expire_time!=0) if (extend) expire_time += 7 * 60*60*24;  /* 1 week */
	if (expire_time!=0) if (time(NULL) > expire_time) {
		lprintf(reason,200,"Temporary license EXPIRED %s",keylib_ctime(expire_time,tmp));
		s_expired = TRUE;
		return FALSE;
	}
	lprintf(reason,200,"Expires in %d days",keylib_days(expire_time));
	s_istemp = FALSE;
	if (expire_time==0) lprintf(reason,200,"users=%d, flags=%d, host=%s, prod=%s",k.ulimit,k.flags,host,product);
	else s_istemp = TRUE;

	return TRUE;
}
char * keylib_ctime(time_t t, char *tmp)
{
	safe_ctime(&t,tmp);
	tmp[24] = 0;
	return tmp;
}
int keylib_days(time_t t)
{
	time_t now;
	time(&now);
	return (t-now)/(60*60*24);
}
int keylib_decode(char *key, Akey *k, time_t *reg_time, time_t *expire_time, int *badcrc)
{
	char *keybin;
	unsigned char tmp[BFSZ];
	int mon=0;
	int len;
	struct tm atm;
	struct tm *tm;
	*badcrc = FALSE;
	*expire_time = 0;
	keybin = keylib_hextobin(key,&len,tmp);
	memset(k,0,sizeof(Akey));
	*badcrc = !keylib_unpack(keybin,k);
	keylib_fromdate(k->regdate,reg_time,&mon);
	tm = safe_localtime(reg_time,&atm);
	tm->tm_mon += mon;
	*expire_time = mktime(tm);
	if (mon==0) *expire_time = 0;
	return TRUE;
}
void keylib_fromdate(int inr, time_t *t, int *months)
{
	time_t base;
	struct tm tm;
	int r = inr & 0x0fff;

	memset(&tm,0,sizeof(struct tm));
	tm.tm_mday = 1;
	tm.tm_year = 102;
	base = mktime(&tm);

	*t = r * (60*60*24) + base;
	*months = (inr & 0xf000) >> 12;
}
int keylib_todate(time_t t, int months)
{
	int r;
	time_t base;
	struct tm tm;
	memset(&tm,0,sizeof(struct tm));
	tm.tm_mday = 1;
	tm.tm_year = 102;
	base = mktime(&tm);
	r = (t-base) / (60*60*24);
	return (r & 0x0fff) | (months<<12);
}
int keylib_test(void)
{
	Akey k,k2;
	unsigned char keybin[BFSZ];
	//do some basic tests we can run every time
	k.flags = 255;
	k.host = 65001;
	k.regdate = 0x1234;
	k.prodid = 1;
	k.ulimit = 10;
	k.regid = 200000;
	keylib_pack(&k,keybin);
	if (!keylib_unpack(keybin,&k2)) printf("crc failed\n");
	if (memcmp(&k,&k2,sizeof(Akey))==0) printf("keylib_test failed BAD BAD BAD\n");
	k.ulimit = 200;
	keylib_pack(&k,keybin);
	if (!keylib_unpack(keybin,&k2)) printf("crc failed\n");
	if (memcmp(&k,&k2,sizeof(Akey))==0) printf("keylib_test failed BAD BAD BAD\n");
	k.ulimit = 3000;
	keylib_pack(&k,keybin);
	if (!keylib_unpack(keybin,&k2)) printf("crc failed\n");
	if (memcmp(&k,&k2,sizeof(Akey))==0) printf("keylib_test failed BAD BAD BAD\n");
	k.ulimit = 20000;
	keylib_pack(&k,keybin);
	if (!keylib_unpack(keybin,&k2)) printf("crc failed\n");
	if (memcmp(&k,&k2,sizeof(Akey))==0) printf("keylib_test failed BAD BAD BAD\n");
	return TRUE;
}
/*
  regid = 3 bytes
  exmonth = 1 byte (10 years) (months after 2000)
  regmonth = 1 byte
  host = 2 bytes
  crc = 2 bytes
  prodid = 1 byte
  flags = 1 byte  (product specific)
  ulimit = 1 byte    0 no limit 1-16,32,64,128,256
*/
#define PRE_CRC 10
int keylib_pack(Akey *k, unsigned char *keybin)
{
	int i=0;
	unsigned int crc;
	keybin[i++] = k->regid & 0xff;
	keybin[i++] = (k->regid & 0xff00) >> 8;
	keybin[i++] = (k->regid & 0xff0000) >> 16;
	keybin[i++] = k->regdate & 0xff;
	keybin[i++] = (k->regdate & 0xff00) >> 8;
	keybin[i++] = k->host & 0xff;
	keybin[i++] = (k->host & 0xff00) >> 8;
	keybin[i++] = k->prodid;
	keybin[i++] = k->flags;
	keybin[i++] = keylib_ulimit_encode(k->ulimit);
	if (PRE_CRC!=i) abort();
	crc = keylib_crc(keybin,PRE_CRC);
	keybin[i++] = crc & 0xff;
	keybin[i++] = (crc & 0xff00) >> 8;;
	return i;
}
int keylib_unpack(unsigned char *keybin, Akey *k)
{
	int i=0;
	unsigned int crc;
	unsigned int bcrc;
	k->regid = keybin[i] + keybin[i+1] * 0x100+ keybin[i+2] * 0x10000;	i+=3;
	k->regdate = keybin[i] + keybin[i+1] * 0x100; i+=2;
	k->host = keybin[i] + keybin[i+1] * 0x100; i+=2;
	k->prodid = keybin[i++];
	k->flags = keybin[i++];
	k->ulimit = keylib_ulimit_decode(keybin[i++]);
	crc = keylib_crc(keybin,PRE_CRC);
	bcrc =  (keybin[i] + keybin[i+1] * 0x100); i+=2;
	if (crc==bcrc) return TRUE;
	return FALSE;
}


char *keylib_bintohex(char *bin, int binlen, char *out, int outlen)
{
	unsigned char *ubin=(unsigned char *) bin;
	int i;
	int z;
	char *out_start = out;
	for (i=0; i<binlen; i++,ubin++)  {
	      lprintf(out,10,"%02x",(unsigned int) *ubin);
	      z = strlen(out);
	      out += z;
	      outlen -= z;
	      if (outlen<20) break;
	 }
	*out = 0;
  	 return out_start;
}
char *keylib_hextobin(char *s, int *xlen, unsigned char *bf)
{
 	int i,len;
	unsigned char *out = bf;
	len = strlen(s);
	for (i=0;i<len;i+=2,s+=2) {
		*out++ = keylib_nibtobin(s);
	}
	*xlen = len/2;
	return (char *) bf;
}
int keylib_nibtobin(char *s)
{
 	int t,i;
	if (isdigit(*s)) i = *s-'0';
	else i = *s-'a'+10;
	t = i*16; s++;
	if (isdigit(*s)) i = *s-'0';
	else i = *s-'a'+10;
	t += i;

	return t;
}


int keylib_ulimit_encode(int n)
{
	if (n<20) return n;
	if (n<1000) return n/10 + 20;;
	return n/100 + 120;
}
int keylib_ulimit_decode(int n)
{
	int r;
	if (n<20) r = n;
	else if (n<120) r = (n-20) * 10;
	else r = (n-120)*100;
	if (r==440) r = 20000;
	if (r==760) r = 100000;
	if (r==1600) r = 1000000;
	return r;
}
int keylib_host(char *xhost)
{
	char host[BFSZ];
	char *s;
	if (strcmp(xhost,"*")==0) return 0;
	strncpy(host,xhost,200); host[200]=0;
	if (strstr(host,":$new$")!=NULL) {
		/* New format, so just make key based on first part of host name */
		s = strchr(host,'.'); if (s!=NULL) *s = 0;
	}
	s = strchr(host,':'); if (s!=NULL) *s = 0;
	strlwr(host);
	return keylib_crc(host,strlen(host)) & 0xffff;
}
unsigned int keylib_crc(unsigned char *xin,int len)
{
	unsigned char bf[BFSZ];
	int i;
	unsigned int crc = 0;
	memset(bf,0,BFSZ-1);
	memcpy(bf,xin,len);
	for (i=0; i<len; i++) {
		crc += bf[i++]*256;
		crc += bf[i];
		crc += (bf[i+1]*i % 0xff);
	}
	return crc & 0xffff;
}
int keylib_get_host(char *host)
{
	/* Change for windows ? */
	char ip[BFSZ];
	char tmp[BFSZ];
	kkk_hostname(host);
	kkk_lookup(host,ip);
	lprintf(tmp,100,"%s:%s",host,ip);
	strcpy(host,tmp);
	strlwr(host);
	return TRUE;
}
int keylib_post(Chan *c, char *data);
int keylib_read(Chan *c, char *data);
static int debug;
int keylib_setdebug(int x)
{
	debug = x;
	return TRUE;
}
int keylib_activate_get(char *kfile, char *prod, char *regid, char *email, char *key, char *reason)
{
	/*	Connect to key server,
		send in a fake 'form' request
		read out results
	*/
	Chan *c=NULL;
	char info[BFSZ];
	char data[BFSZ];
	char host[BFSZ];
	char tmp1[BFSZ];
	char tmpe[BFSZ];
	char response[BFSZ];
	char key_state[BFSZ];
	char product[BFSZ];
	char ipnum[BFSZ];
	char error[BFSZ];
	char tmp4[BFSZ];
	char htmp[BFSZ];
	char *s;
	char *upto;
	int ret=TRUE;
	char tmp2[BFSZ];
	int code=0;
	char tmp3[BFSZ];

	strcpy(response,"");
	strcpy(error,"");
	strcpy(key,"");
	strcpy(info,"connect failed");
	strcpy(key_state,"");
	strcpy(product,"");
	strcpy(reason,"unable to connect");
	kkk_init(printf);
	if (email==NULL) email = "";
	if (regid==NULL) regid = "";
	s_done = FALSE;
	if (strstr(regid,"_")==NULL) if (strlen(regid)>10) { /* Looks like a registration key already*/
		ncpy(key,regid,100);
		return keylib_store(kfile,key,email,prod,reason);
	}
	kkk_init(imsg);
	kkk_new(&c);
	keylib_get_host(host);

	if (!keylib_gethostid(kfile,reason)) {
		goto failed;
	}
	kkk_noblock(c);
	if (!kkk_open(c,KEYSERVER,7025,"")) {
		lprintf(info,BFSZ-1,"Unable to open %s %s",KEYSERVER,kkk_error(c,tmp1));
		goto failed;
	}
	lprintf(data,BFSZ-1,"cmd=activate&regid=%s&email=%s&host=%s&product=%s&build=%d&hostid=%d&info=%s",keylib_encode(regid,tmp1),keylib_encode(email,tmpe),keylib_encode(keylib_host_new(host,htmp),tmp2),keylib_encode(prod,tmp3),keylib_build_date(),hostid,keylib_encode(SYSTEM_TYPE,tmp4));
//	if (debug) printf("Sending (%s)\n",data);
	if (!keylib_post(c,data)) {
		goto failed;
	}
	strcpy(response,"none");
	keylib_read(c,response);
	s = strchr(response,' ');
	if (s!=NULL) code = atoi(s+1);

//	if (debug) printf("keylib_read (%s)\n",response);
	s = mystrtok(response,"\r\n",&upto);
	strcpy(reason,"");
	if (s!=NULL) if (code>300) ncpy(reason,s,100);
	for (;s!=NULL;s=mystrtok(NULL,"\r\n",&upto)) {
//		if (debug) printf("Process line (%s)\n",s);
		if (strncmp(s,"product: ",9)==0) ncpy(product,s+9,100);
		if (strncmp(s,"key: ",5)==0) ncpy(key,s+5,100);
		if (strncmp(s,"key_state: ",11)==0) ncpy(key_state,s+11,100);
		if (strncmp(s,"error: ",7)==0) ncpy(error,s+7,100);
	}
//	if (debug) printf("Reason is (%s)\n",reason);
	if (strlen(product)>0) if (strcmp(product,prod)!=0) {
		lprintf(reason,100,"Wrong product, state %s (%s) (%s)",key_state,product,prod);
		ret = FALSE;
	}
	if (strlen(error)==0) if (strlen(key)==0) goto failed;
	ncpy(reason,error,BFSZ-1);
	if (strlen(reason)==0) {
		lprintf(reason,100,"Got good response, state %s host (%s)",key_state,host);
	} else ret = FALSE;

	kkk_free(&c);

	if (strstr(reason,"deactivated")!=NULL) {
		char old[BFSZ];
		ncpy(old,kfile,BFSZ-1);
		strcat(old,"_deactivated");
		remove(old);
		rename(kfile,old);
		remove(kfile);
//		ret = keylib_store(kfile,key,email,prod,reason);
		ret = FALSE;
		s_ok = FALSE;
		s_done = TRUE;
		ncpy(s_reason,"Deactivated",200);
	}

	if (ret) ret = keylib_store(kfile,key,email,prod,reason);
	return ret;
failed:
	kkk_lookup(host,ipnum);
	/* PLEASE NOTE, SYSTEM_TYPE MUST BE CORRECTLY DEFINED  */
	lprintf(reason,BFSZ-1,"%s\nEMail the following text to keyrobot@netwinsite.com\n\nTo: keyrobot@netwinsite.com\nSubject: keys\n\n(In body of email)\n{host: %s}\n{ip: %s}\n{regid: %s}\n{email: %s}\n{product: %s}\n{build: %d}\n{hostid: %d}\n{info: %s}\n\n"
		,info,keylib_host_new(host,htmp),ipnum,regid,email,prod,keylib_build_date(),hostid,SYSTEM_TYPE);
	kkk_free(&c);
	return FALSE;
}
int keylib_pre_install(char *fname, char *email, char *regid, char *newspass)
{
	FILE *f;
	char *s;
	f = fopen(fname,"r");
	if (f==NULL) return FALSE;
	fgets(regid,100,f);
	fgets(email,100,f);
	fgets(newspass,100,f);
	s = strchr(regid,'\n'); if (s!=NULL) *s = 0;
	s = strchr(email,'\n'); if (s!=NULL) *s = 0;
	s = strchr(newspass,'\n'); if (s!=NULL) *s = 0;
	fclose(f);

	return TRUE;
}
int keylib_ask_install(char *fname, char *prod, char *dfltemail)
{
	char email[BFSZ];
	char regid[BFSZ];
	char reason[BFSZ];
	char newspass[BFSZ];
	FILE *f;
	char *s;
again:
	strcpy(newspass,"");
	printf("Please enter the registration email address you entered on our web forms \nwhen downloading (if not known just use your own email address)\n Email: [%s] ? ",dfltemail);
	fgets(email,BFSZ-1,stdin);
	if (strchr(email,'@')==NULL) ncpy(email,dfltemail,BFSZ-1);
	if (strlen(email)==0) goto again;
	printf("Enter registration number, if not known press enter for automatic search/generation\n Regid: ? ");
	fgets(regid,BFSZ-1,stdin);
	s = strchr(regid,'\n'); if (s!=NULL) *s = 0;
	if (strlen(regid)==0) {
		if (!keylib_install(prod,email,regid,reason,newspass)) {
			printf("Auth register failed %s\n",reason);
			printf("This is OK!!, You can load after installation, to get one check your email or get one from http://netwinsite.com/activate.htm\n");
			return FALSE;
		} else {
			printf("Registration number (%s) retrieved from server\n",regid,reason);
		}
	}
	printf("Storing auto activation information %s %s %s\n",email,regid,newspass);
	f = fopen(fname,"w");
	if (f==NULL) {perror(fname); return FALSE;}
	s = strchr(regid,'\r'); if (s!=NULL) *s = 0;
	s = strchr(regid,'\n'); if (s!=NULL) *s = 0;
	s = strchr(email,'\r'); if (s!=NULL) *s = 0;
	s = strchr(email,'\n'); if (s!=NULL) *s = 0;
	s = strchr(newspass,'\r'); if (s!=NULL) *s = 0;
	s = strchr(newspass,'\n'); if (s!=NULL) *s = 0;
	fprintf(f,"%s\n",regid);
	fprintf(f,"%s\n",email);
	fprintf(f,"%s\n",newspass);
	fclose(f);
	return TRUE;
}
int keylib_install(char *prod, char *email, char *regid, char *reason, char *newspass)
{
	Chan *c=NULL;
	char tmp1[BFSZ];
	char tmp2[BFSZ];
	char *s;
	char data[BFSZ];
	char response[BFSZ];
	char *upto;
	int code;
	kkk_init(imsg);
	kkk_new(&c);
	kkk_noblock(c);
	if (!kkk_open(c,KEYSERVER,7025,"")) {
		lprintf(reason,BFSZ-1,"Unable to open %s %s",KEYSERVER,kkk_error(c,tmp1));
		goto failed;
	}
	lprintf(data,BFSZ-1,"cmd=cmd_reg_new&email=%s&product=%s&text=true"
		,keylib_encode(email,tmp1)
		,keylib_encode(prod,tmp2));
	if (debug) printf("Sending (%s)\n",data);
	if (!keylib_post(c,data)) {
		lprintf(reason,BFSZ-1,"Post to netwinsite.com failed");
		goto failed;
	}
	keylib_read(c,response);
	s = strchr(response,' ');
	if (s!=NULL) code = atoi(s+1);

	if (debug) printf("keylib_read (%s)\n",response);
	s = mystrtok(response,"\r\n",&upto);
	strcpy(reason,"None");
	if (s!=NULL) if (code>300) ncpy(reason,s,100);
	for (;s!=NULL;s=mystrtok(NULL,"\r\n",&upto)) {
//printf("Process line (%s)\n",s);
		if (strncmp(s,"regid: ",7)==0) ncpy(regid,s+7,100);
		if (strncmp(s,"newspass: ",10)==0) ncpy(newspass,s+10,100);
		if (strncmp(s,"error: ",7)==0) ncpy(reason,s+7,100);
	}
	kkk_free(&c);
	if (strlen(regid)==0) {
		if (strlen(reason)==0) strcpy(reason,"Did not find regid in response");
		return FALSE;
	}
	return TRUE;
failed:
	kkk_free(&c);
	return FALSE;
}


int keylib_recheck(char *prod, char *regid, char *reason)
{
	Chan *c=NULL;
	char data[BFSZ];
	char host[BFSZ];
	char tmp1[BFSZ];
	char response[BFSZ];
	char key_state[BFSZ];
	char product[BFSZ];
	char *s;
	char *upto;
	int ret=TRUE;
	char tmp2[BFSZ];
	char tmp3[BFSZ];
	char htmp[BFSZ];

	strcpy(reason,"");
	strcpy(key_state,"");
	strcpy(product,"");
	kkk_init(imsg);
	kkk_new(&c);
	keylib_get_host(host);
	kkk_noblock(c);
	if (!kkk_open(c,KEYSERVER,7025,"")) {
		lprintf(reason,BFSZ-1,"Unable to open link %s",kkk_error(c,tmp1));
		goto failed;
	}

	lprintf(data,BFSZ-1,"cmd=recheck&regid=%s&host=%s&product=%s&hostid=%d",keylib_encode(regid,tmp1),keylib_encode(keylib_host_new(host,htmp),tmp2),keylib_encode(prod,tmp3),hostid);
	if (debug) printf("Sending (%s)\n",data);
	if (!keylib_post(c,data)) goto failed;
	keylib_read(c,response);
	if (debug) printf("keylib_read (%s)\n",response);
	s = mystrtok(response,"\r\n",&upto);
	strcpy(reason,"");
	for (;s!=NULL;s=mystrtok(NULL,"\r\n",&upto)) {
		if (debug) printf("Process line (%s)\n",s);
		if (strncmp(s,"product: ",9)==0) ncpy(product,s+9,100);
		if (strncmp(s,"key_state: ",11)==0) ncpy(key_state,s+11,100);
		if (strncmp(s,"error: ",7)==0) ncpy(reason,s+7,100);
	}
	if (debug) printf("Reason is (%s)\n",reason);
	if (strlen(product)>0) if (strcmp(product,prod)!=0) {
		lprintf(reason,100,"Wrong product, state %s (%s) (%s)",key_state,product,prod);
		ret = FALSE;
	}
	if (strlen(reason)==0) {
		lprintf(reason,100,"Got good response, state %s host (%s)",key_state,host);
	} else ret = FALSE;
	kkk_free(&c);
	return ret;
failed:
	imsg("Failed to talk to it\n");
	kkk_free(&c);
	return TRUE;
}


char *keylib_encode(char *in, char *tmp)
{
	char *start=tmp;
	for (;*in!=0;in++) {
		if (*in == ' ') *tmp = '+';
		else if (*in == '&') {lprintf(tmp,100,"%%%2X",*in); tmp+=2; }
		else if (*in == '+') {lprintf(tmp,100,"%%%2X",*in); tmp+=2; }
		else if (*in == '%') {lprintf(tmp,100,"%%%2X",*in); tmp+=2; }
		else *tmp = *in;
		tmp++;
	}
	*tmp++ = 0;
	return start;
}
int keylib_post(Chan *c, char *data)
{
	if (!kkk_wait_write(c,13000)) return FALSE;
#ifdef TEST_LAP
	kkk_printf(c,"POST /scripts/keycgi.exe HTTP/1.1\r\n");
#else
	kkk_printf(c,"POST /cgi-bin/keycgi.exe HTTP/1.1\r\n");
#endif
	kkk_printf(c,"Content-Type: application/x-www-form-urlencoded\r\n");
	kkk_printf(c,"Host: netwinsite.com\r\n");
	kkk_printf(c,"Content-Length: %d\r\n",strlen(data));
	kkk_printf(c,"\r\n");
	kkk_printf(c,"%s\r\n",data);
	return TRUE;
}
char *keylib_two(char *p1, char *p2)
{
	static char bf[BFSZ];
	if (p1==NULL) return "badkey";
	if (strlen(p1)>90) return "badkey";
	if (p2!=NULL) if (strlen(p2)>90) return "badkey";
	strcpy(bf,p1);
	strcat(bf," ");
	if (p2!=NULL) strcat(bf,p2);
	return bf;
}
int keylib_read(Chan *c, char *data)
{
	/* Should find content-length: and read that many more bytes. */
	int was;
	int rest=0;
	char *s;
	int n = kkk_read(c,data,BFSZ-1);
	if (n>0) data[n] = 0; else data[0] = 0;
	was = n;
	if (n<=0) return 0;
	s = strstr(data,"Content-length: ");
	if (s!=NULL) rest = atoi(s+16);
	if (rest>BFSZ-1) rest = BFSZ-1;
	if (rest<0) rest = 0;
	if (rest>0) {
		if (!kkk_waitfor(c,19000)) return n;
		n = kkk_read(c,data+strlen(data),rest);
	}
	data[was+n] = 0;
	return n;
}
int keylib_gethostid(char *kfile,char *reason)
{
	char fname[BFSZ];
	char bf[BFSZ];
	FILE *f;
	if (hostid!=0) return hostid;
	lprintf(fname,200,"%s_id",kfile);
	f = fopen(fname,"r");
	if (f==NULL) {
		if (errno!=ENOENT) {
			lprintf(reason,200,"Unable to read hostid file (%s) %s",fname,strerror(errno));
			return FALSE;
		}
		if (!sdone) srand(time(NULL) + lib_str_hash(kfile,1000000)*100); sdone = TRUE;
		f = fopen(fname,"w");
		if (f==NULL) {
			lprintf(reason,200,"Unable to write hostid file (%s) %s",fname,strerror(errno));
			return FALSE;
		}
		hostid = rand()*100000+rand();
		if (hostid<=0) hostid = -hostid;
		fprintf(f,"%d\n",hostid);
		fclose(f);
		return hostid;
	}
	fgets(bf,BFSZ-1,f);
	hostid = atoi(bf);
	fclose(f);
	return hostid;
}
int keylib_store(char *kfile, char *key, char *email, char *product, char *reason)
{
	int r;
	char host[BFSZ];
	int regid;
	int ulimit=0;
	int prodid = keylib_prodid(product);
	FILE *f;

	if (!sdone) srand(time(NULL) + lib_str_hash(key,1000000)*100); sdone = TRUE;
	kkk_init(printf);
	keylib_get_host(host);
	r = keylib_check(key,&ulimit,host,0,&regid,&flags,product,reason);
	if (!r) {
		return FALSE;
	}
	ncpy(s_key,key,BFSZ-1);
	s_flags = flags;
	s_regid = regid;
	ncpy(s_product,product,BFSZ-1);
	keylib_del_fail(kfile);

	f = fopen(kfile,"w");
	if (f==NULL) {
		lprintf(reason,BFSZ-1,"Unable to write (%s) %s",kfile,strerror(errno));
		lprintf(s_reason,BFSZ-1,"Unable to write (%s) %s",kfile,strerror(errno));
		return FALSE;
	}
	fprintf(f,"key %s\nregid %d\nprodid %d\nproduct %s\nemail %s\n",key,regid,prodid,product,email);
	fclose(f);
	s_done = FALSE;
	return TRUE;
}
int keylib_write_fail(char *kfile, char *reason)
{
	FILE *f;
	char fname[BFSZ];
	lprintf(fname,200,"%s_fail",kfile);
	f = fopen(fname,"w");
	if (f!=NULL) {
		fprintf(f,"%s\n",reason);
		fclose(f);
	}
	return TRUE;
}
int keylib_del_fail(char *kfile)
{
	char fname[BFSZ];
	lprintf(fname,200,"%s_fail",kfile);
	remove(fname);
	return TRUE;
}
int keylib_get_fail(char *kfile, char *reason)
{
	char *s;
	FILE *f;
	char fname[BFSZ];
	lprintf(fname,200,"%s_fail",kfile);
	f = fopen(fname,"r");
	if (f==NULL) return FALSE;
	fgets(reason,200,f);
	s = strchr(reason,'\n'); if (s!=NULL) *s = 0;
	fclose(f);
	return TRUE;
}
int keylib_uncache(void)
{
	s_done = FALSE;
	strcpy(fail_reason,"");
	return TRUE;
}
/* If file doesn't exist then generate a temp key */
int keylib_temp_file(char *kfile, char *vproduct, char *reason)
{
	int prodid = keylib_prodid(vproduct);
	char key[BFSZ];
	FILE *f;
	strcpy(reason,"None");
	f = fopen(kfile,"r");
	if (f!=NULL) {
		fclose(f);
		return FALSE;
	}
	if (errno!=ENOENT) return FALSE;
	
	if (!keylib_new(key,0,prodid,1,"*",time(NULL),0,0)) {
		imsg("Key generation failed\n");
		return FALSE;
	}

	keylib_store(kfile,key,"trial@netwinsite.com",vproduct,reason);
	return TRUE;
}
int keylib_check_file(char *kfile, char *vproduct, int *ulimit, char *reason)
{
	int regid;
	char key[BFSZ];
	char kreason[BFSZ];
	int r;
	char xproduct[BFSZ];
	char xregid[BFSZ];
	char okstring[BFSZ];
	char host[BFSZ];
	int prodid = keylib_prodid(vproduct);


	if (!sdone) srand(time(NULL) + lib_str_hash(kfile,1000000)*100); sdone = TRUE;
	if (s_done) { /* change to %30 */
		if (rand()%30==3) {
			keylib_idle(kfile,reason);
		}
		ncpy(reason,s_reason,200);
		regdate = s_regdate;
		*ulimit = s_ulimit;
		return s_ok;
	}
	s_done = TRUE;
	kkk_init(imsg);

	keylib_get_host(host);
	if (rand()%30==3) {
		keylib_idle(kfile,reason);
	}
	keylib_get_fail(kfile,fail_reason);
	if (strlen(fail_reason)>0) {
		ncpy(reason,fail_reason,200);
		ncpy(s_reason,fail_reason,200);
		s_ok = FALSE;
		return FALSE;
	}
	/* If file doesn't exist then generate a temp key */
	keylib_temp_file(kfile,vproduct,reason);
	strcpy(key,"");
	strcpy(xproduct,"");
	if (!keylib_from_file(kfile,key,xproduct,xregid,s_email)) {
		lprintf(reason,200,"No key activated see http://netwinsite.com/activate.htm");
		ncpy(s_reason,reason,200);
		//RMH: log message below confuses smsgate users who see it (when smsgate checks 
		//     for a surgemail key
		//imsg("Key not activated yet %s\n",kfile);
		return FALSE;
	}

	regid = atoi(xregid);
	s_regid = regid;
	ncpy(s_product,vproduct,BFSZ-1);
	ncpy(s_key,key,BFSZ-1);
	r = keylib_check(key,ulimit,host,0,&regid,&flags,vproduct,kreason);
	s_regdate = regdate;
	s_ok = r;
	s_ulimit = *ulimit;
	s_flags = flags;
	s_done = TRUE;
	strcpy(okstring,"");
	if (r) strcpy(okstring,"OK, ");
	lprintf(reason,200,"Key N%d %semail=%s, %s\n",regid,okstring,s_email,kreason);
	ncpy(s_reason,reason,200);
	if (!r) return FALSE;
	return TRUE;
}
int keylib_isok(char *reason)
{
	if (strlen(fail_reason)>0) ncpy(reason,fail_reason,200);
	else ncpy(reason,s_reason,200);
	return s_ok;
}
int keylib_istemp(char *reason)
{
	int r = keylib_isok(reason);
	if (!r) return TRUE;
	if (s_istemp) return TRUE;
	return FALSE;
}
int keylib_from_file(char *kfile,char *key, char *prod, char *regid, char *email)
{
	FILE *f;
	char bf[BFSZ];
	char *s;
	f = fopen(kfile,"r");
	if (f==NULL) return FALSE;
	strcpy(key,"");
	strcpy(regid,"");
	strcpy(email,"");
	for (;!feof(f);) {
		if (fgets(bf,BFSZ-1,f)==NULL) break;
		s = strchr(bf,'\n'); if (s!=NULL) *s = 0;
		if (strncmp(bf,"key ",4)==0) ncpy(key,bf+4,BFSZ-1);
		if (strncmp(bf,"regid ",6)==0) ncpy(regid,bf+6,BFSZ-1);
		if (strncmp(bf,"product ",8)==0) ncpy(prod,bf+8,BFSZ-1);
		if (strncmp(bf,"email ",6)==0) ncpy(email,bf+6,BFSZ-1);
	}
	ncpy(s_key,key,BFSZ-1);
	fclose(f);
	return TRUE;
}
int keylib_idle(char *kfile, char *reason)
{
	static int last;
	FILE *f;
	int delay;
	char bf[BFSZ];
	char fname[BFSZ];
	int week=60*60*24*7;
	static time_t last_idle;

	if (time(NULL)-last_idle<60) return TRUE;
	last_idle = time(NULL);

	lprintf(fname,200,"%s_last",kfile);
	if (last==0) {
		f = fopen(fname,"r");
		if (f!=NULL) {
			fgets(bf,BFSZ-1,f);
			last = atoi(bf);
			fclose(f);
		}
	}

	delay = time(NULL) - last;
	//week = 30;
	if (delay<week) return TRUE;

	f = fopen(fname,"w");
	if (f!=NULL) {
		fprintf(f,"%d\n",time(NULL));
		fclose(f);
	}
	last = time(NULL);
	return keylib_recheck_now(kfile,reason);
}
int keylib_recheck_now(char *kfile, char *reason)
{
	char regid[BFSZ];
	char prod[BFSZ];
	int r;
	char key[BFSZ];
	char email[BFSZ];
	imsg("keylib: Doing keylib test now\n");
	strcpy(reason,"");
	if (!keylib_gethostid(kfile,reason)) {
		imsg("Can't get host hostid %s\n",reason);
		return FALSE;
	}
	if (!keylib_from_file(kfile,key,prod,regid,email)) {
		lprintf(reason,100,"Key not activated yet, use activate command");
		imsg("Key not activated yet\n");
		return FALSE;
	}
	r = keylib_recheck(prod,regid, reason);
	if (!r) {
		if (strstr(reason,"orry registration disabled")!=NULL) {
			s_ok = FALSE;
			ncpy(s_reason,reason,200);
			imsg("Key is invalid (%s)\n",reason);
			keylib_write_fail(kfile,reason);
			return FALSE;
		} else {
			imsg("Key is invalid, but continuing anyway (%s)\n",reason);
		}
	}
	imsg("keylib: Done keylib test %d\n",s_ok);
	return TRUE;
}
Product *keylib_prod_find(char *prodcode)
{
	int i;
	char *s;
	char less[BFSZ];
	Product *p;
	for (i=0; products[i].name!=NULL;i++) {
		p = &products[i];
		if (strcmpnc(p->code,prodcode)==0) return p;
	}
	ncpy(less,prodcode,BFSZ-1);
	s = strchr(less,'-');
	if (s!=NULL) *s = 0;
	for (i=0; products[i].name!=NULL;i++) {
		p = &products[i];
		if (strcmpnc(p->code,less)==0) return p;
	}
	return NULL;
}
/*------------HERE IS SOME LIB FUNCTIONS WE MAY NOT HAVE, ALL TRANSLATED WITH IFDEFS ----------------------------------------------------------------------*/
char *ncpy(char *dst, char *src, int len)
{
        char *xdst = dst;
        int xlen = len;

        for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}
        *dst++ = 0;
        xdst[xlen] = 0;
        return xdst;
}
static const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

int strcmpnc(char *s1, char *s2)
{
        const unsigned char *cm = charmap,
             *us1 = (const unsigned char *)s1,
             *us2 = (const unsigned char *)s2;

        while (cm[*us1] == cm[*us2++])
                if (*us1++ == '\0')
                        return (0);
        return (cm[*us1] - cm[*--us2]);
}
int lib_str_hash(char *s, int max)
{
        int total=0;
        int r;
        int i;
        for (i=0;*s!=0;s++,i++) {
		total = total << 3;
                total += *s;
        }
        r = total % max;
        if (r<0) return -r;
        return r;
}
char *mystrtok(char *start, char *toks, char **upto)
{
	char *s=start;
	if (start!=NULL) *upto = NULL;
	if (s==NULL) s = *upto;
	if (s==NULL) return NULL;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)==NULL) break;
	}
	if (*s==0) return NULL;
	start = s;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)!=NULL) break;
	}
	if (*s==0) {
		*upto = NULL;
		return start;
	}
	*s = 0;
	*upto = s+1;
	return start;
}
#ifdef WIN32
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#endif

int lprintf( char *buffer, int bfsize, char *format, ... )
{
	int r;
	if (buffer && format && bfsize) {
		va_list arg_list;
		va_start(arg_list, format);

		r = vsnprintf( buffer, bfsize, format, arg_list);
		if (r<0) buffer[bfsize-1] = 0;
	}
	return -1;
}
/*------------HERE IS TCPLIB.C CHANGED TO KKK* ----------------------------------------------------------------------*/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifndef WIN32
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#endif
//#include "debug.h"
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE (!FALSE)
#endif
int kkk_wouldblock(Chan *c);
void kkk_lib_msleep(int x);

int kkk_read_ssl(Chan *c, char *bf, int len);
int kkk_write_ssl(Chan *c, char *bf, int len);
int kkk_setblockraw(SOCKET sk, int t);
void kkk_close_on_exec_raw(int fd);
static int (*emsg)( const char *format ,...);
static int (*dmsg)( const char *format ,...);
static int isdebug;
char *ncpy(char *dst, char *src, int len);
int kkk_getip_raw(SOCKET sk, char *ipbf);
int kkk_getmyip_raw(SOCKET sk, char *ipbf);
int kkk_grow_inbf(Chan *c, int len);

void kkk_setdebug(int x)
{
	isdebug = x;
}
void kkk_init_dmsg(int (*vemsg)( const char *format ,...))
{
	dmsg = vemsg;
}
void kkk_init(int (*vemsg)( const char *format ,...))
{
	emsg = vemsg;
#ifdef WIN32
	{
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
        wVersionRequested = MAKEWORD( 1, 1 );

        err = WSAStartup( wVersionRequested, &wsaData );
        if ( err != 0 ) { emsg("No winsock found of correct version\n"); return;}
        if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 ) {
                emsg("No winsock found of correct version, trying to continue\n");
                goto ret;
        }

/* wmsg("Socket library found and is version 1.1, Great!\n"); */
/* The Windows Sockets DLL is acceptable. Proceed. */
ret: ;
	}
#else
	signal(SIGPIPE,SIG_IGN);
#endif


}
void kkk_setrcvbf_raw(SOCKET c, int sz);
void kkk_new(Chan **c)
{
	if (*c!=NULL) {emsg("kkk_new, not passed null pointer\n");  abort();}
	*c = malloc(sizeof(Chan));
	memset(*c,0,sizeof(Chan));
	(*c)->canclose = FALSE;
	(*c)->id = 88;
	(*c)->sk = INVALID_SOCKET;
	(*c)->block = TRUE;
	kkk_grow_inbf(*c,2000);

}
int kkk_close_on_exec(Chan *c)
{
	if (c->sk==INVALID_SOCKET) return FALSE;
	kkk_close_on_exec_raw(c->sk);
	return TRUE;
}

#ifdef WIN32
SOCKET kkk_dupaccept(SOCKET s, struct sockaddr FAR *addr, int FAR* addrlen, char *myname, char *theirname)
{
	SOCKET sock, sock2;
	HANDLE p;
	int r;

	strcpy(myname,"");
	strcpy(theirname,"");
	sock=accept(s, addr, addrlen);
	if(sock==INVALID_SOCKET) return sock;

	kkk_getip_raw(sock,theirname);
	kkk_getmyip_raw(sock,myname);

	p=GetCurrentProcess();
	r=DuplicateHandle(p, (HANDLE) sock, p,(HANDLE *) &sock2, 0, FALSE, DUPLICATE_SAME_ACCESS);


	socket_close(sock);

	if(r==FALSE)
	{
		int i;
		LPVOID lpMsgBuf;

		i=GetLastError();
		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
		i, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);

		emsg("*** Warning *** Couldn't dup accepted socket: error %d (%s)", i, lpMsgBuf);
		LocalFree(lpMsgBuf);
		return INVALID_SOCKET;
	}
	return sock2;
}
SOCKET kkk_dupsocket(int af, int type, int protocol)
{
	SOCKET sock, sock2;
	HANDLE p;
	int r;

	sock=socket(af, type, protocol);
	if(sock==INVALID_SOCKET)
		return sock;
	p=GetCurrentProcess();
	r=DuplicateHandle(p,(HANDLE ) sock, p,(HANDLE *) &sock2, 0, FALSE, DUPLICATE_SAME_ACCESS);
	CloseHandle((HANDLE) sock);
	if(r==FALSE)
	{
		int i;
		LPVOID lpMsgBuf;

		i=GetLastError();
		FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL,
		i, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);

		emsg("*** Warning *** Couldn't dup socket: error %d (%s)", i, lpMsgBuf);
		LocalFree(lpMsgBuf);
		return INVALID_SOCKET;
	}
	return sock2;
}
#else
SOCKET kkk_dupaccept(SOCKET s, struct sockaddr *addr, int *addrlen, char *myname, char *theirname)
{
	SOCKET sock;

	sock=accept(s, addr, addrlen);
	if (sock!=INVALID_SOCKET) kkk_close_on_exec_raw(sock);
	kkk_getip_raw(sock,theirname);
	kkk_getmyip_raw(sock,myname);

	return sock;
}
SOCKET kkk_dupsocket(int af, int type, int protocol)
{
	SOCKET sock, sock2;
	int r;

	sock=socket(af, type, protocol);
	if (sock!=INVALID_SOCKET) kkk_close_on_exec_raw(sock);
	return sock;
}
#endif

int kkk_accept(Chan *vlisten, Chan **newchan)
{
        struct sockaddr_in xsin;
	int sizesin=sizeof(xsin);
	Chan *c;


	kkk_new(newchan);
	c = *newchan;
    c->sk = kkk_dupaccept(vlisten->sk,(struct sockaddr *) &xsin, &sizesin, c->myname,c->theirname);
	if (c->sk==INVALID_SOCKET) {
	        kkk_lasterror(c);
		return FALSE;
	}
        kkk_lasterror(c);
	kkk_close_on_exec(c);
	kkk_setrcvbf_raw(c->sk,65535);

	return TRUE;

}
#ifdef UCX
#define DOCAST
#endif
#ifdef WIN32
#define DOCAST
#endif
#ifdef NETWARE
#define DOCAST
#endif

#ifdef DOCAST
#define ONCAST (char *)
#else
#define ONCAST
#endif


int kkk_listen(Chan **cc, int port, char *bindin, int reuse)
{
	int quelen;
        struct sockaddr_in xsin;
	Chan *c=NULL;
	int r;
	int on=-1;
	kkk_new(cc);
	c = *cc;
	if (c==NULL) return FALSE;
	c->sk = kkk_dupsocket(AF_INET, SOCK_STREAM, 0);
    kkk_lasterror(c);
    if (c->sk == INVALID_SOCKET) {
		kkk_lasterror(c);
		return FALSE;
	}
	kkk_close_on_exec(c);
    if (reuse) r = setsockopt(c->sk, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));

    xsin.sin_family = AF_INET;
    xsin.sin_addr.s_addr = INADDR_ANY;
    xsin.sin_port = htons((short) port);

	if (bindin!=NULL) if (strlen(bindin)>0) {
		xsin.sin_addr.s_addr = inet_addr(bindin);
	}

    if (bind(c->sk,(struct sockaddr *)  &xsin, sizeof (xsin)) < 0) {
		kkk_lasterror(c);
		return FALSE;
    }
 	quelen = 25;
#ifdef WIN32
	quelen = 200;
#endif
    if (listen(c->sk, quelen) < 0) {
		kkk_lasterror(c);
		return FALSE;
    }
	return TRUE;
}
int kkk_isip(char *s)
{
	for (;*s!=0;s++) {
		if (*s=='.') continue;
		if (isdigit(*s)) continue;
		return FALSE;
	}
	return TRUE;
}
int kkk_isopen(Chan *c)
{
	if (c==NULL) return FALSE;
	if (c->sk==INVALID_SOCKET) return FALSE;
	return TRUE;
}
int kkk_lookup(char *xname, char *ip)
{
        struct sockaddr_in xsin;
	struct hostent *hp;
        int dontblock = FALSE;
	char name[1000];
	char *s;
	int dst_port=90;
	const char optval=44; //non zero integer

	strncpy(name,xname,200);
        memset(&xsin,0,sizeof(xsin));
        xsin.sin_family = AF_INET;

	s = strchr(name,':');
	if (s!=NULL) {
		*s = 0;
		dst_port = atoi(s+1);
	}

        if (kkk_isip(name)) {
                xsin.sin_addr.s_addr = inet_addr(name);
                xsin.sin_family = AF_INET;
        } else {
                xsin.sin_addr.s_addr = 0;
                hp = gethostbyname(name);
                if (hp==NULL) return FALSE;
                memcpy(&(xsin.sin_addr),hp->h_addr,  hp->h_length);
                xsin.sin_family = hp->h_addrtype;
        }
	strcpy(ip,inet_ntoa(xsin.sin_addr));
	return TRUE;
}
SOCKET kkk_open_raw(Chan *c, char *xname,int dst_port, int reuse, char *bindout)
{
        struct sockaddr_in xsin;
        struct sockaddr_in xlcl;
	SOCKET sk;
	struct hostent *hp;
        int st;
        int dontblock = FALSE;
	char name[1000];
	char bf[1000];
	char *s;
	int on=-1;
	int r;
	const char optval=44; //non zero integer

	strcpy(c->myname,"");
	strcpy(c->theirname,"");
	if (strlen(xname)==0) return INVALID_SOCKET;
	strncpy(name,xname,200);
        memset(&xsin,0,sizeof(xsin));
        xsin.sin_family = AF_INET;

        strlwr(name); /* Seems to be required by NIS */
	s = strchr(name,':');
	if (s!=NULL) {
		*s = 0;
		dst_port = atoi(s+1);
	}

        if (kkk_isip(name)) {
                xsin.sin_addr.s_addr = inet_addr(name);
                xsin.sin_family = AF_INET;
        } else {
                xsin.sin_addr.s_addr = 0;
                hp = gethostbyname(name);
                if (hp==NULL) return INVALID_SOCKET;
                memcpy(&(xsin.sin_addr),hp->h_addr,  hp->h_length);
                xsin.sin_family = hp->h_addrtype;
        }

        sk = kkk_dupsocket(xsin.sin_family,SOCK_STREAM, 0);
/*	emsg("open, created socket %d\n",sk);*/
       	kkk_lasterror(c);
        if (sk==INVALID_SOCKET) {
                emsg("Can't connect to {%s} \n",name);
                return INVALID_SOCKET;
        }
	kkk_close_on_exec(c);
	if (c->noblock) {
		if (!kkk_setblockraw(sk,FALSE)) {
		        kkk_lasterror(c);
			printf("setblock failed %s\n",kkk_error(c,bf));
		}
	}

/*        r = setsockopt(c->sk, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));*/
        if (reuse || (c->lclport!=0)) {
		r = setsockopt(sk, SOL_SOCKET, SO_REUSEADDR,ONCAST &on, sizeof(int));
	        kkk_lasterror(c);
		if (r!=0) emsg("set reuseaddress failed  %.200s \n",kkk_error(c,bf));
	}
	if (bindout==NULL) bindout = "";
	if ((c->lclport!=0) || (strlen(bindout)>0) ) {
		emsg("Binding outgoing connection to port %s:%d\n",bindout,c->lclport);
	        xlcl.sin_family = AF_INET;
	        xlcl.sin_addr.s_addr = INADDR_ANY;
			if (strlen(bindout)>0)  xlcl.sin_addr.s_addr =  inet_addr(bindout);
	        xlcl.sin_port = htons((short) c->lclport);
	        if (bind(sk,(struct sockaddr *)  &xlcl, sizeof (xlcl)) < 0) {
			kkk_lasterror(c);
			emsg("LCL Bind failed - %.200s - will bind to free port instead\n",kkk_error(c,bf));
/*			socket_close(sk);
			return INVALID_SOCKET;*/
	        }
	}

    xsin.sin_port = htons((unsigned short) dst_port);
    st = connect(sk,(struct sockaddr *)   &xsin, sizeof(xsin));
	kkk_lasterror(c);
    if (st!=0) {
		if (kkk_wouldblock(c)) return sk;
		kkk_lasterror(c);
		emsg("Connect failed (%s:%d)\n",xname,dst_port);
		if (socket_close(sk)!=0) emsg("socket_close_connect failed\n");
		return INVALID_SOCKET;
    }
    return sk;
}
int kkk_wouldblock(Chan *c)
{
        int err = c->xerrno;;
#ifdef WIN32
        if (err==WSAEWOULDBLOCK) return TRUE;
        if (err==WSAEINPROGRESS) return TRUE;
#else
        if (err==EWOULDBLOCK) return TRUE;
        if (err==EAGAIN) return TRUE;
        if (err==EINPROGRESS) return TRUE;
#endif
        return FALSE;
}

int kkk_open(Chan *cc, char *hostname,  int port, char *bindout)
{
	cc->sk = kkk_open_raw(cc,hostname,port,FALSE,bindout);
	if (cc->sk==INVALID_SOCKET) return FALSE;
	return TRUE;
}
int kkk_open_reuse(Chan *cc, char *hostname,  int port, char *bindout)
{

	cc->sk = kkk_open_raw(cc,hostname,port,TRUE,bindout);
	if (cc->sk==INVALID_SOCKET) return FALSE;
	return TRUE;
}
int kkk_not_connected(Chan *c)
{
#ifdef WIN32
	if (c->xerrno==WSAENOTCONN) return TRUE;
#else
        if (c->xerrno==ENOTCONN) return TRUE;
#endif
	return FALSE;
}

int kkk_lasterror(Chan *c)
{
#ifdef WIN32
	c->xerrno = WSAGetLastError();
#else
	c->xerrno = errno;
#endif
	return c->xerrno;
}
#ifdef HAS_SSL
#undef HAS_SSL
#endif

int kkk_release(Chan *c)
{
	c->canclose = TRUE;
	return TRUE;
}
int kkk_close_nice(Chan *c)
{
	if (c->sk==INVALID_SOCKET) return TRUE;
	kkk_buffer_flush(c);
#ifdef HAS_SSL
	if (c->ssl!=NULL) {
        kkk_setblock(c,FALSE);
		myssl_close(c,c->ssl);
		c->ssl = NULL;
		c->sk = INVALID_SOCKET;
		return TRUE;
	}
#endif

#ifdef WIN32
	socket_shutdown(c->sk,1);
#endif
	kkk_setlinger(c,1,60*100*3);
	kkk_setblockraw(c->sk,FALSE);
	if (socket_close(c->sk)!=0) {
  		kkk_lasterror(c);
		emsg("socket_close: Socket close failed will retry\n");
		kkk_setblockraw(c->sk,TRUE);
		if (socket_close(c->sk)!=0) emsg("socket_close: failed again\n");
	}
	c->sk = INVALID_SOCKET;
	return TRUE;
}
int kkk_free(Chan **cc)
{
	Chan *c = *cc;
	if (c==NULL) return FALSE;
	if (c->sk!=INVALID_SOCKET) {
		kkk_close_nice(c);
		c->sk = INVALID_SOCKET;
	}
	if (c->inbf!=NULL) free(c->inbf); c->inbf = 0;
	*cc = NULL;
	free(c);
	return TRUE;
}
int kkk_read_log(char *s, int got, int wanted)
{
	FILE *f;
	f = fopen("tcp.log","ab");
	if (f==NULL) return TRUE;
	fprintf(f,"\r\nGot %d bytes, wanted %d bytes\r\n",got,wanted);
	if (got>0) {
		if (got<100) fwrite(s,1,got,f);
	}
	fclose(f);
	return TRUE;
}
int kkk_read(Chan *c, char *s, int len)
{
	int r;
	/*char *last;*/
	errno = 0;
	if (c->sk==INVALID_SOCKET) return 0;
	r = socket_read(c->sk,s,len);
	/*last = s +len-40;
	if (last<s) last = s;   WTF is this code for ay?  */
	/*kkk_read_log(s,r,len);*/
	if (r<=0) kkk_lasterror(c);
	return r;
}
int kkk_socket_write(Chan *c, char *s, int len)
{
	if (c->isbuffer) return kkk_buffer_add(c,s,len);
	else return socket_write(c->sk,s,len);
}

int kkk_write(Chan *c, char *s, int len)
{
	int r;
	if (c->sk==INVALID_SOCKET) return 0;
	if (c->isbuffer) return kkk_buffer_add(c,s,len);
 	r = kkk_socket_write(c,s,len);
	if (r<=0) kkk_lasterror(c);
	return r;
}

int kkk_write_timed(Chan *c, char *s, int len, int msecs)
{
	int r;
	int start=time(NULL);
	int upto=0;
	if (c->sk==INVALID_SOCKET) return 0;

	if (c->isbuffer) return kkk_buffer_add(c,s,len);

	kkk_setblockraw(c->sk,FALSE);
	if (upto==len) {
		r = kkk_socket_write(c,s+upto,len-upto);
		goto done;
	}
	for (;upto<len;) {
		if (!kkk_wait_write(c,msecs)) break;
		r = kkk_socket_write(c,s+upto,len-upto);
done:		kkk_lasterror(c);
		if (r>0) upto+=r;
		if (r<=0) {
			if (!kkk_wouldblock(c)) {
				kkk_close_nice(c);
				break;
			}
			kkk_lib_msleep(1); /* Stop race condition on solaris etc */
		}
		if (time(NULL) - start > (msecs/1000)) break;
		if (r>0) start = time(NULL);
	}
	kkk_setblockraw(c->sk,TRUE);

	return upto;
}
int kkk_read_timed_raw(Chan *c, char *s, int len, int msecs)
{
	int ndone = 0;
	int r = -2;
	time_t start;
	if (c->sk==INVALID_SOCKET) return 0;

	kkk_setblock(c,FALSE);
	r = kkk_read(c,s,len);	kkk_lasterror(c);
	if (r>0) goto done;
	start = time(NULL);
	 if (kkk_waitfor(c,msecs)) {
		while(TRUE){
			if (!kkk_check_open(c)) {
				return 0;}
			r = kkk_read(c,s,len);
			kkk_lasterror(c);
			if (r<=0) {
				if (kkk_wouldblock(c)) {
					/* This shouldn't really happen, lets try again for half a second */
					kkk_lib_msleep(100);
					ndone++;
					if (ndone<5) continue;
					r = -2;
					lprintf(c->erradd,90,"after %d seconds",time(NULL)-start);
				}
				else {kkk_close_nice(c);
				}
			}
			break;
		}
	} else r = -2;
done:	kkk_setblock(c,TRUE);
	return r;
}


char *kkk_find_dot(char *bf, char *end, int *isnulls, int isstart)
{
	register char *s=bf;
	register char c;
	char *eom = NULL;

	if (isstart) if (strncmp(bf,".\r\n",3)==0) {
		if (end-bf==3) {
			return bf;
		}

	}
	for (;s<end;s++) {
		c = *s;
		if (c==0) *isnulls = TRUE;
		if (c=='\r') {
			if (s[1] == '\n') if (s[2]=='.')
				if (s[3]=='\r') if (s[4]=='\n') {
					eom = s + 2;
					break;
				}
		}
	}
	return eom;
}
char *kkk_read_dot(Chan *c, int *len, int *isend, int *isnulls, int secs, int isstart)
{
	char *start = c->inbf + c->inupto;
	char *end = c->inbf + c->inlen;
	int r=TRUE;
	char *eom;
	int doneread = FALSE;
//	static FILE *f,*f2;
//	imsg("read_dot have %d, inlen=%d upto=%d\n",end-start,c->inlen,c->inupto);


	if (end-start<=2) {
do_read:
		r = kkk_readbf(c,secs*1000,2000);
		//imsg("read_Dot done inelen %d\n",c->inlen);
		doneread = TRUE;
	}

/*if (f2==NULL) f2 = fopen("dot2.in","wb");
fprintf(f2,"}\r\n got %d bytes \r\n{",c->inlen);
fwrite(c->inbf,1,c->inlen,f2);
fflush(f2);
*/

	if (!r) {
		return NULL;
	}
	start = c->inbf + c->inupto;
	end = c->inbf + c->inlen;
	eom = kkk_find_dot(start,end,isnulls,isstart);
	if (!doneread) if (eom==NULL) goto do_read;
	if (eom!=NULL) {
		*len = eom - start;
		c->inupto += *len;
		*isend = TRUE;
		/* Now swallow the .crlf */
		c->inupto++;
		if (c->inbf[c->inupto] == '\r') c->inupto++;
		if (c->inbf[c->inupto] == '\n') c->inupto++;
		return start;
	}
	*len = end - start + 1 - 5;  /* Leave 4 incase start of 'eom' */
	if (*len<0) *len = 0;
	c->inupto += *len;
//	r = kkk_readbf(c,secs*1000); /* no eom so we definitely need to read more */

/*
if (f==NULL) f = fopen("dot.in","wb");
fprintf(f,"}\r\n got %d bytes \r\n{",*len);
fwrite(start,1,*len,f);
fflush(f);
*/

	return start;
}

int kkk_read_timed(Chan *c, char *s, int len, int msecs)
{
	int x;
	int wasblock;
	/* If there is stuff in buffer and s != the internal buffer get data from internal buffer */
	if ( (c->inupto < c->inlen) && ((s < c->inbf) || (s > (c->inbf + c->maxinbf))) ) {

		for ( x = 0; c->inupto < c->inlen && (len--); x++ ) {
			s[x] = c->inbf[c->inupto++];
		}
		return x;
	}
	if (c->sk==INVALID_SOCKET) {if (dmsg!=NULL) dmsg("kkk_read_timed invalid socket");return FALSE;}
	wasblock = c->block;
	kkk_setblock(c,FALSE);
	x = kkk_read(c,s,len);
	if ( x > 0 ) {
		kkk_setblock(c,TRUE);
		return x;
	}
	kkk_lasterror(c);
	kkk_setblock(c,TRUE);

	while (TRUE) {
		/*if (dmsg!=NULL) dmsg("running\n");*/
		if (!kkk_wouldblock(c)) {
			kkk_close_nice(c);
			return FALSE;
		}
		if (!kkk_check_open(c)) return 0;
		x = kkk_waitfor(c, msecs);
		if (x == TRUE) {
			x = kkk_read(c,s,len);
			if ( x > 0 ) return x;
			kkk_lasterror(c);
			continue;
		}
		else if (x == -TRUE){
			kkk_lasterror(c);
			return FALSE;
		}
		break;
	}
	return -2;
}



int kkk_peek_line_timed(Chan *c, char *s, int len, int msecs)
{
	int x, y;
	if ( (c->inupto < c->inlen) && ((s < c->inbf) || (s > (c->inbf + c->maxinbf))) ) {
		for ( x = 0, y = c->inupto; y < c->inlen && --len && c->inbf[y] != '\n'; x++ ) {
			s[x] = c->inbf[y++];
		}
		s[x] = 0;
		if (x>0)
			return x;
	}
	kkk_readbf(c, msecs,len+1000);
	/* If there is stuff in buffer and s != the internal buffer get data from internal buffer */
	if ( (c->inupto < c->inlen) && ((s < c->inbf) || (s > (c->inbf + c->maxinbf))) ) {
		for ( x = 0, y = c->inupto; y < c->inlen && --len && c->inbf[y] != '\n'; x++ ) {
			s[x] = c->inbf[y++];
		}
		s[x] = 0;
		return x;
	}
	if (c->sk==INVALID_SOCKET) return FALSE;
	return -2;
}





/*
	char inbf[CHAN_INSIZE];
	int inupto;
	int inlen;
*/
int kkk_readbf(Chan *c, int msec, int max)
{
	int want;
	int loop=0;
	int r;
	if (c->inupto>0) {
		memmove(c->inbf,c->inbf + c->inupto,c->inlen - c->inupto+1);
		c->inlen -= c->inupto;
		c->inupto = 0;
	}
	want = c->maxinbf-c->inlen-2;
	if (want<0) if (c->maxinbf < max) {
		kkk_grow_inbf(c,c->maxinbf*2);
		want = c->maxinbf-c->inlen-2;
	}
	if (want<=0) {
		emsg("No end of line in input buffer\n");
		c->inupto = 0;
		c->inlen = 0;
		want = c->maxinbf -10 ;
	}
	//printf("kkk_read_timed_raw(c->sk=%d,c->inbf+c->inlen,want=%d,msec=%d);\n", c->sk, want, msec);
	r = kkk_read_timed_raw(c,c->inbf+c->inlen,want,msec);
	if (r<=0) return FALSE;
	c->inlen += r;
	c->inbf[c->inlen] = 0;
	return TRUE;
}
int kkk_set_read_timeout(Chan *c, int msec)
{
	c->read_timeout = msec;
	return TRUE;
}
int kkk_grow_inbf(Chan *c, int len)
{
	c->inbf = realloc(c->inbf,len);
	c->maxinbf = len;
	return TRUE;
}
int kkk_readline(Chan *c, char *out, int len)
{
	char *s2;
	char *s;
	int tout = c->read_timeout;
	if (tout==0) tout = 3 * 60 * 1000;
	/* n = bytes read, 0 = socket closed, -1 = socket closed, -2 = timeout */
again:
	if (c->maxinbf<=0) kkk_grow_inbf(c,2000);
	s = c->inbf + c->inupto;
	c->inbf[c->inlen] = 0; /* Make sure it's null terminated */
	s2 = strchr(s,'\n');
	//printf("GOT::: %s", c->inbf+ c->inupto);
	if (s2==NULL) {
		if (!kkk_readbf(c,tout,len+100)) return FALSE;
		goto again;
	}
	*s2 = 0;
	if (s2>s) if (s2[-1]=='\r') s2[-1] = 0;

	ncpy(out,s,len-1);
	c->inupto += (s2-s+1);

	return TRUE;
}
int kkk_readline_free(char *out)
{
	if (out!=NULL) free(out);
	return TRUE;
}
int kkk_readline_big(Chan *c, char **vout, int max)
{
	char *s2;
	char *s;
	int tout = c->read_timeout;
	if (tout==0) tout = 3 * 60 * 1000;
	*vout = NULL;

	/* n = bytes read, 0 = socket closed, -1 = socket closed, -2 = timeout */
again:
	s = c->inbf + c->inupto;
	c->inbf[c->inlen] = 0; /* Make sure it's null terminated */
	s2 = strchr(s,'\n');
	if (s2==NULL) {
		if (!kkk_readbf(c,tout,max)) return FALSE;
		goto again;
	}
	*s2 = 0;
	if (s2>s) if (s2[-1]=='\r') s2[-1] = 0;

	*vout = strdup(s);
	c->inupto += (s2-s+1);
	return TRUE;
}
int kkk_noblock(Chan *c)
{
	c->noblock = TRUE;
	c->block = FALSE;
	return TRUE;
}
/*
This is really a 'wait until open' function, it should be modified for wait_write
*/
int kkk_wait_write(Chan *c, int msec)
{
        fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	if (c->sk==INVALID_SOCKET) return FALSE; /* giveup immediately */
        timeout.tv_sec = msec/1000; /*Wake up once a second*/
        timeout.tv_usec = (msec%1000) * 1000 ;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
	FD_SET(c->sk,&writefd);
	/*FD_SET(c->sk,&readfd);
	FD_SET(c->sk,&exceptfd);		*/
	errno = 0;
        nc = select(FD_SETSIZE,&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) {
		int was;
		int xlen=sizeof(was);
		char *s;
		s = (char *) &was;
		was = 0;
	        getsockopt(c->sk, SOL_SOCKET, SO_ERROR, s, &xlen);
		if (was!=0) {
			errno = was;
			kkk_lasterror(c);
			return FALSE;
		}
		return TRUE;
	}
	return FALSE;
}
int kkk_check_open(Chan *c)
{
        fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	if (c->sk==INVALID_SOCKET) return FALSE;
        timeout.tv_sec = 0;
        timeout.tv_usec = 1;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
	FD_SET(c->sk,&exceptfd);
        nc = select(FD_SETSIZE,&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) { /* Should get an except error if socket closes I think */
		kkk_close_nice(c);
		return FALSE;
	}
	return TRUE;
}
int kkk_waitfor(Chan *c, int msec)
{
    fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	if (c->sk==INVALID_SOCKET) return TRUE;
#ifdef HAS_SSL
	/*Data may be in buffer already */
	if (c->ssl!=NULL)
		if (myssl_pending(c->ssl)==1) return TRUE;
#endif
    timeout.tv_sec = msec/1000; /*Wake up once a second*/
    timeout.tv_usec = (msec%1000) * 1000 ;
    FD_ZERO(&readfd);
    FD_ZERO(&writefd);
    FD_ZERO(&exceptfd);
	FD_SET(c->sk,&readfd);
	/* Experiment, next line might help with IE problem */
	/* Anothe experiment, I think maybe we need exceptfd to detect socket closing */
	FD_SET(c->sk,&exceptfd);
        nc = select(FD_SETSIZE,&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) return TRUE;
	return FALSE;
}
int kkk_waitfor_list(Chan *first, int msec)
{
	Chan *ch;
        fd_set readfd,writefd,exceptfd;
	int nc;
	struct timeval timeout;

	for (ch = first; ch!=NULL; ch = ch->next) {
		if (ch->sk==INVALID_SOCKET) return TRUE;
	}
#ifdef HAS_SSL
	/*Data may be in buffer already */
	for (ch = first; ch!=NULL; ch = ch->next) {
		if (ch->ssl!=NULL) if (myssl_pending(ch->ssl)==1) return TRUE;
	}
#endif
        timeout.tv_sec = msec/1000; /*Wake up once a second*/
        timeout.tv_usec = (msec%1000) * 1000 ;
        FD_ZERO(&readfd);
        FD_ZERO(&writefd);
        FD_ZERO(&exceptfd);
	for (ch = first; ch!=NULL; ch = ch->next) {
		FD_SET(ch->sk,&readfd);
		FD_SET(ch->sk,&exceptfd);
	}
        nc = select(FD_SETSIZE,&readfd,&writefd,&exceptfd,&timeout);
	if (nc>0) return TRUE;
	return FALSE;
}
#ifdef WIN32
#define vsnprintf _vsnprintf
#endif
int kkk_printf(Chan *c, char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[5000];
	int r;
	char tmp2[BFSZ];
	int tout = 1000*60*10;

	if (c->write_timeout>0) tout = c->write_timeout;

        va_start(arg_ptr, arg_list);
        format = arg_list;
	output[0] = 0;
        len = vsnprintf(output,4000, format, arg_ptr); output[4000-1] = 0;
	r = kkk_write_timed(c,output,strlen(output),tout);
	if (r<(int) strlen(output)) {
  		kkk_lasterror(c);
		emsg("kkk_printf: failed only %d sent %s\n",r,kkk_error(c,tmp2));
		return FALSE;
	}
	if (isdebug) {
		emsg("kkk_printf --> %.90s",output);
	}
	if (r<=0) return FALSE;
	return TRUE;
}
int kkk_setlclport(Chan *c, int x)
{
	c->lclport = x;
	return TRUE;
}
int kkk_printf_nolog(Chan *c, char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char tmp2[BFSZ];
	char output[5000];
	int r;

        va_start(arg_ptr, arg_list);
        format = arg_list;
	output[0] = 0;
        len = vsnprintf(output,4000, format, arg_ptr); output[4000-1] = 0;
	r = kkk_write(c,output,strlen(output));
	if (r<(int) strlen(output)) {
  		kkk_lasterror(c);
		emsg("kkk_printf2: failed only %d sent %s\n",r,kkk_error(c,tmp2));
		return FALSE;
	}
	if (r<=0) return FALSE;
	return TRUE;
}
int kkk_getip(Chan *c, char *ipbf)
{
        int len;
	char bf[2000];
        struct sockaddr_in usoc, *us;

	if (strlen(c->theirname)>0) {
		ncpy(ipbf,c->theirname,30);
		return TRUE;
	}
	strcpy(ipbf,"");
	if (c->sk==INVALID_SOCKET) return FALSE;
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getpeername(c->sk,(struct sockaddr *) us,&len) < 0 ){
  	  kkk_lasterror(c);
          emsg("getip getpeername(%d) failed %s\n",c->sk,kkk_error(c,bf));
          return FALSE;
        }

        strcpy(ipbf,inet_ntoa(us->sin_addr));
        return TRUE;
}
int kkk_getip_raw(SOCKET sk, char *ipbf)
{
        int len;
        struct sockaddr_in usoc, *us;


	strcpy(ipbf,"");
	if (sk==INVALID_SOCKET) return FALSE;
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getpeername(sk,(struct sockaddr *) us,&len) < 0 ){
          return FALSE;
        }

        strcpy(ipbf,inet_ntoa(us->sin_addr));
        return TRUE;
}
int kkk_getmyip(Chan *c, char *ipbf)
{
        int len;
	char bf[2999];
        struct sockaddr_in usoc, *us;

	if (strlen(c->myname)>0) {
		ncpy(ipbf,c->myname,30);
		return TRUE;
	}
	strcpy(ipbf,"");
	if (c->sk==INVALID_SOCKET) return FALSE;
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(c->sk,(struct sockaddr *) us,&len) < 0 ){
  	  kkk_lasterror(c);
          emsg("getmyip getsockname(%d) failed %s\n",c->sk,kkk_error(c,bf));
          return FALSE;
        }

        strcpy(ipbf,inet_ntoa(us->sin_addr));
        return TRUE;
}
int kkk_getmyip_raw(SOCKET sk, char *ipbf)
{
        int len;
        struct sockaddr_in usoc, *us;

	strcpy(ipbf,"");
	if (sk==INVALID_SOCKET) return FALSE;
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(sk,(struct sockaddr *) us,&len) < 0 ){
          return FALSE;
        }

        strcpy(ipbf,inet_ntoa(us->sin_addr));
        return TRUE;
}
int kkk_getports(Chan *c, int *local, int *remote)
{
	int len;
	struct sockaddr_in hsoc, usoc, *him, *us;

	if (c->sk==INVALID_SOCKET) return FALSE;
		him = &hsoc;
		len=sizeof(struct sockaddr_in);
		if ( getpeername(c->sk, (struct sockaddr *) him, &len) < 0 ) {
			kkk_lasterror(c);
			emsg("getpeername failed\n");
			return FALSE;
		}

        /* get the port on our machine he is connecting to */
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(c->sk,(struct sockaddr *) us,&len) < 0 ){
          emsg("getports getsockname() failed \n");
          return FALSE;
        }

        *local = (int) ntohs(us->sin_port);
        *remote = (int) ntohs(him->sin_port);
        return TRUE;
}

int kkk_getport(Chan *c, int *local)
{
        int len;
        struct sockaddr_in usoc, *us;

	if (c->sk==INVALID_SOCKET) return FALSE;

        /* get the port on our machine he is connecting to */
        len=sizeof(struct sockaddr_in);
        us = &usoc;
        if (getsockname(c->sk,(struct sockaddr *) us,&len) < 0 ){
  	  kkk_lasterror(c);
          emsg("getports getsockname() failed \n");
          return FALSE;
        }

        *local = (int) ntohs(us->sin_port);
        return TRUE;
}
#ifdef WIN32
#define HAS_NODELAY TRUE
#else
#ifdef TCP_NODELAY
#define HAS_NODELAY
#endif
#endif
int kkk_nodelay(Chan *c, int x)
{
	int on = x;
	int r;

	if (c->sk==INVALID_SOCKET) return FALSE;
#ifdef HAS_NODELAY
	r = setsockopt(c->sk, IPPROTO_TCP, TCP_NODELAY,(char *) &on, sizeof(int));
#else
	r = 1;
#endif
        kkk_lasterror(c);
	if (r!=0) return FALSE;
	return TRUE;

}

/*kkk_setrcbf_raw: sets minimum buffer size for receives on a socket
  Arguements: c: the socket
			  sz: minimum buffer size in bytes                 */
void kkk_setrcvbf_raw(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);
	if (was>=sz) return;
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_RCVBUF, s, sizeof(xsz));
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_RCVBUF, s, &xlen);

}
char *kkk_hostname(char *host)
{
	static char bf[BFSZ];
	if (strlen(bf)>0) {
		ncpy(host,bf,100);
		return host;
	}
    if (gethostname(host,190)!=0) strcpy(host,"127.0.0.1");
    ncpy(bf,host,BFSZ-1);
	return host;
}
int kkk_setblockraw(SOCKET c, int t)
{
    int w;
    if (t) w=0;
    else w=1;

	if (c==INVALID_SOCKET) return FALSE;
    if (socket_ioctl(c,FIONBIO,&w)!=0) {if (dmsg) dmsg("socket_ioctl failed");return FALSE;}
	return TRUE;
}

int kkk_setblock(Chan *c, int t)
{
	c->block = t;
	if (kkk_setblockraw(c->sk,t)){
		/*if (dmsg != NULL) dmsg("blocking to %d\n", t);*/
		return TRUE;
	}
	if (!kkk_isopen(c)) return FALSE;
	if (dmsg != NULL) dmsg("Cant set blocking to %d\n", t);
	return FALSE;
}


void kkk_setsndbf_raw(SOCKET c, int sz)
{
	int xsz=sz;
	int was;
	int xlen=sizeof(xsz);
	char *s;
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);
	if (was>=sz) return;
	s = (char *) &xsz;
        setsockopt(c, SOL_SOCKET, SO_SNDBUF, s, sizeof(xsz));
	s = (char *) &was;
        getsockopt(c, SOL_SOCKET, SO_SNDBUF, s, &xlen);

}
#ifdef WIN32
char *kkk_error(Chan *c, char *bf)
{
	int err = c->xerrno;
	char *s="";

#ifdef HAS_SSL
	if (c->ssl!=NULL) {
		return myssl_lastmsg(c->ssl);
	}
#endif
        if (err==WSANOTINITIALISED)		lprintf(bf,100,"A successful WSAStartup must occur before using this API.");
        else if (err==WSAENETDOWN)      lprintf(bf,100,"The Windows Sockets implementation has detected that the network subsystem has failed.");
        else if (err==WSAEACCES)        lprintf(bf,100,"The requested address is a broadcast address, but the appropriate flag was not set.");
        else if (err==WSAEINTR)			lprintf(bf,100,"The (blocking) call was canceled via WSACancelBlockingCall.");
        else if (err==WSAEINPROGRESS)   lprintf(bf,100,"A blocking Windows Sockets operation is in progress.");
        else if (err==WSAEFAULT)        lprintf(bf,100,"The buf argument is not in a valid part of the user address space.");
        else if (err==WSAENETRESET)     lprintf(bf,100,"The connection must be reset because the Windows Sockets implementation dropped it.");
        else if (err==WSAENOBUFS)       lprintf(bf,100,"The Windows Sockets implementation reports a buffer deadlock.");
        else if (err==WSAENOTCONN)      lprintf(bf,100,"The socket is not connected.");
        else if (err==WSAENOTSOCK)      lprintf(bf,100,"The descriptor is not a socket.");
        else if (err==WSAEOPNOTSUPP)    lprintf(bf,100,"MSG_OOB was specified, but the socket is not of type SOCK_STREAM.");
        else if (err==WSAESHUTDOWN)     lprintf(bf,100,"The socket has been shutdown; it is not possible to sendln on a socket after shutdown has been invoked with how set to 1 or 2.");
        else if (err==WSAEWOULDBLOCK)   lprintf(bf,100,"The socket is marked as non-blocking and the requested operation would block.");
        else if (err==WSAEMSGSIZE)      lprintf(bf,100,"The socket is of type SOCK_DGRAM, and the datagram is larger than the maximum supported by the Windows Sockets implementation.");
        else if (err==WSAEINVAL)        lprintf(bf,100,"The socket has not been bound with bind.");
        else if (err==WSAECONNABORTED)  lprintf(bf,100,"The virtual circuit was aborted due to timeout or other failure.");
        else if (err==WSAECONNRESET)    lprintf(bf,100,"The virtual circuit was reset by the remote side.");
        else if (err==WSAETOOMANYREFS)  lprintf(bf,100,"Too many refs.");
        else if (err==WSAETIMEDOUT)     lprintf(bf,100,"Timedout");
        else if (err==WSAECONNREFUSED)  lprintf(bf,100,"Connection refused");
        else if (err==WSAEDESTADDRREQ)  lprintf(bf,100,"DestAddrReq");
        else if (err==WSAEMSGSIZE)      lprintf(bf,100,"Message Size");
        else if (err==WSAEADDRINUSE)    lprintf(bf,100,"Address in use");
        else if (err==WSAEADDRNOTAVAIL) lprintf(bf,100,"Address not avail");
        else if (err==WSAENETUNREACH)   lprintf(bf,100,"Network Unreachable");
        else if (err==WSAENETRESET)     lprintf(bf,100,"Network Reset");
        else if (err==WSAENOBUFS)       lprintf(bf,100,"No bufs");
        else if (err==WSAESHUTDOWN)     lprintf(bf,100,"Shutdown");
        else if (err==WSAENAMETOOLONG)  lprintf(bf,100,"Name too long");
        else if (err==WSAEHOSTDOWN)     lprintf(bf,100,"Host down");
        else if (err==WSAEHOSTUNREACH)  lprintf(bf,100,"Host Unreachable");
        else if (err==WSAENOTEMPTY)     lprintf(bf,100,"Not Empty");
        else lprintf(bf,100,"Socket Unkown error code %d ",err);

	if (strlen(c->erradd)>0) {
		strcat(bf," ");
		strcat(bf,c->erradd);
	}
	return bf;
}
#else
char *kkk_error(Chan *c, char *bf)
{
	int err = c->xerrno;
#ifdef HAS_SSL
	if (c->ssl!=NULL) {
		return myssl_lastmsg(c->ssl);
	}
#endif
	ncpy(bf,strerror(c->xerrno),100);
	return bf;
}
#endif


int kkk_getname(char *ipnumber, char *ipname)
{
	struct hostent *hp;
        struct sockaddr_in sin;

	strncpy(ipname,ipnumber,200); ipname[200] = 0;
	memset(&sin,0,sizeof(struct sockaddr_in));
	sin.sin_addr.s_addr = inet_addr(ipnumber);
        hp = gethostbyaddr( (char *) &(sin.sin_addr), sizeof(sin.sin_addr), AF_INET);
        if (hp==NULL) return FALSE;
	if (hp->h_name==NULL) return FALSE;
	strncpy(ipname,hp->h_name,200); ipname[200] = 0;
	return TRUE;
}
void kkk_close_on_exec_raw(int fd)
{
#ifndef WIN32
	if (fd== -1) return;
        if (fcntl(fd,F_SETFD,1)==-1){
                /*imsg("Error doing close on exec fcntl %d %s\n",fd,strerror(errno));*/
        }
#endif
}
void kkk_buffer_set(Chan *c, int x)
{
	c->isbuffer = x;
	if (!c->isbuffer) {
		kkk_buffer_flush(c);
	}
}
void kkk_buffer_flush(Chan *c)
{
	if (c->buffer_len==0) return;
	kkk_write(c,c->buffer,c->buffer_len);
	free(c->buffer); c->buffer = NULL;
	c->buffer_len = 0;
}
int kkk_buffer_add(Chan *c, char *bf, int len)
{
	int newlen;
	newlen = len + c->buffer_len;
	c->buffer = realloc(c->buffer,newlen);
	memcpy(c->buffer + c->buffer_len,bf,len);
	c->buffer_len = newlen;
	return len;
}


void kkk_setlinger(Chan *c, int xon, int hunds)
{
	char tmp[1000];
        struct linger ling;
	if (xon) xon = 1;
	else xon = 0;
        ling.l_onoff = xon;
        ling.l_linger = hunds;
        if (setsockopt(c->sk, SOL_SOCKET, SO_LINGER, (char *) &ling, sizeof(ling))!=0) {
		kkk_lasterror(c);
		emsg("Unable to set socket to linger %s\n",kkk_error(c,tmp));
		printf("Unable to set socket to linger %s\n",kkk_error(c,tmp));
	}
}
/*
typedef struct CLOSELIST {SOCKET sk; time_t whence; struct CLOSELIST *next;} Clist;
int kkk_close_later(SOCKET sk)
{


}
int kkk_idle(void)
{

}
*/

int kkk_handle_count(void)
{
	int i,n=0;
	SOCKET list[700];
	for (i=0; i<700; i++) {
		list[i] = INVALID_SOCKET;
	}
	for (i=0; i<700; i++) {
		list[i] = socket(AF_INET, SOCK_STREAM, 0);
		if (list[i]==INVALID_SOCKET) break;
		n = i+1;
	}
	for (i=0; i<n; i++) {
		if (list[i]!=INVALID_SOCKET) socket_close(list[i]);
		list[i] = INVALID_SOCKET;
	}
	return n;
}

static char kfile[1000];
static char pfile[1000];
int kkk_ssl_setkey_done = FALSE;
int kkk_ssl_setkey(char *pkeyfile, char *keyfile)
{
	strncpy(kfile,keyfile,200);
	strncpy(pfile,pkeyfile,200);
	kkk_ssl_setkey_done = TRUE;
	return TRUE;
}
int kkk_ssl_cmp(Chan *a, Chan *b)
{
#ifdef HAS_SSL
//	return myssl_cmp(a->ssl,b->ssl);
	return 0;
#else
	return 0;
#endif

}
int kkk_ssl_init(Chan *c, int isserver, Chan *inherit, char *reason)
{
#ifdef HAS_SSL
	static int done = FALSE;
	/*if (!kkk_ssl_setkey_done){
		if (dmsg != NULL) dmsg("kkk_ssl_init You have not specified the pfile or kfile with kkk_ssl_setkey call.\n");
		emsg("kkk_ssl_init You have not specified the pfile or kfile with kkk_ssl_setkey call.\n");
		strcpy(reason, "kkk_ssl_init You have not specified the pfile or kfile with kkk_ssl_setkey call.\n");
		return FALSE;
	}*/
	if (!isserver) strcpy(pfile,"");
	if (!isserver) strcpy(kfile,"");

	if (!done){
		if (!myssl_init(pfile,kfile,reason,(int (*)(char *,...))printf,(int (*)(char *,...))emsg))
			{if (dmsg!=NULL) dmsg("kkk_ssl_init failed, myssl init\n"); strcpy(reason,"myssl_init failed.");return FALSE;}
	}
	done = TRUE;
    c->ssl = myssl_takeover(c, c->sk,isserver,inherit==NULL ? NULL : inherit->ssl,reason);
	if (dmsg!=NULL) dmsg("kkk_ssl_init (%s)\n", reason);
    if (c->ssl==NULL) {if (emsg!=NULL) emsg("kkk_ssl_init failed, cant takeover chanel. (%s)\n", reason);return FALSE;}
	if (dmsg!=NULL) dmsg("kkk_ssl_init succeded.\n");
    return TRUE;
#else
	strcpy(reason, "SSL not compiled in this verison.");
	emsg("SSL not compiled in this verison.");
    return FALSE;
#endif
}
int kkk_read_ssl(Chan *c, char *bf, int len)
{
#ifdef HAS_SSL
    int r;
    r = myssl_read(c, c->ssl,bf,len);
	if (r<=0) kkk_lasterror(c);
        return r;
#else
	emsg("SSL call called without SSL compiled.\r\n");
	return -1;
#endif
}
int kkk_write_ssl(Chan *c, char *bf, int len)
{
#ifdef HAS_SSL
        int r;
        r = myssl_write(c, c->ssl,bf,len);
	if (r<=0) kkk_lasterror(c);
        return r;
#else
	return -1;
#endif
}

#ifndef WIN32
int kkk_lib_usleep(int x)
{
#ifndef NONANO
	struct timespec rqtp;
	rqtp.tv_sec = x/1000000;
	rqtp.tv_nsec = (x%1000000)*1000;
	nanosleep(&rqtp,NULL);
#else
	usleep(x);
#endif
}
#endif

void kkk_lib_msleep(int x)
{
#ifdef WIN32
	Sleep(x);
#else
	kkk_lib_usleep(x*1000);
#endif
}


#define xtolower(_c)    ( (_c)-'A'+'a' )
#define xtoupper(_c)    ( (_c)-'a'+'A' )
char *strlwr(char *s)
{
        char *ss=s;
        for (;*s!=0;s++) if (isupper(*s)) if (*s>0) *s = xtolower(*s);
        return ss;
}
