
#ifdef HAS_SSL
#define USE_SOCKETS
#ifndef WIN32
#define SOCKET int
#endif

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

#include "tcplib.h"

#ifdef WIN32
#include <windows.h>
#include <wincrypt.h>
#endif

#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "lprintf.h"
#include "lib.h"

#ifdef DO_THREADS
#include "thread.h"
#endif









#ifdef OPENSSL
#include "encrypt_lib.h"
#include <openssl/rsa.h>       /* SSLeay stuff */
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/crypto.h>
#include <openssl/err.h>
#include <openssl/engine.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h>
#define HAS_DH 1 
#ifndef HAS_DH
#include <openssl/dh.h>
#endif

#else
#include "sslc.h"
#include "cert.h"
#endif
/*#include "debug.h"*/
#include "myssl.h"
#ifdef DO_THREADS
#include "mt_cb.h"
#endif
#include <stdio.h>
#include <stdarg.h>
#ifdef WIN32
#include <direct.h>
#define mkdir _mkdir
#endif
#include "lib.h"
#include "calist.h"
#include "tcplib.h"
#define DMID 24
#include "debug.h"

#ifndef FALSE
#define FALSE 0
#endif

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

#include "ssltrack.h" 
static int web_op=SSL_OP_ALL,mail_op=SSL_OP_ALL,use_web_op=FALSE,use_mail_op=FALSE;
static char ciphers[3000];
static char ciphers_web[3000];
void myssl_free(Myssl *m);
static int debug = FALSE;
static int CA_data_init=0;
//int lib_msleep(int x);

#define UTC_TIME_TYPE 23	/* ASN.1 Code for UTC time */

static BIO *in=NULL;
/* write errors to "bio_err" which is bound to stderr by default */
//static BIO *bio_err=NULL;

static char ssl_dir[BFSZ];

typedef struct ctx_list {
	char *domain_name;
	SSL_CTX *domain_ctx;
	SSL_CTX *web_ctx;
	time_t expire;
	struct ctx_list *next;
} CTX_LIST;

//static  char **domain_names;
//static 	SSL_CTX **domain_ctx=NULL;
static struct ctx_list *domain_contexts=NULL;
static struct ctx_list *client_contexts=NULL;		// list of clients contexts for ssl negotiation requiring client certs
static int disable_renegotiation;

static 	SSL_CTX *client_ctx=NULL;
#define lib_str_hash myssl_lib_str_hash
#ifdef ncpy
#undef ncpy
#endif 
#define ncpy myssl_ncpy
int myssl_error_get(char *msg, char *reason, Myssl *m, int *r);
int myssl_init_all(char *xpriv_file, char *xcert_file, Wordlist *domains, char *reason, int (*z_imsg)(char *args,...), int (*z_emsg)(char *args,...)); 
int myssl_init_client(void);
int myssl_show_errors(void);
void myssl_lib_create_dir(char *s, int mode);
char *myssl_etext(int x);
int myssl_report(char *domain, char *result,int size, int perdom, char *fname);
char *myssl_fname(char *mid, char *bf);
//int myssl_cb(SSL *ssl, SSLCERT *x[], int num, char *arg, long *verify_result);
//void myssl_print_dn(SSLCERT_NAME *xn);
//int myssl_storeit(SSLCERT *x);
int myssl_show_cert(char *subj, void (*zprintf)(char *,...));
static int xdebug = FALSE;
int myssl_storeok(char *subj, char *etext);
char *ncpy(char *dst, char *src, int len);
int lib_str_hash(char *s, int max);
static char ca_path[BFSZ];
static int (*imsg)(char *msg,...);
static int (*emsg)(char *msg,...);
void  myssl_set_noverify(SSL_CTX *ctx);

void setup_dh(SSL_CTX *ctx);
#ifndef MUTEX_TIME
#define MUTEX_TIME 70000
#endif

#ifdef DO_THREADS
static Mutex *m;
#endif
static int donem;

void myssl_mutex_init(void)
{
	donem = TRUE;
#ifdef DO_THREADS
	m = th_mutex_new("myssl_mutex");
#endif
}
int myssl_set_mode(SSL_CTX *ctx);

static time_t mtime;
int myssl_mutex_wait(void)
{
	if (!donem) myssl_mutex_init();
#ifdef DO_THREADS
	if (m==NULL) return TRUE;
	if (!th_mutex_wait(m,MUTEX_TIME)) { /* 10 minutes */
		lib_msleep(15000);
		lib_abort();
	}
#endif
	mtime = time(NULL);
	return TRUE;
}
void myssl_mutex_release(int line)
{
#ifdef DO_THREADS
	int gap;
	if (m==NULL) return;
	gap = (int)(time(NULL) - mtime);
	th_mutex_release(m);
	if (gap>2) emsg("myssl_mutex held for %d seconds.  Line %d\n",gap,line);
#endif
}


int myssl_verify_cb_always(int ok, X509_STORE_CTX *x_ctx){
//printf("myssl_verify_cb_always %d\n",ok);

return 1;
}

static int use_dm=FALSE;
void *myssl_malloc(size_t sz)
{
	if (!use_dm) return simple_malloc(sz,"ssl",__LINE__);
	return d_mallocid(9,sz,"openssl_malloc",__LINE__);
}
void *myssl_realloc(void *ptr, size_t sz)
{
	if (!use_dm) return simple_realloc(ptr,sz,"ssl",__LINE__);
	return d_realloc(ptr,sz,"openssl_realloc",__LINE__);
}
void myssl_mfree(void *ptr)
{
	if (!use_dm) simple_free(ptr,"ssl",__LINE__);
	else d_free(ptr,"openssl_free",__LINE__);
}

void *myssl_malloc2(size_t sz, const char *file, int line)
{
	if (!use_dm) return simple_malloc(sz,(char *)file,line);
	return d_mallocid(9,sz,(char *) file,line);
}
void *myssl_realloc2(void *ptr, size_t sz, const char *file, int line)
{
	if (!use_dm) return simple_realloc(ptr,sz,(char *)file,line);
	return d_realloc(ptr,sz,(char *) file,line);
}
void myssl_mfree2(void *ptr,const  char *file, int line)
{
	if (!use_dm) simple_free(ptr,(char *)file,line);
	else d_free(ptr,(char *) file,line);
}
/*int myssl_mem_trace(void)
{
	FILE *f;
	SSL *ssl;
	//CRYPTO_set_mem_debug(1) ;
	CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);

	ssl = SSL_new(NULL); // TEST A LEAK  

	f = fopen("ssltrace.log","w");
	if (f!=NULL) {
		CRYPTO_mem_leaks_fp(f) ;
		fclose(f);
	}
	return TRUE;
}*/
int myssl_setup_dmalloc(void)
{
#ifndef openssl101
	int r = CRYPTO_set_mem_functions(myssl_malloc2,myssl_realloc2,myssl_mfree2);
#else
	int r = CRYPTO_set_mem_functions(myssl_malloc,myssl_realloc,myssl_mfree);
#endif
	if (r!=1) printf("crypto: setup_dmalloc failed\n");
	return TRUE;
}
int myssl_init_crypto(void)
{
#ifndef openssl101
	OPENSSL_init_crypto(0,NULL);
#endif
	return TRUE;
}

int myssl_verify_cb(int ok, X509_STORE_CTX *x_ctx)
{
	X509 *x;
	char *s;
	char bf[BFSZ];
	X509_NAME *name;
	X509_NAME *nameis;
	char etext[BFSZ];


	if (ok) return ok;

	x = X509_STORE_CTX_get_current_cert(x_ctx);
	nameis = X509_get_issuer_name(x);
	name = X509_get_subject_name(x);
	s = X509_NAME_oneline(name,bf,BFSZ-1);

	strcpy(etext,myssl_etext(X509_STORE_CTX_get_error(x_ctx)));
	X509_STORE_CTX_set_error(x_ctx,X509_V_OK);
	if (myssl_storeok(s,etext)) return 1;
	X509_STORE_CTX_set_error(x_ctx,X509_V_ERR_APPLICATION_VERIFICATION);
	return 0;
}
#ifdef OPENSSL_FIPS
int myssl_init_fips(void)
{
	char errmsg[BFSZ];
	int ret = 0; 
	unsigned long err = 0;
	int xmode = FIPS_mode();

	if (xmode == 0) {
	    ret = FIPS_mode_set(1);
		if (ret != 1) {
	        err = ERR_get_error();
			ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
			imsg("myssl: Fips_mode_set failed, remove g_ssl_fips setting,  %s\n",errmsg);
			lib_abort();
			return FALSE;
		}
		imsg("myssl: Fips_mode_set succeeded\n");
	} else {
		imsg("myssl: FIPS is already enabled\n");
	}
	return TRUE;
}
#else
int myssl_init_fips(void)
{
	imsg("myssl: FIPS mode failed as it is not compiled, remove the setting g_ssl_fips from surgemail.ini!!!!\n");
	lib_abort();
	return FALSE;
}
#endif




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;
}
static int (*confirm_cb)(char *subj, char *info);
int myssl_set_confirm_cb(int (*cb)(char *subj, char *info))
{
		confirm_cb = cb;
        return TRUE;
}
void make_chain_file(char *chain_file,char *cert_file)
{
	char *s;
	ncpy(chain_file,cert_file,BFSZ-1);
	s = strstr(chain_file,"surge_cert.pem");
	if (s!=NULL) {
		*s = 0;
		ncat(chain_file,"surge_chain.pem",BFSZ-1);
	}
}
int myssl_storeok(char *subj, char *etext)
{
	int h;
	char bf[BFSZ];
	char mid[BFSZ];
	char tmp[BFSZ];
	FILE *f;
	int shown = FALSE;
	static int done;

	if (confirm_cb==NULL) if (!done) {myssl_show_cert(subj,(void (*)(char *,...))printf); shown = TRUE;}
	done = TRUE;

	h = lib_str_hash(subj,32000*32000);
	sprintf(mid,"%x",h);
	f = fopen(myssl_fname(mid,tmp),"r");
	if (f!=NULL) {
		fclose(f);
		return 1;
	}

    if (confirm_cb==NULL) {
        if (!shown) myssl_show_cert(subj,(void (*)(char *,...))printf);
	    printf("Warn: %s, Accept [y] ",etext);
	    fgets(bf,100,stdin);
		if (bf[0]=='n') return 0;
    } else {
        if (!confirm_cb(subj,etext)) return 0;
    }

	f = fopen(myssl_fname(mid,tmp),"w");
	if (f==NULL)  {
//		printf("Could not write (%s) %s\n",myssl_fname(mid,tmp),strerror(errno));
		return 1;
	}
	fprintf(f,"%s\n%s\n",subj,etext);
	fclose(f);
	return 1;
}

int myssl_load_ca(char *path)
{
	ncpy(ca_path,path,BFSZ-1);
	return TRUE;
}

int myssl_show_cert(char *subj, void (*zprintf)(char *,...))
{
	char *s;
	char *upto;
	char bf[BFSZ];
	ncpy(bf,subj,BFSZ-1);
	s = mystrtok(bf,"/",&upto);
	zprintf(" Remote Sites Certificate:-\n");
	for (;s!=NULL;s=mystrtok(NULL,"/",&upto)) {
		if (strncmp(s,"C=",2)==0)       zprintf("	Country: %s\n",s+2);
		else if (strncmp(s,"ST=",3)==0) zprintf("	State:   %s\n",s+3);
		else if (strncmp(s,"O=",2)==0)  zprintf("	Org:     %s\n",s+2);
		else if (strncmp(s,"OU=",3)==0) zprintf("	Unit:    %s\n",s+3);
		else if (strncmp(s,"CN=",3)==0) zprintf("	Name:    %s\n",s+3);
		else if (strncmp(s,"L=",2)==0)  zprintf("	City:    %s\n",s+2);
		else if (strncmp(s,"Email=",6)==0) zprintf("	Email:   %s\n",s+6);
		else zprintf("	%s\n",s);
	}
	return TRUE;
}
Wordlist * myssl_get_ciphers(void) // ONLY CALLED BY A TELLMAIL COMMAND, SO DOESN'T MATTER IF IT LEAKS.  
{
	Wordlist *w=NULL;
	CTX_LIST *ctx;
	int num;
	int i;
	char *name;
	STACK_OF(SSL_CIPHER) *sk;
//    jobjectArray array;
    SSL_CIPHER *cipher;

	for(ctx=domain_contexts; ctx; ctx=ctx->next) {
		sk = SSL_CTX_get_ciphers(ctx->domain_ctx);
	    num = sk_SSL_CIPHER_num(sk);
		for (i = 0; i < num; i++) {
			cipher = (SSL_CIPHER*) sk_SSL_CIPHER_value(sk, i);
	        name = (char *) SSL_CIPHER_get_name(cipher);
			word_add(&w,name);
		}
		break;
	}
	return w;
}
void  myssl_verify_set(Myssl *m) {
	CTX_LIST *ctx;

#ifdef SOLARIS
	if (client_ctx!=NULL) SSL_CTX_set_verify(client_ctx,SSL_VERIFY_NONE,myssl_verify_cb);/* no return value for this function */
#else
	if (client_ctx!=NULL) SSL_CTX_set_verify(client_ctx,SSL_VERIFY_PEER,myssl_verify_cb);/* no return value for this function */
#endif
	myssl_mutex_wait();
	for(ctx=domain_contexts; ctx; ctx=ctx->next) {
#ifdef SOLARIS
		SSL_CTX_set_verify(ctx->domain_name,SSL_VERIFY_NONE,myssl_verify_cb);/* no return value for this function */
#else
		SSL_CTX_set_verify(ctx->domain_ctx,SSL_VERIFY_PEER,myssl_verify_cb);/* no return value for this function */
		SSL_CTX_set_verify(ctx->web_ctx,SSL_VERIFY_PEER,myssl_verify_cb);/* no return value for this function */
#endif
	}
	myssl_mutex_release(__LINE__);
}

time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s)
{
    struct tm tm;
    int offset;
    memset(&tm,'\0',sizeof tm);
#define g2(p) (((p)[0]-'0')*10+(p)[1]-'0')
    tm.tm_year=g2(s->data);
    if(tm.tm_year < 50)
        tm.tm_year+=100;
    tm.tm_mon=g2(s->data+2)-1;
    tm.tm_mday=g2(s->data+4);
    tm.tm_hour=g2(s->data+6);
    tm.tm_min=g2(s->data+8);
    tm.tm_sec=g2(s->data+10);
    if(s->data[12] == 'Z'){
        offset=0;
    }else{
        offset=g2(s->data+13)*60+g2(s->data+15);
        if(s->data[12] == '-')
            offset= -offset;
    }
#undef g2
    return mktime(&tm)-offset*60;
}

static int debug_verify_cb(int preverify_ok, X509_STORE_CTX *ctx)
 {
    char    buf[256];
    X509   *err_cert;
    int     err, depth;

    err_cert = X509_STORE_CTX_get_current_cert(ctx);
    err = X509_STORE_CTX_get_error(ctx);
    depth = X509_STORE_CTX_get_error_depth(ctx);

    /*
     * Retrieve the pointer to the SSL of the connection currently treated
     * and the application specific data stored into the SSL object.
     */
    X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
    if (!preverify_ok) {
		imsg("verify: error:num=%d:%s:depth=%d:%s\n", err, X509_verify_cert_error_string(err), depth, buf);
    }
    else 
    {
		//dmsg("verify: depth=%d:%s\n", depth, buf);
    }

    /*
     * At this point, err contains the last verification error. We can use
     * it for something special
     */
    if (!preverify_ok && (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT))
    {
//      X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
 //     printf("issuer= Unable to get issuer cert\n");
    }

     return 1;
 }
void  myssl_set_noverify(SSL_CTX *ctx)
{
	SSL_verify_cb bob = debug_verify_cb;
	int mode = SSL_VERIFY_NONE;  /// need a setting to eanble this... 	
	SSL_CTX_set_verify(ctx,mode,bob);/* no return value for this function */
}

void  myssl_NO_verify_set(Myssl *m)
{
	CTX_LIST *ctx;
	SSL_verify_cb bob = debug_verify_cb;
	int mode = SSL_VERIFY_NONE;  /// need a setting to eanble this... 
	
//	bob =myssl_verify_cb_always ;
	//mode = SSL_VERIFY_NONE;

// should be 	myssl_verify_cb_always
	if (client_ctx!=NULL) myssl_set_noverify(client_ctx);

	myssl_mutex_wait();
	for(ctx=domain_contexts; ctx; ctx=ctx->next) {
		myssl_set_noverify(ctx->domain_ctx);
		myssl_set_noverify(ctx->web_ctx);
	}
	myssl_mutex_release(__LINE__);
}

int get_certinfo(char *file, char *cert, char *issuer, char *altname, char *valid, int maxlen)
{
	FILE *f;
	X509 *c;

	X509_NAME * name;
	ASN1_UTCTIME * expire;
	X509_EXTENSION * ext;
	time_t  tm;
	int ext_id,i,n;
	STACK_OF(GENERAL_NAME) *alt;
    GENERAL_NAME *gn;
//	char *altname;

	if(!(f = fopen(file, "r"))) {
		emsg("Can't open certificate file%s\n", file);
		return FALSE;
	}
	if(!(c = PEM_read_X509(f, NULL, NULL, NULL))) {
		emsg("Can't read certificate %s\n", file);
		fclose(f);
		return FALSE;
	}
	fclose(f);

	name = X509_get_subject_name(c);
	X509_NAME_oneline(name,cert,maxlen-1);

	name=X509_get_issuer_name(c);
	X509_NAME_oneline(name,issuer,maxlen-1);

	expire=X509_get_notAfter(c);
	tm=ASN1_UTCTIME_get(expire);
	safe_ctime(&tm,valid);

//	name=X509_get_alt_name(c);
//	X509_NAME_oneline(name,issuer,maxlen-1);

	// Partially complete awaitign response from COMODO's support desk on implementation
	altname[0]='\0';
 	ext_id = X509_get_ext_by_NID( c, NID_subject_alt_name, -1);
	if (ext_id>=0) {
		ext = X509_get_ext(c, ext_id);
		alt = X509V3_EXT_d2i( ext );
		if (alt){
 			n = sk_GENERAL_NAME_num(alt);
 			for (i=0; i<n; i++) {
 				char *sn;
 				gn = sk_GENERAL_NAME_value(alt, i);
 				if (gn->type == GEN_DNS) {
#ifndef openssl101
					sn = (char *) ASN1_STRING_get0_data(gn->d.ia5);
#else
					sn = (char *) ASN1_STRING_data(gn->d.ia5);
#endif
					if (altname[0]) strcat(altname,",");
					strcat(altname,sn);
				}
			}
		}
	}

	return TRUE;
}


char *myssl_etext(int x)
{
	static char bf[BFSZ];
    switch (x) {
	case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
	    return "Server's certificate is self signed";
	case X509_V_ERR_CERT_HAS_EXPIRED:
	    return "Server's certificate has expired";
	case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
	    return "Server's certificate issuer's certificate isn't available locally";
	case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
	    return "Unable to verify leaf signature";
	case X509_V_ERR_CERT_REVOKED:
	    return "Certificate revoked";
	case X509_V_ERR_CRL_HAS_EXPIRED:
	    return "WARNING: CRL has expired";
	default:
	    sprintf(bf,"Certificate error %d",x);
	    return bf;
    }
}



int myssl_pending(Myssl *m)
{
	/*if (m->wouldblock) return 1;*/
	if (SSL_pending(m->ssl)>0)
		return 1;
	else
		return 0;
/*	emsg("bio pending call\n");
	if ( 1 == BIO_pending((BIO *) m->bio))
	{emsg("bio pending data\n"); return 1;}
	else
	{emsg("bio pending nodata\n");return 0;}*/
}

static int my_honor = FALSE; // if true then use server cipher order.
/*
static int my_ssl_no_TLSv1 = FALSE;
static int my_ssl_no_TLSv1_1 = FALSE;
static int my_ssl_no_TLSv1_2 = FALSE;
static int my_ssl_no_SSLv2 = FALSE;
static int my_ssl_no_SSLv3 = FALSE;
*/
static int myssl_done = FALSE;

void myssl_pre_init_set_protocols(int TLSV1, int SSLV2, int SSLV3, int honor, int TLSV1_1, int TLSV1_2)
{
	int op;
	op = SSL_OP_ALL;

	if (!TLSV1) op = op | SSL_OP_NO_TLSv1;
#ifndef OLDSSL
	if (!TLSV1_1) op = op | SSL_OP_NO_TLSv1_1;
	if (!TLSV1_2) op = op | SSL_OP_NO_TLSv1_2;
#endif
	if (!SSLV2) op = op | SSL_OP_NO_SSLv2;
	if (!SSLV3) op = op | SSL_OP_NO_SSLv3;



//	op = (~SSL_OP_NO_TLSv1_3) & op;
	mail_op = web_op = op;

	my_honor = honor;
}
//void myssl_get_protocols(int *TLSV1, int *SSLV2, int *SSLV3){
	// not used. 

//}

#ifdef openssl101
 int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
 {
    /* If the fields p and g in d are NULL, the corresponding input
     * parameters MUST be non-NULL.  q may remain NULL.
     */
    if ((dh->p == NULL && p == NULL)
        || (dh->g == NULL && g == NULL))
        return 0;

    if (p != NULL) {
        BN_free(dh->p);
        dh->p = p;
    }
    if (q != NULL) {
        BN_free(dh->q);
        dh->q = q;
    }
    if (g != NULL) {
        BN_free(dh->g);
        dh->g = g;
    }

    if (q != NULL) {
        dh->length = BN_num_bits(q);
    }

    return 1;
 }
#endif

void myssl_set_use_protocols_from_now(int TLSV1, int SSLV2, int SSLV3, int honor)
{
	// not used by surgemail anyway...
	myssl_pre_init_set_protocols(TLSV1, SSLV2, SSLV3, honor,  TRUE,TRUE);
}
DH *get_dh2048(void);
DH *get_dh2048(void)
{
	static unsigned char dh2048_p[]={
		0xF6,0x42,0x57,0xB7,0x08,0x7F,0x08,0x17,0x72,0xA2,0xBA,0xD6,
		0xA9,0x42,0xF3,0x05,0xE8,0xF9,0x53,0x11,0x39,0x4F,0xB6,0xF1,
		0x6E,0xB9,0x4B,0x38,0x20,0xDA,0x01,0xA7,0x56,0xA3,0x14,0xE9,
		0x8F,0x40,0x55,0xF3,0xD0,0x07,0xC6,0xCB,0x43,0xA9,0x94,0xAD,
		0xF7,0x4C,0x64,0x86,0x49,0xF8,0x0C,0x83,0xBD,0x65,0xE9,0x17,
		0xD4,0xA1,0xD3,0x50,0xF8,0xF5,0x59,0x5F,0xDC,0x76,0x52,0x4F,
		0x3D,0x3D,0x8D,0xDB,0xCE,0x99,0xE1,0x57,0x92,0x59,0xCD,0xFD,
		0xB8,0xAE,0x74,0x4F,0xC5,0xFC,0x76,0xBC,0x83,0xC5,0x47,0x30,
		0x61,0xCE,0x7C,0xC9,0x66,0xFF,0x15,0xF9,0xBB,0xFD,0x91,0x5E,
		0xC7,0x01,0xAA,0xD3,0x5B,0x9E,0x8D,0xA0,0xA5,0x72,0x3A,0xD4,
		0x1A,0xF0,0xBF,0x46,0x00,0x58,0x2B,0xE5,0xF4,0x88,0xFD,0x58,
		0x4E,0x49,0xDB,0xCD,0x20,0xB4,0x9D,0xE4,0x91,0x07,0x36,0x6B,
		0x33,0x6C,0x38,0x0D,0x45,0x1D,0x0F,0x7C,0x88,0xB3,0x1C,0x7C,
		0x5B,0x2D,0x8E,0xF6,0xF3,0xC9,0x23,0xC0,0x43,0xF0,0xA5,0x5B,
		0x18,0x8D,0x8E,0xBB,0x55,0x8C,0xB8,0x5D,0x38,0xD3,0x34,0xFD,
		0x7C,0x17,0x57,0x43,0xA3,0x1D,0x18,0x6C,0xDE,0x33,0x21,0x2C,
		0xB5,0x2A,0xFF,0x3C,0xE1,0xB1,0x29,0x40,0x18,0x11,0x8D,0x7C,
		0x84,0xA7,0x0A,0x72,0xD6,0x86,0xC4,0x03,0x19,0xC8,0x07,0x29,
		0x7A,0xCA,0x95,0x0C,0xD9,0x96,0x9F,0xAB,0xD0,0x0A,0x50,0x9B,
		0x02,0x46,0xD3,0x08,0x3D,0x66,0xA4,0x5D,0x41,0x9F,0x9C,0x7C,
		0xBD,0x89,0x4B,0x22,0x19,0x26,0xBA,0xAB,0xA2,0x5E,0xC3,0x55,
		0xE9,0x32,0x0B,0x3B,
		};
	static unsigned char dh2048_g[]={
		0x02,
		};
	DH *dh;

	if ((dh=DH_new()) == NULL) return(NULL);
	{
		BIGNUM *p,*g;
		p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
		g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
		if ((p == NULL) || (g == NULL)) { DH_free(dh); return(NULL); }
		DH_set0_pqg(dh,p,NULL,g);
	}
	return(dh);
}
DH *get_dh1024() // openssl dhparam -out dhparam.pem -2 1024 -C
{
        static unsigned char dh1024_p[]={
                0x84,0xFB,0x32,0xB8,0xD6,0x19,0xCC,0x60,0xC6,0x6C,0xB4,0xB1,
                0x5E,0x39,0xCB,0xAE,0x20,0x17,0x8E,0x6B,0x97,0x5A,0xB3,0xEF,
                0xFE,0x69,0x34,0xF0,0xDF,0x77,0x27,0xBF,0x07,0xDB,0x0A,0xE3,
                0x53,0xC4,0xFF,0x25,0xA9,0xF7,0x11,0xAA,0xDB,0x9C,0x15,0xB7,
                0xCC,0x94,0x11,0xD6,0xE7,0x7F,0x44,0xBB,0xDA,0x93,0x95,0x2C,
                0xF6,0x6F,0x4C,0x52,0x74,0xC9,0xF5,0xE7,0xAB,0xEA,0x76,0x3D,
                0x5D,0x2E,0xF2,0x2E,0xCE,0x41,0xC6,0xB9,0xA1,0x4A,0x72,0x63,
                0x56,0xF4,0x30,0xF4,0xEC,0x11,0xD7,0xDC,0x13,0xB6,0xF5,0x5E,
                0x2D,0x2C,0x59,0x81,0x34,0x56,0x9D,0x8A,0x83,0xCF,0x61,0xAE,
                0x19,0xE1,0x76,0xCF,0x32,0x0B,0x4D,0x23,0xFF,0x44,0xD0,0xB2,
                0x35,0x55,0x4C,0xB4,0x79,0x84,0x03,0x43,
                };
        static unsigned char dh1024_g[]={
                0x02,
                };
        DH *dh;

        if ((dh=DH_new()) == NULL) return(NULL);
	{
		BIGNUM *p,*g;
		p=BN_bin2bn(dh1024_p,sizeof(dh1024_p),NULL);
		g=BN_bin2bn(dh1024_g,sizeof(dh1024_g),NULL);
		if ((p == NULL) || (g == NULL)) { DH_free(dh); return(NULL); }
		DH_set0_pqg(dh,p,NULL,g);
	}
	return(dh);
}
void setup_dh(SSL_CTX *ctx)
{
//	DH *dh = get_dh1024 ();
	DH *dh = get_dh2048 ();
	if (1 != SSL_CTX_set_tmp_dh (ctx, dh)) {
		emsg("setup_dh failed\n");
	}
	DH_free (dh);
}



void setup_ecdh(SSL_CTX *ctx)
{
//  SSL_CTX_set_tmp_ecdh_callback(ctx,ssl_callback_TmpECDH);
//   SSL_CTX_set_tmp_ecdh(ctx,

	EC_KEY *ecdh = EC_KEY_new_by_curve_name (NID_X9_62_prime256v1);
	if (! ecdh) {emsg("ecdh failed to create by curve name"); return;}
	if (1 != SSL_CTX_set_tmp_ecdh (ctx, ecdh))
	{emsg("ecdh failed to set ecdh"); return;}
	EC_KEY_free (ecdh);
}



char *myssl_get_protocol_name_used(Myssl *m){
	return (char *) SSL_get_version((SSL*) m->ssl);
}

int myssl_setdir(char *dir) {
	char *ptr;

	for(ptr = ncpy(ssl_dir,dir,BFSZ-1); *ptr; ptr++);
	if (ptr == dir) {
		return TRUE;
	}
	if (ptr[-1] != '/' && ptr[-1] != '\\') {
		*ptr++ = '/';
		*ptr = '\0';
	}
	return TRUE;
}

int myssl_set_ciphers(char *xciphers)
{
	FILE *f;
	ncpy(ciphers,xciphers,2000);
	ncpy(ciphers_web,xciphers,2000);
	f = fopen("ciphers.txt","w");
	if (f!=NULL) {
		fprintf(f,"%s\n",ciphers);
		fclose(f);
	}
	return TRUE;
}
int myssl_set_ciphers_web(char *xciphers)
{
	FILE *f;

	if (lib_strlen(xciphers)>0) {
		ncpy(ciphers_web,xciphers,2000);
		f = fopen("ciphers_web.txt","w");
		if (f!=NULL) {
			fprintf(f,"%s\n",ciphers_web);
			fclose(f);
		}
	}
	return TRUE;
}
 enum { 
        RENEG_INIT = 0, /* Before initial handshake */
        RENEG_REJECT, /* After initial handshake; any client-initiated
                       * renegotiation should be rejected */
        RENEG_ALLOW, /* A server-initated renegotiation is taking
                      * place (as dictated by configuration) */
        RENEG_ABORT /* Renegotiation initiated by client, abort the
                     * connection */
    };
void ssl_callback_Info(const SSL *ssl, int w, int rc)
{
#ifdef openssl101

	Myssl *my;
//	char bf[BFSZ];

	if (!disable_renegotiation) return;

    if ((my = (Myssl *)SSL_get_app_data((SSL *)ssl)) == NULL) {
        return;
    }
    if ((w & SSL_CB_ACCEPT_LOOP) && my->state >0) {
        int state = SSL_get_state(ssl);       
        if (state == SSL3_ST_SR_CLNT_HELLO_A 
            || state == SSL23_ST_SR_CLNT_HELLO_A) {
            my->state++; // = RENEG_ABORT;
			if (my->state>3) {			
				lib_msleep(3000);
				if (imsg) imsg("ssl: renegotiation delay to prevent dos");			
				/*
					lprintf(bf,BFSZ-1,"\r\nSorry Renegotiating connection not permitted\r\n");
					SSL_write(ssl,bf,lib_strlen(bf));
					SSL_shutdown(ssl);
				*/
			}
        }
    } else if ((w & SSL_CB_HANDSHAKE_DONE) && my->state == 0) {
        my->state = 1;
    }
#endif
}
char *myssl_show_text(char *bf, int op)
{
	if (SSL_OP_NO_SSLv2==0) {
		lprintf(bf,BFSZ-1,"%s%s%s%s%s"
								,op & SSL_OP_NO_SSLv3 ? "" : "SSLV3,"
								,op & SSL_OP_NO_TLSv1 ? "" : "TLSV1,"
								,op & SSL_OP_NO_TLSv1_1 ? "" : "TLSV1.1,"
								,op & SSL_OP_NO_TLSv1_2 ? "" : "TLSV1.2,"
#ifdef SSL_OP_NO_TLSv1_3
								,op & SSL_OP_NO_TLSv1_3 ? "" : "TLSV1.3"
#else
								, ""
#endif
			);
	} else {
		lprintf(bf,BFSZ-1,"%s%s%s%s%s%s",op & SSL_OP_NO_SSLv2 ? "" : "SSLV2," // flag has zero value as was removed from openssl, so this won't display correctly. but can not be true anyway.
								,op & SSL_OP_NO_SSLv3 ? "" : "SSLV3,"
								,op & SSL_OP_NO_TLSv1 ? "" : "TLSV1,"
								,op & SSL_OP_NO_TLSv1_1 ? "" : "TLSV1.1,"
								,op & SSL_OP_NO_TLSv1_2 ? "" : "TLSV1.2,"
#ifdef SSL_OP_NO_TLSv1_3
								,op & SSL_OP_NO_TLSv1_3 ? "" : "TLSV1.3"
#else
								,""
#endif 
			);
	} 
	return bf;
}
char * myssl_show_protocols(char *bf)
{
	char tmp1[BFSZ];
	char tmp2[BFSZ];
	lprintf(bf,BFSZ-1,"mail=(%s) web=(%s)",myssl_show_text(tmp1,mail_op),myssl_show_text(tmp2,web_op) );
	return bf;
}
int myssl_disable(char *list, int webonly, char *reason)
{
	int ret=TRUE;  // don't use imsg, this is called too early...
	char *s;
	char bf[BFSZ];
	char *upto=NULL;
	int op = SSL_OP_ALL;
	strcpy(reason,"");
	ncpy(bf,list,BFSZ-1);
//printf("myssl_disablea %d (%s)\n",webonly,list);
	if (lib_strlen(bf)==0) return TRUE;

	//op = (~SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION) & op;



//printf("myssl_disableb %d (%s)\n",webonly,list);
	for (s = mystrtok(bf," ,",&upto);s!=NULL;s=mystrtok(NULL," ,",&upto)) {
		if (strcmpnc(s,"tlsv1")==0) op = op | SSL_OP_NO_TLSv1;
#ifndef OLDSSL
		else if (strcmpnc(s,"tlsv1_1")==0) op = op | SSL_OP_NO_TLSv1_1;
		else if (strcmpnc(s,"tlsv1_2")==0) op = op | SSL_OP_NO_TLSv1_2;
#ifdef SSL_OP_NO_TLSv1_3
		else if (strcmpnc(s,"tlsv1_3")==0) op = op | SSL_OP_NO_TLSv1_3;
#endif
#endif
		else if (strcmpnc(s,"sslv2")==0) {
			op = op | SSL_OP_NO_SSLv2;
		}
		else if (strcmpnc(s,"sslv3")==0) op = op | SSL_OP_NO_SSLv3;
		else { ncat(reason,s,BFSZ-1), ncat(reason," ",BFSZ-1);  ret=FALSE;}
	}
	web_op = op;
	if (!webonly) mail_op = op;  
	if (webonly) use_web_op = TRUE;
	else {use_web_op = TRUE; use_mail_op = TRUE;}

	return ret;
}
void myssl_disable_renegotiation( int x)
{
	if (imsg) imsg("ssl: disable_renegotiation is %d\n",x);
	disable_renegotiation = x;
}
void init_ctx(SSL_CTX *ctx, int isweb)
{

	long op;
	SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
	setup_dh(ctx);
#ifndef NO_ECDH
	setup_ecdh(ctx);
#endif

#ifdef SSL_OP_NO_RENEGOTIATION
	if (disable_renegotiation) {
		mail_op |= SSL_OP_NO_RENEGOTIATION;
		web_op |= SSL_OP_NO_RENEGOTIATION;
	}
#endif 
	op = mail_op;

	if (use_mail_op) op = mail_op;
	if (isweb) if (use_web_op) op = web_op;

	if (my_honor) op = op | SSL_OP_CIPHER_SERVER_PREFERENCE;

	// disable renegotiation I think:
	op = op | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;

	//imsg("init_ctx: set options %x\n",op);


	SSL_CTX_set_options(ctx,op);

//	SSL_CTX_set_options(ctx,SSL_OP_NO_RENEGOTIATION);
//	SSL_CTX_clear_options(ctx,SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION);

	myssl_set_mode(ctx);
}
int myssl_set_mode(SSL_CTX *ctx)
{
#ifndef openssl101
#ifndef is_osx
	SSL_CTX_set_mode(ctx,SSL_MODE_RELEASE_BUFFERS);
#endif 
	// enable old bad ciphers if listed. 
#endif
	return TRUE;
}

int myssl_init_common(char *reason, int (*z_imsg)(char *args,...), int (*z_emsg)(char *args,...)) {
	if (myssl_done) return -TRUE;
	myssl_done = TRUE;

	imsg = z_imsg;
	emsg = z_emsg;

	/* Setup error reporting */
	//o_err=BIO_new_fp(stderr,BIO_CLOSE);

	/* load the error text strings for better error reporting */

	/* initialise the library */

#if OPENSSL_VERSION_NUMBER < 0x10100000L
	SSL_library_init();
#else
	OPENSSL_init_ssl(0, NULL);
#endif
	SSL_load_error_strings();

#ifdef DO_THREADS
	APP_thread_trace(FALSE);
	APP_locking_cb_add();
	APP_thread_initialize(1000); /* Hopefully this is enough, I don't think this is used */
#endif



#ifdef WIN32
//	MW: 20/7/03	 Remove this for now and see if it still works OK This function should not be used
//	on servers and it slows down startup by up to 10 senconds...
//	RAND_screen();
#endif

	if (lib_exists("random_data.dat")) {
		char *data;
		int data_len;
		if (lib_file_read("random_data.dat",&data,&data_len,10000000)) {
			imsg("Using %d bytes of random data from random_data.dat\n",data_len);
			RAND_seed(data,data_len);
			free(data);
		}
	}

	return TRUE;
}
int (*fix_domain)(char *s);

void myssl_set_fix_domain(int (*fn)(char *s))
{
	fix_domain = fn;
}


#ifndef NO_SNI
static int sni_cb(SSL *s, int *ad, void *arg) { // arg is undefined...
  const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
  CTX_LIST *ctx;
  int r=0;
  if (servername) {
	  char sname[BFSZ];
	  ncpy(sname,(char *) servername,BFSZ-1);
	  if (fix_domain!=NULL) fix_domain(sname);
	  //imsg("sni_cb: got name (%s) (%s)\n",servername,sname);	  
 		myssl_mutex_wait();
		for(ctx=domain_contexts,r=0; ctx; ctx=ctx->next,r++) {
		if (strcmpnc(ctx->domain_name,sname) == 0) {
			//imsg("sni: setting context for domain (%s)\n",sname);  must not write log lines with mutex.
			SSL_set_SSL_CTX(s,ctx->domain_ctx);
			break;
		}
		}
		myssl_mutex_release(__LINE__);

	  // Ok find and set context... 
  } // else printf("sni_cb: failed\n");
  //  *reinterpret_cast<bool*>(arg) = true;
 

  return SSL_TLSEXT_ERR_OK;
}
static int sni_cb_web(SSL *s, int *ad, void *arg) { // arg is undefined...
  const char* servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
  CTX_LIST *ctx;
  int r=0;
  if (servername) {
	  char sname[BFSZ];
	  ncpy(sname,(char *) servername,BFSZ-1);
	  if (fix_domain!=NULL) fix_domain(sname);
	  //imsg("sni_cb: got name (%s) (%s)\n",servername,sname);	  
 //printf("sni_cb: got name (%s) (%s)\n",servername,sname);	  
		myssl_mutex_wait();
		for(ctx=domain_contexts,r=0; ctx; ctx=ctx->next,r++) {
//printf("sni: compare domain (%s)\n",sname,ctx->domain_name); 
			if (strcmpnc(ctx->domain_name,sname) == 0) {
				//imsg("sni: setting context for domain (%s)\n",sname); mutex held, no logging..
//	printf("sni: setting context for domain (%s)\n",sname); 
				SSL_set_SSL_CTX(s,ctx->web_ctx);
				break;
			}
		}
		myssl_mutex_release(__LINE__);
	  // Ok find and set context... 
  }  else imsg("sni_cb: failed\n");
  //  *reinterpret_cast<bool*>(arg) = true;

  return SSL_TLSEXT_ERR_OK;
}
#endif

int myssl_use_certificate(SSL_CTX *ctx, char *cert_file, char *priv_file , int isweb, char *reason)
{
	int got_chain = FALSE;
	char chain_file[BFSZ];
	char errmsg[BFSZ];
	int used_chain = FALSE;
	strcpy(errmsg,"");
	imsg("myssl: myssl_use_certificate %s\n",cert_file);
	//printf("myssl: myssl_use_certificate %s\n",cert_file);
	make_chain_file(chain_file,cert_file);
	
		/* load the server certificate from the file */
		if (!SSL_CTX_use_certificate_file(ctx,cert_file,SSL_FILETYPE_PEM)) {
#ifdef OPENSSL
			ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
#endif
			lprintf(reason,BFSZ-2,"use_certificate_file failed (%s)(%s)", cert_file,errmsg);
			goto err;
		}
		{
			if (lib_exists(chain_file)) {
				imsg("myssl: myssl_use_certificate_chain %s\n",chain_file);
				if (!SSL_CTX_use_certificate_chain_file(ctx,chain_file)) {
					ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
					lprintf(reason,BFSZ-2,"use_certificate_chain_file failed (%s)", chain_file);
					goto err;
				} else {
					got_chain = TRUE;
					used_chain = TRUE;
				}
			}
		}
		if (!got_chain) {
			imsg("myssl: reading certificate chain file %s\n",cert_file);
			if (!SSL_CTX_use_certificate_chain_file(ctx,cert_file)) {
#ifdef OPENSSL
				ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
#endif
				lprintf(reason,BFSZ-2,"use_certificate_chain_file failed (%s)", cert_file);
				goto err;
			}
		}

		/* load the server private key from the file */
		if (!SSL_CTX_use_PrivateKey_file(ctx,priv_file,SSL_FILETYPE_PEM)) {
			ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
			lprintf(reason,BFSZ-2,"use_privatekey_file failed (%s)(%s) ",priv_file,errmsg);
			if (used_chain) lpcat(reason,BFSZ-2," Try removing %s",chain_file);
			imsg("ssl: Load failed: %s %s\n",errmsg,reason);
			// try another order...
			if (!SSL_CTX_use_certificate_chain_file(ctx,cert_file)) {
				ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
				lprintf(reason,BFSZ-2,"use_certificate_chain_file3 failed (%s)", cert_file);
				goto err;
			}
			if (!SSL_CTX_use_PrivateKey_file(ctx,priv_file,SSL_FILETYPE_PEM)) {
				ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
				lprintf(reason,BFSZ-2,"use_privatekey_file3 failed (%s)(%s) ",priv_file,errmsg);
				goto err;
			}
			lprintf(reason,BFSZ-1,"Worked, but please remove %s",chain_file);
		}

		/* Check private key matches certificate */
		if (!SSL_CTX_check_private_key(ctx)) {
#ifdef OPENSSL
			ERR_error_string_n(ERR_get_error(),errmsg,BFSZ-1);
#endif

			lprintf(reason,BFSZ-2,"check private key failed (%s)",errmsg);
			goto err;
		}
	return TRUE;
err:
	return FALSE;
}
int myssl_init_domains(char *xpriv_file, char *xcert_file, Wordlist *domains, char *reason, int (*z_imsg)(char *args,...), int (*z_emsg)(char *args,...)) 
{
	return myssl_init_all(xpriv_file,xcert_file,domains,reason,z_imsg,z_emsg);
}

static Wordlist *alldomains;
static char privname[BFSZ];
static char certname[BFSZ];
static char primary[BFSZ];
int myssl_set_primary(char *s)
{
	ncpy(primary,s,BFSZ-1);
	return TRUE;
}

CTX_LIST *myssl_reload_all(char *reason) ;
int myssl_init_all(char *xpriv_file, char *xcert_file, Wordlist *domains, char *reason, int (*z_imsg)(char *args,...), int (*z_emsg)(char *args,...)) 
{
	strcpy(reason,"nofaults");
	if (xdebug) printf("myssl_init_all 1\n");
	myssl_mutex_init();
	if (xdebug) printf("myssl_init_all 2\n");
	if (!myssl_init_common(reason,z_imsg,z_emsg)) return FALSE;
	if (xdebug) printf("myssl_init_all 3\n");

	myssl_init_client();
	if (xdebug) printf("myssl_init_all 4\n");
	// Save the list of domains, and the priv/cert filenames
	myssl_set_domains(domains);
	if (xdebug) printf("myssl_init_all 5\n");
	myssl_mutex_wait();
	if (xdebug) printf("myssl_init_all 6\n");
	ncpy(privname,xpriv_file,BFSZ-1);
	ncpy(certname,xcert_file,BFSZ-1);
	myssl_mutex_release(__LINE__);

	// reload the ssl based on that list.	
	if (xdebug) printf("myssl_init_all 7\n");
	myssl_do_reload(reason); 
	if (xdebug) printf("myssl_init_all 8\n");
	return TRUE;
}
int myssl_set_domains(Wordlist *domains)
{
	myssl_mutex_wait();
	word_copy(&alldomains,domains);		
	myssl_mutex_release(__LINE__);
	return TRUE;
}

CTX_LIST *myssl_reload_all(char *reason) ;
void myssl_ctx_free(CTX_LIST *list)
{
	CTX_LIST *c,*next;
	for (c=list;c!=NULL;c=next) {
		next = c->next;
		SSL_CTX_free(c->domain_ctx);
		if (c->web_ctx!=NULL) SSL_CTX_free(c->web_ctx);
		c->domain_ctx = NULL;
		c->web_ctx = NULL;
		free(c); 
	}
}
int myssl_exit(void)
{
	CTX_LIST *old;
	myssl_mutex_wait();
	old = domain_contexts; 
	domain_contexts = NULL; // replace it
	myssl_ctx_free(old); // free the old one, so we must hold mutex while accessing 'allctx'...
	old = client_contexts;
	client_contexts = NULL;
	myssl_ctx_free(old); // free the old one, so we must hold mutex while accessing 'allctx'...
	if (client_ctx!=NULL) SSL_CTX_free(client_ctx); client_ctx = NULL;

	SSL_COMP_free_compression_methods();

	FIPS_mode_set(0);
	CRYPTO_set_locking_callback(NULL);
	CRYPTO_set_id_callback(NULL);
	ENGINE_cleanup();
	CONF_modules_unload(1);
	ERR_free_strings();
	EVP_cleanup();
	CRYPTO_cleanup_all_ex_data();
	ERR_free_strings(); 
#ifdef openssl101
	//ERR_remove_state(0);
	ERR_remove_thread_state(0); // these two MUST be last or it will leak memory
#endif
#ifndef openssl101
	OPENSSL_cleanup();
#endif 
	myssl_mutex_release(__LINE__);
	return TRUE;
}
int myssl_cleanup(void)
{
	int r;
	for (;r=ERR_get_error();) {
		if (r==0) break;
	}
	ERR_clear_error();
//	ERR_remove_state(0); // leaks error state otherwise, stupid buggy thing
//	ERR_remove_thread_state(0);
	return 0;
}
int myssl_exit_thread(void)
{
	myssl_cleanup();
#ifndef openssl101
	CRYPTO_cleanup_all_ex_data();  // crashes on pre openssl 11 builds...
#endif 

	ERR_clear_error();
// chrisp removing remove-state incase it's buggy???
//	ERR_remove_state(0); // leaks error state otherwise, stupid buggy thing
#ifdef openssl101
	ERR_remove_thread_state(NULL);
#endif
	// in api 1.1, this function must be called last...
#ifndef openssl101
	OPENSSL_thread_stop();
#endif 
	return TRUE;
}
int myssl_do_reload(char *reason)  // safe to call to update the contexts...
{
	CTX_LIST *ctxlist,*old;
	strcpy(reason,"");
	if (xdebug) printf("myssl_init_reload 1\n");
	myssl_mutex_wait();
	if (xdebug) printf("myssl_init_reload 2a\n");
	ctxlist = myssl_reload_all(reason);
	if (xdebug) printf("myssl_init_reload 3b\n");
	old = domain_contexts; 
	if (xdebug) printf("myssl_init_reload 4\n");
	domain_contexts = ctxlist; // replace it
	if (xdebug) printf("myssl_init_reload 5\n");
	myssl_ctx_free(old); // free the old one, so we must hold mutex while accessing 'allctx'...
	if (xdebug) printf("myssl_init_reload 6\n");
	myssl_mutex_release(__LINE__);
	return TRUE;
}
char *myssl_cert_file(char *tmp, char *domain, char *fname)
{
	lprintf(tmp,BFSZ-1,"%s/%s/%s",ssl_dir,domain,fname);
	return tmp;
}
CTX_LIST *myssl_reload_all(char *reason) 
{
	char priv_file[BFSZ],cert_file[BFSZ];//,host_domain[BFSZ];
	CTX_LIST *nctx,*list;
	Wordlist *dptr;
	list = NULL;
	strcpy(reason,"");

	if (xdebug) printf("reload 1.x\n");
	
	for(dptr = alldomains; dptr; dptr = dptr->next){
		

		nctx = (CTX_LIST*)calloc(1,sizeof(CTX_LIST));
		if (!nctx) {
			ncpy(reason,"Out of memory (new domain_context)",BFSZ-1);
			return NULL;
		}
		if (xdebug) printf("reload 2 %s\n",dptr->word);

		nctx->domain_name = strdup(dptr->word);
		lprintf(priv_file,BFSZ-1,"%s%s/%s",ssl_dir,dptr->word,privname);
		if (strcmp(dptr->word,"none")==0) lprintf(priv_file,BFSZ-1,"%s%s",ssl_dir,privname);
		imsg("ssl: loading (%s) (%s)\n",dptr->word,priv_file);

#ifdef OPENSSL
		if (!lib_exists(priv_file)) {
			int bits=2048;
			int rr;
			if (RAND_status()!=1) {
				emsg("Warning - insufficient random data available. Private key may not be secure. To supply random data create a file called random_data.dat in the surgemail directory containing random data.\n");
				if (xdebug) printf("NO RANDOM DATA\n");
			}
			imsg("Creating %d bit private key...\n",bits);
			lib_create_dir(priv_file,0700);
			if (xdebug) printf("reload 3aa\n");
			rr = encrypt_lib_create_private_key(priv_file,bits,NULL);
			if (xdebug) printf("reload 3ab\n");
			if (!rr) emsg("FAILED Created private key file (%s) %d\n",priv_file,rr);
			emsg("Created private key file (%s) %d\n",priv_file,rr);
		}
		lprintf(cert_file,BFSZ-1,"%s%s/%s",ssl_dir,dptr->word,certname);
		if (strcmp(dptr->word,"none")==0) {
			lprintf(cert_file,BFSZ-1,"%s%s",ssl_dir,certname);
		}
		if (!lib_exists(cert_file)) {
			char *url = dptr->word;
			if (strcmp(url,"none")==0) url = primary;
			lib_create_dir(cert_file,0700);
			if (xdebug) printf("reload 3bb\n");
			encrypt_lib_create_certificate(cert_file,priv_file,NULL,url,365*10);
			emsg("Created certificate file (%s)\n",cert_file);
		}
#endif
		if (xdebug) printf("reload 3 %s\n",dptr->word);
		nctx->domain_ctx = SSL_CTX_new(SSLv23_server_method());
		nctx->web_ctx = SSL_CTX_new(SSLv23_server_method());
		if (xdebug) printf("reload 4 %s\n",dptr->word);
		if (!myssl_use_certificate(nctx->domain_ctx,cert_file,priv_file,FALSE,reason)) goto err;
		if (!myssl_use_certificate(nctx->web_ctx,cert_file,priv_file,TRUE,reason)) goto err;

/*	host_domain is undefined, and domain_ctx is not used so this code is rubbish
	if (strcmp(nctx->domain_name,host_domain)==0) {
			imsg("Default domain certificate (%s)\n",host_domain);
			nctx->domain_ctx;
		}
*/
		// Give it a cipher list if we have one;

	//	SSL_CTX_set_security_level(nctx->domain_ctx, 0);
	//	SSL_CTX_set_security_level(nctx->web_ctx, 0);

		if (lib_strlen(ciphers)!=0) {

			if (!SSL_CTX_set_cipher_list(nctx->domain_ctx, ciphers)) {
				imsg("No valid ciphers specified (%s)\n",ciphers);
			}

		}
		if (lib_strlen(ciphers_web)!=0) {
			if (!SSL_CTX_set_cipher_list(nctx->web_ctx, ciphers_web)) {
				imsg("No valid ciphers specified (%s)\n",ciphers_web);
			}
		}
		if (xdebug) printf("reload 7 %s\n",dptr->word);

#ifndef NO_SNI
		SSL_CTX_set_tlsext_servername_callback(nctx->domain_ctx, sni_cb);
		SSL_CTX_set_tlsext_servername_callback(nctx->web_ctx, sni_cb_web);
#endif
		init_ctx(nctx->domain_ctx,FALSE); // SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
		init_ctx(nctx->web_ctx,TRUE); // SSL_CTX_set_info_callback(ctx, ssl_callback_Info);
		nctx->next=list;
		list=nctx;
		if (xdebug) printf("reload loop \n");
		continue;
err:
		imsg("SSL init domains failed {%s} (%s)\n",nctx->domain_name,reason);
		emsg("SSL init domains failed (%s) (%s)\n",nctx->domain_name,reason);
		
		if (xdebug) printf("reload 8 \n");
		SSL_CTX_free(nctx->domain_ctx);
		if (nctx->web_ctx!=NULL) SSL_CTX_free(nctx->web_ctx);
		free(nctx->domain_name);
	}
	imsg("SSL init completed OK\n");
	return list;
}



int myssl_init_client(void)
{
	static int done;
	if (done) return TRUE;
	done = TRUE;
	/* Allow sslv3 and v2 */
	if (client_ctx!=NULL) SSL_CTX_free(client_ctx);
	client_ctx=SSL_CTX_new(SSLv23_client_method());
	myssl_set_noverify(client_ctx);

	/* Enable all vendor bug compatibility options.  To disable TLS,
	 * 'or' in SSL_OP_NO_TLSv1 */

	{	long op;
		op = mail_op;
		if (use_mail_op) op = mail_op;
		if (my_honor) op = op | SSL_OP_CIPHER_SERVER_PREFERENCE;
		op = op | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
		
		SSL_CTX_set_options(client_ctx,op);
		myssl_set_mode(client_ctx);

		if (client_ctx) if (lib_strlen(ciphers)!=0) {
			if (!SSL_CTX_set_cipher_list(client_ctx, ciphers)) {
				imsg("No valid ciphers specified (%s)\n",ciphers);
			}
		}
	}
	if (!SSL_CTX_load_verify_locations(client_ctx,"caroot.pem",NULL)) {
	}


	return TRUE;
}
int myssl_init(char *xpriv_file, char *xcert_file, char *reason, int (*z_imsg)(char *args,...), int (*z_emsg)(char *args,...)) 
{
	int r;
	Wordlist *wlist=NULL;
	word_add(&wlist,"none");
	r = myssl_init_all(xpriv_file,xcert_file,wlist,reason,z_imsg,z_emsg);
	word_free(&wlist);
	return r;
}
int myssl_reload_ctx(char *reason)
{
  return true;
}

/*  return values	1 = no error, carry on
					2 = try again
					3 = abandon connection
					4 = check socket still alive, retry or "die with socket".
					5 = SOLARIS ssl error, trya again  */
int handle_ssl_error_case(Myssl *m, int return_value, char *action, int *wantread){
	int d, r, i;
	const char *s;
	*wantread = FALSE;
	r = return_value;
	switch (d=SSL_get_error(m->ssl, r)){
		case SSL_ERROR_NONE:
			imsg("SSL no errors on %s SSL_ERROR_NONE \r\n", action);
			return 2;
		case SSL_ERROR_SSL:
			imsg("SSL error on %s SSL_ERROR_SSL  (Handshake failure)\r\n", action);
			return 3;
		case SSL_ERROR_WANT_READ:
			*wantread = TRUE;
			s = SSL_rstate_string_long(m->ssl);
			if (strncmp(s,"read ",5)!=0)
				imsg("SSL error on %s SSL_ERROR_WANT_READ (%s)\r\n", action, SSL_rstate_string_long(m->ssl));
			if (debug) imsg("SSL, want read (remove when finished debugging)\n");
			return 6; /* Want read specifically */
		case SSL_ERROR_WANT_WRITE:
			s = SSL_rstate_string_long(m->ssl);
			if (strncmp(s,"read ",5)!=0)
				imsg("SSL error on %s SSL_ERROR_WANT_WRITE (%s)\r\n", action, s);
			if (debug) imsg("SSL, want write (remove when finished debugging)\n");
			return 2;
		case SSL_ERROR_WANT_CONNECT:
			imsg("SSL error on %s SSL_ERROR_WANT_CONNECT\r\n",action);
			return 3;
		case SSL_ERROR_ZERO_RETURN:
			imsg("SSL error on %s SSL_ERROR_ZERO_RETURN\r\n", action);
			return 4;
		case SSL_ERROR_WANT_X509_LOOKUP:

			i = SSL_get_verify_mode(m->ssl);
			if ((i & SSL_VERIFY_NONE) == SSL_VERIFY_NONE){
				imsg("SSL error on %s SSL_ERROR_WANT_X509_LOOKUP  SSL_VERIFY_NONE?? (i==%d) dropping connection.\r\n", action, i);
				return 3;
			}
			imsg("SSL error on %s SSL_ERROR_WANT_X509_LOOKUP  (client does not have x509 cert?, dropping cert requirement...)\r\n", action);

// MW... Todo: implement check based upon some SSL_VERSION flag
#ifdef __NEW_SSL
#pragma message( "__NEW_SSL should not yet be defined" )
#endif
#ifdef is_osx
#define __NEW_SSL
#endif
#ifdef is_linux
#define __NEW_SSL
#endif
#ifdef is_freebsd
#define __NEW_SSL
#endif
#ifdef is_solaris
#ifdef OPENSSL
#define __NEW_SSL
#endif
#endif
#ifdef DO_IOS
#ifdef OPENSSL
#define __NEW_SSL
#endif
#endif
#ifdef WIN32
#ifdef OPENSSL
#define __NEW_SSL
#endif
#endif
#ifdef __NEW_SSL
			SSL_set_verify(m->ssl, SSL_VERIFY_NONE, NULL); /*ssl on linux mork is newer version? */
#else
			SSL_set_verify(m->ssl, SSL_VERIFY_NONE);
#endif
			return 2;
		case SSL_ERROR_SYSCALL:
			//imsg("SSL, SYSCALL error (remove when done) d=%d %s\n",d,strerror(errno));
			/* 5 = General input output error, socket closed I expect */
#ifndef WIN32
			if (errno==EBUSY || errno==EAGAIN || errno==EWOULDBLOCK ) {
					return 2;
			}
#endif
			if (d==0) {
				imsg("SSL Protocol error as SSL_ERR_SYSCALL and d==0 (%s)\n",action);
				return 3;
			}

#ifdef WIN32
			{
				LPVOID lpMsgBuf;
				FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
					FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,// Default language
					0, NULL );
				if (lpMsgBuf==NULL) return 1;
				if (strstr(lpMsgBuf, "completed successfully")!=NULL){
					LocalFree( lpMsgBuf );
					return 1;
				}
				if (debug) imsg("SSL error (win32) on %s SSL_ERROR_SYSCALL : %s\r\n", action, lpMsgBuf);
				LocalFree( lpMsgBuf );
			}
#endif
			if (d==5) return 3;


#ifdef WIN32
#else
				if (
#ifdef SOLARIS
					errno == 11 ||
#endif
					strcmpnc(strerror(errno), "Resource temporarily unavailable")==0){
					/*emsg("SSL system error on %s SSL_ERROR_SYSCALL: (%d) (%s) (trying again...)\r\n", action, errno, strerror(errno));*/
#ifdef SOLARIS
					return 2; /* changed by chrisp as can't see point in returning 5 */
#else
					return 2;
#endif
				}
				if (errno == 0) {
					imsg("SSL error (system) on %s SSL_ERROR_SYSCALL 1: (%d)%s\r\n", action, errno, strerror(errno));
					return 2;
				}
			imsg("SSL error (system) on %s SSL_ERROR_SYSCALL 2: (%d)%s\r\n", action, errno, strerror(errno));
#endif
			return 4;
/*			return 3;   Used to return '3', but got messages like  "SSL system error on read SSL_ERROR_SYSCALL: (4)Interrupted system call"  */
		default:
			imsg("SSL error unknown code %d (this should never happen, right?) abandoning socket connection.\r\n", d );
		return 4;
	}
}

static int require_client_certificates = FALSE;

void myssl_set_require_client_certificates(int truth){
	require_client_certificates = truth; // NOT USED
}

// Context currently configured for client_cert instead of using callback
//int client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
//{
//}

//	Lookup / create client context with identifying certificates
//  Marijn: Function added Aug 2010 - kind of duplicates other init, but want to keep standalone
//	Note: Still slightly dodgy in terms of inital leak and thread safety, expect this to get called once 
// 	and on one thread so that is ok
SSL_CTX *myssl_client_ctx_withcert(int isserver, char *pem_file, char *reason, char *mstate)
{			
	char fname[BFSZ];
	CTX_LIST *ctx;
	SSL_CTX *new_ctx;
//	STACK_OF(X509_NAME) *list;

	// if needed just use old style single client context
	if (isserver || !pem_file) return client_ctx;	

	// check for already created client_contexts 
	strcpy(mstate,"sslctx1");
	myssl_mutex_wait();	
	for(ctx=client_contexts; ctx; ctx=ctx->next) {
		if (strcmp(ctx->domain_name,pem_file) != 0) continue;
		if (!ctx->domain_ctx) {
			myssl_mutex_release(__LINE__);
			return client_ctx;
		}
		myssl_mutex_release(__LINE__);
		return ctx->domain_ctx;
	}
	myssl_mutex_release(__LINE__);
	strcpy(mstate,"sslctx2");

	ctx = (CTX_LIST*)calloc(1,sizeof(CTX_LIST));
	if (!ctx) return client_ctx;

	ctx->domain_name=strdup(pem_file);

	//init an appropriate new context // 	cf myssl_init_client
	strcpy(mstate,"sslctx3");
	new_ctx=SSL_CTX_new(SSLv23_client_method());
	strcpy(mstate,"sslctx4");
	myssl_set_noverify(new_ctx);
	strcpy(mstate,"sslctx5");
	/* Enable all vendor bug compatibility options.  To disable TLS,
	 * 'or' in SSL_OP_NO_TLSv1 */
	{	long op;
		op = mail_op;
		if (my_honor) op = op | SSL_OP_CIPHER_SERVER_PREFERENCE;
		op = op | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION;
//printf("init client2 op = %x %x\n",op,SSL_OP_NO_TLSv1_3);

		SSL_CTX_set_options(new_ctx,op);
		myssl_set_mode(new_ctx);

		// Give it a cipher list if we have one;
		if (new_ctx && lib_strlen(ciphers)!=0) {
			if (!SSL_CTX_set_cipher_list(new_ctx, ciphers)) {
				imsg("No valid ciphers specified (%s)\n",ciphers);
			}
		}
	}

	// Maybe actually using the callback is a better way of dealing with this??
	// SSL_CTX_set_client_cert_cb(client_ctx,client_cert_cb);	

	lprintf(fname,BFSZ-1,"%s%s",ssl_dir,pem_file);
	if(!lib_exists(fname)){ lprintf(reason,BFSZ-2,"certificate not found (%s)", fname); goto err;}
	/* load the server certificate from the file */
	if (!SSL_CTX_use_certificate_file(new_ctx,fname,SSL_FILETYPE_PEM)) {lprintf(reason,BFSZ-2,"use_certificate_file failed (%s)", fname); goto err;}
	/* load any trust chain certificates from the same file */
	if (!SSL_CTX_use_certificate_chain_file(new_ctx,fname)) {lprintf(reason,BFSZ-2,"use_certificate_chain_file failed (%s)", fname); goto err;}
	/* load the server private key from the file */
	if (!SSL_CTX_use_PrivateKey_file(new_ctx,fname,SSL_FILETYPE_PEM)) { lprintf(reason,BFSZ-2,"use_privatekey_file failed (%s)",fname); goto err;}
	/* Check private key matches certificate */
	if (!SSL_CTX_check_private_key(new_ctx)) { strcpy(reason,"check private key failed"); goto err;}

//	SSL_CTX_set_verify(new_ctx,SSL_VERIFY_NONE,myssl_verify_cb);/* no return value for this function */
	strcpy(mstate,"sslctx6");
	myssl_set_noverify(new_ctx);

	// chrisp moved here, so it doesn't do on every context...
	strcpy(mstate,"sslctx6.1");
	if (!SSL_CTX_load_verify_locations(new_ctx,"caroot.pem",NULL)) {
		//char msg[BFSZ];
		//int r;
		//strcpy(mstate,"sslctx6.2");
		//myssl_error_get(msg,"load_verify",m,&r);
		imsg("ssl: Load verify_locations failed caroot.pem\n");
	}


	//lprintf(fname,BFSZ-1,"%s%s",ssl_dir,"apns_ca_root.pem");
	//if(!lib_exists(fname)){ lprintf(reason,BFSZ-2,"ca root certificate not found (%s)", fname); goto err;}

	//list=SSL_load_client_CA_file(fname);
	//SSL_CTX_set_client_CA_list(new_ctx,list);
	//SSL_CTX_load_verify_locations(new_ctx, NULL, "ssl/");

	strcpy(mstate,"sslctx7");
	myssl_mutex_wait();	
	ctx->domain_ctx=new_ctx;
	ctx->next=client_contexts;
	client_contexts=ctx;
	myssl_mutex_release(__LINE__);	

	strcpy(mstate,"sslctx8");

	return ctx->domain_ctx;
err:
	emsg("%s",reason);
	ctx->next=client_contexts;
	client_contexts=ctx;

	return client_ctx;
}

/* require_cert ==TRUE(1) == FALSE(0) ==-TRUE(-1)(use require_client_certificates) */

// This function appears to leak 'SSL' structures if it fails and returns null
static int max_retry=20; // adjusted to 10, was 30, then 5, maybe 10 will be safer. This might be too small, considering changing back to 20...
void myssl_set_retry_seconds(int x)
{
	if (x==0) x = 20;
	max_retry = x;
}
char *myssl_cipher(Myssl *m)
{
	if (m==NULL) return "Nullcipher";
	if (m->ssl==NULL) return "Nullcipher2";
	return (char *) SSL_get_cipher(m->ssl);
}
char *myssl_protocol(Myssl *m)
{
	if (m==NULL) return "Nullprotocol";
	if (m->ssl==NULL) return "Nullprotocol2";
	return (char *) SSL_get_version(m->ssl);
}

static char * codes[]={
 "X509_V_OK"
 ,"X509_V_ERR_UNSPECIFIED"
 ,"X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
 ,"X509_V_ERR_UNABLE_TO_GET_CRL"
 ,"X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
 ,"X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
 ,"X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
 ,"X509_V_ERR_CERT_SIGNATURE_FAILURE"
 ,"X509_V_ERR_CRL_SIGNATURE_FAILURE"
 ,"X509_V_ERR_CERT_NOT_YET_VALID"
 ,"X509_V_ERR_CERT_HAS_EXPIRED"
 ,"X509_V_ERR_CRL_NOT_YET_VALID"
 ,"X509_V_ERR_CRL_HAS_EXPIRED"
 ,"X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
 ,"X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
 ,"X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
 ,"X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
 ,"X509_V_ERR_OUT_OF_MEM"
 ,"X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
 ,"X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
 ,"X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
 ,"X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
 ,"X509_V_ERR_CERT_CHAIN_TOO_LONG"
 ,"X509_V_ERR_CERT_REVOKED"
 ,"X509_V_ERR_INVALID_CA"
 ,"X509_V_ERR_PATH_LENGTH_EXCEEDED"
 ,"X509_V_ERR_INVALID_PURPOSE"
 ,"X509_V_ERR_CERT_UNTRUSTED"
 ,"X509_V_ERR_CERT_REJECTED"
 ,"X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
 ,"X509_V_ERR_AKID_SKID_MISMATCH"
 ,"X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
 ,"X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
 ,"X509_V_ERR_UNABLE_TO_GET_CRL_ISSUE"
 ,"X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
 ,"X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
 ,"X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
 ,"X509_V_ERR_INVALID_NON_CA"
 ,"X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
 ,"X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
 ,"X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
 ,"X509_V_ERR_INVALID_EXTENSION"
 ,"X509_V_ERR_INVALID_POLICY_EXTENSION"
 ,"X509_V_ERR_NO_EXPLICIT_POLICY"
 ,"X509_V_ERR_DIFFERENT_CRL_SCOPE"
 ,"X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
 ,"X509_V_ERR_UNNESTED_RESOURCE"
 ,"X509_V_ERR_PERMITTED_VIOLATION"
 ,"X509_V_ERR_EXCLUDED_VIOLATION"
 ,"X509_V_ERR_SUBTREE_MINMAX"
 ,"X509_V_ERR_APPLICATION_VERIFICATION"
 ,"X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
 ,"X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
 ,"X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
 ,"X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
 ,"X509_V_ERR_PATH_LOOP"
 ,"X509_V_ERR_SUITE_B_INVALID_VERSION"
 ,"X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
 ,"X509_V_ERR_SUITE_B_INVALID_CURVE"
 ,"X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
 ,"X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
 ,"X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256"
 ,"X509_V_ERR_HOSTNAME_MISMATCH"
 ,"X509_V_ERR_EMAIL_MISMATCH"
 ,"X509_V_ERR_IP_ADDRESS_MISMATCH"
 ,"X509_V_ERR_DANE_NO_MATCH"
 ,"X509_V_ERR_EE_KEY_TOO_SMALL"
 ,"X509_V_ERR_CA_KEY_TOO_SMALL"
 ,"X509_V_ERR_CA_MD_TOO_WEAK"
 ,"X509_V_ERR_INVALID_CALL"
 ,"X509_V_ERR_STORE_LOOKUP"
 ,"X509_V_ERR_NO_VALID_SCTS"
 ,"X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
};

char *myssl_code_name(int code)
{
	if (code<0) return "BADCODE";
	if (code>72) return "BADCODE";
	return codes[code];
}
char *myssl_hostname(Myssl *myssl, char *bf)
{
	char *s = SSL_get_servername(myssl->ssl,TLSEXT_NAMETYPE_host_name);
	strcpy(bf,"");
	if (s!=NULL) ncpy(bf,s,BFSZ-1);
	return bf;
}
Myssl *myssl_takeover_real(Chan *c, SOCKET sk, int require_cert, int isserver, char *domain, Myssl *inherit, char *reason, int isweb, char *mstate)
{
	/* Create a BIO to use with this socket
	*/
	int x;
	Myssl *m;
	CTX_LIST *ctx;
	CTX_LIST *lastone=NULL;
	SSL_CTX *xclient_ctx;
	int r;
	char *lastmsg="";
	char msg[BFSZ];
	SSL *ssl=NULL;
	char *s;
	int wantread;
	int attempt=0;
	char ip[BFSZ];
	time_t start = time(NULL);

	m = calloc(1,sizeof(Myssl));
	strcpy(mstate,"ssltake1");

	// This code is wrong, the contexts should not be changed.
	/*
	{	int do_it = FALSE;
		if (require_cert == -TRUE)
			if (require_client_certificates)
				do_it = TRUE;
		if (require_cert == TRUE)
			do_it = TRUE;
		if (do_it) 
			myssl_verify_set(m);
		else
			myssl_NO_verify_set(m);
	}
	*/


	tcp_getip(c,ip);
	// Support for multiple client contexts with unique certs (notably for APNS Apps)
	//printf("isserver=%d ssl_clientcert=%p\n",isserver,c->ssl_clientcert);
	strcpy(mstate,"ssltakectx");
	xclient_ctx = myssl_client_ctx_withcert(isserver, c->ssl_clientcert, reason,mstate);
	strcpy(msg,"No Reason");

	/* Load the CA path */
	
//	strcpy(mstate,"sslverifyloc"); chrisp moved so done once on new context... as it seems really slow.. 
//	if (!SSL_CTX_load_verify_locations(xclient_ctx,"caroot.pem",NULL)) {
//		int r;
//		strcpy(mstate,"sslverifyloc2");
//		myssl_error_get(msg,"load_verify",m,&r);
//		imsg("ssl: Load path verify failed %s\n",msg);
//	}


//require_cert = TRUE;
	strcpy(mstate,"ssltake3");
	tcp_setblock(c,FALSE); /* Can lock up if we do this with a blocking socket */
	strcpy(mstate,"ssltake4");
	msg[0] = '\0';
	/* create the SSL structure */
	//m->bio=BIO_new_socket(sk,FALSE);  WAS BIO_CLOSE, now we must close the socket in tcplib.c
	// Now it's BIO_CLOSE, which means we are responsible for closing the socket.
	// by which I mean the ssl library must close it, or myssl if new_socket fails
	strcpy(mstate,"ssltake5");
	m->bio=BIO_new_socket((int)sk,BIO_CLOSE); // cast is safe because windows sockets are really 32bit on 64bit systems...
	strcpy(mstate,"ssltake6");
	if (m->bio == NULL){strcpy(reason, "ssl cant on BIO_new_socket call."); myssl_free(m); tcp_socket_close(sk,__FILE__,__LINE__); myssl_cleanup(); return NULL;}




	if (isserver) {
		strcpy(mstate,"ssltake7");
		if (domain_contexts==NULL){
			lprintf(reason, BFSZ, "SSL no valid contexts check certificates are valid or remove them");
			imsg("leakbug: domain_contexts is null, so freeing the bio\n");
			// chrisp adding to fix leak:
			BIO_free_all(m->bio); m->bio = NULL; 
			myssl_free(m);
			myssl_cleanup();

			return NULL;
		}
			myssl_mutex_wait();
			strcpy(mstate,"ssltake8");
			for(ctx=domain_contexts,r=0; ctx; ctx=ctx->next,r++) {
				lastone = ctx;
//printf("ssl context search %s %s\n",ctx->domain_name,domain);
				if (strcmp(ctx->domain_name,domain) != 0) continue;
//printf("ssl context search %s FOUND %s\n",ctx->domain_name,domain);
				if (ssl!=NULL) lib_abort();

				strcpy(mstate,"ssltake9");
				if (isweb) ssl = SSL_new(ctx->web_ctx);
				else ssl = SSL_new(ctx->domain_ctx);
				break;
			}
			if (ssl==NULL) {
//				imsg("myssl: domain not found (%s) using (%s)\n",domain,lastone->domain_name);
//printf("ssl context search failed %s using 'lastone'\n",domain);
				ssl = SSL_new(lastone->domain_ctx); // use the first one.
			}
			myssl_mutex_release(__LINE__);
	}
	else {
		ssl = SSL_new(xclient_ctx);
		if (ssl==NULL) emsg("SSL_NEW(CLIENT_CTX) FAILED %p\n",xclient_ctx);
	}
	strcpy(mstate,"ssltake10");
	if (ssl==NULL) {
		myssl_show_errors();
	}
	strcpy(mstate,"ssltake11");
	if (ssl!=NULL) SSL_set_app_data(ssl,m);
	strcpy(mstate,"ssltake12");

	if (ssl!=NULL) if (strlen(c->rhosts)>0) {
	  char bf[BFSZ];
	  char *upto;
	  ncpy(bf,c->rhosts,BFSZ-1);
	  s = mystrtok(bf,",:",&upto);
	  strcpy(mstate,"ssltake13");
	  if (s!=NULL) {
		  if (strlen(s)==0) s = mystrtok(NULL,",:",&upto);
		  if (s!=NULL) {
			 imsg("myssl: Set host %s\n",s);
			 if (!SSL_set1_host(ssl,s)) {
				  imsg("ssl_set1_host: set_host failed %s\n",s);
				 /* handle error */
			 }
			 if (!SSL_set_tlsext_host_name(ssl, s)) {
				 imsg("ssl_set_tlsext_host_name failed: %s\n",s);
			 }
		

			 for (s=mystrtok(NULL,",:",&upto);s!=NULL;s=mystrtok(NULL,",",&upto)) {
				 if (strchr(s,'.')==NULL) continue;
			  if (!SSL_add1_host(ssl, s)) {
				  imsg("ssl_add1_host failed %s\n",s);
				/* handle error */
			  }
			 }
		 }
	  }
	}
	strcpy(mstate,"ssltake14");


	m->ssl = (void *) ssl;
	if (ssl == NULL)
		{strcpy(reason, "ssl cant on SSL_new call."); BIO_free_all(m->bio); m->bio = NULL;  myssl_free(m); 	myssl_cleanup(); return NULL;}
	// after here I think we don't call bio_Free_all
	strcpy(mstate,"ssltake15");
	SSL_set_bio(ssl,m->bio,m->bio); /* no return value for this function */
	
//require_cert = TRUE; printf("test require cert true \n");
	x = SSL_get_options(ssl);
//	printf("ssl_get_options %x %x %x\n",x,SSL_OP_NO_RENEGOTIATION,x&SSL_OP_NO_RENEGOTIATION);
	if (isserver) {
		SSL_set_accept_state(ssl); /* no return value for this function */
	} else {
		SSL_set_connect_state(ssl); /* no return value for this function */
	}


	strcpy(mstate,"ssltake16");

	for(;TRUE;){
		errno = 0;
		// Chrisp changing from 30 seconds to 5 seconds.
		strcpy(mstate,"sslcon1");
		if ((time(NULL)-start)>max_retry){ // Maybe I should increase this, or make it adjustable...
			lprintf(msg, BFSZ, "SSL error gave up after %d seconds (%d attempts) %s",(int) (time(NULL)-start),attempt,lastmsg);
			lprintf(reason, BFSZ, "SSL error gave up after %d seconds (%d attempts) %s",(int) (time(NULL)-start),attempt,lastmsg);
			if (debug) emsg(msg);
			imsg("leakbug: gave up after %d seconds %s\n",max_retry,lastmsg);
			// chrisp adding to fix leak:
			//BIO_free_all(m->bio); m->bio = NULL; 
			myssl_free(m);
			myssl_cleanup();
			return NULL;
		}
		attempt++;
		if (attempt>100) {
			lprintf(mstate,100,"sslcon-sleep %d",attempt);
			lib_msleep(50); /* So we definitely don't burn cpu doing this - was 500.*/
		}

		strcpy(mstate,"sslhand");
		r = SSL_do_handshake((SSL *) m->ssl);
		strcpy(mstate,"sslcon3");

		if (r!=1) {
			int i;

			strcpy(mstate,"sslcon4");
			if (SSL_is_init_finished(m->ssl) == 0){ /** init not finnished **/
				strcpy(mstate,"sslfin1");
				i = SSL_want(m->ssl);
				if (i==SSL_NOTHING) {imsg("SSL: communication could not establish encryption. %s\n",ip); lastmsg="SSL_nothing";}
				else if (i==SSL_WRITING) {imsg("SSL: init not finished, ssl want=SSL_WRITING %s\n",ip);
					strcpy(mstate,"sslfin2");
					lib_msleep(20);
					lastmsg="SSL_writing";
					continue;
				} else if (i==SSL_READING) {
					// imsg("SSL: init not finished, ssl want=SSL_READING %s %dsec\n",ip,(int)(time(NULL)-start));
					x = handle_ssl_error_case(m, r, "handshake",&wantread);
					if (x==3) {i = x; goto doerr;}
					lprintf(mstate,100,"sslcon-read %d",attempt);
					tcp_waitfor(c,2000);
					if (attempt>5) lib_msleep(40); 
					continue;
				}
				else if (i==SSL_X509_LOOKUP) {
					strcpy(mstate,"sslfin4");
					imsg("SSL: init not finished, ssl want=SSL_X509_LOOKUP\n"); 
					lastmsg="SSL_X509_LOOKUP";
				}
				else imsg("SSL: init_waiting on %d\n",i);
			}
			strcpy(mstate,"sslerr1");
			i = handle_ssl_error_case(m, r, "handshake",&wantread);
doerr:
			if (4==i) {
				strcpy(mstate,"sslerr2");
				if (!tcp_check_open_real(c,TRUE)){
					sprintf(reason,"SSL could not start, socket closed (%d)",r);
					// chrisp adding to fix leak:
					imsg("leakbug: ssl could not start, socket closed\n");
					//BIO_free_all(m->bio); m->bio = NULL;   removed, I think it's freed ok now.
					myssl_free(m);
					myssl_cleanup();
					return 0;
				}
				strcpy(mstate,"sslerr2");
				if (debug) emsg("SSL error retry attempt %d variation 1\n", attempt);
				imsg("SSL: error retry attempt %d variation 1\n", attempt);
				lastmsg="HANDLE_ERR4";

				lib_msleep(20);
				continue;
			}
			if (5==i || 2==i || i==6) {
				strcpy(mstate,"sslerr3");
				if (debug) emsg("SSL error retry attempt %d variation 2, i=%d\n", attempt,i);
				imsg("SSL: error retry attempt %d variation 2, i=%d\n", attempt,i);
				lib_msleep(20);
				lastmsg="HANDLE_ERR5";
				continue;
			}
			strcpy(mstate,"sslretry1");
			if (BIO_should_retry((BIO *) m->bio)) {
				strcpy(mstate,"sslretry2");
				emsg("SSL error retry attempt %d (BIO_should_retry)\n", attempt);
				lib_msleep(20);
				continue;
			}
			strcpy(mstate,"sslretry2");
			myssl_error(msg,"handaccept",m,&r);
			if (lib_strlen(msg)==0)	tcp_error(c,msg);
			sprintf(reason,"SSL could not start, unknown reason (%d)(%s)",r,msg);
			// chrisp adding to fix leak:
			//imsg("leakbug: ssl could not start for unknown reason\n");
			// removed as this one caused a crash I think.
			//BIO_free_all(m->bio); m->bio = NULL; 
			myssl_free(m);

			return NULL;
		}else if (r == 1){
			int r;		
			char vmsg[BFSZ];
			strcpy(mstate,"sslverify1");
			r = SSL_get_verify_result(ssl);
			strcpy(mstate,"sslverify2");

			if (r == X509_V_OK) {
				c->ssl_verified = TRUE;
				strcpy(vmsg,"Verified=OK");
			} else {
				c->ssl_verified = FALSE;
				sprintf(vmsg,"UnVerified=%d",r);
			}
			c->ssl_code = r;
			sprintf(reason, "SSL Accepted Protocol: %s, %s", SSL_get_version((SSL*) m->ssl), vmsg);
			return m;
		}else{
			strcpy(mstate,"sslverify2");
			myssl_error(msg,"handaccept2",m,&r);
			sprintf(reason,"SSL could not start, SSL returned bad code, shouldn't happen! (%d) (%s)",r,msg);
			// chrisp adding to fix leak:
			imsg("leakbug: ssl start failed\n");
			//BIO_free_all(m->bio); m->bio = NULL;  removed as I think causes crash
			myssl_free(m);
			return NULL;
		}
	}
}
Myssl *myssl_takeover(Chan *c, SOCKET sk, int isserver, Myssl *inherit, char *reason, int isweb, char *mstate) {
	return myssl_takeover_real(c, sk, -TRUE, isserver, NULL, inherit, reason,isweb,mstate);
}

Myssl *myssl_takeover_domain(Chan *c, SOCKET sk, int isserver, char *domain, Myssl *inherit, char *reason, int isweb, char *mstate) {
	return myssl_takeover_real(c, sk, -TRUE, isserver, domain, inherit, reason, isweb,mstate);
}



/* myssll_read and myssl write should not retry
themselves
*/
int myssl_check_retry(Chan *c, Myssl *m, int r, char *reason, int *wantread)
{
	int x = handle_ssl_error_case(m,r,reason,wantread);
	if (x==4) if (!tcp_check_open_real(c,TRUE)) return FALSE; /* don't retry */
	if (x==2) return TRUE; /* retry */
	if (x==3) return FALSE;
	if (x==0) return TRUE;
	if (x==5) return TRUE;
	return FALSE; /* Unknown error, don't retry */
}
int myssl_read(Chan *c, Myssl *m, char *bf, int len)
{

	int r;
	int wantread = FALSE;
	int retry;
	time_t start=time(NULL);
	if (m->closeit) {
		emsg("myssl read to errored socket, failing\n");
		return 0;
	}
	errno = 0; // clear any old error condition...
	m->wouldblock = TRUE;
again:
	r = SSL_read((SSL *) m->ssl,bf,len);
//imsg("ssl_read returned %d bytes\n",r);
	if (r<=0) {
		retry = myssl_check_retry(c,m,r,"read",&wantread);
//imsg("ssl_read returned %d bytes retry=%d wantread=%d errno=%d BLOCK=%d\n",r,retry,wantread,errno,c->block);
		if (retry || wantread ) { // || errno==EAGAIN
			if (c->block==TRUE) {
				if (time(NULL)-start<120) {
					lib_msleep(20);
					goto again;
				}
				imsg("myssl_read: blocking call waited for 2 minutes\n"); // should not be emsg... fills up log
				return 0;
			}
			return -1; /* Retry as wouldblock is set to true */
		}
	}
	if (c->ssl!=NULL) m->wouldblock = FALSE;
	if (r<0) return 0;
	return r;
}
int myssl_write(Chan *c, Myssl *m, char *bf, int len)
{
	int r;
	int retry;
	int wantread=FALSE;
	time_t start=time(NULL);
	m->wouldblock = TRUE;
	if (m->closeit) {
		imsg("myssl write to errored socket, failing\n"); // was emsg, changed to imsg...
		return 0;
	}
again:
	r = SSL_write((SSL *) m->ssl,bf,len);
	if (r<=0) {
		retry = myssl_check_retry(c,m,r,"write",&wantread); // I've seen one crash in in the ssl_write bytes function... maybe if the socket is dead and I haven't noticed
		if (retry && !wantread) {
			if (c->block==TRUE) {
				if (time(NULL)-start<120) {
					lib_msleep(20);
					goto again;
				}
				imsg("myssl_write: blocking call waited for 2 minutes %dsec\n",(int)(time(NULL)-start)); // crash shortly after this event, while writing to ssl...
				// Here I think I should close the socket...
				m->closeit = TRUE;				
				return 0;
			}
			return -1; /* Retry as wouldblock is set to true */
		}
	}
	if (c->ssl!=NULL) m->wouldblock = FALSE;
	if (r<0) return 0;
	return r;
}
void myssl_free(Myssl *m)
{
	SSL *b;
	b = (SSL *)m->ssl;
	if (b!=NULL) {
	//	if (b->rbio) BIO_free_all(b->rbio); b->rbio = NULL;
	//	if (b->wbio) BIO_free_all(b->wbio); b->wbio = NULL;

		SSL_clear(b);
		SSL_free(b);
	}
	m->ssl = NULL;
	free(m);
	
	myssl_cleanup();
}
int myssl_close(Chan *c,Myssl *m)
{
	int i;
	time_t start = time(NULL);
	int retry;
	int doneit=FALSE;
	int wantread=FALSE;
	SSL *b;
	if (m==NULL) return TRUE;
	c->ssl = NULL; // Set it null now, then we can't accidentally close it again.
	b = (SSL *)m->ssl;
again:
	i = SSL_shutdown(b);
	if (i!=0) if ((time(NULL)-start)>20){
		emsg("SSL error myssl_close gave up after 20 seconds\n");
		i=1;
	}
  	if (i==0 && !doneit) { 
		shutdown(c->sk, 1);  // this should close the socket from the other ends point of view nicely.
		doneit = TRUE;
		goto again; 
	} 
	//if (i==0) emsg("ssl_shutdown returned 0, failed but no error, so just closing it\n");
	//if (i==0) goto retryit;

	if (i==-1) {
		retry = myssl_check_retry(c,m,i,"close",&wantread);
		if (retry && !wantread) {
			if (time(NULL)-start<20) {
				lib_msleep(20);
				//emsg("myssl_close retrying\n");
				goto again;
			}
			imsg("myssl_close: blocking call waited for 20 sec then gave up.\n"); // fills up log.,
		}
	}
	SSL_clear(b);
	SSL_free(b);
	m->bio = NULL;
	m->ssl = NULL;
	free(m);
	  myssl_cleanup();
	//ERR_clear_error();
	//ERR_remove_state(0); // leaks error state otherwise, stupid buggy thing
	//ERR_remove_thread_state(0);

	return TRUE;
}
int myssl_wouldblock(Myssl *m)
{
	return m->wouldblock;
}
char *myssl_lastmsg(Myssl *m)
{
	return m->msg;
}
char *nullok(char *s);
int myssl_error(char *msg, char *reason, Myssl *m, int *r)
{
    char *s;
    int e; //, num = 0;
	if (BIO_should_retry((BIO *)m->bio)) {
                sprintf(msg,"SSL: %.90s %.90s","Should retry",reason);
		return FALSE;
	}
    strcpy(msg,"");
    for (;TRUE;) {
			const char *func,  *desc;
            e = ERR_get_error();
            if (e==0) break; //{if (num==0) return FALSE; else break;} /* no errors? return false so we're blocking :-)*/
            s = (char *)  ERR_lib_error_string(e);
			//emsg("SSL: %d:%s\n",e,reason);
			func = ERR_func_error_string(e);
			desc = ERR_reason_error_string(e);
            if (lib_strlen(msg)==0) sprintf(msg,"%.90s %.90s %.80s %.80s",reason,nullok(s),nullok((char *) func),nullok((char *) desc));
			imsg("SSL: %d:%s r=%s f=%s d=%s\n",e,reason,nullok(s),nullok((char *) func),nullok((char *) desc));
			//num++;
    }
	return TRUE;
}
int myssl_error_get(char *msg, char *reason, Myssl *m, int *r)
{
    char *s;
    int e; //, num = 0;
    strcpy(msg,"");
    for (;TRUE;) {
			const char *func,  *desc;
            e = ERR_get_error();
            if (e==0) break; //{if (num==0) return FALSE; else break;} /* no errors? return false so we're blocking :-)*/
            s = (char *)  ERR_lib_error_string(e);
			//emsg("SSL: %d:%s\n",e,reason);
			func = ERR_func_error_string(e);
			desc = ERR_reason_error_string(e);
            if (lib_strlen(msg)==0) sprintf(msg,"%.90s %.90s %.80s %.80s",reason,nullok(s),nullok((char *) func),nullok((char *) desc));
			imsg("SSL: %d:%s r=%s f=%s d=%s\n",e,reason,nullok(s),nullok((char *) func),nullok((char *) desc));
			//num++;
    }
	return TRUE;
}
int myssl_show_errors(void)
{
    char *s;
    int e;
    for (;TRUE;) {
			const char *func,  *desc;
            e = ERR_get_error();
            if (e==0) break; //{if (num==0) return FALSE; else break;} /* no errors? return false so we're blocking :-)*/
            s = (char *)  ERR_lib_error_string(e);
			func = ERR_func_error_string(e);
			desc = ERR_reason_error_string(e);
			emsg("SSL: %d:%s f=%s d=%s\n",e,s,func,desc);
    }
	return TRUE;
}
int myssl_state(Myssl *m)
{
	return TRUE;
}


char *myssl_fname(char *mid, char *bf)
{
#ifdef WIN32
	char tmp[BFSZ];
	GetSystemDirectory(tmp,200);
	tmp[2] = 0;
	sprintf(bf,"%s\\sftp\\finger_%s.dat",tmp,mid);
#else
	sprintf(bf,"%s/.sftp/finger_%s.dat",lib_mygetenv("HOME"),mid);
#endif
	myssl_lib_create_dir(bf,0700);
	return bf;
}

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;
}

int myssl_lib_mkdir(char *s, int mode)
{
#ifdef WIN32
	return mkdir(s);
#else
	return mkdir(s,mode);
#endif
}
void myssl_lib_create_dir(char *s, int mode)
{
   int r;
   char bf[BFSZ];

   ncpy(bf,s,BFSZ-1);
   if (lib_strlen(s)<2) return;
   s = bf+2;
   for (;*s!=0; s++)  {
      if ((*s=='/') || (*s == '\\'))  {
         *s = 0;
         r = myssl_lib_mkdir(bf,mode);
         *s = '/';
      }
   }
}


// Typedefs from ssl_locl.h - Bit nasty but should be OK
typedef struct cert_pkey_st {
	X509 *x509;
	EVP_PKEY *privatekey;
} CERT_PKEY;
struct cert_st {
	CERT_PKEY *key; /* ALWAYS points to an element of the pkeys array */
	/* etc */
};

int myssl_valid(char *fname, char *reason)
{
	ASN1_UTCTIME *expire;
	time_t tm;
	char bf1[BFSZ];
	char subj[BFSZ];
	char issue[BFSZ];
	FILE *fp=NULL;
	X509 *cert=NULL;
	int days;
	strcpy(reason,"");
	fp = fopen(fname,"r");
	if (fp==NULL) {
		lprintf(reason,BFSZ-1,"Cannot find file %s %s",fname,strerror(errno));
		return FALSE;
	}
	cert = PEM_read_X509(fp, NULL, NULL, NULL);
	fclose(fp);
	if (!cert) {
		lprintf(reason,BFSZ-1,"Cannot read certificate file %s",fname);
		return FALSE;
	}

	expire=X509_get_notAfter(cert);
	tm=ASN1_UTCTIME_get(expire);
	days=(int) ((tm-time(NULL))/(60*60*24));

	// if issuer == subject, then self signed.
	// if days < 7 then return false.

	ncpy(subj,X509_NAME_oneline(X509_get_subject_name(cert),bf1,BFSZ-1),BFSZ-1); 
	ncpy(issue,X509_NAME_oneline(X509_get_issuer_name(cert),bf1,BFSZ-1),BFSZ-1); 

	X509_free(cert);
	if (days<30) { // chrisp increased to 30 days.  
		lprintf(reason,BFSZ-1,"Expires in %d days",days);
		return FALSE;
	}
	if (strcmp(subj,issue)==0) {
		lprintf(reason,BFSZ-1,"Self signed certificate %s",subj);
		return FALSE;
	}
	lprintf(reason,BFSZ-1,"For %s signed by %s expires in %d days",subj,issue,days);
	return TRUE;
}



// return list expiring certs
int myssl_report(char *domain, char *result,int size, int perdom, char *fname)
{
	// do a report on the url/domain...
	
	// Load certificate
	// get expiry date and registrar and print.
	ASN1_UTCTIME *expire;
	time_t tm;
	char bf1[BFSZ];
	char *s;
	char bf2[BFSZ];
	FILE *fp=NULL;
	X509 *cert=NULL;
	int days;
	char tmp[BFSZ];

	if (perdom) {
		myssl_cert_file(fname,domain,"surge_cert.pem");
	} else {
		myssl_cert_file(fname,".","surge_cert.pem");
	}
	fp = fopen(fname,"r");
	if (fp==NULL) {
		lprintf(result,size,"Cannot find file %s %s",fname,strerror(errno));
		return FALSE;
	}
	cert = PEM_read_X509(fp, NULL, NULL, NULL);
	if (!cert) {
		lprintf(result,size,"Cannot read certificate file %s",fname);
		fclose(fp);
		return FALSE;
	}

	expire=X509_get_notAfter(cert);
	tm=ASN1_UTCTIME_get(expire);
	safe_ctime(&tm,tmp);	
	s = strchr(tmp,'\n'); if (s!=NULL) *s = 0;
	days=(int) ((tm-time(NULL))/(60*60*24));
	lprintf(result, BFSZ-1,"subj(%s) issued(%s) %s %s",X509_NAME_oneline(X509_get_subject_name(cert),bf1,BFSZ-1) ,X509_NAME_oneline(X509_get_issuer_name(cert),bf2,BFSZ-1),  tm<time(NULL)?"Expired":"Expires",tmp);
	fclose(fp);
	X509_free(cert);
	return TRUE;
}

int myssl_get_expiry_dates(char *result,int size)
{
#ifndef openssl101
	strcpy(result,""); // not implemented in openss11 because we can't look inside the context...
#else
	CTX_LIST *ctx;
	ASN1_UTCTIME *expire;
	time_t tm;
	char bf[BFSZ], tmp[BFSZ];
	struct cert_st *p;
	X509 *x;
	int i=0, days;

	myssl_mutex_wait();
	for(ctx=domain_contexts; ctx; ctx=ctx->next) {		
		//ctx->domain_ctx 
		// Hack to access cert associated with current domain context
		p=(struct cert_st *)ctx->domain_ctx->cert;
		if (!p || !p->key) goto failed;
		x=(X509*)p->key->x509;
		if (!x) goto failed;
		
		expire=X509_get_notAfter(x);
		tm=ASN1_UTCTIME_get(expire);
		safe_ctime(&tm,tmp);	

		days=(int) ((tm-time(NULL))/(60*60*24));
		if (days>-365*2 && days<14){
			i++;
//			imsg("ssl: certificate for %s expires %s\n",ctx->domain_name, bf);
			if (i>10)
				continue;			
			lprintf(bf, BFSZ-1," - %s  %s: %s\n", ctx->domain_name, tm<time(NULL)?"Expired":"Expires",tmp);
			strncat(result,bf,size);
		}
	}
	myssl_mutex_release(__LINE__);
	return i;
failed:
	myssl_mutex_release(__LINE__);
#endif
	return FALSE;
}

int myssl_load_domainkey(char* file, unsigned int keybfsize, char *keybf)
{
	FILE *f;
	int ret=FALSE, inkey=FALSE;
	char bf[BFSZ];

	keybf[0]='\0';

	f=fopen(file,"r");
	if (f==NULL){
		imsg("myssl: can't open domainkey public key (%s)", file);
		return FALSE;
	}
	while (fgets(bf,BFSZ,f)) {
		strip_eol(bf);
		if (lib_strlen(bf)==0) continue;
		if (strcmp(bf,"-----BEGIN PUBLIC KEY-----")==0){
			inkey=TRUE;
			continue;
		}
		if (inkey && strcmp(bf,"-----END PUBLIC KEY-----")==0){
			inkey=FALSE;
			ret=TRUE;
			break;
		}

		if (inkey){
			if (lib_strlen(keybf)+lib_strlen(bf) >= keybfsize){
				emsg("myssl: buffer too small\n");
				break;
			}
			strcat(keybf,bf);
		}
	}		
	fclose(f);

	return ret;
}

int myssl_gen_domainkey(char *priv, char *pub, int reset, char *result, int *created, int dkimsize)
{
	char priv_file[BFSZ],domkey_file[BFSZ];
	char *ddssl_dir="";
	*created = FALSE;
	
	lprintf(priv_file,BFSZ-1,"%s%s",ddssl_dir,priv);
	if (reset && lib_exists(priv_file)) {
		char priv_file_old[BFSZ];
		lprintf(priv_file_old,BFSZ-1,"%s_old",priv_file);
		rename(priv_file,priv_file_old);
		remove(priv_file);
	}
	if (!lib_exists(priv_file)) {
//		int bits=768;
//		int bits=2048;
		int bits=dkimsize;
		int rr;
		if (RAND_status()!=1) {
			emsg("Warning - insufficient random data available. Private key may not be secure. To supply random data create a file called random_data.dat in the surgemail directory containing random data.\n");
		}
		imsg("myssl: Creating %d bit private key...\n",bits);
		lib_create_dir(priv_file,0700);
		rr = encrypt_lib_create_private_key(priv_file,bits,NULL);
		if (!rr) {
			emsg("myssl: FAILED Created private key file (%s) %d\n",priv_file,rr);
			return FALSE;
		}
		*created = TRUE;
		emsg("myssl: Created private key file (%s) %d\n",priv_file,rr);
	}

	lprintf(domkey_file,BFSZ-1,"%s%s",ddssl_dir,pub);
	if (reset && lib_exists(domkey_file))
		remove(domkey_file);
	if (!lib_exists(domkey_file)) {
		lib_create_dir(domkey_file,0700);
		encrypt_lib_create_pubkey(domkey_file,priv_file);
		emsg("myssl: Created domain key (%s)\n",domkey_file);
	}

	lprintf(result,BFSZ-1,"error reading public key");
	if (lib_exists(domkey_file)) {
		emsg("myssl: Loading domain key (%s)\n",domkey_file);
		if (!myssl_load_domainkey(domkey_file,BFSZ,result))
			lprintf(result,BFSZ-1,"error reading public key 2");
	}
	
	return TRUE;
}

#ifdef _MARIJN_DEBUG
// My SSL build environment seems rather messed up. I can't get surgemail to build without this
// These fucntions are ONLY FOR USE BY ME LOCALLY until I resolve my ssl environment
void BIO_set_flags(BIO *b, int flags)
{
}
int  BIO_test_flags(const BIO *b, int flags)
{
	return 0;
}
#endif


#endif


