/*

this file no longer used.

------------ SOME IDIOT LET THESE TWO DRIFT, i'M NOW USING AUTH.C IN ALL CASES, NOT SURE IF IT WILL WORK..--------

chrisp
---------------



 
 NOTE NOTE NOTE, THIS IS A COPY OF ..\MYSQLAUTH\AUTH.C THE FILES SHOULD MATCH 

 Module Name: External Authentication Protocol
Programed By: Lynden Sherriff
Created Date: 15/12/99

MySQLAuth compile instructions

	- need to include mysqlclient.a on make line
	- need to build library for each os
How to build libmysqlclient on reqd platform:

/*	- download and untar mysql-3.22.32.tar.Z
	- create makefile (./configure)
	- type make
	- find it in client/.libs/libmysqlclient.a
	- NB: need math library when you link it into project -lm

  Solaris_Sparc:
	cp libmysqlclient_solarissparc.a libmysqlclient.a
	gcc -o mysqlauth -g -w -Dsnprintf=plp_snprintf -Dvsnprintf=vplp_snprintf auth.c snprintf.c ssnprintf.c -lc -lnsl -lcrypt -lsocket -lm ./libmysqlclient.a
*/
/*
History:

	15 March 2000: 1.0a released with dmail version 2.8e for 
					NT and linux_libc6
	16 March 2000: 1.0b built
					- if unix_password set to true in ini file 
						check command looks for unix style encrypted password 
						as well as plain password and sql encrypted 
						passwords.
					- if unix_password set to true in ini file 
						set command stores password in unix encypted form.
	28 April 2000: 1.0c built
					TRW - made SELECT calls' WHERE filter handle when field_username is 
					not username, username was hardcoded.
	8  May 2000: 1.0d 	- hopefully in dmail2.8h
					TRW - made all usernames get lowercased before lookup, check, mod and set, if lowercase_username is set to true.
					TRW - code now always returns username as entered as per Netwin's EAP.
					TRW - added lowercase_username_set true, which means only lowercase usernames are added 
						to the database.  It can be used instead of lowercase_username and is 
						obsolete if lowercase_username is set to true.
	26 Jun 2000; 1.0e 
					TRW - added -version command
					TRW - added log true ini setting.
					TRW - removed debug ini setting as it causes confusion with 
							too many users, use log true instead.
					TRW - added logging of all that is written to STDOUT
					TRW - improved logging - added emsg, imsg, pid and date stamp
					TRW - added 'quit' command.
					TRW - FIXED BUG: NEXT_LINE was sending \r\n rather than just \n
						(caused dsmtp to report 'invalid authent response()')
	1.0f 
					TRW - added logging of last two characters of lines written out 
						for debugging invalid authent response bug.
	closed 27 Jun 2000;
	1.0g			
					TRW - fixed responses so that a uid is always given, previously 
						might respond, '+ok username path' which breaks E.A.P.
	closed 28 June 2000;
	1.0h
					TRW - added filtername x,y ini setting
					TRW - added plain_password setting(not used for set, only 
							on check)
	closed 4 July 2000;
					CPL - added hash_dir n_chars depth <base>, e.g.,
					 hash_dir 2 3 /var/spool/mail/
					 for 123456-bob,gives drop path of, 
						/var/spool/mail/12/34/56/123456-bob
	closed 5 July 2000;
	1.0i
	1.0j
					CPL - added alias_table and second lookup stuff for 
					interliant people.
	closed 19 July 2000;
	1.0k			
					Lynden - Changed some logging information from fixed to say 'MySQLAuth' to 
							 be product dependant. ie, RadiusAuth, MySQLAuth etc..

	1.0l for 2.8q build, 
					TRW - fixed death on unknown user when using ip mask, i.e. 
					myfree not checking for NULL and wrapped ipmask check in 
					h if (user){}.
	closed 21 July 2000;
	1.0m
					Lynden - Updated RadiusAuth to create a file (default 'radius_users.dat')
							That stores the username and encoded password so that the 'lookup'
							command works.
							Any time you check the pop server the username/password is added
							to the file if not already available.

					Lynden - The 'radius_users.dat' will attempt to be open three times before 
							failure. (2 secod delays between tries) Write only.
	1.0n TRW 9 Aug 2000
			- fixed death when no field_alias setting given.
	1.0o TRW 14 Aug 2000
			- fixed sql_del command using fixed username field.
	1.0p TRW 31 Oct 2000
			- added -insensitive option, for case insensitive matches on username.
	1.0q TRW 21 Feb 2001
		- Fixed bug where search returned a user with null password (caused seg fault on linux)
		(sql_select was strdup(NULL))
		- added mysql_emsg which prints mysql client library errno and error message
		- fixed bug where set would try to set NULL values for any fields set in ini file 
			but not entered on set line (depending on table setup fields may or may not 
			allow NULL values, so best not to set them rather than set them to NULL).
		- added settings,
			sqlsuffix_lookup_where <string>
			sqlsuffix_check_where <string>
			so that lookup SQL statement becomes,
			SELECT username,password,fwd FROM maildb WHERE username='bob' <string>;
			e.g.
			sqlsuffix_lookup_where <string>
			SELECT username,password,fwd FROM maildb WHERE username='bob' and attribute='Password'; 
		- added setting,
			sqlsuffix_set <string>
			so that set command will always have <string> added, e.g.,
			INSERT INTO maildb SET username='tam', value=PASSWORD('tam') <string>;
			(also used on the del command as well as the set command).
		- added setting,
			domain_split <db_domain_field>
			If set then on set, user@domain will be split into user and domain, so that username 
			added is just 'user' and the field db_domain_field is set with the value 'domain'. If the 
			username entered does not have a domain then the domain setting value will be used if it 
			is set, otherwise nothing is set in the db_domain_field. Similarly 
			on a lookup or check or search the username will be split up and the WHERE clause will 
			contain filter on, db_domain_field='domain'.  For del it does the same as for set.
		- removed support for mod command as was doing it in a dangerous manner - needs to be 
			rethought before adding back in.
		- startup log shows values of ini settings found in log.
	1.0r TRW
		- added close function, so that mysqlauth closes connection to server on exit (connection 
		should stay open between lookups, may need to investigate what happens on connection timeout if there 
		is one).
		- made failed to connect to database message specify db name and not always just 'maildb'.

  
	2.0a Lynden
		- Added LDAPAuth.

		- MYSQLAuth Changes:
				- moved mail_uid to be stored as part of the USER_INFO as LDAP can change depending of user.
				  SQL had only ini setting.
				- Fix memory leak in search command caused by '-from' '-max' settings.
				  USER_INFO was not being cleared on all possible conditions.
				- Moved some areas of the 'load_ini' in to 'sql_load' function as they were SQL settings
				  not global settings.
				- Moved SQL only global variables into the sql structure.

		******
			NOTE:	Since that this is suppose to support many different authenation methods.
					MySQL, LDAP, PAM etc.
					Then any changes to one area SHOULD NOT effect the other methods. :-)
					The way that I orginal wrote this allowed for this, this is why their is a seperate
					load function to account for specific ini setting for the methods. 

			PLEASE: Stop using global variables outside the method sturcture:
						ie.	The MySQL module as a struct called:
									struct { ...} mysql_xinfo;

							LDAP module is called:
									struct { ...} ldap_info;
							etc.
					This is so that it is easy to determine what module the varaible is for.
					This also stops confusion and other people changing what the variable does
					and effect other modules. :-)

					You need a new variable, add it to the correct structure DO NOT add it to the
					section of code named:
							 * Global Variables *
					As these variables should be limited to a very few.
		******




	2.0d Lynden
			Updated MYSQL to remove breaks and memory leaks in sql_check and sql_lookup.

	2.0e Lynden
			Updated MYSQL to also have the ini value:			
					info_fields <field_value_pair>
					info_fields <field_value_pair>
					...

			Fixed: Radius Auth to correctly load the userpass file.
	2.2f ChrisP
		Changed logging so it goes to 'mysqlauth.log, radiusauth.log, ldapauth.log by default
		Corrected manual page regarding log_path setting
		
	2.2g Lynden
		Added LDAP 'referrals'
	2.2h ChrisP
		Added ldap_maildeliveryoption, if defined then if that field contains 'mailbox'
		then if forwarding also cc it to the user fwd="$user,..."
		(these changes are to make it compatible with netscapes ldap system)
	2.2j ChrisP
		It now lookups up 'mailhost' and checks it matches settings
		it finds in dmail.conf for host_domain, if it doesn't match then
		it responds with a forward to that named host. 
	
	2.2k Lynden
		Updated: an Error message was getting display with LDAP alias that was not valid removed out.
		Changed: 'mysql_info' to 'mysql_xinfo' as some systems already have 'mysql_info' defined.
		Added:	Setup New INI setting called:	mysql_password_field
				That will allow users to replace:
							PASSWORD('xxxx')
				with		ENCRYPT('xxxx')
				
				 The setting would be:	mysql_password ENCRYPT

	2.2m Matt (Oct 4 2002)
		Added quotes around dn field so that it now obeys authentication protocol.
		I hope this doesn't break anything else that relied on there being no quotes,
		and that the dn field can't contain values with quotes.

	2.2n Matt (Oct 7 2002)
		Fixed load_ini function to prevent crashes and reading garbage values into settings with empty values.

  TO DO:
	- add info_fields <field_value_pair>, setting like in ldapauth
	- add logging of commands taking longer than 10 seconds.
	- add set option that does not touch the passwords (i.e. set_no_encryption true).




*/

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
static char *ncpy(char *dst,const char *src, int len);
#ifdef unix
void	ldap_memfree(void *a);
void	ldap_memfree(void *a){
		free(a);
}
#endif

#include <string.h>
#ifdef WIN32
#include <process.h>
#endif
#include <time.h>

/* mysql Links Required */
#ifdef WIN32
/* Ensure that the libs
			wsock32.lib
			mysqlclient.lib	(for MYSQL)
	are added to the project 

	and ignore the following libs
			libcd.lib		(for MYSQL, required for Radius)
*/
/*	char *crypt(char *pass, char *dpass); */
#	include <windows.h>
#	define getpid _getpid
#else
#	include <unistd.h>
#endif

int lprintf( char *buffer, int bfsize, char *format, ... );
int lvprintf( char *buffer, int bfsize, char *format, va_list arg_list);
#define	mymalloc	malloc

/************************************************************************************/
/************************************************************************************/
/* Local Defines */
/*****************/
/*
//		#define	SQLAUTH
		#define	PAMAUTH
		#define	RADIUSAUTH	(Not Available)

		#define	LDAPAUTH
*/
/*#define	RADIUSAUTH		*/
/*#define	LDAPAUTH */


#ifndef RADIUSAUTH
#	ifndef LDAPAUTH
#		ifndef	SQLAUTH
#			define	SQLAUTH
#		endif
#	endif
#endif

#define	myfree(x)		{ if (x) free(x); x = NULL; }
#define	mystrdup(x,y)	{ if (y) x = strdup(y); else x = NULL; }

/* PS:	If you change the below version place update the version changes above
		with reasons atleast so that when people come to edit them they only need to 
		check the below field. :-)
*/
#define	VERSION			"External Authentication v2.2n"
#define	FALSE			0
#define	TRUE			1

#define	BFSZ			512
#define	LBUFSZ			1024

#define	MAX_PARAMETERS	4

/*#define	NEXT_LINE		"\r\n"*/
#define	NEXT_LINE		"\n"
#define	LINE_OUT		stdout

#ifdef WIN32
#		define SEP '\\'
#else
#		define SEP '/'
#endif

enum {	CMD_HELP, CMD_EXIT, CMD_CHECK, CMD_LOOKUP, 
		CMD_SET, CMD_MOD, CMD_DEL, CMD_SEARCH, 
		CMD_INFO, CMD_DEBUG, CMD_QUIT, NUM_COMMAND };

char *command_names[] =
	{	"help", "exit", "check", "lookup", 

		"set", "mod", "del", "search", 
		
		"info", "debug", "quit", NULL };

/* Local Struct's */
/******************/
typedef struct user_info
{
	char *username;
	char *password;
	char *ipmask;
	char *drop_file_path;
	char *uid;
	char *mailhost;
	char *maildeliveryoption; /* If set to 'mailbox' then deliver local and forward */
	int limit_access;
	char *information;

	int mail_uid;
} USER_INFO;
typedef USER_INFO *pUSER_INFO;


typedef struct auth
{
	void (*load)(char *bf, char *str);
	void (*setup)(void);
	void (*clear)(void);
	int (*connect)(void);
	int (*close)(void);

/* General Commands */
	int (*exit)(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO (*check)(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO (*lookup)(char *parameter[MAX_PARAMETERS]);
	void (*help)(char *parameter[MAX_PARAMETERS]);

/* Extended Commands */
	int (*set)(char *parameter[MAX_PARAMETERS]);
	int (*mod)(char *parameter[MAX_PARAMETERS]);
	int (*del)(char *parameter[MAX_PARAMETERS]);
	int (*search)(char *parameter[MAX_PARAMETERS]);

/* Extended Externded Commands */
	int (*extended)(char *parameter[MAX_PARAMETERS]);
	
} AUTH;


/* Internal Functions */
/**********************/
void display_user(pUSER_INFO user, char *pre, char *orig_user);
int process_command(char *str);
int load_ini(char *path,char *arg);
int wild_match(char *wild,char *text);
int ld_match_list(char *xwild, char *xgrp2);

void imsg(char * arg_list, ...);
void emsg(char * arg_list, ...);
void mysql_emsg(char * arg_list, ...);/*add mysql client library errorno and reason to emsg*/
void outmsg(char * arg_list, ...);
void outmsg_nolog(char * arg_list, ...);
static char startuplog_bf[LBUFSZ];
static int startuplog_size;
static int startuplog_stop;
static int use_startuplog;
void startuplog(char * arg_list, ...);
void startuplog_init(void);
void startuplog_close(void);


#ifdef WIN32
#define DMAIL_CONFIG "dmail.conf" /* Just used for testing as is unix feature */
#else
#define DMAIL_CONFIG "/etc/dmail.conf"
#endif
typedef struct HOST {char *name; struct HOST *next;} Host;
static Host *hosts;
int ld_host_free(void);
int ld_host_add(char *s);
int ld_load_dmail(void);


/*#define stricmp strcmpnc*/
#define strnicmp strncmpnc
static int strncmpnc(const char *s1, const char *s2, size_t n);
static int strcmpnc(const char *s1, const char *s2);
char *line_build(char *argv[], int i, int argc);

void cmd_help(char *parameter[MAX_PARAMETERS]);
int cmd_info(char *parameter[MAX_PARAMETERS]);
static int isfwd_not_null;


/************************************************************************************/
/************************************************************************************/
/*
***************************
********** MYSQL **********
***************************
Supported Commands:

	exit
	check username password [fromIPaddress]
	lookup username


	set user [[password] info]
	mod user info
	del user
	search string


For more details about the Protocol see the URL:
	<http://netwinsite.com/dmail/authprot.htm>

NOTE:	mysql setup on FRED:
			~lynden/mysql/mysql
			./configure			<--- To start it

			./bin/mysqladmin -u root password qwerty
			 bin/mysql -u root -pqwerty


Create the SQL database as follows:
	CREATE DATABASE maildb;
	
	USE maildb;

	CREATE TABLE maildb (
		username CHAR(128) binary DEFAULT '' NOT NULL,
		passwd CHAR(128) DEFAULT '*' NOT NULL,
		forward CHAR(255) DEFAULT '',
		quota CHAR(20) DEFAULT '',
		mailmask CHAR(18) DEFAULT '0.0.0.0' NOT NULL,
		maildrop CHAR(255),

		created CHAR(50) DEFAULT '',
		full_name CHAR(128) DEFAULT '',
		phone CHAR(100) DEFAULT '',
		pass_question CHAR(255) DEFAULT '',
		pass_answer CHAR(255) DEFAULT '',
		groups CHAR(255) DEFAULT '',

		PRIMARY KEY (username)
	);

	CREATE TABLE alais (
		username CHAR(128) binary DEFAULT '' NOT NULL,
		alais CHAR(128) binary DEFAULT '' NOT NULL,
		PRIMARY KEY (username)
	);


Access issues 

	USE mysql;
	
	SELECT * FROM user;

	INSERT INTO user VALUES ('*', 'root', PASSWORD('qwerty'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

	INSERT INTO user VALUES ('161.29.2.41','root', PASSWORD('qwerty'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
	
	FLUSH PRIVILEGES;


You might need to do this:
	 ./bin/mysqladmin -u root -pqwerty flush-hosts 

	bin/mysql -u root -pqwerty maildb  
	
	


Create a test user with: [Database Structure Above]

	INSERT INTO maildb VALUES 
	('test@test.org', 'o7t9fpdDMGNJY', '', '100k',
     '0.0.0.0','/usr/spool/mail/test');

	INSERT INTO alais VALUES 
	('john@1.2.3.4', 'lynden@netwinsite.com');

	SELECT * FROM maildb;
	SELECT * FROM alais;

This will give you a user 'test@test.org' with a password of 'test'


How Alais works:
----------------
What it will do is attempt to seach in the 
alias database ('mysql_mail_alias_db') in the alias table 
('mysql_mail_alias_table') for the alias field ('field_alias') 
matches the username.

ie.	USE 'mysql_mail_alias_db'
	SELECT 'field_aliasuser' FROM 'mysql_mail_alias_table' 
			WHERE 'field_alias'='username'

	
	USE maildb
	SELECT email FROM maildb WHERE alias='username'

The ini setting should be:

	mysql_mail_alias_db maildb
	field_aliasuser email
	mysql_mail_alias_table maildb
	field_alias alias


*/

#ifdef SQLAUTH

#	ifdef WIN32
#		include "mysql\mysql.h"
#	else
#		include "mysql.h"
#	endif

#	define	X_TYPE		"MySQLAuth"
	void sql_load(char *bf, char *str);
	void sql_setup(void);
	void sql_clear(void);
	int sql_connect(void);
	int sql_connect2(int respond);
	int sql_close(void);
	
	int sql_info(char *parameter[MAX_PARAMETERS]);

	int sql_exit(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO sql_check(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO sql_lookup(char *parameter[MAX_PARAMETERS]);

/* Extended Commands */
	int sql_set(char *parameter[MAX_PARAMETERS]);
	int sql_mod(char *parameter[MAX_PARAMETERS]);
	int sql_del(char *parameter[MAX_PARAMETERS]);
	int sql_search(char *parameter[MAX_PARAMETERS]);

	AUTH auth_module = {
			sql_load,
			sql_setup, sql_clear, sql_connect, sql_close,
			sql_exit, sql_check, sql_lookup, cmd_help,
		/*	NULL, NULL, NULL, NULL */
			sql_set, NULL, sql_del, sql_search,
	
			NULL};


	struct 
	{
		MYSQL mysql;

		char *server;
		char *server2;
		char *login;
		char *password;
		char *mail_users_db;
		char *mail_users_table;
		char *mail_alias_db;
		char *mail_alias_table;
		char *mail_uid;

	/* SQL Fields */
		char *field_username;
		char *field_aliasuser;
		char *field_alias;
		char *field_password;
		char *field_forward;
		char *field_quota;
		char *field_mailmask;
		char *field_maildrop;

	/* SQL Password Field (PASSWORD/ENCRYPT)*/
		char *mysql_password;
		int encode_pass;


	/* Extra Fields */
		int field_count;
		char **field_name;			/* Other fields */


	/* Extra Variables needed */
		char *sqlsuffix_lookup_where;
		char *sqlsuffix_check_where;
		char *sqlsuffix_set;

		char *domain_split;

	} mysql_xinfo;

#endif

/************************************************************************************/
/************************************************************************************/
/*
*************************
********** PAM **********
*************************
(Pluggable Authentication Modules)

Supported Commands:

	exit
	check username password [fromIPaddress]
	lookup username


For more details about the Protocol see the URL:
	<http://netwinsite.com/dmail/authprot.htm>

*/

#ifdef PAMAUTH

#	ifdef WIN32
#		include "pam\pam_appl.h"
#	else
#		include "pam_appl.h"
#	endif

#	define	X_TYPE		"PAMAuth"
	void pam_load(char *bf, char *str);
	void pam_setup(void);
	void pam_clear(void);
	int pam_connect(void);
	
	int pam_exit(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO pam_check(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO pam_lookup(char *parameter[MAX_PARAMETERS]);

/* Internal Fn's */
	int pam_my_conv(int msg, const struct pam_message **msgx, struct pam_response **resp, void *appdata_ptr);

	AUTH auth_module = {  		
			pam_load,
			pam_setup, pam_clear, pam_connect,
			pam_exit, pam_check, pam_lookup, cmd_help,
			NULL, NULL, NULL, NULL,
	
			NULL};

	struct 
	{
		pam_handle_t *pamh=NULL;

		char *pam_type;
		struct pam_conv conv = { pam_my_conv, NULL };

		char *password;
	} pam_info;

#endif


/************************************************************************************/
/************************************************************************************/
/*
****************************
********** RADIUS **********
****************************
(Remote Authentication Dial In User Service)


  	and ignore the following libs
			libcd.lib		(for MYSQL)


Supported Commands:

	exit
	check username password [fromIPaddress]
	lookup username


For more details about the Protocol see the URL:
	<http://netwinsite.com/dmail/authprot.htm>


Change the following:

	/etc/raddb/clients
		161.29.2.41			testing123
		(test machine)
	
*/

#ifdef RADIUSAUTH

#	define MAXPWNAM        64
#	define MAXPASS         16

#	include <time.h>
#	include <string.h>

#	ifdef WIN32
/* #		include <winsock.h> */
#		include <process.h>
#		include <sys\types.h>
#		include <sys\stat.h>
#		include "radius\md5.c"
#		include "radius\radius.h"

#		define sleep(a) Sleep(a * 1000)
#		define msleep(a) Sleep(a)
#	else
#		include <unistd.h>
#include <sys/stat.h>
#include	<sys/types.h>
#include	<sys/socket.h>
#include	<sys/time.h>
#include	<netinet/in.h>
#include	<netdb.h>
#include	<pwd.h>
#include	<unistd.h>
#define SOCKET short
#define INVALID_SOCKET -1
char *strlwr( char *string );

#		include "md5.h"
#		include "radius.h"

#		define msleep(a) usleep(a*1000)
#	endif


#	define	X_TYPE		"RadiusAuth"

	void radius_load(char *bf, char *str);
	void radius_setup(void);
	void radius_clear(void);
	int radius_connect(void);
	int radius_close(void);
	
	int radius_exit(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO radius_check(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO radius_lookup(char *parameter[MAX_PARAMETERS]);


	int rad_request(char *username, char *passwd);
	int rad_send(void);
	int rad_read(void);
	int rad_decode(pUSER_INFO user );
	AUTH_REQ *rad_radrecv(pUSER_INFO user);
	DICT_ATTR *dict_attrget(int attribute);


	typedef struct user_data
	{
		char *user;
		char *password;
	} USER_DATA;

	typedef struct list
	{
		int size;
		USER_DATA *list;
	} LIST;

#	define	USER_HASH	(40*40)
	int user_loaded = FALSE, user_file_size = 0;
	char *user_file = "radius_users.dat";
	LIST full_user_list[USER_HASH];



/* External FN */
	void md5_calc(unsigned char *output, unsigned char *input, unsigned int inlen);

	AUTH auth_module =  {
			radius_load,
			radius_setup,radius_clear, radius_connect, radius_close,
			radius_exit, radius_check, radius_lookup, cmd_help,
			NULL, NULL, NULL, NULL,

			NULL
	};


		
	struct 
	{
		char *host;
		int port;

		UINT4 auth_ipaddr;
		int svc_port;

		SOCKET sockfd;
		int local_port;
		
	/* Sending/Receiving Data */
		char *secret;
		char *attribute;

		int store_id;

		int extra_code;

		struct sockaddr saremote;
		char vector[AUTH_VECTOR_LEN];
		char send_buffer[2048*sizeof(int)];
		int total_length;
		char recv_buffer[2048*sizeof(int)];
		int recv_length;
	} rad_info;

#endif

/************************************************************************************/
/************************************************************************************/
/*
**************************
********** LDAP **********
**************************
Supported Commands:

	exit
	check username password [fromIPaddress]
	lookup username [fromIPaddress]
	help

	set user [[password] info]
	del user
	search string -max x -from x

	open username password
	getval variable
	setval variable
		+DATA xx
		+DATA xx
		.
	close

For more details about the Protocol see the URL:
	<http://netwinsite.com/dmail/authprot.htm>


Build Notes:
	On NT, you have to include in preprocessor options,
			ldapauth\ldapsdk\include
	and link in the library,
			ldapauth\ldapsdk\lib\nsldap32v11.lib
	(user then has to put, nsldap32v11.dll into system directory).
	On UNIX, you have to include the directory,
			-I../ldap/include
	(for ldap.h and lber.h)
	and link in the liraries,
			-L../ldap/libraries -lldap -lber

	On UNIX can copy around 2 libraries and 3 header files.
		liblber.a
		libldap.a
		ldap.h
		lber.h
		ldap_cdefs.h
		(copies kept in c:\dmail\build29).  then use,
		LDAP_SPECLIBS = -lc -lnsl -lsocket -I./ ./libldap.a ./liblber.a

	On some system if the LDAPAuth failes to build due to:
		symbol		in file
		inet_aton 		/usr/local/openldap/openldap-2.0.18/libraries/libldap.a(os-ip.o)

	Then you will need to add:
 		-lresolv 
	to the compiler instructions.

  
    Notes:
    fromip may be blank on lookup or check
	returned uid of 0 or blank means dont check uid
	returned path of config means use username and dmail.conf drop_path for drop file location


*/

#ifdef LDAPAUTH

#	include "ld_sha.h"

#	ifdef WIN32
#		include <ldap.h>
#		ifdef HAS_SSL
#			include <ldap_ssl.h>
#		endif
#	else
#		include <unistd.h>
#		include <lber.h>
#		include <ldap.h>
#		ifdef HAS_SSL
#			include <ldap_ssl.h>
#		endif
#	endif

#ifdef HAS_SSL
#	define	X_TYPE		"LDAPAuth (SSL)"
#else
#	define	X_TYPE		"LDAPAuth"
#endif
	void ld_load(char *bf, char *str);
	void ld_setup(void);
	void ld_clear(void);
	int ld_connect(void);
	int ld_close(void);
	
	int ld_info(char *parameter[MAX_PARAMETERS]);

	int ld_exit(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO ld_check(char *parameter[MAX_PARAMETERS]);
	pUSER_INFO ld_lookup(char *parameter[MAX_PARAMETERS]);

	pUSER_INFO ld_filter(char *filter, char *xuser, char *pass, char *ip, int dblcheck);
	pUSER_INFO ld_scan_results(LDAPMessage *result, char *user, char *pass, int dblcheck);

/* Extended Commands */
	int ld_set(char *parameter[MAX_PARAMETERS]);
	int ld_mod(char *parameter[MAX_PARAMETERS]);
	int ld_del(char *parameter[MAX_PARAMETERS]);
	int ld_search(char *parameter[MAX_PARAMETERS]);

/* Extended Extended Commands */
	int ld_extended(char *parameter[MAX_PARAMETERS]);
	int ld_user_open(char *parameter[MAX_PARAMETERS]);
	int ld_user_set(char *parameter[MAX_PARAMETERS]);
	int ld_user_get(char *parameter[MAX_PARAMETERS]);
	int ld_user_close(char *parameter[MAX_PARAMETERS]);

	AUTH auth_module = {
			ld_load,
			ld_setup, ld_clear, ld_connect, ld_close,
			ld_exit, ld_check, ld_lookup, cmd_help,
		/*	NULL, NULL, NULL, NULL */
			ld_set, NULL, ld_del, ld_search,
	
			ld_extended};


	struct 
	{
		LDAP *ldap;

		char *host;
		char *host2;
		int which_host;
		int port;
		char *manager_dn;
		char *manager_pass;
		char *search_base;
	/* */
		char *search_name;
		char *search_name_alt;
		char *search_name_alt2;
		char *uid;
		char *objectclass;
		char pass_crypt;		/*  0 - ssha_hash_dont true
									1 - sha_hash true
									2 - plain text
								*/

	/* Other LDAP options */
		int user_extend;
		int scope;
		int verbose;

	/* Group Fields */
		char *group_base;
		char *group_search;
		char *group_attrib;
		char *group_field;

	/* LDAP Fields */		
		char *ldap_dmail_forward;	/* field_forward */
		char *ldap_drop_file;		/* field_maildrop */
		char *ldap_mailhost;
		char *ldap_maildeliveryoption;
		char *ldap_access_wild;
		char *ldap_access_field;
		char *ldap_mailmask;		/* field_mailmask */
		char *ldap_dmail_uid;		

		int field_count;
		char **field_name;			/* Other fields */

		int must_count;
		char **must_name;			/* Must fields */

		char *field_username;
		char *field_aliasuser;
		char *field_alias;
		char *field_password;

	/* Added Ini setting */
		int dnshort;				/* When set to 'TRUE' then serarch for name only */

	/* Externed Variables */
		LDAPMessage	*result;
		LDAPMessage *entry;

		int set_data;
		char *user, *pass;

	/* SSL Connection Variables */
		int sslenable;
		char *certdbpath;
	} ldap_info;


#endif

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/* Main Body of Code */
/*********************/
/****** Lynden.
	NOTE:	Since that this is suppose to support many different authenation methods.
			MySQL, LDAP, PAM etc.
			Then any changes to one area SHOULD NOT effect the other methods. :-)
			The way that I orginal wrote this allowed for this, this is why their is a seperate
			load function to account for specific ini setting for the methods. 

	PLEASE: Stop using global variables outside the method sturcture:
				ie.	The MySQL module as a struct called:
							struct { ...} mysql_xinfo;

					LDAP module is called:
							struct { ...} ldap_info;
					etc.
			This is so that it is easy to determine what module the varaible is for.
			This also stops confusion and other people changing what the variable does
			and effect other modules. :-)

			You need a new variable, add it to the correct structure DO NOT add it to the
			section of code named:
					 * Global Variables *
			As these variables should be limited to a very few.
******/

/* Global Variables */
int isdebug = 0, mail_uid = 0, islowercase_username = 0, islowercase_username_set=0, islog = 0;
char *log_path = NULL;
char *domain = NULL;
char *info_pass = NULL;
int isunix_password = 0;
int isplain_password = 0;
char char_search=0, char_replace=0;
char dropdir[256];
int hashchars, hashdepth;
static int pid;

int search_min = 0;

void clear_mem(void)
{
	if (auth_module.close){
		imsg("Closing Connection");
		auth_module.close();
	}
		
	if (auth_module.clear)
		auth_module.clear();

	myfree(log_path);
}


int main(int argc, char *argv[])
{
	char bf[LBUFSZ], *s;
	char *path = NULL;
	int end = FALSE;
	int t=1, i;
#ifdef unix_password
	srand(time(NULL));
#endif

	startuplog_init();

	imsg("***Entered Argument***");
	for (i=1;i<argc;i++) {
		imsg("*** %d - %s ***",i, argv[i]);
		if (!stricmp(argv[i],"-debug"))
			isdebug = TRUE;
		else if (!stricmp(argv[i],"-path")) {
			i++;
			if (i<argc) {
				imsg("*** %d - %s ***",i, argv[i]);
				path = argv[i];
			}
		}
		else if (!stricmp(argv[i],"-log"))
			islog = TRUE;
		else if (!stricmp(argv[i],"-version")){
			printf("Netwin's %s Version %s\n+OK\n",X_TYPE,VERSION);
			exit(0);
		}
		else if (!strnicmp(argv[i], "-filtername,", 11)){
			s=strchr(argv[i], ',');
			if(s==NULL){
				printf("Invalid parameter to filtername\n");
				exit(0);
			}
			s++;
			char_search=*s++;
			s=strchr(s, ',');
			if(s==NULL){
				printf("Invalid second parameter to filtername\n");
				exit(0);
			}
			s++;
			if(*s==0)
				char_replace=' ';
			else
				char_replace=*s;
		}
	}
	pid = getpid();
#ifdef RADIUSAUTH
	log_path = strdup("radiusauth.log");
#endif
#ifdef LDAPAUTH
	log_path = strdup("ldapauth.log");
#endif
#ifdef	SQLAUTH
	log_path = strdup("mysqlauth.log");
#endif
	if (log_path==NULL) log_path = strdup("auth.log");

	/*trw moved startup log lines from here*/
	if (auth_module.setup)
		auth_module.setup();

	load_ini(path,argv[0]);
	
	startuplog_close();
	if (isdebug)
		imsg("**%s %s Started in debug mode, pid=%d,exe/log_path={%s}\n",X_TYPE,VERSION,pid,log_path);
	if (islog)
		imsg("**%s %s Started in logging mode, pid=%d,exe/log_path={%s}\n",X_TYPE,VERSION,pid,log_path);


	if (auth_module.connect) {
		if (!auth_module.connect()) 
			exit(0);
	}
	/*TRW add command line checks for most commands*/
again:
	if (argc>t) {
		imsg("***Detected command line command***\n");

		if (strcmp(argv[t],"-add")==0)	{if (t+3>=argc)  lprintf(bf,LBUFSZ-1,"set %s %s %s\n",argv[t+1],argv[t+2],line_build(argv,t+3,argc)); goto fakecommand;}
		else if (strcmp(argv[t],"-set")==0) {if (t+2>=argc) goto badp; lprintf(bf,LBUFSZ-1,"set %s %s %s\n",argv[t+1],argv[t+2],line_build(argv,t+3,argc)); goto fakecommand;}
		else if (strcmp(argv[t],"-mod")==0) {if (t+1>=argc) goto badp; lprintf(bf,LBUFSZ-1,"set %s %s %s\n",argv[t+1],"(NULL)",line_build(argv,t+3,argc)); goto fakecommand;}
		else if (strcmp(argv[t],"-del")==0) {if (t+1>=argc) goto badp; lprintf(bf,LBUFSZ-1,"del %s\n",argv[t+1]); goto fakecommand;}
		else if (strcmp(argv[t],"-check")==0) {if (t+2>=argc) goto badp; lprintf(bf,LBUFSZ-1,"check %s %s\n",argv[t+1],argv[t+2]); goto fakecommand;}
		else if (strcmp(argv[t],"-lookup")==0) {if (t+1>=argc) goto badp; lprintf(bf,LBUFSZ-1,"lookup %s\n",argv[t+1]); goto fakecommand;}
		else if (strcmp(argv[t],"-log")==0) {t++;  goto again;}
		else if (strcmp(argv[t],"-debug")==0) {t++;  goto again;}
		else if (strcmp(argv[t],"-path")==0) {t += 2;  goto again;}
		else {
			emsg("-ERR: invalid switch {%s}\n",argv[t]);
			printf("-ERR: invalid switch {%s}\n",argv[t]);/*TRW added 2.0f*/
		}
		clear_mem();
		return 0;
badp:		printf("Wrong number of paramters\n");
			fflush(stdout);
			clear_mem();
		return 0;
fakecommand:
		bf[LBUFSZ]=0;
		process_command(bf);
		fflush(stdout);
		clear_mem();
		return 0;
	}
	
/*	fprintf(LINE_OUT,"+OK %s (%s)%s",VERSION, X_TYPE, NEXT_LINE); */
	imsg("***Waiting for command***\n");

/* Input from stdin */
	while (!end) {
		if (!fgets(bf,1024,stdin))
			break;

		end = process_command(bf);
	}

	imsg("***Exiting***\n");

	fflush(stdout);
	clear_mem();
	return 0;
}
char *line_build(char *argv[], int i, int argc)
{
	static char info[BFSZ];
	strcpy(info,"");
	for (;i<argc;i++) {
		strcat(info,argv[i]);
		strcat(info," ");
	}
	return info;
}

/************************************************************************************/
/************************************************************************************/
void clear_user(pUSER_INFO user)
{
	if (user) {
		myfree(user->username);
		myfree(user->password);
		myfree(user->ipmask);
		myfree(user->drop_file_path);
		myfree(user->uid);
		myfree(user->mailhost);
		myfree(user->maildeliveryoption);
		myfree(user->information);
	}
}

void display_user(pUSER_INFO user, char *pre, char *orig_user)
{
	int dohash=FALSE;
	char *duinfo = NULL;
	char *drop = NULL;
	char *space =""; 
	char fakeuser[256];

	int mail_uid = user->mail_uid;

	imsg("...Displaying User");

	if (user->information){
		duinfo = strdup(user->information);
		space = " ";
	}
	else
		duinfo = strdup("");

	if (!user->drop_file_path)
		dohash=TRUE;
	else if(strcmp(user->drop_file_path, "config")==0)
		dohash=TRUE;

	if(dohash&&dropdir[0]!=0)
	{
		char tbuf[256], *cur;
		int i;
		char *duser;

		duser = user->username;
#ifdef LDAPAUTH
		if (ldap_info.uid) duser = user->uid;
#endif
		if (duser==NULL) duser = user->username;
		strcpy(fakeuser, duser);
	
		/* Trim off the domain */
		cur=fakeuser;
		while(strchr(cur, '@')!=NULL)
		{
			cur=strchr(cur, '@');
			cur++;
		}
		if(cur!=fakeuser)
			*(cur-1)=0;

		/* Pad out the username */
		strcat(fakeuser, "_________________________");

		/* Prepare the root path */
		strcpy(tbuf, dropdir);
		if((tbuf[strlen(tbuf)-1]!='\\')&&(tbuf[strlen(tbuf)-1]!='/'))
			strcat(tbuf, "/");

		cur=tbuf+strlen(tbuf);
		for(i=0;i<hashdepth;i++)
		{
			strcpy(cur, fakeuser+(i*hashchars));
			cur[hashchars]=0;
			strcat(cur, "/");
			cur+=hashchars;
			cur++;
		}
		strcat(tbuf,duser);

		drop=strdup(tbuf);
	}
	else if(user->drop_file_path!=NULL)
		drop=strdup(user->drop_file_path);
	else
		drop=strdup("config");
	


	if (!pre) pre = "-ERR";
	if (!orig_user) orig_user = "unknown";
	if (!duinfo) duinfo = "unknown";
/*
	imsg("...pre = %s",pre);
	imsg("...orig_user = %s",orig_user);
	imsg("...duinfo = %s",duinfo);
	imsg("...drop = %s",drop);
	imsg("...mail_uid = %d",mail_uid);
	imsg("...space = %s",space);
	imsg("...duinfo = %s",duinfo);
*/

	if (mail_uid && drop)
			outmsg("%s %s %s %d%s%s%s"
					, pre
					, orig_user
					, drop
					, mail_uid
					, space
					, duinfo
					,NEXT_LINE);
	else if (!mail_uid && drop)
		outmsg("%s %s %s 0%s%s%s"
				, pre
				, orig_user
				, drop
				, space
				, duinfo
				,NEXT_LINE);
	else if (!mail_uid && !drop)
		outmsg("%s %s config 0%s%s%s"
				, pre
				, orig_user
				, space
				, duinfo
				,NEXT_LINE);
	else if (mail_uid && !drop)
		outmsg("%s %s config %d%s%s%s"
				, pre
				, orig_user
				, mail_uid
				, space
				, duinfo
				,NEXT_LINE);

	imsg("...Part 3");
/*trw 1.0g; 
	if (user->information) {
		if (mail_uid && user->drop_file_path)
			outmsg("%s %s %s %s %s%s"
					, pre
					, orig_user
					, user->drop_file_path
					, mail_uid
					, user->information
					,NEXT_LINE);
		else if (!mail_uid && user->drop_file_path)
			outmsg("%s %s %s 0 %s%s"
					, pre
					, orig_user
					, user->drop_file_path
					, user->information
					,NEXT_LINE);
		else if (!mail_uid && !user->drop_file_path)
			outmsg("%s %s config 0 %s%s"
					, pre
					, orig_user
					, user->information
					,NEXT_LINE);
		else if (mail_uid && !user->drop_file_path)
			outmsg("%s %s config %s %s%s"
					, pre
					, orig_user
					, mail_uid
					, user->information
					,NEXT_LINE);
	} else if (user->drop_file_path)
			outmsg("%s %s %s%s"
					, pre
					, orig_user
					, user->drop_file_path										
					,NEXT_LINE);
	else if (!mail_uid && !user->drop_file_path)
		outmsg("%s %s config 0%s"
				, pre
				, user->username
				,NEXT_LINE);
	*/
	if (duinfo) myfree(duinfo);

	if(drop)
		myfree(drop);

	clear_user(user);
}


void filtername(char *name){
	/*replace all instances of char filterfind with char filterreplace*/
	char *p,*s;

	if(!char_search)
		return;
	if(!char_replace)
		return;

	imsg("filtername given (%s), (%c) (%c)\n",name,char_search,char_replace?char_replace:' ');
	for (s=p=name;*p!=0 && p!=NULL;p++){
		if (*p==char_search) {
			if (char_replace) {
				*s++=char_replace;
				continue;
			}
			else	/*blank out*/
				continue;
		}
		*s++=*p;
	}
	*s=0;
	imsg("filtername result (%s)\n",name);
}
#if 0
void set_db_and_table(char *name){
	/*find domain information */
	/*if (domain_drop) chop username at @*/
	/*lookup domain in table for domain and table info which for given domain*/
}
#endif

int process_command(char *str)
{
	char *cmd, *ptr, *end;
	char *parameter[MAX_PARAMETERS];
	int i = 0, OK = TRUE;
	char *orig_user;

/* Cut up cmd */
	memset(parameter, 0, sizeof(char *) * MAX_PARAMETERS);
	cmd = strdup(str);

	end = cmd + strlen(cmd) - 1;
	while (end > cmd && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\t'))
		*end-- = '\0';

	ptr = cmd;
	while (ptr && i < MAX_PARAMETERS-1) {
		end = strchr(ptr,' ');
		if (end) {
			while (*end == ' ')	*end++ = '\0';
		}

		parameter[i++] = strdup(ptr);
		ptr = end;
	}
	if (ptr)
		parameter[i] = strdup(ptr);

/* Locate Command */
	for (i=0;i<NUM_COMMAND;i++) {
		if (command_names[i][0] == parameter[0][0] &&
			!stricmp(command_names[i], parameter[0]))
			break;
	}

/* Process Command */
	switch (i) {
	/* Basic Commands */
		case CMD_QUIT:
		case CMD_EXIT:
			if (auth_module.exit) {
				OK = auth_module.exit(parameter);

				if (OK)
					outmsg("+OK%s",NEXT_LINE);
				else
					outmsg("-ERR Failed Exit%s",NEXT_LINE);				
			}
			else
				outmsg("-ERR 'exit' is not supported%s",NEXT_LINE);
			return TRUE;


		case CMD_CHECK:
			if (auth_module.check) {
				if (parameter[1] && parameter[2]) {
					pUSER_INFO user;
					orig_user = strdup(parameter[1]);
					if (islowercase_username) strlwr(parameter[1]);

					filtername(parameter[1]);
					/*set_db_and_table(parameter[1]);*/

					user = auth_module.check(parameter);
				
					if (user)
						display_user(user, "+OK", orig_user);
					else
						outmsg("-ERR %s password wrong or not a valid user%s", orig_user,NEXT_LINE);
					myfree(orig_user);
					clear_user(user);
				}
				else
					outmsg("-ERR No username/password provided%s", NEXT_LINE);
			}
			else
				outmsg("-ERR 'check' is not supported%s",NEXT_LINE);
			break;


		case CMD_LOOKUP:
			if (auth_module.lookup) {
				if (parameter[1]) {
					pUSER_INFO user;
					orig_user = strdup(parameter[1]);
					if (islowercase_username) strlwr(parameter[1]);

					filtername(parameter[1]);	

					user = auth_module.lookup(parameter);
									

					if (user)
						display_user(user, "+OK",orig_user);
					else
						outmsg("-ERR %s Not Located%s", orig_user, NEXT_LINE);
					myfree(orig_user);
				}
				else
					outmsg("-ERR No username/password provided%s", NEXT_LINE);
			}
			else
				outmsg("-ERR 'lookup' is not supported%s",NEXT_LINE);
			break;


		case CMD_HELP:
			if (auth_module.help) {
				auth_module.help(parameter);
				if (auth_module.extended) 
					auth_module.extended(parameter);
				outmsg("+OK%s", NEXT_LINE);
			}
			else
				outmsg("-ERR 'help' is not supported%s",NEXT_LINE);			
			break;


	/* Extended Commands */
		case CMD_SET:
			if (auth_module.set) {
				if (parameter[1]) {	
					if (parameter[2]) {	
						orig_user = strdup(parameter[1]);
						if (islowercase_username || islowercase_username_set) strlwr(parameter[1]);

						filtername(parameter[1]);	

						OK = auth_module.set(parameter);

						if (OK > 0)
							outmsg("+OK '%s' has been added to database%s",orig_user,NEXT_LINE);
						else if (!OK)
							outmsg("-ERR Failed to add '%s' (check database for fields match)%s",orig_user,NEXT_LINE);
						myfree(orig_user);
					}
					else
						outmsg("-ERR No password provided%s", NEXT_LINE);
				}
				else
					outmsg("-ERR No username provided%s", NEXT_LINE);
			}
			else
				outmsg("-ERR 'set' is not supported%s",NEXT_LINE);
			break;


		case CMD_MOD:
			/*if (auth_module.mod) {
				if (parameter[1]) {		
					orig_user = strdup(parameter[1]);
					if (islowercase_username) strlwr(parameter[1]);

					filtername(parameter[1]);	

					OK = auth_module.mod(parameter);

					filtername(parameter[1]);	

					if (OK)
						outmsg("+OK '%s' information has been updated%s",orig_user,NEXT_LINE);
					else
						outmsg("-ERR '%s' is not a valid user%s",orig_user,NEXT_LINE);
					myfree(orig_user);
				}
				else
					outmsg("-ERR No username provided%s", NEXT_LINE);	
			}
			else*/
				outmsg("-ERR 'mod' is not supported%s",NEXT_LINE);
				
			break;


		case CMD_DEL:
			if (auth_module.del) {
				if (parameter[1]) {
					orig_user = strdup(parameter[1]);
					if (islowercase_username) strlwr(parameter[1]);

					filtername(parameter[1]);	

					OK = auth_module.del(parameter);

					if (OK == TRUE)
						outmsg("+OK '%s' has been deleted%s",orig_user,NEXT_LINE);
					else if (!OK)
						outmsg("-ERR '%s' is not a valid user%s",orig_user,NEXT_LINE);
					myfree(orig_user);
				}
				else
					outmsg("-ERR No username provided%s", NEXT_LINE);
				
			}
			else
				outmsg("-ERR 'del' is not supported%s",NEXT_LINE);
			break;


		case CMD_SEARCH:
			if (auth_module.search) {
				if (parameter[1]) {
					int OK = TRUE;

					if (search_min > 0) {
						char *str = parameter[1];
						int count = 0;
						
						while (*str != '\0' && *str != '@') {
							if (*str != '*')
								count++;
							str++;
						}
						

						if (count < search_min)
							OK = FALSE;
					}

					if (OK) {
						int num = auth_module.search(parameter);
						if (num >= 0) 
							outmsg("+OK Search Completed %d items found%s", num,NEXT_LINE);
					}
					else
						outmsg("-ERR Search Field to short%s", NEXT_LINE);
				}
				else
					outmsg("-ERR No search field provided%s", NEXT_LINE);
			}
			else
				outmsg("-ERR 'search' is not supported%s",NEXT_LINE);
			break;


		
		case CMD_INFO:
			if (cmd_info(parameter))
				outmsg("+OK%s", NEXT_LINE);
			else
				outmsg("+ERR Info Display Failed%s", NEXT_LINE);
			break;

		case CMD_DEBUG:
			if (isdebug) {
				isdebug = FALSE;
				outmsg("+OK Debug Turned OFF%s", NEXT_LINE);
			}
			else {
				isdebug = TRUE;
				outmsg("+OK Debug Turned ON%s", NEXT_LINE);
			}
			break;

		default:
		/* Pass on to extended commands */
			if (auth_module.extended) {
				if (!auth_module.extended(parameter))
					outmsg("-ERR Command not supported '%.20s'%s",parameter[0],NEXT_LINE);
			}
			else
				outmsg("-ERR Command not supported '%.20s'%s",parameter[0],NEXT_LINE);
			break;
	}
	fflush(LINE_OUT);

	for (i = 0; i < MAX_PARAMETERS;i++) 
		myfree(parameter[i]);

	myfree(cmd);
	return FALSE;
}

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

int load_ini(char *path,char *arg)
{
	FILE *stream;
	char filename[BFSZ];
	char filepath[BFSZ];
	char bf[LBUFSZ];
	char oldname[BFSZ];
	char *str,*s;

	strcpy(dropdir, "");
	hashchars=1;
	hashdepth=1;

	
/* Build auth.ini from arg[0] */
	str = arg + strlen(arg);
	while (*str != SEP &&  str > arg) str--;
	if (*str == SEP) str++;
	strcpy(filename,str);
	str = strchr(filename,'.');
	if (str) *str = '\0';
	strcat(filename,".ini");
/* bf = auth.ini */

	if (path) {
		sprintf(filepath,"%s%c%s",path,SEP,filename);
	}
	else {
		strcpy(filepath,arg);
		str = strchr(filepath,'.');
		if (str) *str = '\0';
		strcat(filepath,".ini");
	}


	stream = fopen(filepath,"rb");
	ncpy(oldname,filepath,BFSZ-1);
	if (!stream) {
#ifdef WIN32
		GetSystemDirectory(bf,BFSZ-1);
		strcat(bf,"\\");
		strcat(bf,filename);
#else
		sprintf(bf,"/etc/%s",filename);
#endif
		strcpy(filepath,bf);
		stream = fopen(filepath,"rb");
		if (!stream) {
			stream = fopen(filename,"rb");
		}
	}
	if (stream==NULL) {
		printf("-DEAD Unable to open(%s) or (%s)\n",oldname,filename);
	}

	if (stream) {
		imsg("INI: Loading {%s}",filename);
		myfree(log_path);

		while (!feof(stream)) {
			if (!fgets(bf,LBUFSZ-1,stream))
				break;

			if (*bf == '#' || *bf == '\r' || *bf == '\n' || *bf == '\0')
				continue;

			str = bf + strlen(bf) - 1;
			while (str > bf && (*str == ' ' || *str == '\t' || *str == '\r' || *str == '\n')) /* str>bf added by Matt to prevent crashing. */
				*str-- = '\0';
			
			str = bf;
			while (*str!=0 && *str != ' ' && *str != '\t') /* *str!=0 && added by Matt to prevent crashing  */
				str++;

			if (*str || *str==0) { /*  || *str==0 added by Matt so it reads in empty ini values */
				while (*str == ' ' || *str == '\t')
					*str++ = '\0';

				imsg("INI:         Located {%s} {%s}",bf,str);
			/* removed as causes confusion when accidently left on 				
				if (!stricmp(bf,"debug")) {
					if (!stricmp(str,"true")) {
						isdebug = TRUE;
						if (!log_path)
							log_path = strdup("auth.log");
					}
					else
						isdebug = FALSE;
				}
			*/
				if (!stricmp(bf,"log")) {
					if (!stricmp(str,"true")) {
						islog = TRUE;
						if (!log_path)
							log_path = strdup("auth.log");
					}
					else
						islog = FALSE;
				}
				else if (!stricmp(bf,"fwd_not_null")) {
					if (!stricmp(str,"true")) {
						isfwd_not_null = TRUE;
					}else
						isfwd_not_null = FALSE;
				}
				else if (!stricmp(bf,"dir_hash")) {
					char *s2;

					s2=str;
					
					if(((*s2>='0')&&(*s2<='9'))&&(*(s2+1)==' '))
					{
						hashchars=atoi(s2);
						if(hashchars>5)
							hashchars=5;
						if(hashchars<1)
							hashchars=1;
						s2+=2;
					}

					if(((*s2>='0')&&(*s2<='9'))&&(*(s2+1)==' '))
					{
						hashdepth=atoi(s2);
						if(hashdepth>5)
							hashdepth=5;
						if(hashdepth<1)
							hashdepth=1;
						s2+=2;
					}
					
					strcpy(dropdir, s2);
					imsg("dir_hash - base(%s),chars(%d),depth(%d)%s",dropdir,hashchars,hashdepth,s2-2);
				}
#ifdef unix_password
				else if (!stricmp(bf,"unix_password")) {
					if (!stricmp(str,"true")) {
						isunix_password = TRUE;
						isplain_password = TRUE;
					}
					else
						isunix_password = FALSE;
				}
#else
				else if (!stricmp(bf,"unix_password")) {
					emsg("Found unexpected unix_password setting. Please contact DMail support");
				}
#endif
				else if (!stricmp(bf,"plain_password")) {
					if (!stricmp(str,"true")) {
						isplain_password = TRUE;
					}
					else
						isplain_password = FALSE;
				}
				else if (!strnicmp(bf,"filtername",10)) {
				
					s=strchr(str, ',');
					if(s==NULL){
						emsg("Invalid parameter to filtername\n");
						continue;
					}
					*s++=0;
					char_search=str[0];
					if(*s==0)
						char_replace=' ';
					else
						char_replace=*s;
				}
				else if (!stricmp(bf,"log_path")) {
					myfree(log_path);
					log_path = strdup(str);
				}
				else if (!stricmp(bf,"domain")) {
					myfree(domain);
					domain = strdup(str);
				}
				else if (!stricmp(bf,"info_pass")) {
					myfree(info_pass);
					info_pass = strdup(str);
				}
				else if (!stricmp(bf,"lowercase_username")) {
					if (!stricmp(str,"true")) {
						islowercase_username = TRUE;
					}
					else
						islowercase_username = FALSE;
				}  
				else if (!stricmp(bf,"lowercase_username_set")) {
					if (!stricmp(str,"true")) {
						islowercase_username_set = TRUE;
					}
					else
						islowercase_username_set = FALSE;
				}
				else if (!stricmp(bf,"search_min")) {
					search_min = atoi(str);
				}
				else if (auth_module.load)
					auth_module.load(bf,str);
			}
		}
		fclose(stream);
		return TRUE;
	}
	else
		emsg("INI: Failed to load {%s}",filename);
	return FALSE;
}

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

void imsg(char * arg_list, ...)
{	
	va_list arg_ptr;
  	char data[LBUFSZ],*s;
	time_t stamp;
	char bf[512],*p;
	FILE *stream;

	if (!isdebug && !islog && !use_startuplog) {/*log_path should be set at least to exe directory.*/
		return;
	}
	
	

	va_start(arg_ptr, arg_list);
	/*vsprintf(data, arg_list, arg_ptr);*/
	lvprintf(data, LBUFSZ, arg_list, arg_ptr);
	if ((s=strrchr(data,'\r'))!=NULL) *s=0;
	if ((s=strrchr(data,'\n'))!=NULL) *s=0;
	
	time(&stamp);
	strcpy(bf,ctime(&stamp));
	if ((p=strchr(bf,'\n'))!=NULL) *p=0;/*chop off cr*/

	/*if (!log_path) {
		printf("No log_path is set ... %d %s %s%s",pid,bf+4,data,NEXT_LINE);
		return;
	}*/
	/*if (isdebug)
		fprintf(LINE_OUT,"%s%s",data,NEXT_LINE);*/
	if (use_startuplog) {
		startuplog("%d %s %s%s",pid,bf+4,data,NEXT_LINE);
		return;
	}
	if (isdebug) outmsg_nolog("%d %s %s%s",pid,bf+4,data,NEXT_LINE);
	if (log_path) {
		stream = fopen(log_path,"a");
		if (stream) {
			/*fprintf(stream,"%s%s",data,NEXT_LINE);*/
			fprintf(stream,"%d %s %s\n",pid,bf+4,data);
			fclose(stream);
		}
	}
	/*}*/
}
void emsg(char * arg_list, ...)/*added from odbcauth*/
{
	va_list arg_ptr;
  	char data[LBUFSZ],*s;
	time_t stamp;
	char bf[512],*p;
	FILE *stream;

	
	/*if (!log_path) {//should be set at least to exe directory.
		if (isdebug || islog) printf("ERROR: No log_path set\n");
		return;
	}*/
	

	va_start(arg_ptr, arg_list);
	lvprintf(data,LBUFSZ-1, arg_list, arg_ptr);
	if ((s=strrchr(data,'\r'))!=NULL) *s=0;
	if ((s=strrchr(data,'\n'))!=NULL) *s=0;
	
	time(&stamp);
	strcpy(bf,ctime(&stamp));
	if ((p=strchr(bf,'\n'))!=NULL) *p=0;/*chop off cr*/
	if (use_startuplog) {
		startuplog("%d %s ERROR: %s%s",pid,bf+4,data,NEXT_LINE);
		return;
	}
	if (isdebug) outmsg_nolog("%d %s ERROR: %s%s",pid,bf+4,data,NEXT_LINE);

/* Fixed By Lynden to account for no log path. */
	if (log_path) {
		stream = fopen(log_path,"a");
		if (stream) {
			fprintf(stream,"%d %s ERROR: %s\n",pid,bf+4,data);
			fclose(stream);
		}
	}
}


void outmsg(char * arg_list, ...)
{
	/*write line to stdout, but also log it*/
	va_list arg_ptr;
  	char data[LBUFSZ];
	int len;

	va_start(arg_ptr, arg_list);
	lvprintf(data,LBUFSZ-1, arg_list, arg_ptr);
	fprintf(LINE_OUT,"%s",data);
	imsg("--> (%s)",data);
	if (islog || isdebug){
		len = strlen(data);
		if (len>2)
			imsg("end is (%02x)(%02x)",data[len-2],data[len-1]);
	}
}

void outmsg_nolog(char * arg_list, ...){
	/*write line to stdout, but DO NOT LOG IT*/
	va_list arg_ptr;
  	char data[LBUFSZ];

	va_start(arg_ptr, arg_list);
	lvprintf(data,LBUFSZ-1, arg_list, arg_ptr);
	fprintf(LINE_OUT,"%s",data);
	
}

void startuplog(char * arg_list, ...){
	va_list arg_ptr;
  	char data[LBUFSZ],*s;
	if (!use_startuplog) return;
	if (startuplog_stop) return;
	va_start(arg_ptr, arg_list);
	startuplog_size += lvprintf(data,LBUFSZ-1, arg_list, arg_ptr);
	if ((s=strrchr(data,'\r'))!=NULL) *s=0;
	if ((s=strrchr(data,'\n'))!=NULL) *s=0;
	
	if (startuplog_size<LBUFSZ-1){
		strcat(startuplog_bf,"##");startuplog_size +=2;
		strcat(startuplog_bf,data);
	}else
		startuplog_stop=1;
	
}
void startuplog_init(void){
	/*write line to stdout, but DO NOT LOG IT*/
	startuplog_size=0;
	strcpy(startuplog_bf,"");
	startuplog_stop=0;
	use_startuplog=1;
	
}
void startuplog_close(void){
	char *s;
	use_startuplog=0;
	imsg("Contents of Start Up log buffer {\n");
	s=startuplog_bf;
	s=strtok(startuplog_bf,"##");
	if (s!=NULL)
		imsg("%s",s);
	for (;s!=NULL;s=strtok(NULL,"##")){
		if (s!=NULL)
			imsg("%s",s);
	}
	imsg("}\n");
	startuplog_size=0;
	strcpy(startuplog_bf,"");
	startuplog_stop=0;
}
static char *ncpy(char *dst,const 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 u_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
};

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

	while (cm[*us1] == cm[*us2++])
		if (*us1++ == '\0')
			return (0);
	return (cm[*us1] - cm[*--us2]);
}
static int strncmpnc(const char *s1, const char *s2, size_t n) {
	if (n != 0) {
		const u_char *cm = charmap,
			     *us1 = (const u_char *)s1,
			     *us2 = (const u_char *)s2;

		do {
			if (cm[*us1] != cm[*us2++])
				return (cm[*us1] - cm[*--us2]);
			if (*us1++ == '\0')
				break;
		} while (--n != 0);
	}
	return (0);
}

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

void cmd_help(char *parameter[MAX_PARAMETERS])
{
	outmsg("+DATA Valid commands%s",NEXT_LINE);

	if (auth_module.check)
		outmsg("+DATA     check <username> <password> [fromIPaddress]%s",NEXT_LINE);
	if (auth_module.lookup)
		outmsg("+DATA     lookup <username>%s",NEXT_LINE);
	if (auth_module.exit)
		outmsg("+DATA     exit%s",NEXT_LINE);
	
	if (auth_module.set || auth_module.mod || 
		auth_module.search  || auth_module.del) {
		outmsg("+DATA%s",NEXT_LINE);
		outmsg("+DATA Supported Extended Commands%s",NEXT_LINE);
		if (auth_module.set)
			outmsg("+DATA     set <username> [<password>|(NULL) [var=\"val\" ...]]%s",NEXT_LINE);
		if (auth_module.mod)
			outmsg("+DATA     mod <username> [var=\"val\" ...]%s",NEXT_LINE);
		if (auth_module.search)
			outmsg("+DATA     search <string> [-from x] [-max y]%s",NEXT_LINE);
		if (auth_module.del)
			outmsg("+DATA     del <username>%s",NEXT_LINE);
	}
}

int cmd_info(char *parameter[MAX_PARAMETERS])
{
	int isdebug = 0;
	char *log_path = NULL;
	char *domain = NULL;

	outmsg("+DATA %s (%s)%s",VERSION, X_TYPE, NEXT_LINE);
	if (info_pass && parameter[1] && 
		!strcmp(info_pass,parameter[1])) {
		outmsg("+DATA%s",NEXT_LINE);
		if (domain)
			outmsg("+DATA       Domain = %s%s",domain,NEXT_LINE);
		else
			outmsg("+DATA       Domain = (NULL)%s",NEXT_LINE);

		if (log_path)
			outmsg("+DATA     Log Path = %s%s",log_path,NEXT_LINE);
		else
			outmsg("+DATA     Log Path = (NULL)%s",NEXT_LINE);

		if (isdebug)
			outmsg("+DATA (DEBUG MODE)%s",log_path,NEXT_LINE);
	}
	return TRUE;
}

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/* MYSQL Body of Code */
/**********************/

#ifdef WIN32
#else
#ifndef strlwr
	char *strlwr( char *string )
	{
		if (string) {
			char *ptr;
			ptr = string;

			while (*ptr != '\0') {
				*ptr = tolower(*ptr);
				ptr++;
			}
		}
		return string;		
	}
	int stricmp( char *string1, char *string2)
	{
		if (string1 && string2) {
			int ret = FALSE;
			char *ptr,*buf;

			ptr = string1;
			buf = string2;

			while (!ret && 
					*ptr != '\0' &&
					*buf != '\0') {
				ret = toupper(*ptr) - toupper(*buf);
				ptr++; buf++;
			}

			if (ret)
				return ret;
			else if (*ptr == '\0' && *buf == '\0')
				return FALSE;
		}
		return TRUE;
	}
#endif
#endif

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

#ifdef SQLAUTH
/* Other Internal SQL Function */
void domain_split_func(char *result,char *par1);


/*
# INI settings for MYSQLAuth
# --------------------------

mysql_server 161.29.2.30
mysql_server2 161.29.2.30
mysql_login lynden
mysql_password qwerty
# mysql_mail_user_db maildb
# mysql_mail_user_table maildb
# mysql_mail_uid 0

# SQL Table Field Names 
# ---------------------

field_username username
field_password passwd
field_forward forward
# field_quota quota
# field_mailmask mailmask
# field_maildrop maildrop

mysql_password_field ENCRYPT
*/
void sql_load(char *bf, char *str)
{
	if (!stricmp(bf,"mysql_server")) {
		myfree(mysql_xinfo.server);
		mysql_xinfo.server = strdup(str);
	}
	else if (!stricmp(bf,"mysql_server2")) {
		myfree(mysql_xinfo.server2);
		mysql_xinfo.server2 = strdup(str);
	}
	else if (!stricmp(bf,"mysql_login"))
		mysql_xinfo.login = strdup(str);
	else if (!stricmp(bf,"mysql_password"))
		mysql_xinfo.password = strdup(str); 
	else if (!stricmp(bf,"mysql_mail_user_db")) {
		myfree(mysql_xinfo.mail_users_db);
		mysql_xinfo.mail_users_db = strdup(str);
	}
	else if (!stricmp(bf,"mysql_mail_user_table")) {
		myfree(mysql_xinfo.mail_users_table);
		mysql_xinfo.mail_users_table = strdup(str);
	}
	else if (!stricmp(bf,"mysql_mail_alias_db")) {
		myfree(mysql_xinfo.mail_alias_db);
		mysql_xinfo.mail_alias_db = strdup(str);
	}
	else if (!stricmp(bf,"mysql_mail_alias_table")) {
		myfree(mysql_xinfo.mail_alias_table);
		mysql_xinfo.mail_alias_table = strdup(str);
	}
	else if (!stricmp(bf,"mysql_mail_uid"))
		mysql_xinfo.mail_uid = strdup(str);

/* SQL Table Field Names */
	else if (!stricmp(bf,"field_alias"))
		mysql_xinfo.field_alias = strdup(str);
	else if (!stricmp(bf,"field_aliasuser"))
		mysql_xinfo.field_aliasuser = strdup(str);
	else if (!stricmp(bf,"field_username"))
		mysql_xinfo.field_username = strdup(str);
	else if (!stricmp(bf,"field_password"))
		mysql_xinfo.field_password = strdup(str);
	else if (!stricmp(bf,"field_forward"))
		mysql_xinfo.field_forward = strdup(str);
	else if (!stricmp(bf,"field_quota"))
		mysql_xinfo.field_quota = strdup(str);
	else if (!stricmp(bf,"field_mailmask"))
		mysql_xinfo.field_mailmask = strdup(str);
	else if (!stricmp(bf,"field_maildrop"))
		mysql_xinfo.field_maildrop = strdup(str);

	else if (!stricmp(bf,"mysql_password_field")) {
		myfree(mysql_xinfo.mysql_password);
		mysql_xinfo.mysql_password = strdup(str);
	}
	else if (!stricmp(bf,"info_fields")) {
		char *start, *ptr, *end;

		start = str;
		while (start) {

			while (*start == '\t' || *start == ' ' || *start == '\n') 
				start++;

			end = strchr(start,',');
			if (end) *end++ = '\0';

			ptr = strchr(start,' ');
			if (ptr) {
				while (*ptr == ' ' || *ptr == '\t')
					*ptr++ = '\0';

			/* Add new Field */
				mysql_xinfo.field_name = (char **)realloc(mysql_xinfo.field_name, sizeof(char *) * (mysql_xinfo.field_count+2));
				mysql_xinfo.field_name[mysql_xinfo.field_count] = strdup(start);
				mysql_xinfo.field_name[mysql_xinfo.field_count+1] = strdup(ptr);
				mysql_xinfo.field_count += 2;
			}
			start = end;
		}
	}



/* Extra Variables */
	else if (!stricmp(bf,"sqlsuffix_lookup_where")) {
		myfree(mysql_xinfo.sqlsuffix_lookup_where);
		mysql_xinfo.sqlsuffix_lookup_where = strdup(str);
	}
	else if (!stricmp(bf,"sqlsuffix_check_where")) {
		myfree(mysql_xinfo.sqlsuffix_check_where);
		mysql_xinfo.sqlsuffix_check_where = strdup(str);
	}
	else if (!stricmp(bf,"sqlsuffix_set")) {
		myfree(mysql_xinfo.sqlsuffix_set);
		mysql_xinfo.sqlsuffix_set = strdup(str);
	}
	else if (!stricmp(bf,"domain_split")) {
		myfree(mysql_xinfo.domain_split);
		mysql_xinfo.domain_split = strdup(str);
	}

	else if (!stricmp(bf,"mysql_pwd_set_only")) {
		if (!stricmp(str,"true") || !stricmp(str,"on") || atoi(str) > 0)
			mysql_xinfo.encode_pass = TRUE;
		else
			mysql_xinfo.encode_pass = FALSE;
	}
}

void sql_setup(void)
{
	mysql_xinfo.server = strdup("localhost");
	mysql_xinfo.login = mysql_xinfo.password = NULL;
	mysql_xinfo.mail_users_db = strdup("maildb");
	mysql_xinfo.mail_users_table = strdup("maildb");
	mysql_xinfo.mail_alias_db = NULL;
	mysql_xinfo.mail_alias_table = strdup("aliasdb");
	mysql_xinfo.mail_uid = NULL;

	mysql_xinfo.field_username = 
		mysql_xinfo.field_alias = 
		mysql_xinfo.field_aliasuser = 
		mysql_xinfo.field_password = 
		mysql_xinfo.field_forward = 
		mysql_xinfo.field_quota =
		mysql_xinfo.field_mailmask = 
		mysql_xinfo.field_maildrop = NULL;

	mysql_xinfo.sqlsuffix_lookup_where =
		mysql_xinfo.sqlsuffix_check_where =
		mysql_xinfo.sqlsuffix_set = NULL;

	mysql_xinfo.domain_split = NULL;

	mysql_xinfo.mysql_password = strdup("PASSWORD");
	
#ifdef unix_password
	mysql_xinfo.encode_pass = FALSE;
#endif
}

void sql_clear(void)
{
	int i;

	myfree(mysql_xinfo.server);
	myfree(mysql_xinfo.login)
	myfree(mysql_xinfo.password);
	myfree(mysql_xinfo.mail_users_db);
	myfree(mysql_xinfo.mail_users_table);
	myfree(mysql_xinfo.mail_alias_db);
	myfree(mysql_xinfo.mail_alias_table);
	myfree(mysql_xinfo.mail_uid);

	myfree(mysql_xinfo.field_username);
	myfree(mysql_xinfo.field_alias);
	myfree(mysql_xinfo.field_aliasuser);
	myfree(mysql_xinfo.field_password);
	myfree(mysql_xinfo.field_forward);
	myfree(mysql_xinfo.field_quota);
	myfree(mysql_xinfo.field_mailmask);
	myfree(mysql_xinfo.field_maildrop);


	for (i=0;i<mysql_xinfo.field_count;i++)
		myfree(mysql_xinfo.field_name[i]);
	myfree(mysql_xinfo.field_name);


	myfree(mysql_xinfo.sqlsuffix_lookup_where);
	myfree(mysql_xinfo.sqlsuffix_check_where);
	myfree(mysql_xinfo.sqlsuffix_set);
}

int sql_close(void)
{
	if (&mysql_xinfo != NULL) {
		mysql_close(&mysql_xinfo.mysql);
		return TRUE;
	}
	else
		imsg("no mysql_xinfo structure to close");
	return FALSE;
}

int sql_connect(void)
{
	return sql_connect2(1);
}

int sql_connect2(int respond)
{
	unsigned int clientflag = FALSE;
	char unix_socket[BFSZ] = "";

/* Double check that username/password is provided */
	if (!mysql_xinfo.field_username || !mysql_xinfo.field_password) {
		emsg("-ERR: No MySQL field_username/field_password in ini file\n");
		if (respond)
			printf("-ERR: No MySQL field_username/field_password in ini file\n");
		return FALSE;
	}

	if (!mysql_xinfo.login || !mysql_xinfo.password) {
		emsg("-ERR: No MySQL Username/Password to connect with\n");
		if (respond)
			printf("-ERR: No MySQL Username/Password to connect with\n");
		return FALSE;
	}

	mysql_init(&mysql_xinfo.mysql);
	if (!(mysql_connect(&mysql_xinfo.mysql, mysql_xinfo.server, mysql_xinfo.login, mysql_xinfo.password))) {
		if (!mysql_xinfo.server2) {
			emsg("Failed to connect to MYSQL server {%s} {%s}",mysql_xinfo.server, mysql_error(&mysql_xinfo.mysql));
			if (respond)
				printf("-ERR: Cannot Connect to %s as %s (%s)\n", mysql_xinfo.server, mysql_xinfo.login, mysql_error(&mysql_xinfo.mysql));
			return FALSE;
		}
		imsg("Main MYSQL Failed to connect {%s}",mysql_xinfo.server);
		if (!(mysql_connect(&mysql_xinfo.mysql, mysql_xinfo.server2, mysql_xinfo.login, mysql_xinfo.password))) {
		/* Trying */
			emsg("Second MYSQL Failed to connect {%s}",mysql_xinfo.server2);
			if (respond)
				printf("-ERR: Cannot Connect to %s as %s\n", mysql_xinfo.server, mysql_xinfo.login);
			return FALSE;
		}
		imsg("Connected to Second MYSQL {%s}",mysql_xinfo.server2);
	}
	imsg("...SQL: Connected to server..");

	if (mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db)) {
		/*printf("-ERR: Cannot Connect to database maildb\n");*/
		if (mysql_xinfo.mail_users_db) {
			emsg("-ERR: Cannot Connect to database {%s}\n",mysql_xinfo.mail_users_db);
			if (respond)
				printf("-ERR: Cannot Connect to database {%s}\n",mysql_xinfo.mail_users_db);
		}
		else {
			emsg("-ERR: Cannot Connect to database (NULL)\n");
			if (respond)
				printf("-ERR: Cannot Connect to database (NULL)\n");
		}
		return FALSE;
	}
	imsg("...SQL: Connected to database..");
	return TRUE;
}

int sql_exit(char *parameter[MAX_PARAMETERS])
{
	return TRUE;
}

pUSER_INFO sql_user_select(MYSQL_ROW	row, char *realname)
{
	static USER_INFO user;
	char info[LBUFSZ] = "";
	int i = 2, j;
	memset(&user,0,sizeof(USER_INFO));
	/* trw 1.0q, when password was null was trying to strdup it
	user.username = strdup(row[0]);
	user.password = strdup(row[1]);
	*/	
	if (row[0])
		user.username = strdup(row[0]);
	else {
		user.username = NULL;
		imsg("sql_user_select: username is a null");
	}
	if (row[1])
		user.password = strdup(row[1]);
	else {
		user.password = NULL;
		imsg("sql_user_select: password is a null");
	}
	
	if (mysql_xinfo.field_forward) {
		if (row[i] && *row[i] && stricmp(row[i], "NULL"))
			sprintf(info,"fwd=\"%s\"",row[i]);
		else if (realname) 
			sprintf(info,"fwd=\"%s\"",realname);
		i++;
	}
	else if (realname) 
		sprintf(info,"fwd=\"%s\"",realname);
	
	if (mysql_xinfo.field_quota) {
		if (row[i] && *row[i]) {
			if (info[0] == '\0')
				sprintf(info,"quota=\"%s\"",row[i]);
			else {
				char *ptr = info + strlen(info);
				sprintf(ptr," quota=\"%s\"",row[i]);
			}
		}
		i++;
	}

	if (mysql_xinfo.field_mailmask) {
		if (row[i] && *row[i])
			user.ipmask = strdup(row[i]);
		i++;
	}
	if (mysql_xinfo.field_maildrop) {
		if (row[i] && *row[i])
			user.drop_file_path = strdup(row[i]);
		i++;
	}

	if (mysql_xinfo.mail_uid)
		user.mail_uid = atoi(mysql_xinfo.mail_uid);
	else
		user.mail_uid = 0;

/* Account for Extra Information */
	for (j=0;j<mysql_xinfo.field_count;j+=2) {
		if (row[i] && *row[i]) {
			if (info[0] == '\0')
				sprintf(info,"%s=\"%s\"", mysql_xinfo.field_name[j],row[i]);
			else {
				char *ptr = info + strlen(info);
				sprintf(ptr," %s=\"%s\"", mysql_xinfo.field_name[j],row[i]);
			}
		}
		else 
			imsg("No data for %s",mysql_xinfo.field_name[j+1]);
		i++;
	}

	if (*info) 
		user.information = strdup(info);

	return &user;
}

/* Lynden: This fuction will do the alais lookup 
			Used in sql_check and sql_lookup
*/
MYSQL_RES	*sql_alias_check(char *parameter[MAX_PARAMETERS], char *info_bf, char **realname)
{
	MYSQL_RES	*result = NULL, *aresult = NULL;
	char query[LBUFSZ],query2[LBUFSZ];
	int res;

/* Activate the alias database */
	if (mysql_xinfo.mail_alias_db) {
		if (mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_alias_db)) {
		/* We couldn't connect to the alias database */
			mysql_emsg("Could not connect to alias database (%s)", mysql_xinfo.mail_alias_db);
			mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db);
			return result;
		}
	}

/* Prepare the query */
	sprintf(query, "SELECT %s FROM %s WHERE %s='%s' ",
		mysql_xinfo.field_aliasuser?mysql_xinfo.field_aliasuser:mysql_xinfo.field_username,
		mysql_xinfo.mail_alias_table,
		mysql_xinfo.field_alias,
		parameter[1]);

/* Perform the alais query */
	imsg("...Sending Query to check for alias {%s}", query);
	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}

	if (!res) {
	/* Extract the actual username */
		aresult = mysql_store_result(&mysql_xinfo.mysql);
		if (aresult) {

			if (mysql_num_rows(aresult) == 1) {
				MYSQL_ROW	row;

				row = mysql_fetch_row(aresult);
				(*realname) = strdup(row[0]);
				

			/* Retrieve the real user's info using the alias */
				imsg("Found name (%s) for alias (%s)", *realname, parameter[1]);

				if (parameter[2]) {
					if (isunix_password || isplain_password) {
						sprintf(query, "SELECT %s FROM %s WHERE %s='%s' ",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, *realname);
						sprintf(query2, "SELECT %s FROM %s WHERE %s='%s' AND %s=%s('%s')",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, *realname, mysql_xinfo.field_password,mysql_xinfo.mysql_password, parameter[2]);
					}
					else
						sprintf(query, "SELECT %s FROM %s WHERE %s='%s' AND %s=%s('%s')",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, *realname, mysql_xinfo.field_password,mysql_xinfo.mysql_password, parameter[2]);
				}
				else
					sprintf(query, "SELECT %s FROM %s WHERE %s='%s' ",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, *realname);

				if (mysql_xinfo.mail_alias_db)
					mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db);

				imsg("...Sending Query {%s}", query);
				res = mysql_query(&mysql_xinfo.mysql, query);
				if (res) {
					imsg("...Query Failed");
				/* Query failed - Reconnect to server and try again */
					if (sql_connect2(0)) {
						imsg("...Sending Query again {%s}", query);
						res = mysql_query(&mysql_xinfo.mysql, query);
					}
				}
				if (!res) {
					imsg("...Done Query");
					result = mysql_store_result(&mysql_xinfo.mysql);
					imsg("...Done store");
					if (result) {
						if (mysql_num_rows(result) != 1) {
							if (result)	mysql_free_result(result);
							result = NULL;
						}							
					}
				}
			}
			else {
			/* Retrieve the real user's info using the alias */
				if (mysql_num_rows(aresult) > 1)
					imsg("%d alias found for name (%s) - Only works with 1 alias.",mysql_num_rows(aresult), parameter[1]);
				if (mysql_xinfo.mail_alias_db)
					mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db);
			}

			if (aresult)	mysql_free_result(aresult);
			aresult = NULL;
		}
		else if (mysql_xinfo.mail_alias_db)
			mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db);
	}
	else {
		mysql_emsg("Could not retrieve real name of (%s) {%d}", parameter[1], res);
		if (mysql_xinfo.mail_alias_db != NULL)
			mysql_select_db(&mysql_xinfo.mysql, mysql_xinfo.mail_users_db);
	}

	return result;
}


void free_user(pUSER_INFO);

pUSER_INFO sql_check(char *parameter[MAX_PARAMETERS])
{
	pUSER_INFO user = NULL;
	char query[LBUFSZ], *ptr;
	char username[BFSZ], info_bf[LBUFSZ*2],suffix[BFSZ],suffix2[BFSZ];
	int res, j;
	int good_pass = FALSE;
	char query2[1000] = "";

	if (domain && !strchr(parameter[1], '@')) {
		strcpy(username, parameter[1]);
		ptr = username + strlen(username);
		sprintf(ptr, "@%s", domain);
		myfree(parameter[1]);
		parameter[1] = strdup(username);
		imsg("...Attached default domain {%s}", parameter[1]);
	}
	domain_split_func(suffix2,parameter[1]);/*may modify parameter1*/
	if (parameter[3])
		imsg("...Checking: User='%s'   Pass='xxxx'  IP='%s'", parameter[1], parameter[3]);
	else
		imsg("...Checking: User='%s'   Pass='xxxx'  IP='(NULL)'", parameter[1]);

/* Build the string of the inforamtion that we need.*/
	sprintf(info_bf,"%s,%s",mysql_xinfo.field_username,mysql_xinfo.field_password);
	ptr = info_bf + strlen(info_bf);
	if (mysql_xinfo.field_forward) {
		sprintf(ptr,",%s",mysql_xinfo.field_forward);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_quota) {
		sprintf(ptr,",%s",mysql_xinfo.field_quota);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_mailmask) {
		sprintf(ptr,",%s",mysql_xinfo.field_mailmask);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_maildrop) {
		sprintf(ptr,",%s",mysql_xinfo.field_maildrop);
		ptr += strlen(ptr);
	}

/* Account for Extra Information */
	for (j=0;j<mysql_xinfo.field_count;j+=2) {
		sprintf(ptr,",%s",mysql_xinfo.field_name[j+1]);
		ptr += strlen(ptr);
	}

	if (mysql_xinfo.sqlsuffix_check_where)	
		lprintf(suffix,BFSZ-1,"%s",mysql_xinfo.sqlsuffix_check_where);
	else 
		strcpy(suffix,"");

#ifdef unix_password2
	if (parameter[2] && mysql_xinfo.encode_pass) {
		char salt[4];
		salt[0] = abs(rand() % 25) + 'a';
		salt[1] = abs(rand() % 25) + 'a';
		if (mysql_xinfo.mysql_password)
			sprintf(pass, "%s('%s')",mysql_xinfo.mysql_password,crypt(parameter[2],salt));
		else 
			sprintf(pass, "%s",crypt(parameter[2],salt));
	}
#endif


	if (isunix_password || isplain_password) {
		lprintf(query, LBUFSZ-1,"SELECT %s FROM %s WHERE %s='%s'%s%s ",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, parameter[1],suffix,suffix2);
		lprintf(query2, 999, "SELECT %s FROM %s WHERE %s='%s' AND %s=%s('%s')%s%s",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, parameter[1], mysql_xinfo.field_password,mysql_xinfo.mysql_password, parameter[2],suffix,suffix2);
	}
	else
		lprintf(query, LBUFSZ-1,"SELECT %s FROM %s WHERE %s='%s' AND %s=%s('%s')%s%s",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, parameter[1], mysql_xinfo.field_password,mysql_xinfo.mysql_password, parameter[2],suffix,suffix2);


/*	mail_uid = 0; */

	/* Password check code */
	/* If either plain_password or unix_password is set, the first query was *not* a MySQL password() check */

/* Lynden New Code to remove 'breaks' and memory leaks */
	{
		MYSQL_RES	*result = NULL;

		imsg("...Sending Query {%s}", query);

		res = mysql_query(&mysql_xinfo.mysql, query);
		if (res) {
		/* Query failed - Reconnect to server and try again */
			if (sql_connect2(0))
				res = mysql_query(&mysql_xinfo.mysql, query);
		}

		if (!res) {
			result = mysql_store_result(&mysql_xinfo.mysql);

			if (result) {
				char *realname = NULL;
			
			/* Account for Alias if result failed */
				if (mysql_num_rows(result) != 1) {
					if (result)	mysql_free_result(result);
					result = NULL;
					if (mysql_xinfo.mail_alias_table && mysql_xinfo.field_alias)
						result = sql_alias_check(parameter, info_bf, &realname);
				}
				
				if (result) {

					user = sql_user_select(mysql_fetch_row(result), realname);
					if (user) {
		
						if (!isunix_password && !isplain_password)
							good_pass=TRUE;
						else {
#ifdef unix_password
							if (isunix_password) {
								if (!strcmp(user->password,crypt(parameter[2],user->password)) ) {
									emsg("...unix password succeeded for user (%s)",user->username);
									good_pass = TRUE;
								}
								else
									emsg("...unix password wrong for user (%s), (%s)!=(%s)",user->username,user->password,crypt(parameter[2],user->password));
							}
#endif
							if (!good_pass) {
								if (isplain_password) {
									if (strcmp(user->password,parameter[2]) == 0) {
										emsg("...plain password check succeded for user (%s)",user->username);
										good_pass = TRUE;
									}
									else
										emsg("...plain password wrong for user (%s)",user->username);
								}
							}

							if (!good_pass) {
							/* Try the second command querry: Ie check password using ldap PASSWORD() fn. */
								imsg("...Sending Second SQL Query {%s}", query2);
								res = mysql_query(&mysql_xinfo.mysql, query2);
								if (res) {
								/* Query failed - Reconnect to server and try again */
									if (sql_connect2(0))
										res = mysql_query(&mysql_xinfo.mysql, query);
								}

								if (!res) {
									if (result)	mysql_free_result(result);
									result = NULL;
									result = mysql_store_result(&mysql_xinfo.mysql);

									if (result) {
										if (mysql_num_rows(result) == 1) {
											imsg("freeing user as password checks failed");
											clear_user(user);
											
											user = sql_user_select(mysql_fetch_row(result),realname);
											if (user)
												good_pass = TRUE;
										}
									}
								}
							}
						}
					}

				}

			/* Clear the result */
				if (result) {
					if (result)	mysql_free_result(result);
					result = NULL;
				}
				myfree(realname);
			}

		}
	}

	if (user && parameter[3] && user->ipmask && good_pass) {
	/* Check IP Mask */
		int IPMask = 0, CurrentIP = 0;
		char *ptr;
		imsg("...Checking IPMask");

		ptr = user->ipmask -1;
		while (ptr) {
			ptr++;
			IPMask *=  255;
			IPMask += atoi(ptr);
			ptr = strchr(ptr,'.');
		}

		ptr = parameter[3] -1;
		while (ptr) {
			ptr++;
			CurrentIP *=  255;
			CurrentIP += atoi(ptr);
			ptr = strchr(ptr,'.');
		}

		if ((CurrentIP-(CurrentIP^IPMask)) != IPMask) {
			emsg("...IPMask Failed");
			good_pass=FALSE;
		}
	}

	if (!good_pass && user) {
		imsg("freeing user as password checks failed");
		clear_user(user);
		user = NULL;
	}

	return user;
}
	
pUSER_INFO sql_lookup(char *parameter[MAX_PARAMETERS])
{
	char query[LBUFSZ], *ptr;
	char username[BFSZ], info_bf[LBUFSZ*2];
	char suffix[BFSZ];
	pUSER_INFO user = NULL;
	MYSQL_RES	*result;
	int res, j;
	char suffix2[BFSZ];

	if (domain && !strchr(parameter[1], '@')) {
		strcpy(username, parameter[1]);
		ptr = username + strlen(username);
		sprintf(ptr, "@%s", domain);
		myfree(parameter[1]);
		parameter[1] = strdup(username);
		imsg("...Attached default domain {%s}", parameter[1]);
	}

	domain_split_func(suffix2,parameter[1]);/*may modify parameter1*/

	imsg("...Lookup: User='%s'", parameter[1]);

	sprintf(info_bf,"%s,%s",mysql_xinfo.field_username,mysql_xinfo.field_password);
	ptr = info_bf + strlen(info_bf);
	if (mysql_xinfo.field_forward) {
		sprintf(ptr,",%s",mysql_xinfo.field_forward);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_quota) {
		sprintf(ptr,",%s",mysql_xinfo.field_quota);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_mailmask) {
		sprintf(ptr,",%s",mysql_xinfo.field_mailmask);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_maildrop) {
		sprintf(ptr,",%s",mysql_xinfo.field_maildrop);
		ptr += strlen(ptr);
	}
/* Account for Extra Information */
	for (j=0;j<mysql_xinfo.field_count;j+=2) {
		sprintf(ptr,",%s",mysql_xinfo.field_name[j+1]);
		ptr += strlen(ptr);
	}

	if (mysql_xinfo.sqlsuffix_lookup_where)	
		lprintf(suffix,BFSZ-1,"%s",mysql_xinfo.sqlsuffix_lookup_where);
	else 
		strcpy(suffix,"");

	lprintf(query,LBUFSZ-1, "SELECT %s FROM %s WHERE %s='%s'%s%s",info_bf, mysql_xinfo.mail_users_table,mysql_xinfo.field_username, parameter[1],suffix,suffix2);
	imsg("...Sending Query {%s}", query);

/* Lynden New Code to remove 'breaks' and memory leaks */
	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}

	if (!res) {
		result = mysql_store_result(&mysql_xinfo.mysql);
		if ( result ) {
			char *realname = NULL;

		/* Account for Alias if result failed */
			if (mysql_num_rows(result) != 1) {
				if (result)	mysql_free_result(result);
				result = NULL;
				if (mysql_xinfo.mail_alias_table && mysql_xinfo.field_alias)
					result = sql_alias_check(parameter, info_bf, &realname);
			}
			
			if (result) {
				if (mysql_num_rows(result) == 1) {
					user = sql_user_select(mysql_fetch_row(result), realname);
					imsg("...User Located");
				}
				else
					mysql_emsg("...Failed to Locate User");
			}
			else
				emsg("...Failed to Get SQL Result");

		/* Clear the result */
			if (result) {
				if (result)	mysql_free_result(result);
				result = NULL;
			}
			myfree(realname);
		}
		else
			mysql_emsg("...Failed to Get SQL Result");
	}
	else
		mysql_emsg("...Query Failed");

/* Lynden 
	if (mysql_xinfo.mail_uid)
		mail_uid = atoi(mysql_xinfo.mail_uid);
	else
		mail_uid = 0;
*/
	if (user)
		imsg("...Return User");
	else
		imsg("...None Found");
	return user;
}


/* Extended Commands */
int sql_set(char *parameter[MAX_PARAMETERS])
{
	char query[LBUFSZ], bf[LBUFSZ*2], *ptr, *end,suffix[BFSZ];
	char username[BFSZ], pass[BFSZ] = "",fwd[BFSZ] = "NULL", quota[BFSZ] = "NULL";
	int res, j;
	char domain_split_bf[BFSZ], *str,temp[BFSZ], c;
#ifdef unix_password
	char salt[4];
#endif
	if (domain && !strchr(parameter[1], '@')) {
		strcpy(username, parameter[1]);
		ptr = username + strlen(username);
		sprintf(ptr, "@%s", domain);
		myfree(parameter[1]);
		parameter[1] = strdup(username);
		imsg("...Attached default domain {%s}", parameter[1]);
	}
	strcpy(domain_split_bf,"");
	if (mysql_xinfo.domain_split) {
		/*basically the same as domain_split_func(suffix2,parameter[1]);/*may modify parameter1*/
		if ((ptr=strchr(parameter[1], '@'))!=NULL) {
			lprintf(domain_split_bf,BFSZ-1,"%s",ptr+1);
			*ptr=0;
			imsg("...split username {%s} and domain {%s}", parameter[1],domain_split_bf);
		}
		else if (domain) {
			lprintf(domain_split_bf,BFSZ-1,"%s",domain);
			imsg("...using default db_domain_field {%s} as set by domain setting",domain_split_bf);
		}
	}

	imsg("...Set: User='%s' Data='%s'", parameter[1], parameter[3]);
	if (isfwd_not_null){
		strcpy(fwd,"");
		strcpy(quota,"");
	}
	if (parameter[3]) {
		if (ptr = strstr(parameter[3],"fwd=\"")) {
			ptr += 5;
			end = strchr(ptr,'\"');
			if (end)
				sprintf(fwd,"'%.*s'",end-ptr,ptr);
		}
		if (ptr = strstr(parameter[3],"quota=\"")) {
			ptr += 7;
			end = strchr(ptr,'\"');
			if (end)
				sprintf(quota,"'%.*s'",end-ptr,ptr);
		}
	}
	if (parameter[2] && !stricmp(parameter[2],"(NULL)"))  {
		pUSER_INFO user;
		user = sql_lookup(parameter);
		if (!user) {
			emsg("...Username does not exist");
			return FALSE;
		}
		sprintf(pass, "'%s'",user->password);

		clear_user(user);
	}
#ifdef unix_password
/*	else if (parameter[2] && mysql_xinfo.encode_pass) {
		char salt[4];
		salt[0] = abs(rand() % 25) + 'a';
		salt[1] = abs(rand() % 25) + 'a';
		if (mysql_xinfo.mysql_password)
			sprintf(pass, "%s('%s')",mysql_xinfo.mysql_password,crypt(parameter[2],salt));
		else
			sprintf(pass, "%s",crypt(parameter[2],salt));
	}
*/
	else if (parameter[2] && (!isunix_password || mysql_xinfo.encode_pass)) {
		sprintf(pass, "%s('%s')",mysql_xinfo.mysql_password,parameter[2]);
	}
	else if (parameter[2] && isplain_password)
		sprintf(pass, "'%s'",parameter[2]);
	else if (parameter[2])	{
		salt[0] = abs(rand() % 25) + 'a';
		salt[1] = abs(rand() % 25) + 'a';
		sprintf(pass, "'%s'",crypt(parameter[2],salt));
	}
#else
	else if (parameter[2] && isplain_password)
		sprintf(pass, "'%s'",parameter[2]);
	else if (parameter[2])
		sprintf(pass, "%s('%s')",mysql_xinfo.mysql_password,parameter[2]);
#endif
	myfree(parameter[2]);
	parameter[2] = strdup(pass);

/* Remove current setting */
	sql_del(parameter);
	
	sprintf(bf,"%s='%s', %s=%s"
			,mysql_xinfo.field_username, parameter[1]
			,mysql_xinfo.field_password, parameter[2]);
	ptr = bf + strlen(bf);
	if (mysql_xinfo.field_forward && fwd && strcmp(fwd,"") && strcmp(fwd,"NULL")) {
		sprintf(ptr,", %s=%s",mysql_xinfo.field_forward, fwd);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_quota && quota && strcmp(quota,"") && strcmp(quota,"NULL")) {
		sprintf(ptr,", %s=%s",mysql_xinfo.field_quota, quota);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_mailmask) {
		sprintf(ptr,", %s='0.0.0.0'",mysql_xinfo.field_mailmask);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_maildrop) {
		sprintf(ptr,", %s=NULL",mysql_xinfo.field_maildrop);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.domain_split && strlen(domain_split_bf)) {
		lprintf(ptr,LBUFSZ-1-strlen(ptr),", %s='%s'",mysql_xinfo.domain_split,domain_split_bf);
		ptr += strlen(ptr);
	}

/* Account for Saving Extra Information */
	for (j=0;j<mysql_xinfo.field_count;j+=2) {
		if (parameter[3])
			str = strstr(parameter[3],mysql_xinfo.field_name[j]);
		else
			str = NULL;
		if (str) {
			str += strlen(mysql_xinfo.field_name[j]);
			while (*str == '=')
				str++;

			c = *str;
			if (c == '\'' || c == '\"') {
				str++;
				end = strchr(str,c);
				if (end) {
					sprintf(temp,"%.*s",end-str,str);
					str = temp;
				}
				else {
					str = NULL;
					imsg("Ending {%c} was missing:  %s (%s)",c,mysql_xinfo.field_name[j],parameter[3]);
				}
			}
			else {
				end = strchr(str,' ');
				if (end) {
					sprintf(temp,"%.*s",end-str,str);
					str = temp;
				}
				else {
					sprintf(temp,"%s",str);
					str = temp;
				}
			}
		}
		else
			imsg("Failed to locate: %s (%s)",mysql_xinfo.field_name[j],parameter[3]);

		if (str) 
			sprintf(ptr,",%s='%s'",mysql_xinfo.field_name[j+1], str);
		else
			sprintf(ptr,",%s=''",mysql_xinfo.field_name[j+1]);
		ptr += strlen(ptr);
	}


	if (mysql_xinfo.sqlsuffix_set)	
		lprintf(suffix,BFSZ-1,"%s",mysql_xinfo.sqlsuffix_set);
	else 
		strcpy(suffix,"");
	sprintf(query, "INSERT INTO %s SET %s%s;"
				,mysql_xinfo.mail_users_table
				,bf,suffix);
	imsg("...Sending Query {%s}", query);

	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}

	if (!res) {
		imsg("...Data Saved");
		return TRUE;
	}
	else 
		mysql_emsg("...Query Failed");
	
/*	if (mysql_xinfo.mail_uid)
		mail_uid = atoi(mysql_xinfo.mail_uid);
	else
		mail_uid = 0;
*/
	return FALSE;
}

void mysql_emsg(char * arg_list, ...)/*add mysql client library errorno and reason to emsg*/
{
	va_list arg_ptr;
  	char data[LBUFSZ],*s;
	char data2[LBUFSZ],data3[LBUFSZ];
	

	/*mysql_emsg("...Query Failed");
	*/
	strcpy(data,""); strcpy(data2,""); strcpy(data3,"");
	va_start(arg_ptr, arg_list);
	lvprintf(data,LBUFSZ-1, arg_list, arg_ptr);
	if ((s=strrchr(data,'\r'))!=NULL) *s=0;
	if ((s=strrchr(data,'\n'))!=NULL) *s=0;
	if (mysql_errno(&mysql_xinfo.mysql))
		lprintf(data2,LBUFSZ-1,"%d",mysql_errno(&mysql_xinfo.mysql));
	if (mysql_error(&mysql_xinfo.mysql))
		lprintf(data3,LBUFSZ-1,"%s",mysql_error(&mysql_xinfo.mysql));
	
	emsg("%s (%s:%s)",data,data2,data3);
}

int sql_mod(char *parameter[MAX_PARAMETERS])
{
	char query[LBUFSZ], *ptr, *end, *temp = NULL;
	char username[BFSZ], fwd[LBUFSZ] = "NULL", quota[LBUFSZ] = "NULL";
	int res;

	/*TRW version 
	I think this implementation is too dangerous.  It should use the SQL to modify values 
	which does not require password.  The set command should also really modify values rather than 
	deleting the user and readding given the account already exists.
	
	If this is put back in then it needs to be updated to match set command, e.g. with suffix 
	and suffix2 values
	*/
	emsg("Command no longer supported");
	return FALSE;

	if (domain && !strchr(parameter[1], '@')) {
		strcpy(username, parameter[1]);
		ptr = username + strlen(username);
		sprintf(ptr, "@%s", domain);
		myfree(parameter[1]);
		parameter[1] = strdup(username);
		imsg("...Attached default domain {%s}", parameter[1]);
	}
	imsg("...Modify: User='%s' Data='%s %s'", parameter[1], parameter[2], parameter[3]);

	if (parameter[2] && parameter[3]) {
		sprintf(temp,"%s %s",parameter[2],parameter[3]);			
		myfree(parameter[3]);
		parameter[3] = strdup(temp);
	}
	else if (parameter[2]) {
		myfree(parameter[3]);
		parameter[3] = strdup(parameter[2]);
	}


	if (parameter[3]) {
		if (ptr = strstr(parameter[3],"fwd=\"")) {
			ptr += 5;
			end = strchr(ptr,'\"');
			if (end && end-ptr < LBUFSZ)
				sprintf(fwd,"'%.*s'",end-ptr,ptr);
		}
		if (ptr = strstr(parameter[3],"quota=\"")) {
			ptr += 7;
			end = strchr(ptr,'\"');
			if (end && end-ptr < LBUFSZ)
				sprintf(quota,"'%.*s'",end-ptr,ptr);
		}
	}
	{
		pUSER_INFO user;
		user = sql_lookup(parameter);
		if (!user) {
			emsg("...Username does not exist");
			myfree(temp);
			return FALSE;
		}
		myfree(parameter[2]);
		parameter[2] = strdup(user->password);
	}

/* Remove current setting */
	sql_del(parameter);

	sprintf(query, "INSERT INTO %s VALUES ('%s', '%s', %s, %s, '0.0.0.0', NULL);"
				,mysql_xinfo.mail_users_table
				,parameter[1]
				,parameter[2]
				,fwd
				,quota);
	imsg("...Sending Query {%s}", query);
/*
	if (mysql_xinfo.mail_uid)
		mail_uid = atoi(mysql_xinfo.mail_uid);
	else
		mail_uid = 0;
*/
	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}

	if (!res) {
		imsg("...Data Saved");
		myfree(temp);
		return TRUE;
	}
	else
		mysql_emsg("...Query Failed");

	myfree(temp);
	return FALSE;
}

int sql_del(char *parameter[MAX_PARAMETERS])
{
	char query[LBUFSZ];
	char username[BFSZ], *ptr;
	int res;
	char suffix[BFSZ];
	char suffix2[BFSZ];

	if (domain && !strchr(parameter[1], '@')) {
		strcpy(username, parameter[1]);
		ptr = username + strlen(username);
		sprintf(ptr, "@%s", domain);
		myfree(parameter[1]);
		parameter[1] = strdup(username);
		imsg("...Attached default domain {%s}", parameter[1]);
	}
	domain_split_func(suffix2,parameter[1]);/*may modify parameter1*/
	if (mysql_xinfo.sqlsuffix_set)	
		lprintf(suffix,BFSZ-1,"%s",mysql_xinfo.sqlsuffix_set);
	else 
		strcpy(suffix,"");

	imsg("...Delete: User='%s'", parameter[1]);

	/*sprintf(query, "DELETE FROM %s WHERE username='%s';"
				,mysql_xinfo.mail_users_table
				,parameter[1]);*/
	sprintf(query, "DELETE FROM %s WHERE %s='%s'%s%s;"
				,mysql_xinfo.mail_users_table
				,mysql_xinfo.field_username,parameter[1],suffix,suffix2);
/*	if (mysql_xinfo.mail_uid)
		mail_uid = atoi(mysql_xinfo.mail_uid);
	else
		mail_uid = 0;
*/
	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}
	if (!res)
		return TRUE;
	mysql_emsg("could not delete user");
	return FALSE;
}

int sql_search(char *parameter[MAX_PARAMETERS])
{

	char query[LBUFSZ], bf[BFSZ], *ptr;
	int res, j;
	int count = 0;
	int from = -1, max=-1;

	my_ulonglong num_rows=0;
	
	strlwr(parameter[1]);
	imsg("...Search: '%s'", parameter[1]);


	sprintf(bf,"%s,%s",mysql_xinfo.field_username,mysql_xinfo.field_password);
	ptr = bf + strlen(bf);
	if (mysql_xinfo.field_forward) {
		sprintf(ptr,",%s",mysql_xinfo.field_forward);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_quota) {
		sprintf(ptr,",%s",mysql_xinfo.field_quota);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_mailmask) {
		sprintf(ptr,",%s",mysql_xinfo.field_mailmask);
		ptr += strlen(ptr);
	}
	if (mysql_xinfo.field_maildrop) {
		sprintf(ptr,",%s",mysql_xinfo.field_maildrop);
		ptr += strlen(ptr);
	}
/* Account for Extra Information */
	for (j=0;j<mysql_xinfo.field_count;j+=2) {
		sprintf(ptr,",%s",mysql_xinfo.field_name[j+1]);
		ptr += strlen(ptr);
	}

	sprintf(query, "SELECT %s FROM %s",bf, mysql_xinfo.mail_users_table);
	imsg("...Sending Query {%s}", query);

	res = mysql_query(&mysql_xinfo.mysql, query);
	if (res) {
		imsg("...Query Failed");
	/* Query failed - Reconnect to server and try again */
		if (sql_connect2(0))
			res = mysql_query(&mysql_xinfo.mysql, query);
	}

	if (!res) {
		MYSQL_RES	*result;
		imsg("...Done Query");
		if ( (result = mysql_store_result(&mysql_xinfo.mysql)) ) {
			pUSER_INFO user;

			num_rows = mysql_num_rows(result);
			imsg("...%d rows", num_rows);

		/* Get -from and -max */
			if ( parameter[2] && parameter[3])
			{
				char *temp;	
				temp = strtok( parameter[3], " " );
				if (strcmp(parameter[2],"-from")==0)
					from = atoi(temp);
				else if (strcmp(parameter[2],"-max")==0)
					max = atoi(temp);
				while( temp != NULL )
				{
					 temp = strtok( NULL, " " );
					 if (temp != NULL && strcmp(temp,"-max")==0)
					 {
						 temp = strtok(NULL," ");
						 if (temp)
						 max = atoi(temp);
					 }
					 else if (temp != NULL && strcmp(temp,"-from")==0)
					 {
						 temp = strtok(NULL," ");
						 if (temp)
						 from = atoi(temp);
					 }
				 }
				
			}
			from --;


			for (;num_rows;num_rows--)
			{					
				if (max > -1 && count > max)
					break;
				user = sql_user_select(mysql_fetch_row(result),NULL);

			/* Lynden Changed some of the code below: added the
					clear_user(user);
				Otherwise memory leaks occurs.
			*/
				if (user && user->username)	{
					if (strchr(parameter[1] , '*'))	{
						char *name = strdup(user->username);
						char *param = parameter[1];

						strlwr(name);
						if 	( (*param == '*' && param++ == '\0') || wild_match(parameter[1],name)) {
						/*	if (mysql_xinfo.mail_uid)
								mail_uid = atoi(mysql_xinfo.mail_uid);
							else
								mail_uid = 0;
						*/
							if (from > -1 ) {
								if (count >= from)
									display_user(user, "+DATA", user->username);
								else
									clear_user(user);
							}
							else
								display_user(user, "+DATA", user->username);
							count++;
						}
						myfree(name);
					}
					else {
						char *name;
						name = strdup(user->username);
						strlwr(name);
						if (strstr(name, parameter[1])) {
						/*	if (mysql_xinfo.mail_uid)
								mail_uid = atoi(mysql_xinfo.mail_uid);
							else
								mail_uid = 0;
						*/
							if (from > -1) {
								if( count >= from)
									display_user(user, "+DATA", user->username);
								else
									clear_user(user);
							}
							else
								display_user(user, "+DATA", user->username);
							count++;
						}
						myfree(name);
					}
				}
				else
					clear_user(user);
			}
		}
	}

	return count;

}

/* par1 = user@domain */
void domain_split_func(char *result,char *par1)
{
	/*NB: Modifies par1*/
	char domain_split_bf[BFSZ] = "",*ptr;

	*result = '\0';
	if (!mysql_xinfo.domain_split) return;
	
	if (par1) {
		if ( (ptr = strchr(par1, '@')) != NULL ) {
			lprintf(domain_split_bf,BFSZ-1,"%s",ptr+1);
			*ptr = 0;
			imsg("...lookup split username {%s} and domain {%s}", par1,domain_split_bf);
		}
		else if (domain) {
			lprintf(domain_split_bf,BFSZ-1,"%s",domain);
			imsg("...lookup using default db_domain_field {%s} as set by domain setting",domain_split_bf);
		}

		if (strlen(domain_split_bf))
			lprintf(result,LBUFSZ-1," AND %s='%s'",mysql_xinfo.domain_split,domain_split_bf);
	}
}

#endif

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/* Radius Body of Code */
/***********************/

#ifdef RADIUSAUTH
/*
# INI settings for RadiusAuth
# ---------------------------

radius_host 1.2.3.4
radius_port 1645
radius_secret fred
radius_attribute 26
*/

	int good_ipaddr(char *addr)
	{
        int     dot_count;
        int     digit_count;

        dot_count = 0;
        digit_count = 0;
        while(*addr != '\0' && *addr != ' ') {
                if(*addr == '.') {
                        dot_count++;
                        digit_count = 0;
                }
                else if(!isdigit(*addr)) {
                        dot_count = 5;
                }
                else {
                        digit_count++;
                        if(digit_count > 3) {
                                dot_count = 5;
                        }
                }
                addr++;
        }
        if(dot_count != 3) {
                return(-1);
        }
        else {
                return(0);
        }
	}

	UINT4 ipstr2long(char *ip_str)
	{
        char    buf[6];
        char    *ptr;
        int     i;
        int     count;
        UINT4   ipaddr;
        int     cur_byte;

        ipaddr = (UINT4)0;
        for(i = 0;i < 4;i++) {
                ptr = buf;
                count = 0;
                *ptr = '\0';
                while(*ip_str != '.' && *ip_str != '\0' && count < 4) {
                        if(!isdigit(*ip_str)) {
                                return((UINT4)0);
                        }
                        *ptr++ = *ip_str++;
                        count++;
                }
                if(count >= 4 || count == 0) {
                        return((UINT4)0);
                }
                *ptr = '\0';
                cur_byte = atoi(buf);
                if(cur_byte < 0 || cur_byte > 255) {
                        return((UINT4)0);
                }
                ip_str++;
                ipaddr = ipaddr << 8 | (UINT4)cur_byte;
        }
        return(ipaddr);
	}

	UINT4 get_ipaddr(char *host)
	{
        struct hostent  *hp;
        UINT4           ipstr2long();

        if(good_ipaddr(host) == 0) {
                return(ipstr2long(host));
        }
        else if((hp = gethostbyname(host)) == (struct hostent *)NULL) {
                return((UINT4)0);
        }
        return(ntohl(*(UINT4 *)hp->h_addr));
	}

	int check_winstock(void)
	{		

#	ifdef WIN32
		WORD wVersionRequested;	
		WSADATA wsaData;

		wVersionRequested = MAKEWORD( 1, 1 );

		if ( WSAStartup( wVersionRequested, &wsaData ) )
			return FALSE;
		
		if ( LOBYTE( wsaData.wVersion ) != 1 || HIBYTE( wsaData.wVersion ) != 1 )
			return -TRUE;		
#	endif
		return TRUE;
	}


#	ifdef WIN32
		char *rad_tcp_error(void)
		{
			static char bf[500];
			int err = WSAGetLastError();

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


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

char *PASSWORD_KEYWORD = "welcometoauthprogramtoseehowitworks";

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

	int hextobin(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;
	}

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

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

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

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

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

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


/* The Radius Hash Is used to generate a Hash key for the username */
	int radius_hash(char *user)
	{
		int hash = 0;

		if (user != NULL) {			
			unsigned char t1,t2;
			char *ptr;
			int i=0;
		
			t1 = t2 = 0;
			for (ptr = user; *ptr ; ptr++){
				t1 += *ptr++;
				t2 += *ptr;
				if (!*ptr)
					break;
			}
	
			hash = (t1 % 39) * 39 + (t2 % 39);
		}
		return hash;
	}
	int radius_close(void)
	{
		return TRUE;
	}

/* stored_password is set to the password that is stored
		need to free memory if returned.
	if stored_password == NULL then not wanting stored password
*/
	int radius_check_user(char *user, char *password, int add, char **stored_password)
	{
		if (user) {
			int hash = radius_hash(user), i;
			FILE *stream;

			if (stored_password)
				*stored_password = NULL;

		/* Get Password from user list. Only if asked to */
			if (add >= 0) {
			/* Check Memory */
				for (i=0;i<full_user_list[hash].size;i++) {
					if (full_user_list[hash].list[i].user[0] == user[0] &&
						!strcmp(full_user_list[hash].list[i].user, user)) {

						if (stored_password)
							mystrdup(*stored_password,full_user_list[hash].list[i].password);

						if (!password)
							return TRUE;
						else if (!stricmp(password,full_user_list[hash].list[i].password))
							return TRUE;
						else
							return FALSE;
					}
				}

			}
			else if (add == -TRUE && password) {
				imsg("Locating %s in memory: {%s}",user);

			/* Update Memory Password List (if already available) */
				for (i=0;i<full_user_list[hash].size;i++) {
				
					if (full_user_list[hash].list[i].user[0] == user[0] &&
						!strcmp(full_user_list[hash].list[i].user, user)) {

						myfree(full_user_list[hash].list[i].password);
						full_user_list[hash].list[i].password = strdup(password);
						
						imsg("Updated password in memory: {%s}",user);
						return -TRUE;
					}

				}

			}

			if (add && password) {
			/* Add user to database */
				int pos, count = 3;

				pos = full_user_list[hash].size++;
				full_user_list[hash].list = realloc(full_user_list[hash].list, sizeof(USER_DATA) * full_user_list[hash].size);
				full_user_list[hash].list[pos].user = strdup(user);
				full_user_list[hash].list[pos].password = strdup(password);

				if (stored_password)
					mystrdup(*stored_password,password);

			/* Have to update userfile */
				if (add != -TRUE) {
					imsg("Added user to memory and file: {%s}",user);
					while (count--) {
						stream = fopen(user_file,"ab");
						if (stream) {
							fprintf(stream,"%s %s\n",user, x_hide(PASSWORD_KEYWORD, password, 1) );
							fclose(stream);
							break;
						}
						sleep(2);
					}
				}
				else
					imsg("Added user to memory only: {%s}",user);
	
				return -TRUE;
			}
		}
		return FALSE;
	}

	void radius_load_users()
	{
		struct stat st;
		int load_file = FALSE;

		if (user_loaded) 
			load_file = TRUE;
		else {
		/* Check file Size from last Time */
			if ( stat( user_file, &st ) != -1) {
				if (st.st_size != user_file_size)
					load_file = TRUE;
			}
			else
				load_file = TRUE;
		}

		if (user_file && load_file) {
		/* Only load file if not already loaded. */
			FILE *stream;
			char bf[BFSZ], *ptr, *pass;

			stream = fopen(user_file,"rb");
			if (stream) {
				imsg("Loading Userfile again: {%s}",user_file);

				while (stream) {

					if (!fgets(bf,BFSZ,stream))
						break;

					if (bf[0] == '#' || bf[0] == '\r' || bf[0] == '\n' || bf[0] == '\0')
						continue;

					ptr = bf + strlen(bf) -1;
					while (ptr > bf && (*ptr == '\r' || *ptr == '\n')) *ptr-- = '\0';

					pass = strchr(bf,' ');
					if (pass) {						
						*pass++ = '\0';						
						pass = x_unhide(PASSWORD_KEYWORD, pass, 1);
						radius_check_user(bf,pass,-TRUE,NULL);
					}
				}

				fclose(stream);
				user_loaded = TRUE;
				
			/* Check file Size from last Time */
				if ( stat( user_file, &st ) != -1)
					user_file_size = st.st_size;

				imsg("Finished Loading Userfile: {%s}",user_file);
			}
			else
				imsg("Failed to Load Userfile: {%s}",user_file);

		}

	}

	void radius_load(char *bf, char *str)
	{
		if (!stricmp(bf,"radius_host")) {
			myfree(rad_info.host);
			rad_info.host = strdup(str);
		}
		else if (!stricmp(bf,"radius_port"))
			rad_info.port = atoi(str);
		else if (!stricmp(bf,"radius_secret"))
			rad_info.secret = strdup(str);
		else if (!stricmp(bf,"radius_attribute"))
			rad_info.attribute = strdup(str);
		else if (!stricmp(bf,"radius_user_file"))
			user_file = strdup(str);
	}

	void radius_setup(void)
	{
		memset(&rad_info, 0, sizeof(rad_info));
		rad_info.host = strdup("localhost");
		rad_info.port = 1645;

		memset(&full_user_list, 0, sizeof(LIST) * USER_HASH);
	}

	void radius_clear(void)
	{
		int i,j;

		myfree(rad_info.host);
		myfree(rad_info.secret);
		myfree(rad_info.attribute);

		for (i=0;i<USER_HASH;i++) {

			for (j=0;j<full_user_list[i].size;j++) {
				myfree(full_user_list[i].list[j].user);
				myfree(full_user_list[i].list[j].password);
			}

		}
	}

	int radius_connect(void)
	{
        struct  sockaddr_in *sin;
        static struct sockaddr salocal;
        struct  servent *svp;
	/*
    *      Open a connection to the server.
    */

		if (!check_winstock()) {
			emsg("-ERR: Couldn't find WINSTOCK"); 
			printf("-ERR: Couldn't find WINSTOCK"); 
			return FALSE;
		}

	/* Get the IP address of the authentication server and port */

        svp = getservbyname ("radius", "udp");
        if (svp == (struct servent *) 0) rad_info.svc_port = PW_AUTH_UDP_PORT;
        else rad_info.svc_port = ntohs((u_short) svp->s_port);

		if (rad_info.port!=0) rad_info.svc_port = rad_info.port;
	
        if ((rad_info.auth_ipaddr = get_ipaddr(rad_info.host)) == 0) { 
			emsg("-ERR: Couldn't find host {%s}", rad_info.host); 
			printf("-ERR: Couldn't find host {%s}", rad_info.host); 
			return FALSE;
		}

	/* Setup structure to talk to Radius */
		sin = (struct sockaddr_in *) &rad_info.saremote;
		memset (sin, 0, sizeof (rad_info.saremote));
		sin->sin_family = AF_INET;
		sin->sin_addr.s_addr = htonl(rad_info.auth_ipaddr);
		sin->sin_port = htons((short) rad_info.svc_port);


	/* Setup Socket for Radius to return results to (Bind)*/
        rad_info.sockfd = socket (AF_INET, SOCK_DGRAM, 0);
        if (rad_info.sockfd == INVALID_SOCKET) {
			emsg("-ERR: Socket error {%s}", rad_tcp_error() );
			printf("-ERR: Socket error {%s}", rad_tcp_error() );
			return FALSE;
		}

        sin = (struct sockaddr_in *) &salocal;
        memset (sin, 0, sizeof (salocal));
        sin->sin_family = AF_INET;
        sin->sin_addr.s_addr = INADDR_ANY;
        sin->sin_port = 0;


        rad_info.local_port = 1025;
        do {
                rad_info.local_port++;
                sin->sin_port = htons((u_short)rad_info.local_port);
        } while((bind(rad_info.sockfd, &salocal, sizeof (struct sockaddr_in)) < 0) &&
                                                rad_info.local_port < 64000);

	/*
        if (bind(sockfd, &salocal, sizeof (struct sockaddr_in)) !=0) {
                rad_tcp_last();
                printf("bind() %s",rad_tcp_error()); return FALSE; 
        }
	*/

        return TRUE;
	}
	
	int radius_exit(char *parameter[MAX_PARAMETERS])
	{
		return TRUE;
	}

	pUSER_INFO radius_check(char *parameter[MAX_PARAMETERS])
	{
		char username[BFSZ];
		pUSER_INFO user = NULL;
		char *ptr;
		int res;
		

		if (domain && !strchr(parameter[1], '@')) {
			strcpy(username, parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		if (parameter[3])
			imsg("...Checking: User='%s'   Pass='xxxx'  IP='%s'", parameter[1], parameter[3]);
		else
			imsg("...Checking: User='%s'   Pass='xxxx'  IP='(NULL)'", parameter[1]);


		rad_info.extra_code = 26;

		if (rad_request(parameter[1],parameter[2])) {
			int count = 5;

			res = rad_send();
			
			while (count--) {
				res = rad_read();
				if (res) {

					user = mymalloc(sizeof(USER_INFO));
					memset(user,0,sizeof(USER_INFO));

					user->username = strdup(parameter[1]);
					user->password = strdup(parameter[2]);

					res = rad_decode(user);
					if ( res == 3 ) {
						myfree(user->username);
						myfree(user->password);
						myfree(user);
						continue;
					}
					else if (!res) {
						myfree(user->username);
						myfree(user->password);
						myfree(user);
					}
					else {
					/* don't need to free parameter[2] as this is done for us. */
						char *pass = NULL;
						radius_check_user(parameter[1], parameter[2], TRUE,&pass);

					/* Double Check Stores and current Password */
						if (! (pass && parameter[2] && !stricmp(parameter[2],pass)) ) {
							myfree(user->username);
							myfree(user->password);
							myfree(user);
						}
					}
					
					break;
				}
			}

		}

		return user;
	}

	pUSER_INFO radius_lookup(char *parameter[MAX_PARAMETERS])
	{
		char username[BFSZ];
		char *password = NULL;
		pUSER_INFO user = NULL;
		char *ptr;

		if (domain && !strchr(parameter[1], '@')) {
			strcpy(username, parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		imsg("...Lookup: User='%s'", parameter[1]);

		radius_load_users();
		if (radius_check_user(parameter[1], NULL, FALSE,&password) == TRUE) {
			parameter[2] = strdup(password);
			myfree(password);
			user = radius_check(parameter);
		}

		return user;
	}

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

	void random_vector(u_char *vector)
	{
        int     randno;
        int     i;

        srand(time(0));
        for(i = 0;i < AUTH_VECTOR_LEN;) {
			randno = rand();
            memcpy(vector, &randno, sizeof(int));
            vector += sizeof(int);
            i += sizeof(int);
        }
	}

	int rad_request(char *username, char *passwd)
	{
		static int idbase = 0;
        AUTH_HDR *auth = NULL;
		int length, secretlen;
		char *ptr, passbuf[AUTH_PASS_LEN], md5buf[BFSZ], nasname[BFSZ];
		int ui, i;
		UINT4 nas_ipaddr;

		rad_info.total_length = 0;
		if (username) {
		/* Setup Sending Buffer */
			auth = (AUTH_HDR *)rad_info.send_buffer;

			if (passwd)
				auth->code = PW_AUTHENTICATION_REQUEST;
			else
				auth->code = PW_SERVICE_TYPE; 
			
			//PW_ACCOUNTING_STATUS; //PW_ACCOUNTING_REQUEST;
				
			rad_info.store_id = auth->id = getpid()+idbase++ % 256 ;

			random_vector(rad_info.vector);
			memcpy(auth->vector, rad_info.vector, AUTH_VECTOR_LEN);
			
			rad_info.total_length = AUTH_HDR_LEN;
			ptr = auth->data;
			
		/* User Name */
			*ptr++ = PW_USER_NAME;
			length = strlen(username);
			if(length > MAXPWNAM) length = MAXPWNAM;
			*ptr++ = length + 2;
			memcpy(ptr, username, length);
			ptr += length;
			rad_info.total_length += length + 2;

		/* Password  */
			if (passwd) {
				*ptr++ = PW_PASSWORD;
				*ptr++ = AUTH_PASS_LEN + 2;

				length = strlen(passwd);
				if(length > MAXPASS) length = MAXPASS;
				memset(passbuf, 0, AUTH_PASS_LEN);
				memcpy(passbuf, passwd, length);

			/* Calculate the MD5 Digest */
				secretlen = strlen(rad_info.secret);
				strcpy(md5buf, rad_info.secret);
				memcpy(md5buf + secretlen, auth->vector, AUTH_VECTOR_LEN);
				md5_calc(ptr, md5buf, secretlen + AUTH_VECTOR_LEN);

			/* Xor the password into the MD5 digest */
				for(i = 0;i < AUTH_PASS_LEN;i++) {
					*ptr++ ^= passbuf[i];
				}
				rad_info.total_length += AUTH_PASS_LEN + 2;
			}
			else {
			/* Acct-Status-Type = START */
/*				*ptr++ = PW_ACCT_STATUS_TYPE;
				*ptr++ = 6;
				*ptr++ = 0;
				*ptr++ = 2;
				rad_info.total_length += 6;
*/			}

		/* Local Port ID */
			*ptr++ = PW_NAS_PORT_ID;
			*ptr++ = 6;
			ui = htonl(rad_info.svc_port);
			memcpy(ptr, &ui, sizeof(UINT4));
			ptr += 4;
			rad_info.total_length += 6;

			gethostname(nasname, sizeof(nasname));
			nas_ipaddr = get_ipaddr(nasname);
                
		/* Local Address */
			*ptr++ = PW_NAS_IP_ADDRESS;
			*ptr++ = 6;
			ui = htonl(nas_ipaddr);
			memcpy(ptr, &ui, sizeof(UINT4));
			ptr += 4;
			rad_info.total_length += 6;

			auth->length = htons((short) rad_info.total_length);
						
			return TRUE;
		}
		return FALSE;
	}

	int rad_send(void)
	{
		int res;
        res = sendto(rad_info.sockfd, rad_info.send_buffer, rad_info.total_length, 0,&rad_info.saremote, sizeof(struct sockaddr_in));
        if (res <= 0)
			printf("radius: Sent %d bytes %s\n",res,rad_tcp_error());
        
        return res;
	}

	int rad_read(void)
	{
        int salen;
        struct timeval          tv;
        fd_set                  readfds;
        tv.tv_sec = 2;
        tv.tv_usec = 0;
        FD_ZERO(&readfds);
        FD_SET(rad_info.sockfd, &readfds);
        if (select(rad_info.sockfd + 1, &readfds, NULL, NULL, &tv) == 0) 
			return 0;

        salen = sizeof (rad_info.saremote);
        rad_info.recv_length = recvfrom (rad_info.sockfd, rad_info.recv_buffer, sizeof(rad_info.recv_buffer),
                0, &rad_info.saremote, &salen);
        
        if (rad_info.recv_length <= 0) 
			printf("radius: only got %d bytes %s\n",rad_info.recv_length,rad_tcp_error());
        return rad_info.recv_length;
	}

	int rad_decode(pUSER_INFO user)
	{
        AUTH_HDR        *auth;
        int             totallen;
        char            reply_digest[AUTH_VECTOR_LEN];
        char            calc_digest[AUTH_VECTOR_LEN];
        int             secretlen;
        AUTH_REQ        *authreq;
        VALUE_PAIR      *req;

        auth = (AUTH_HDR *)rad_info.recv_buffer;
        totallen = ntohs(auth->length);

        if (totallen != rad_info.recv_length) {
			printf("Received invalid reply length from server (want %d/ got %d)\n", totallen, rad_info.recv_length);
			return FALSE;
		}

	/* Verify the reply digest */
        memcpy(reply_digest, auth->vector, AUTH_VECTOR_LEN);
        memcpy(auth->vector, rad_info.vector, AUTH_VECTOR_LEN);
        secretlen = strlen(rad_info.secret);
        memcpy(rad_info.recv_buffer + rad_info.recv_length, rad_info.secret, secretlen);
        md5_calc(calc_digest, (char *)auth, rad_info.recv_length + secretlen);

        if (memcmp(reply_digest, calc_digest, AUTH_VECTOR_LEN) != 0) {
			printf("Warning: Received invalid reply digest from server\n");
        }

        authreq = rad_radrecv(user);
        if (authreq == NULL) return FALSE;

        req = authreq->request;

        if (auth->id != rad_info.store_id) {
			clear_user(user);			
			return 3; /* Wrong id */
		}

        if (auth->code != PW_AUTHENTICATION_ACK &&
			auth->code != PW_ACCOUNTING_RESPONSE) {
			clear_user(user);
			return FALSE;
		}

        return TRUE;
	}

	AUTH_REQ *rad_radrecv(pUSER_INFO user)
	{
		int length = rad_info.recv_length;
        u_char          *ptr;
        AUTH_HDR        *auth;
        int             totallen;
        int             attribute;
        int             attrlen;
        DICT_ATTR       *attr;
        UINT4           lvalue;
        VALUE_PAIR      *pair;
        static VALUE_PAIR vpair;
        AUTH_REQ        *authreq;
        static AUTH_REQ authdata;
        /*
         *      Pre-allocate the new request data structure
         */

        authreq = &authdata;
        auth = (AUTH_HDR *)rad_info.recv_buffer;
        totallen = ntohs(auth->length);

        /*
         *      Fill header fields
         */
        authreq->ipaddr = 0; /* host */
        authreq->udp_port = 0; /*udp_port;*/
        authreq->id = auth->id;
        authreq->code = auth->code;
        memcpy(authreq->vector, auth->vector, AUTH_VECTOR_LEN);

        /*
         *      Extract attribute-value pairs
         */
        ptr = (u_char *)auth->data;
        length -= AUTH_HDR_LEN;

        while(length > 0) {
                attribute = *ptr++;
                attrlen = *ptr++;
                if(attrlen < 2) {
                        length = 0;
                        continue;
                }
                attrlen -= 2;
                if((attr = dict_attrget(attribute)) == (DICT_ATTR *)NULL) {
                        printf("radius: Received unknown attribute %d\n", attribute);
                }
                else if ( attrlen >= AUTH_STRING_LEN ) {
                        printf("radius: attribute %d too long, %d >= %d\n", attribute,
                                attrlen, AUTH_STRING_LEN);
                }
                else {
                        pair = &vpair;
                        memset(pair,0,sizeof(*pair));
                        strcpy(pair->name, attr->name);
                        pair->attribute = attr->value;
                        pair->type = attr->type;

                        switch(attr->type) {

                        case PW_TYPE_STRING:
                                memcpy(pair->strvalue, ptr, attrlen);
                                pair->strvalue[attrlen] = '\0';
                                pair->length = attrlen;
                                /*printf("radius: Got pair %d (%s) (%s)\n",pair->attribute,attr->name,pair->strvalue);*/
                                if (user && pair->attribute == rad_info.extra_code) { /* vendor specific */
                                        user->information = strdup(pair->strvalue);
                                }

                                break;
                        
                        case PW_TYPE_INTEGER:
                        case PW_TYPE_IPADDR:
                                memcpy(&lvalue, ptr, sizeof(UINT4));
                                pair->lvalue = ntohl(lvalue);
                                break;
                        
                        default:
                                printf("radius:     %s (Unknown Type %d)\n", attr->name,attr->type);
                                break;
                        }

                }
                ptr += attrlen;
                length -= attrlen + 2;
        }
        authreq->request = NULL;
        return(authreq);
	}


	typedef struct {char *name; int id; int type; } DICINPUT;
	static DICINPUT dicinput[]= {
	    "User-Name", 1,PW_TYPE_STRING
       ,"Password", 2,PW_TYPE_STRING
       ,"CHAP-Password", 3,PW_TYPE_STRING
       ,"NAS-IP-Address", 4, PW_TYPE_IPADDR
       ,"NAS-Port-Id", 5, PW_TYPE_INTEGER
       ,"Service-Type", 6, PW_TYPE_INTEGER
       ,"Framed-Protocol", 7, PW_TYPE_INTEGER
       ,"Framed-IP-Address", 8, PW_TYPE_IPADDR
       ,"Framed-IP-Netmask", 9, PW_TYPE_IPADDR
       ,"Framed-Routing", 10, PW_TYPE_INTEGER
       ,"Filter-Id", 11,PW_TYPE_STRING
       ,"Framed-MTU", 12, PW_TYPE_INTEGER
       ,"Framed-Compression", 13, PW_TYPE_INTEGER
       ,"Login-IP-Host", 14, PW_TYPE_IPADDR
       ,"Login-Service", 15, PW_TYPE_INTEGER
       ,"Login-TCP-Port", 16, PW_TYPE_INTEGER
       ,"Reply-Message", 18,PW_TYPE_STRING
       ,"Callback-Number", 19,PW_TYPE_STRING
       ,"Callback-Id", 20,PW_TYPE_STRING
       ,"Framed-Route", 22,PW_TYPE_STRING
       ,"Framed-IPX-Network", 23, PW_TYPE_IPADDR
       ,"State", 24,PW_TYPE_STRING
       ,"Class", 25,PW_TYPE_STRING
       ,"Vendor-Specific", 26,PW_TYPE_STRING
       ,"Session-Timeout", 27, PW_TYPE_INTEGER
       ,"Idle-Timeout", 28, PW_TYPE_INTEGER
       ,"Termination-Action", 29, PW_TYPE_INTEGER
       ,"Called-Station-Id", 30,PW_TYPE_STRING
       ,"Calling-Station-Id", 31,PW_TYPE_STRING
       ,"NAS-Identifier", 32,PW_TYPE_STRING
       ,"Proxy-State", 33,PW_TYPE_STRING
       ,"Login-LAT-Service", 34,PW_TYPE_STRING
       ,"Login-LAT-Node", 35,PW_TYPE_STRING
       ,"Login-LAT-Group", 36,PW_TYPE_STRING
       ,"Framed-AppleTalk-Link", 37, PW_TYPE_INTEGER
       ,"Framed-AppleTalk-Network", 38, PW_TYPE_INTEGER
       ,"Framed-AppleTalk-Zone", 39,PW_TYPE_STRING

       ,"Acct-Status-Type", 40, PW_TYPE_INTEGER
       ,"Acct-Delay-Time", 41, PW_TYPE_INTEGER
       ,"Acct-Input-Octets", 42, PW_TYPE_INTEGER
       ,"Acct-Output-Octets", 43, PW_TYPE_INTEGER
       ,"Acct-Session-Id", 44,PW_TYPE_STRING
       ,"Acct-Authentic", 45, PW_TYPE_INTEGER
       ,"Acct-Session-Time", 46, PW_TYPE_INTEGER
       ,", Acct-Input-Packets", 47, PW_TYPE_INTEGER
       ,", Acct-Output-Packets", 48, PW_TYPE_INTEGER
       ,"Acct-Terminate-Cause", 49, PW_TYPE_INTEGER
       ,"Acct-Multi-Session-Id", 50,PW_TYPE_STRING
       ,"Acct-Link-Count", 51, PW_TYPE_INTEGER

       ,"CHAP-Challenge", 60,PW_TYPE_STRING
       ,"NAS-Port-Type", 61, PW_TYPE_INTEGER
       ,"Port-Limit", 62, PW_TYPE_INTEGER
       ,"Login-LAT-Port", 63, PW_TYPE_INTEGER

       ,"Connect-Info", 77,PW_TYPE_STRING

       ,"Huntgroup-Name", 221,PW_TYPE_STRING
       ,"User-Category", 1029,PW_TYPE_STRING
       ,"Group-Name", 1030,PW_TYPE_STRING
       ,"Simultaneous-Use", 1034, PW_TYPE_INTEGER
       ,"Strip-User-Name", 1035, PW_TYPE_INTEGER
       ,"Fall-Through", 1036, PW_TYPE_INTEGER
       ,"Add-Port-To-IP-Address", 1037, PW_TYPE_INTEGER
       ,"Exec-Program", 1038,PW_TYPE_STRING
       ,"Exec-Program-Wait", 1039,PW_TYPE_STRING
       ,"Hint", 1040,PW_TYPE_STRING
       ,"Pam-Auth", 1041,PW_TYPE_STRING
       ,"Login-Time", 1042,PW_TYPE_STRING

       ,"Expiration",  21, PW_TYPE_DATE
       ,"Auth-Type", 1000, PW_TYPE_INTEGER
       ,"Menu", 1001,PW_TYPE_STRING
       ,"Termination-Menu", 1002,PW_TYPE_STRING
       ,"Prefix", 1003,PW_TYPE_STRING
       ,"Suffix", 1004,PW_TYPE_STRING
       ,"Group", 1005,PW_TYPE_STRING
       ,"Crypt-Password", 1006,PW_TYPE_STRING
       ,"Connect-Rate", 1007, PW_TYPE_INTEGER
       ,NULL,0,0
	};


	DICT_ATTR *dict_attrget(int attribute)
	{
        static DICT_ATTR        attr;
        int i;
        for (i=0; dicinput[i].name!=NULL; i++) {
                if (attribute==dicinput[i].id) {
                        attr.value = attribute;
                        strncpy(attr.name,dicinput[i].name,30);
                        attr.type = dicinput[i].type;
                        attr.vendor = 0;
                        return &attr;
                }
        }
        return NULL;
	}

#endif

/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/* Pam Body of Code */
/********************/

#ifdef PAMAUTH
/*
# INI settings for RadiusAuth
# ---------------------------

pam_host 1.2.3.4
pam_type service_name

*/


	void pam_load(char *bf, char *str)
	{
		if (!stricmp(bf,"pam_host")) {
			myfree(pam_info.host);
			pam_info.host = strdup(str);
		}
		else if (!stricmp(bf,"pam_type"))
			myfree(pam_info.pam_type);
			pam_info.pam_type = strdup(str);
		}
	}

	void pam_setup(void)
	{
		memset(&pam_info, 0, sizeof(pam_info));
	}

	void pam_clear(void)
	{
		myfree(pam_info.pam_type);
	}

	int pam_connect(void)
	{
		return TRUE;
	}
	
	int pam_exit(char *parameter[MAX_PARAMETERS])
	{
		return TRUE;
	}

	pUSER_INFO pam_check(char *parameter[MAX_PARAMETERS])
	{
		pUSER_INFO user = NULL;
		int res;

		if (domain && !strchr(parameter[1], '@')) {
			strcpy(username, parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		if (parameter[3])
			imsg("...Checking: User='%s'   Pass='xxxx'  IP='%s'", parameter[1], parameter[3]);
		else
			imsg("...Checking: User='%s'   Pass='xxxx'  IP='(NULL)'", parameter[1]);


		res = pam_start(pam_type, user, &conv, &pamh);
		if (r == PAM_SUCCESS) {

			r = pam_authenticate(pamh, 0);
			if (r == PAM_SUCCESS) {

				r = pam_acct_mgmt(pamh, 0);
				if (r == PAM_SUCCESS) {

					user = mymalloc(sizeof(USER_INFO));
					memset(user,0,sizeof(USER_INFO));

					user->username = strdup(parameter[1]);
					user->password = strdup(parameter[2]);
					
					imsg("Authenticated ok");
				}
				else
					imsg("%s",pam_strerror(pamh,r));

			}
			else
				imsg("pam_authenticate failed %s",pam_strerror(pamh,r));

		/* exit pam */
			pam_end(pamh,r);

		}
		else
			imsg("pam_start failed %s",pam_strerror(pamh,r));

		return user;
	}

	pUSER_INFO pam_lookup(char *parameter[MAX_PARAMETERS])
	{
		pUSER_INFO user = NULL;
		if (domain && !strchr(parameter[1], '@')) {
			strcpy(username, parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		imsg("...Lookup: User='%s'", parameter[1]);


		return user;
	}

#endif /* PAMAUTH */


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

/* Safe sprintf funcitons */
/* Lynden's version */
char *lcpy(char *dst, char *src, int len)
{
	if (dst && src && len > 0) {
		char *ptr = dst;

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

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

		return lvprintf( buffer, bfsize, format, arg_list);
	}
	return FALSE;
}

int lvprintf( char *buffer, int bfsize, char *format, va_list arg_list)
{
	if (buffer && format && bfsize) {
		char *bf = buffer;
		char temp[BFSZ],*data = NULL;
		char *start, *end;
		int size_left = bfsize, size, extras, i, min, max;
		long value[3];
		double dvalue;
		char *strvalue;
		
		bf[0] = '\0';

		start = format;
		end = strchr(format,'%');

		while (end && size_left > 0) {

		/* Update Buffer With ".....%" the dots first */
			size = end-start;	
			if (size+1 > size_left) size = size_left-1; 

			lcpy(bf,start,size+1);	/* Account for NULL */
			size_left -= size;
			bf += size;
			
		/* Find out where the ending '%xx' value is */
			start = end + 1;
			extras = 1;
			max = min = -1;
			while (	*start != '\0' &&
					*start != 'c' && *start != 'C' &&
					*start != 'd' && *start != 'i' &&
					*start != 'o' && *start != 'u' &&
					*start != 'x' && *start != 'X' &&
					*start != 'e' && *start != 'E' &&
					*start != 'f' && *start != 'g' &&
					*start != 'G' && *start != 'n' &&
					*start != 'p' && *start != '%' && 
					*start != 's' && *start != 'S') {
				if (*start == '*') {
					extras++;
					if (max == -1 && min == -1) {
						max = va_arg(arg_list, long);
						if (max < 0)
							max = 0;
					}
				}
				else if (*start == '.') {
					if (start[1] == '*')
						min = va_arg(arg_list, long);
					else
						min = atoi(start+1);
					if (min < 0)
						min = 0;
				}
				else if (max == -1 && (*start >= '0' && *start <= '9'))
					max = atoi(start);
				start++;
			}
			
			if (*start != ' ' && *start != '\0' && extras <= 3) {
				lcpy(temp,end,(start+1-end) + 1);

			/* Setup Values min-max variables */
				if (min > -1) {
					value[0] = min;
					value[1] = max;
				}
				else {
					value[0] = max;
					value[1] = min;
				}

				if (*start == 'f' || *start == 'e' || *start == 'g' || 
					*start == 'E' || *start == 'G') {
					start++;

					dvalue = va_arg(arg_list, double);

					data = mymalloc( 2000 );
					if (data) {
						data[0] = '\0';
						
					/* User sprintf to display */
						if (extras == 1)
							sprintf(data,temp,dvalue);
						else if (extras == 2)
							sprintf(data,temp,value[0], dvalue);
						else if (extras == 3)
							sprintf(data,temp,value[0],value[1],dvalue);
					}
				}
				else if (*start == 's' || *start == 'S') {
					start++;

				/* Setup Values */
					strvalue = va_arg(arg_list, char *);

				/* Determine the amount of space needed. (min + max + 5) */
					if (strvalue) {
						if (min != -1) {
							if (max > min)
								size = max + 2;
							else
								size = min + 2;
						}
						else {
							size = max + 2;
				
						/* Have to realloc data */
							i = strlen(strvalue);
							if (i+10 > size)
								size = i +10;
						}

						data = mymalloc( size );
						if (data) {
							data[0] = '\0';

							if (extras == 1)
								sprintf(data,temp,strvalue);
							else if (extras == 2)
								sprintf(data,temp,value[0], strvalue);
							else if (extras == 3)
								sprintf(data,temp,value[0],value[1],strvalue);
						}
					}
					else {
						data = mymalloc( 10 );
						if (data)
							strcpy(data,"(NULL)");
					}
				}
				else if (*start == '%') {
					data = mymalloc( 3);
					strcpy(data,"%");
					start++;
				}
				else {
					data = mymalloc( 23 );
					if (data) {
						data[0] = '\0';
						start++;

					/* Setup Values */
						value[extras-1] = va_arg(arg_list, long);
					
					/* User sprintf to display */
						sprintf(data,temp,value[0],value[1],value[2]);
					}
				}

				if (data) {
					size = strlen(data);
					if (size+1 > size_left) size = size_left-1;
					lcpy(bf,data,size+1);
					size_left -= size;
					bf += size;

					myfree(data);
				}
			}
			else {
			/* Not sure if so leave untouched */
				size = ++start-end;
				if (size+1 > size_left) size = size_left-1;

				lcpy(bf,end,size+1);
				size_left -= size;
				bf += size;
				start++;
			}
			end = strchr(start,'%');
		}

		lcpy(bf,start,size_left);
		size_left -= strlen(bf);
		return bfsize - size_left;
	}
	return FALSE;
}

#define NWA_ABORT			-1
#define NWA_NEGATE_CLASS	'^'

int wmatch(char *text, char *wild)
{
    register int	last;
    register int	matched;
    register int	reverse;

	if (!text || !wild)
		return FALSE;

    for ( ; wild && *wild; text++, wild++) {
		if (*text == '\0' && *wild != '*')
			return NWA_ABORT;
		switch (*wild) {
			case '\\':
				/* Literal match with following character. */
				wild++;
				/* FALLTHROUGH */
			default:
				if (*text != *wild)
					return FALSE;
				continue;
			case '?':
				/* Match anything. */
				continue;
			case '*':
				while (*++wild == '*')
					/* Consecutive stars act just like one. */
					continue;
				if (*wild == '\0')
					/* Trailing star matches everything. */
					return TRUE;
				while (*text)
					if ((matched = wmatch(text++, wild)) != FALSE)
						return matched;
				return NWA_ABORT;
			case '[':
				reverse = wild[1] == NWA_NEGATE_CLASS ? TRUE : FALSE;
				if (reverse)
					/* Inverted character class. */
					wild++;
				matched = FALSE;
				if (wild[1] == ']' || wild[1] == '-')
					if (*++wild == *text)
						matched = TRUE;
				for (last = *wild; *++wild && *wild != ']'; last = *wild)
					if (*wild == '-' && wild[1] != ']'
						? *text <= *++wild && *text >= last : *text == *wild)
						matched = TRUE;
				if (matched == reverse)
					return FALSE;
			continue;
		}
    }
    return *text == '\0';
}

int ld_match_list(char *xwild, char *xgrp2)
{
        char bf[1000];
        char grp[1000];
        int m=TRUE,lastm;
        char *s,*p,*w;
        int done,len;

	if (strlen(xwild)==0) return TRUE;
        ncpy(grp,xgrp2,1000-10); strlwr(grp);
        lastm = FALSE;
        p = xwild;
        done = FALSE;
        for (;!done;) {
                s = strchr(p,',');
                if (s==NULL) {s = xwild + strlen(xwild); done = TRUE;}
                if (*p==0) break;
                len = s - p;
                if (len>100) len = 100;
                strncpy(bf,p,len); bf[len] = 0;
                strlwr(bf); w = bf;

                m = TRUE;
                if (*w=='!') {
                        w++;
                        m = FALSE;
                }
                if (wild_match(w,grp)) { lastm = m; } 

                p = s + 1;
        }
        return lastm;
}

int wild_match(char *wild,char *text)
{
	if (wild) {
		if (wild[0] == '*' && wild[1] == '\0')
			return TRUE;
		return wmatch(text, wild) == TRUE;
	}
	return FALSE;
}


/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/************************************************************************************/
/* LDAP Body of Code */
/*********************/

#ifdef LDAPAUTH
/* Safe String Fn's */

char *s_stradd(char *base, char *add)
{
	char *value;
	if (add) {
		unsigned int new_size;
		if (base)
			new_size = strlen(base) + strlen(add);
		else
			new_size = strlen(add);
	
		value =(char *)realloc(base, new_size + 5);
		if (value) {
			if (base)
				strcat(value,add);
			else
				strcpy(value,add);

			return value;
		}
	}
	return base;
}

/*
# INI settings for LDAPAuth
# -------------------------

# pop_domain 161.29.2.41 (users domain now)
ldap_host 161.29.2.63
ldap_mgr_pw qwerty
ldap_mgr_dn cn=Manager,dc=netwin,dc=co,dc=nz
ldap_search_base dc=netwin,dc=co,dc=nz

#ldap_dmail_forward fwd			(Note does not setup saving only displaying)
#ldap_drop_file drop_file
#ldap_mailmask ipmask

ldap_search_name mail
ldap_objectclass person

#info_fields usergroups groups,mail test
#info_fields mailquota quota
  
*/
//	referrals

	void ld_load(char *bf, char *str)
	{
		if (!stricmp(bf,"pop_domain")) {
			myfree(domain);
			domain = strdup(str);
		}
		else if (!stricmp(bf,"ldap_host")) {
			myfree(ldap_info.host);
			ldap_info.host = strdup(str);
		}
		else if (!stricmp(bf,"ldap_host2")) {
			myfree(ldap_info.host2);
			ldap_info.host2 = strdup(str);
		}
		else if (!stricmp(bf,"ldap_port")) {
			ldap_info.port = atoi(str);
		}
		else if (!stricmp(bf,"ldap_mgr_pw")) {
			myfree(ldap_info.manager_pass);
			ldap_info.manager_pass = strdup(str);
		}
		else if (!stricmp(bf,"ldap_mgr_dn")) {
			myfree(ldap_info.manager_dn);
			ldap_info.manager_dn = strdup(str);
		}
		else if (!stricmp(bf,"ldap_search_base")) {
			myfree(ldap_info.search_base);
			ldap_info.search_base = strdup(str);
		}
		else if (!stricmp(bf,"ldap_search_name")) {
			myfree(ldap_info.search_name);
			ldap_info.search_name = strdup(str);
		}
		else if (!stricmp(bf,"ldap_search_name_alt")) {
			myfree(ldap_info.search_name_alt);
			ldap_info.search_name_alt = strdup(str);
		}
		else if (!stricmp(bf,"ldap_search_name_alt2")) {
			myfree(ldap_info.search_name_alt2);
			ldap_info.search_name_alt2 = strdup(str);
		}
		else if (!stricmp(bf,"ldap_uid")) {
			myfree(ldap_info.uid);
			ldap_info.uid = strdup(str);
		}
		else if (!stricmp(bf,"ldap_objectclass")) {
			myfree(ldap_info.objectclass);
			ldap_info.objectclass = strdup(str);
		}
		else if (!stricmp(bf,"ssha_hash_dont")) {
			ldap_info.pass_crypt = 2;
		}
		else if (!stricmp(bf,"sha_hash")) {
			ldap_info.pass_crypt = 1;
		}

	/* Other LDAP options */
		else if (!stricmp(bf,"user_extend")) {
			if (!stricmp(str,"true") || !stricmp(str,"on") || atoi(str) > 0)
				ldap_info.user_extend = TRUE;
			else
				ldap_info.user_extend = FALSE;
		}
		else if (!strnicmp(bf,"ldap_scope",10)) {
			if (!stricmp(bf,"ldap_scope_base")) 
				ldap_info.scope = LDAP_SCOPE_BASE;
			if (!stricmp(bf,"ldap_scope_onelevel")) 
				ldap_info.scope = LDAP_SCOPE_ONELEVEL;
			if (!stricmp(bf,"ldap_scope_subtree")) 
				ldap_info.scope = LDAP_SCOPE_SUBTREE;
		}
		else if (!stricmp(bf,"verbose"))
			ldap_info.verbose = TRUE;
		
	/* Group Search */
		else if (!stricmp(bf,"ldap_dnshort")) 
			ldap_info.dnshort = TRUE;
		else if (!stricmp(bf,"ldap_group_base")) {
			myfree(ldap_info.group_base);
			ldap_info.group_base = strdup(str);
		}
		else if (!stricmp(bf,"ldap_group_search")) {
			myfree(ldap_info.group_search);
			ldap_info.group_search = strdup(str);
		}
		else if (!stricmp(bf,"ldap_group_attrib")) {
			myfree(ldap_info.group_attrib);
			ldap_info.group_attrib = strdup(str);
		}
		else if (!stricmp(bf,"ldap_group_field")) {
			myfree(ldap_info.group_field);
			ldap_info.group_field = strdup(str);
		}

	/* Table Field Names */
		else if (!stricmp(bf,"ldap_dmail_forward"))
			ldap_info.ldap_dmail_forward = strdup(str);
		else if (!stricmp(bf,"field_forward"))
			ldap_info.ldap_dmail_forward = strdup(str);
		else if (!stricmp(bf,"ldap_mailmask"))
			ldap_info.ldap_mailmask = strdup(str);
		else if (!stricmp(bf,"ldap_dmail_uid"))
			ldap_info.ldap_dmail_uid = strdup(str);		
		else if (!stricmp(bf,"ldap_drop_file"))
			ldap_info.ldap_drop_file = strdup(str);
		else if (!stricmp(bf,"ldap_mailhost"))
			ldap_info.ldap_mailhost = strdup(str);
		else if (!stricmp(bf,"ldap_maildeliveryoption"))
			ldap_info.ldap_maildeliveryoption = strdup(str);
		else if (!stricmp(bf,"ldap_access_wild"))
			ldap_info.ldap_access_wild = strdup(str);
		else if (!stricmp(bf,"ldap_access_field"))
			ldap_info.ldap_access_field = strdup(str);
		else if (!stricmp(bf,"info_fields")) {
			char *start, *ptr, *end;

			start = str;
			while (start) {

				while (*start == '\t' || *start == ' ' || *start == '\n') 
					start++;

				end = strchr(start,',');
				if (end) *end++ = '\0';

				ptr = strchr(start,' ');
				if (ptr) {
					while (*ptr == ' ' || *ptr == '\t')
						*ptr++ = '\0';

				/* Add new Field */
					ldap_info.field_name = (char **)realloc(ldap_info.field_name, sizeof(char *) * (ldap_info.field_count+2));
					ldap_info.field_name[ldap_info.field_count] = strdup(start);
					ldap_info.field_name[ldap_info.field_count+1] = strdup(ptr);
					ldap_info.field_count += 2;
				}
				start = end;
			}
		}

		else if (!stricmp(bf,"must_set_fields")) {
			char *start, *ptr, *end;

			start = str;
			while (start) {

				while (*start == '\t' || *start == ' ' || *start == '\n') 
					start++;

				end = strchr(start,',');
				if (end) *end++ = '\0';

				ptr = strchr(start,' ');
				if (ptr) {
					while (*ptr == ' ' || *ptr == '\t')
						*ptr++ = '\0';

				/* Add new must need for set */
					
					ldap_info.must_name = (char **)realloc((void *)ldap_info.must_name, sizeof(char *) * (ldap_info.must_count+2));
					ldap_info.must_name[ldap_info.must_count] = strdup(start);
					ldap_info.must_name[ldap_info.must_count+1] = strdup(ptr);
					ldap_info.must_count += 2;
				}
				start = end;
			}
		}

		else if (!stricmp(bf,"field_alias"))
			ldap_info.field_alias = strdup(str);
		else if (!stricmp(bf,"field_aliasuser"))
			ldap_info.field_aliasuser = strdup(str);
		else if (!stricmp(bf,"field_username"))
			ldap_info.field_username = strdup(str);
		else if (!stricmp(bf,"field_password"))
			ldap_info.field_password = strdup(str);

	/* SSL */
		else if (!stricmp(bf,"ldap_sslenable")) {
			if (!stricmp(str,"true") || !stricmp(str,"on") || atoi(str) > 0)
				ldap_info.sslenable = TRUE;
			else
				ldap_info.sslenable = FALSE;			
		}
		else if (!stricmp(bf,"ldap_certdbpath")) {
			myfree(ldap_info.certdbpath);
			mystrdup(ldap_info.certdbpath,str);
		}
	}

	void ld_setup(void)
	{
		ldap_info.ldap = NULL;
		ldap_info.host = strdup("localhost");
		ldap_info.port = 389;
		ldap_info.manager_dn = ldap_info.manager_pass = NULL;
		ldap_info.search_base = NULL;
		ldap_info.search_name = strdup("mail");
		ldap_info.objectclass = strdup("person");
		ldap_info.pass_crypt = 2; /* Default to plain text passwords */

		ldap_info.field_count = ldap_info.verbose = ldap_info.user_extend = FALSE;
		ldap_info.field_name = NULL;
		ldap_info.scope = LDAP_SCOPE_SUBTREE;


		ldap_info.group_base =
		ldap_info.group_search =
		ldap_info.group_attrib =
		ldap_info.group_field =

		ldap_info.field_username = 
		ldap_info.field_alias = 
		ldap_info.field_aliasuser = 
		ldap_info.field_password = 

		ldap_info.ldap_dmail_forward = 
		ldap_info.ldap_mailmask = 
		ldap_info.ldap_drop_file = NULL;
		ldap_info.ldap_mailhost = NULL;
		ldap_info.ldap_maildeliveryoption = NULL;
		ldap_info.ldap_access_wild = NULL;
		ldap_info.ldap_access_field = NULL;

		ldap_info.result = NULL;
		ldap_info.entry = NULL;
		ldap_info.user = NULL;
		ldap_info.pass = NULL;

	/* SSL */
		ldap_info.sslenable = FALSE;
		ldap_info.certdbpath = NULL;
	}

	void ld_clear(void)
	{
		int i;

		myfree(ldap_info.host);
		myfree(ldap_info.host2);
		myfree(ldap_info.manager_dn);
		myfree(ldap_info.manager_pass);
		myfree(ldap_info.search_base);

		myfree(ldap_info.group_base);
		myfree(ldap_info.group_search);
		myfree(ldap_info.group_attrib);
		myfree(ldap_info.group_field);

		myfree(ldap_info.field_username);
		myfree(ldap_info.field_alias);
		myfree(ldap_info.field_aliasuser);
		myfree(ldap_info.field_password);

		myfree(ldap_info.ldap_dmail_forward);		
		myfree(ldap_info.ldap_mailmask);
		myfree(ldap_info.ldap_drop_file);
		myfree(ldap_info.ldap_mailhost);
		myfree(ldap_info.ldap_maildeliveryoption);
		myfree(ldap_info.ldap_access_wild);
		myfree(ldap_info.ldap_access_field);

		for (i=0;i<ldap_info.field_count;i++)
			myfree(ldap_info.field_name[i]);
		myfree(ldap_info.field_name);

		for (i=0;i<ldap_info.must_count;i++)
			myfree(ldap_info.must_name[i]);
		myfree(ldap_info.must_name);

		myfree(ldap_info.user);
		myfree(ldap_info.pass);

	/* SSL */
		myfree(ldap_info.certdbpath);

		ld_user_close(NULL);
	}

/* Return: FALSE  - Had to reconnect - Try command Again
			TRUE  - Continue
			-TRUE - Failed to reconnect to LDAP server 
			-2 - Unstated Error 
*/
	LDAP *ld_connect_host(char *host);
	void ldap_check_server(void)
	{
		if (ldap_info.host2 && ldap_info.which_host) {
			LDAP *templdap = ld_connect_host(ldap_info.host);
			if (templdap) {
				ld_close();
				ldap_info.which_host = 0;
				ldap_info.ldap = templdap;

				imsg("Main LDAP Server Up Again - Reconnected to Server OK.\n");
			}
		}
	}

	int ldap_check_codes(int res)
	{
		if ( res != LDAP_SUCCESS ) {
			if (res == LDAP_SERVER_DOWN || (!ldap_info.ldap && res != LDAP_INVALID_CREDENTIALS) ) {
				ld_close();
				if (ld_connect()) {
					imsg("LDAP Server Down - Reconnected OK.\n");
					return FALSE;
				}
				imsg("LDAP Server Down - Failed to Reconnect.\n");
				return -TRUE;
			}
#ifdef LDAP_PARTIAL_RESULTS
			else if (res == LDAP_PARTIAL_RESULTS) {
			/* Handle the Referral */
				imsg("Partial Results and REFERRAL - %d - %s\n",res,ldap_err2string(res));
			}
#endif
#ifdef LDAP_REFERRAL
			else if (res == LDAP_REFERRAL) {
			/* Handle the Referral */
				imsg("Unexpected REFERRAL Error - %d - %s\n",res,ldap_err2string(res));
			}
#endif			
			else 
				imsg("LDAP Error Code - %d - %s\n",res,ldap_err2string(res));
			return -2;
		}

		return TRUE;
	}

	int ld_manager_auth(LDAP *ldap_con) 
	{
		if (ldap_con) {
		/* Authenticate as Manager */
			int res;

			imsg("Authent as Manager {%s}\n",ldap_info.manager_dn);

			res = ldap_simple_bind_s( ldap_con, ldap_info.manager_dn, ldap_info.manager_pass );
			if (!ldap_check_codes(res)) {
				imsg("Authent as Manager {%s}\n",ldap_info.manager_dn);
				res = ldap_simple_bind_s( ldap_con, ldap_info.manager_dn, ldap_info.manager_pass );
			}
			if (res == LDAP_SUCCESS)
				return TRUE;
			else {
				emsg("-ERR: Can't bind to ldap host as %s: {%s}", ldap_info.manager_dn, ldap_err2string(res));
				printf("-ERR: Can't bind to ldap host as %s: {%s}", ldap_info.manager_dn, ldap_err2string(res));
			}
		}
		else {
			emsg("-ERR: Cannot Connect to LDAP database {%s:%d}\n",ldap_info.host, ldap_info.port);
			printf("-ERR: Cannot Connect to LDAP database {%s:%d}\n",ldap_info.host, ldap_info.port);
		}

		return FALSE;
	}

	LDAP *ld_connect_host(char *host)
	{
		LDAP *checkldap = NULL;
		
#ifdef HAS_SSL
		if (ldap_info.sslenable) {
		/* Do not plan to use certificate-based client authentication */
			if (ldapssl_client_init(ldap_info.certdbpath,NULL)) {
				emsg( "-ERR Failed to initialize SSL client...\n" ); 
				printf( "-ERR Failed to initialize SSL client...\n" ); 
				return NULL;
			}

		/*	plan to use certificate-based client authentication
			ldapssl_clientauth_init(); */
		}
#endif

#ifdef WIN32
		{
			LDAPVersion version;
#ifdef LDAP_VERSION3
			int v3 = LDAP_VERSION3;
#else
			int v3 = LDAP_VERSION2;
#endif

			ldap_version( &version );
			imsg("LDAP API supports - LDAP v%.2f - Supports SSL v%.2f\n",version.protocol_version/100.0,version.SSL_version/100.0);

/*			if (ldap_set_option( ldap_info.ldap, LDAP_OPT_PROTOCOL_VERSION, &v3 ) != 0) {
				ldap_perror( ldap_info.ldap, "ldap_set_option");
				return FALSE;
			}
*/
		}
#endif


	/* Double check that Mangers setting are provided */
		if (!ldap_info.manager_dn || !ldap_info.manager_pass) {
			emsg("-ERR: No LDAP ldap_mgr_dn/ldap_mgr_pw in ini file\n");
			printf("-ERR: No LDAP ldap_mgr_dn/ldap_mgr_pw in ini file\n");
			return NULL;
		}

	/* Open LDAP connection */
		imsg("Connecting to %s:%d\n",host, ldap_info.port);
#ifdef HAS_SSL
		if (ldap_info.sslenable)
			checkldap = ldapssl_init( host, ldap_info.port, 1 );
		else
			checkldap = ldap_open( host, ldap_info.port );
#else
		checkldap = ldap_open( host, ldap_info.port );
#endif

		if (checkldap) {
/*#ifdef LDAP_OPT_REFERRALS Changed because opt_on is not defined in earlier versions but opt_referrals is */
#ifdef LDAP_OPT_ON
#	ifndef WIN32
#		warning Supporting LDAP v3 Referrals
#	endif

			imsg("Connected setting up referals to 'ON'\n");
		/* Turn Automatical Folow Referrals to be on. */
			ldap_set_option(checkldap, LDAP_OPT_REFERRALS, LDAP_OPT_ON);

#else
#	ifndef WIN32
#		warning NOT Supporting LDAP v3 Referrals
#	else
#		error NOT Supporting LDAP v3 Referrals
#	endif
#endif

			if (ld_manager_auth(checkldap)) {
				imsg("...LDAP: Connected to database..");
			}
			else {
				ldap_unbind( checkldap );
				checkldap = NULL;
			}
		}

		return checkldap;
	}

	int ld_connect(void)
	{
		if (ldap_info.ldap) {
			if (ldap_info.which_host)
				imsg("Already Connected %s:%d\n",ldap_info.host2, ldap_info.port);
			else
				imsg("Already Connected %s:%d\n",ldap_info.host, ldap_info.port);
			return TRUE;
		}

		ldap_info.which_host = 0;
		ldap_info.ldap = ld_connect_host(ldap_info.host);
		if (ldap_info.ldap)
			return TRUE;
		else if (ldap_info.host2) {
			ldap_info.ldap = ld_connect_host(ldap_info.host2);
			if (ldap_info.ldap) {
				ldap_info.which_host = 1;
				return TRUE;
			}		
		}
		return FALSE;
	}

	int ld_close(void)
	{
		if (ldap_info.ldap) {
			ldap_unbind( ldap_info.ldap );
			ldap_info.ldap = NULL;
		}
		return TRUE;
	}

	int ld_exit(char *parameter[MAX_PARAMETERS])
	{
		return TRUE;
	}


	int ld_extend_search(char *search_base, char *user,char *rsearch_base, char *ruser)
	{
	/* Deals with multiple users with same name
		If user appends ldap search info to the username they are stripped and
		used in the search.  
			E.g. user logs in as
					user = "uname,ou=orgunit,o=org"
				search = search + "ou=orgunit,o=org"
				user = "uname"
	*/
		char *s;
		char tmp[LBUFSZ];

		ncpy(rsearch_base,search_base,LBUFSZ-1);
		ncpy(ruser,user,LBUFSZ-1);
		if (ldap_info.user_extend) {
			
			if (strncmp(ruser,"cn=",3)==0) {
				ncpy(tmp,ruser+3,LBUFSZ-1);
				ncpy(ruser,tmp,LBUFSZ-1);
			}
			s = strchr(ruser,',');
			if (s==NULL) 
				return FALSE;
			ncpy(rsearch_base,s+1,LBUFSZ-1);
			*s = 0;

		}
		return TRUE;
	}

	void ld_print_attributes(LDAPMessage *entry)
	{
		BerElement	*ber;
		char		*a, *dn;
		char		**vals;
		int			i;

		dn = ldap_get_dn( ldap_info.ldap, entry );
		if (dn) {
			printf( "dn: %s\n", dn );
			ldap_memfree( dn );
		}

		a = ldap_first_attribute( ldap_info.ldap, entry, &ber );
		while (a) {

			vals = ldap_get_values( ldap_info.ldap, entry, a);
			if (vals) {
				for ( i = 0; vals[i] != NULL; i++ ) {
					printf( "%s: %s\n", a, vals[i] );
				}
				ldap_value_free( vals ); 
			}
		/*	ldap_memfree( a ); */

			a = ldap_next_attribute( ldap_info.ldap, entry, ber );
		}
/*
		if (ber) {
			ber_free( ber, 0 );
		}
*/
		printf( "\n" );
	}


	pUSER_INFO ld_user_select(LDAPMessage *entry, char *dn, char *pass)
	{
		static USER_INFO user;
	/*	char info[LBUFSZ] = ""; */
		char *info = NULL;
		char **vals;
		int i, n;

		memset(&user,0,sizeof(USER_INFO));

	/* get username */
		if (ldap_info.search_name) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.search_name);
			if (vals) {
				if (vals[0])
					user.username = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}
		mystrdup(user.password,pass);
	/* Get the mailhost (BT addition) */
		if (ldap_info.ldap_mailhost) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_mailhost);
			if (vals) {
				if (vals[0]) user.mailhost = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}
	/* Get the maildeliveryoption (BT addition) */
		if (ldap_info.ldap_maildeliveryoption) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_maildeliveryoption);
			if (vals) {
				if (vals[0]) user.maildeliveryoption = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}
	/* Get the mailhost (BT addition) */
		if (ldap_info.ldap_access_field) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_access_field);
			if (vals) {
				for (i=0; i<10; i++) {
					if (vals[i]==NULL) break;
					if (strstr(vals[i],"pop")!=NULL) user.limit_access = TRUE;
				}
			}
		}
	/* Get the mailhost (BT addition) */
		if (ldap_info.uid) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.uid);
			if (vals) {
				if (vals[0]) user.uid = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}

	/* get drop_file_path */
		if (ldap_info.ldap_drop_file) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_drop_file);
			if (vals) {
				if (vals[0])
					user.drop_file_path = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}

		if (dn) {
			char temp[LBUFSZ];
			lprintf(temp,LBUFSZ-1,"dn=\"(%s)\"",dn); /* 2.2m added quotes around field so that it now obeys authentication protocol */
			info = strdup(temp);
		}

	/* get Forward */
		if (ldap_info.ldap_dmail_forward) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_dmail_forward);
			if (vals) {
				if (vals[0]) {
					if (info)
						info = s_stradd(info," ");

					info = s_stradd(info,"fwd=\"");

					if (user.maildeliveryoption!=NULL) {
						imsg("Delivery option is (%s)\n",user.maildeliveryoption);
						if (strstr(user.maildeliveryoption,"mailbox")!=NULL) {
							info = s_stradd(info,"$user,");
						}
					}	

					info = s_stradd(info,vals[0]);

					for ( i = 1; vals[i] != NULL; i++ ) {
						info = s_stradd(info,",");
						info = s_stradd(info,vals[i]);
					}

					info = s_stradd(info,"\"");
					imsg("Forward at this point is (%s)\n",info);

					
				}
				ldap_value_free( vals );
			}
		}

	/* Other Fields like Disk Qouta etc..*/
		for (i=0;i<ldap_info.field_count;i += 2) {
			if (ldap_info.field_name[i] && ldap_info.field_name[i+1]) {
				vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.field_name[i+1]);
				if (vals) {
					if (vals[0]) {
						if (info)
							info = s_stradd(info," ");

						info = s_stradd(info,ldap_info.field_name[i]);
						info = s_stradd(info,"=\"");
						info = s_stradd(info,vals[0]);
						info = s_stradd(info,"\"");
					}
					ldap_value_free( vals );
				}
			}
		}
		user.information = info;

	/* IP Mask */
		if (ldap_info.ldap_mailmask) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_mailmask);
			if (vals) {
				if (vals[0])
					user.ipmask = strdup(vals[0]);
				ldap_value_free( vals );
			}
		}

	/* Mail uid  */
		if (ldap_info.ldap_dmail_uid) {
			vals = ldap_get_values( ldap_info.ldap, entry, ldap_info.ldap_dmail_uid);
			if (vals) {
				if (vals[0])
					user.mail_uid = atoi(vals[0]);
				ldap_value_free( vals );
			}
		}


	/* Account for User Groups */
		if (ldap_info.group_base && ldap_info.group_search) {
			LDAPMessage	*result;
			LDAPMessage *entry;
			char **explode;
			int res;
			char bf[BFSZ];
			char *usergroups = NULL;
			imsg("group: lookup group stuff (%s) (%s)\n",ldap_info.group_base,ldap_info.group_search);

			if (ldap_info.dnshort) {
			/* attrib=name */
				char dnshort[BFSZ], *s;

				s = dn;
				if (!strnicmp(s,"cn=",3))
					s +=3;
				lprintf(dnshort,BFSZ,"%s",s);

				s = strchr(dnshort,','); 
				if (s) *s = 0;
				
				sprintf(bf,"(&(%.200s)(%.100s=%.100s))",ldap_info.group_search,ldap_info.group_attrib,dnshort);
			}
			else 
			/* attrib=cn=name,dn=netwin,dn=co,dn=nz */
				lprintf(bf,BFSZ,"(&(%.200s)(%.100s=%.100s))",ldap_info.group_search,ldap_info.group_attrib,dn);


			
			imsg("group: search (%s)\n",bf);
			ldap_check_server();
			res = ldap_search_s( ldap_info.ldap, ldap_info.group_base, ldap_info.scope, bf, NULL, 0, &result );
			if (!ldap_check_codes(res))
				res = ldap_search_s( ldap_info.ldap, ldap_info.group_base, ldap_info.scope, bf, NULL, 0, &result );

			if ( res != LDAP_SUCCESS ) {
				imsg("group: search failed (%s) \n",ldap_info.group_search);
			}
			if ( res == LDAP_SUCCESS ) {
				entry = ldap_first_entry( ldap_info.ldap, result );				
				if (entry==NULL) imsg("Search failed to find any (%p)\n",entry);
				while (entry) {
					if (isdebug) ld_print_attributes(entry);
					dn = ldap_get_dn( ldap_info.ldap, entry );
					if (dn) {
						explode = ldap_explode_dn(dn, TRUE);
						if (explode && explode[0]) {
							imsg("Explode worked (%s)\n",explode[0]);
							if (ldap_info.group_attrib) {							
								vals = ldap_get_values(ldap_info.ldap,entry,ldap_info.group_field);
								if (vals) {
									n = ldap_count_values(vals);
									lprintf(bf,BFSZ-1,"%.200s=%.200s,",ldap_info.group_field,user.username);
									if (usergroups) usergroups = s_stradd(usergroups,",");
									usergroups = s_stradd(usergroups,explode[0]);
									ldap_value_free(vals);
								}
							}

							ldap_value_free(explode);
						}
						
						ldap_memfree( dn );
					}

					entry = ldap_next_entry( ldap_info.ldap, entry );
				}

				ldap_msgfree( result );

				if (usergroups) {
					if (info)
						info = s_stradd(info," ");

					info = s_stradd(info,"groups=\"");
					info = s_stradd(info,usergroups);
					info = s_stradd(info,"\"");
					user.information = info;
				}
			}
		}

		return &user;
	}
	int ld_load_dmail(void)
	{
		char bf[BFSZ];
		char *s;
		char var[BFSZ];
		FILE *f;
		ld_host_free();
		f = fopen(DMAIL_CONFIG,"r");
		if (f==NULL) return TRUE;
		for (;!feof(f);) {
			if (fgets(bf,BFSZ-1,f)==NULL) break;
			strlwr(bf);
			s = strtok(bf," \t\r\n"); if (s==NULL) continue;
			ncpy(var,s,BFSZ-1);
			s = strtok(NULL," \t\r\n"); if (s==NULL) continue;
			if (strcmp(var,"host_domain")==0) {
				imsg("Found host_domain (%s)\n",s);
				ld_host_add(s);
			}
		}
		fclose(f);
		return TRUE;
	}
	int ld_host_add(char *s)
	{
		Host *h;
		h = calloc(1,sizeof(Host));
		h->next = hosts;
		h->name = strdup(s);
		hosts = h;
		return TRUE;
	}
	int ld_host_free(void)
	{
		Host *h,*next;
		for (h=hosts;h!=NULL;h=next) {
			next = h->next;
			free(h->name);
			free(h);
		}
		hosts = NULL;
		return TRUE;
	}
	int ld_is_mailhost(char *dom)
	{
		Host *h;
		static int done;
		if (!done) ld_load_dmail(); done = TRUE;
		if (domain!=NULL) if (strcmpnc(dom,domain)==0) return TRUE;
		for (h=hosts;h!=NULL;h=h->next) {
			if (strcmpnc(dom,h->name)==0) return TRUE;
		}
		return FALSE;
	}

	int ld_forward_mailhost(pUSER_INFO users, char *user)
	{
		/* If no fwd, change it to fwd="@mailhost:user@domain" */
		char *info;
		char *s=NULL;
		char tmp[BFSZ];

		if (!ldap_info.ldap_mailhost) {
			return TRUE;
		}
		if (users->mailhost==NULL) {
			imsg("No mailhost for this user\n");
			return TRUE;
		}
		/* Check if mailhost is in dmail.conf */
		if (ld_is_mailhost(users->mailhost)) {
			imsg("Mailhost matched (%s) so we can just return data for (%s)\n",users->mailhost,user);	
			return TRUE;
		}
		imsg("mailhost was wrong so adding forward rule\n");
		info = users->information;
		if (info!=NULL) s = strstr(info," fwd=\"");
		if (s!=NULL) return TRUE;
		if (info==NULL) {info = users->information = strdup(" ");}
		lprintf(tmp,BFSZ-1,"%s fwd=\"@%.100s:%s\"",info,users->mailhost,user);
		free(info);
		users->information = info = strdup(tmp);
		imsg("Changed to  %s (%s)\n",user,info);
		return TRUE;
	}
	pUSER_INFO ld_lookup_check(char *user, char *pass, char *ip)
	{
		char xuser[BFSZ];
		char filter[BFSZ];
		pUSER_INFO users;
		
		if (domain && !strchr(user, '@')) {
			lprintf(xuser,BFSZ-1,"%s@%s",user,domain);
			user = xuser;
		}

		lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name,user);
		users = ld_filter(filter,user,pass,ip,TRUE);
		if (users!=NULL) {
			ld_forward_mailhost(users,user);
			return users;
		}

		/* This section is changes for BT */
		if (ldap_info.search_name_alt != NULL) {

		/* Search for alternate names for this user and respond with a fwd...*/
			lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name_alt,user);
			users = ld_filter(filter,user,pass,ip,FALSE);
			if (users != NULL) {
				ld_forward_mailhost(users,user);
				return users;
			}
			if (ldap_info.search_name_alt2 != NULL) {
				lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name_alt2,user);
				users = ld_filter(filter,user,pass,ip,FALSE);
				if (users != NULL) {
					ld_forward_mailhost(users,user);
					return users;
				}
			}


			/* Last search for uid=user && mailhost==domain */
			if (domain!=NULL) {
				if (ldap_info.ldap_mailhost) {
					char justuser[BFSZ];
					char justdom[BFSZ];
					char *s;
					ncpy(justuser,user,BFSZ-1);
					s = strchr(justuser,'@');
					if (s!=NULL) {
						*s = 0;
						ncpy(justdom,s+1,BFSZ-1);
						lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.uid,justuser);
						users = ld_filter(filter,user,pass,ip,FALSE);
						if (users!=NULL) {
							ld_forward_mailhost(users,user);
							return users;
						} else {
							imsg("Didn't find any with filter (%s)\n",filter);
						}
					}
				}
			}

		}

		imsg("Lookups all failed for user (%s)\n",user);
		return NULL;
	}

		

	pUSER_INFO ld_check(char *parameter[MAX_PARAMETERS])
	{
		pUSER_INFO users;
		users = ld_lookup_check(parameter[1], parameter[2], parameter[3]);

		if (users) {
			if (users->limit_access) {
				if (parameter[3]) {
					if (!ld_match_list(ldap_info.ldap_access_wild,parameter[3])) {
						clear_user(users); users=NULL;
						imsg("user (%s) denied access (%s) (%s)\n",parameter[1],ldap_info.ldap_access_wild,parameter[3]);
						return NULL;
					}
				}
			}
		}
		return users;
	}


	pUSER_INFO ld_filter(char *filter, char *xuser, char *pass, char *ip, int dblcheck)
	{
		char sbase[LBUFSZ],user[LBUFSZ];
		LDAPMessage	*result;

		int res;

		if (!ld_extend_search(ldap_info.search_base,xuser,sbase,user))
			return NULL;
		
		/* search for matching entries */
		imsg("filter = %s\n",filter);

		ldap_check_server();
		res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );
		if (!ldap_check_codes(res))
			res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );

		if ( res == LDAP_SUCCESS ) {
			return ld_scan_results(result, user, pass, dblcheck);
		}

		return NULL;
	}

	pUSER_INFO ld_scan_results(LDAPMessage *result, char *user, char *pass, int dblcheck)
	{
		LDAPMessage *entry;
		pUSER_INFO users = NULL;
		char *dn;

		entry = ldap_first_entry( ldap_info.ldap, result );
		users = NULL;
		while (entry && !users) {
			if (isdebug) ld_print_attributes(entry);

			dn = ldap_get_dn( ldap_info.ldap, entry );
			if (dn) {
			/* Try to bind as user (Check password) */
				if ( pass ) {
					if ( ldap_simple_bind_s( ldap_info.ldap, dn, pass) == LDAP_SUCCESS ) {
						ld_manager_auth(ldap_info.ldap);
						users = ld_user_select(entry, dn, pass);
					} 
					else {
						ld_manager_auth(ldap_info.ldap);	/* Auth back to main account - Just incase */
						imsg("simple bind failed, invalid password\n");
					}
				} 
				else {
					users = ld_user_select(entry, dn, pass);
				}
				if (users!=NULL) {
					/* Double check user is the same (just incase of wild cards) */
					if (dblcheck) if (!user || !users->username || stricmp(user,users->username)) {
						if (users->username) imsg("User name didn't match (%s) (%s)\n",users->username,user);
						clear_user(users);
						users = NULL;
					} 
				}				
				ldap_memfree( dn );
			}

			entry = ldap_next_entry( ldap_info.ldap, entry );
		}

		ldap_msgfree( result );

		return users;
	}


	pUSER_INFO ld_lookup(char *parameter[MAX_PARAMETERS])
	{
		return ld_lookup_check(parameter[1], NULL, parameter[3]);
	}

	typedef LDAPMod *pLDAPMod;

	typedef struct ldap_data
	{
		int number;
		pLDAPMod *data;
	} LDAP_DATA;

	typedef LDAP_DATA *pLDAP_DATA;
	

	void data_clear(pLDAP_DATA data)
	{	
		if (data) {
			int i, count;

			for (i=0;i<data->number;i++) {
				if (data->data[i]) {
					myfree(data->data[i]->mod_type);

					count = 0;
					while (data->data[i]->mod_values[count]) {
						myfree(data->data[i]->mod_values[count]);
						count++;
					}
					myfree(data->data[i]->mod_values);
				}
			}
		}
	}

	void data_add_values(pLDAP_DATA data, int mod, char *label, char **values)
	{
		if (data) {
			int no = data->number++;
			
			data->data = (pLDAPMod *)realloc(data->data, sizeof(pLDAPMod) * data->number);

			if (data->data) {
				if (label) {
					data->data[no] = (pLDAPMod)malloc( sizeof(LDAPMod) );

					memset(data->data[no], 0, sizeof(LDAPMod) );

					data->data[no]->mod_op = mod;
					data->data[no]->mod_type = strdup(label);

					data->data[no]->mod_values = values;
				}
				else
					data->data[no] = NULL;
			}
		}
	}

	void data_add(pLDAP_DATA data, int mod, char *label, char *value)
	{
		if (data) {
			int no = data->number++;
			
			data->data = (pLDAPMod *)realloc(data->data, sizeof(pLDAPMod) * data->number);

			if (data->data) {
				if (label) {
					data->data[no] = (pLDAPMod)malloc( sizeof(LDAPMod) );

					memset(data->data[no], 0, sizeof(LDAPMod) );

					data->data[no]->mod_op = mod;
					data->data[no]->mod_type = strdup(label);
					data->data[no]->mod_values = (char **)malloc( sizeof(char *) * 2 );
					data->data[no]->mod_values[0] = strdup(value);
					data->data[no]->mod_values[1] = NULL;
				}
				else
					data->data[no] = NULL;
			}
		}
	}
	
	char *info_fields_check_and_replace(char *name)
	{
		if (name) {
			int i;

			if (!strcmp(name,"dn"))
				return NULL;

			for (i=0;i<ldap_info.field_count;i += 2) {
				if (!stricmp(name,ldap_info.field_name[i])) {
					return ldap_info.field_name[i+1];
				}
			}

			imsg("Warning: '%s' - Was not passed to the LDAP server (see 'info_fields' in manual,b)",name);
		}
	/*	return name; */
		return NULL;
	}

	#define MAX_MODS 20	/* max a=b pairs we will handle */

/* Extended Commands */
	int ld_set(char *parameter[MAX_PARAMETERS])
	{
		LDAPMessage	*result;
		LDAPMessage *entry;
		char sbase[LBUFSZ],user[LBUFSZ];
		char filter[BFSZ];
		char dname[LBUFSZ];
		int res, x = FALSE;
				
		if (domain && !strchr(parameter[1], '@')) {
			char username[BFSZ], *ptr;
			strcpy(username,parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		imsg("...Set: User='%s' Data='%s'", parameter[1], parameter[3]);

		if (ld_extend_search(ldap_info.search_base,parameter[1],sbase,user)) {
		/* search for matching entries */
			lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name,user);

			ldap_check_server();
			res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );
			x = ldap_check_codes(res);
			if (!x)
				res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );
			if ( res == LDAP_SUCCESS ) {
				LDAP_DATA data;
				char *dn = NULL, *s;
				int count = 0, i;

				memset(&data,0, sizeof(LDAP_DATA));

			/* Checking each matching entry...*/
				entry = ldap_first_entry( ldap_info.ldap, result );
				if (!entry) {
				/* User doesn't exists. (Add a new entry) */
					if (sbase[0])
						lprintf(dname,BFSZ-1,"cn=%s,%s", parameter[1], sbase);
					else
						lprintf(dname,BFSZ-1,"cn=%s", parameter[1]);
					imsg("         dn = (%s)\n",dname);
					dn = dname;
					if (ldap_info.objectclass && *ldap_info.objectclass) {
						data_add(&data, LDAP_MOD_REPLACE, "objectClass", ldap_info.objectclass);
						imsg("         objectClass = (%s)\n",ldap_info.objectclass);
					}

					imsg("         %s = (%s)\n",ldap_info.search_name,user);
					data_add(&data, LDAP_MOD_REPLACE, ldap_info.search_name, parameter[1]);
				}
				else
					dn = ldap_get_dn( ldap_info.ldap, entry );

				if (parameter[2] && stricmp(parameter[2],"(NULL)"))  {
					char bf[LBUFSZ];
					char *encode_pass = NULL;

				/* change password */
					if (!ldap_info.pass_crypt) {
					/* ssha_hash Method */
						strcpy(bf,"{ssha}");
						if (create_ssha_base64_digest(parameter[2],&bf[6],BFSZ-1)) {
							encode_pass = bf;
							imsg("         userPassword = (%s) (ssha_hash)\n",encode_pass);
						}
						else {
							emsg("SSHA hashing debug and error information (%.200s)",get_sha_error());
							emsg("Could not hash password for (%s) using SSHA",parameter[1]);
							return FALSE;
						}
					}
					else if (ldap_info.pass_crypt == 1) {
					/* sha_hash Method */
						strcpy(bf,"{sha}");
						if (create_sha_base64_digest(parameter[2],&bf[5],BFSZ-1)) {
							encode_pass = bf;
							imsg("         userPassword = (%s) (ssha_hash)\n",encode_pass);
						}
						else {
							emsg("SHA hashing debug and error information (%.200s)",get_sha_error());
							emsg("Could not hash password for (%s) using SSHA",parameter[1]);
							return FALSE;
						}
					}
					else {
					/* Plain Text Method */
						imsg("         userPassword = (xxxx) (Plain Text)\n");
						encode_pass = parameter[2];
					}

					if (encode_pass) {
						data_add(&data, LDAP_MOD_REPLACE, "userPassword", encode_pass);
					}
				}

			/* Deal with any name="value" on end of line */
				if (parameter[3] && *parameter[3]) {
					char *start = parameter[3], *end;
					char *middle;
					int qoute = FALSE;

					imsg("Start (%s)\n",paramter[3]);
					while (start && *start) {
					/* Find middle/end */
						while (*start == '\t' || *start == ' ')
							start++;

						middle = start;
						while (*middle != '\0' && *middle != ' ' && *middle != '=')
							middle++;

						if (*middle == '=') {
							*middle++ = '\0';

							end = middle;
							while ((*end != ' ' || qoute) && *end != '\0') {
								if (*end == '\"')
									qoute = !qoute;
								end++;
							}

						/* Remove Qoutes */
							if (*middle == '\"') {
								middle++; 
								end--;
								if (*end == '\"')
									*end = '\0';
								end++;
							}

							if (end != '\0')
								*end++ = '\0';

							if (*middle && *start) {
								char *name = info_fields_check_and_replace(start);
								if (name) {
									imsg("         %s (%s) = (%s)\n", start, name ,middle);								
									data_add(&data, LDAP_MOD_REPLACE, name, middle);
								}
							}
						}
						else 
							end = middle;

						start = end;
					}

				}
				
			/* Account for any variables that MUST be defined */
				if (!entry) {
					for (i=0;i<ldap_info.must_count;i += 2) {

						if (ldap_info.must_name[i] && ldap_info.must_name[i+1]) {
							
							if (!stricmp(ldap_info.must_name[i+1],"name"))
								s = parameter[1];
							else if (!stricmp(ldap_info.must_name[i+1],"email"))
								s = parameter[1];
							else if (!stricmp(ldap_info.must_name[i+1],"dn"))
								s = dn;
							else							
								s = ldap_info.must_name[i+1];

							imsg("         %s = (%s)\n",ldap_info.must_name[i],s);
							data_add(&data, LDAP_MOD_REPLACE, ldap_info.must_name[i], s);

						}

					}
				}

				data_add(&data, LDAP_MOD_REPLACE, NULL, NULL);

				if (!entry) {
				/* Add New Entry */
					ldap_check_server();
					res = ldap_add_s(ldap_info.ldap, dn, data.data );
					x = ldap_check_codes(res);
					if (!x)
						res = ldap_add_s(ldap_info.ldap, dn, data.data );
					if (res != LDAP_SUCCESS) {
						char *s = ldap_err2string(res);
						emsg("Cant add/modify entry for %s: %s", dn, s);
					}
				}
				else if (dn) {
				/* Modifiy Entry */
					ldap_check_server();
					res = ldap_modify_s(ldap_info.ldap, dn, data.data);
					x = ldap_check_codes(res);
					if (!x)
						res = ldap_add_s(ldap_info.ldap, dn, data.data );
					if (res != LDAP_SUCCESS) 
						emsg("Cant add/modify entry for %s: %s", dn, ldap_err2string(res));
					ldap_memfree( dn );
				}

				if (res == LDAP_SUCCESS) 
					return TRUE;

				ldap_msgfree( result );
			}
			else
				imsg("No users located for (%s)\n" ,filter);
		}
		else
			imsg("Specify context, not just (%s)\n",parameter[1]);

		if (x == -TRUE)
			return -TRUE;
		return FALSE;
	}

	int ld_del(char *parameter[MAX_PARAMETERS])
	{
		LDAPMessage	*result;
		LDAPMessage *entry;
		char sbase[LBUFSZ],user[LBUFSZ];
		char filter[BFSZ];
		int res = -1, x = FALSE;

		if (domain && !strchr(parameter[1], '@')) {
			char username[BFSZ], *ptr;
			strcpy(username,parameter[1]);
			ptr = username + strlen(username);
			sprintf(ptr, "@%s", domain);
			myfree(parameter[1]);
			parameter[1] = strdup(username);
			imsg("...Attached default domain {%s}", parameter[1]);
		}

		imsg("...Delete: User='%s'", parameter[1]);


		if (ld_extend_search(ldap_info.search_base,parameter[1],sbase,user)) {
		/* search for matching entries */
			lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name,user);

			ldap_check_server();
			res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );
			x = ldap_check_codes(res);
			if (!x)
				res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &result );

			if ( res == LDAP_SUCCESS ) {
				char *dn = NULL;
				res = -1;

			/* Checking each matching entry...*/
				entry = ldap_first_entry( ldap_info.ldap, result );			
				if (entry) {
					dn = ldap_get_dn( ldap_info.ldap, entry );
					if (dn) {
						res = ldap_delete_s(ldap_info.ldap, dn);
						if (res != LDAP_SUCCESS)
							imsg("Can't delete %s: %s", dn, ldap_err2string(res));
						ldap_memfree( dn );
					}
					else
						imsg("ldap_get_dn failed for %s", filter);
				}
				else
					imsg("User does not exist");
				ldap_msgfree( result );
				if (res == LDAP_SUCCESS)
					return TRUE;
			}
			else
				imsg("No users located for (%s)\n" ,filter);
		}
		else
			imsg("Specify context, not just (%s)\n",parameter[1]);

		if (x == -TRUE)
			return -TRUE;
		return FALSE;
	}

	int ld_search(char *parameter[MAX_PARAMETERS])
	{
		pUSER_INFO users = NULL;
		LDAPMessage	*result;
		LDAPMessage *entry;
		char filter[LBUFSZ];
		char *user_domain;
		int start = -1, max = -1, res = FALSE, count = 0;
		int x = FALSE;

		strlwr(parameter[1]);
		imsg("...Search: '%s'", parameter[1]);

		if (parameter[2]) {
			if (!strcmp(parameter[2],"-from"))
				start = atoi(parameter[3]) - 1;
			else if (!strcmp(parameter[2],"-max"))
				max = atoi(parameter[3]);
		}
		if (parameter[3]) { /* Phase Extra Parameters -max, -from */
			char *temp;

			temp = strtok( parameter[3], " " );
			while(temp) {
				 if (temp && !strcmp(temp,"-max")) {
					 temp = strtok(NULL," ");
					 if (temp)
						max = atoi(temp);
				 }
				 else if (temp && !strcmp(temp,"-from")) {
					 temp = strtok(NULL," ");
					 if (temp)
						start = atoi(temp) - 1;
				 }
				 temp = strtok( NULL, " " );
			}
		}

	/* Build filter */		
		user_domain = strchr(parameter[1],'@');
		if (user_domain)	*user_domain++ = '\0';

		if (!user_domain || !*user_domain) {
			if (!domain || !*domain) {
				if (!strcmp(parameter[1],"*"))
					lprintf(filter,LBUFSZ-1, "(%s=*)", ldap_info.search_name);
				else
					lprintf(filter,LBUFSZ-1, "(%s=*%s*)", ldap_info.search_name, parameter[1]);
			}
			else if (!strcmp(parameter[1],"*"))
				lprintf(filter,LBUFSZ-1, "(%s=*@%s)", ldap_info.search_name, parameter[1], domain);
			else
				lprintf(filter,LBUFSZ-1, "(%s=*%s*@%s)", ldap_info.search_name, domain);
		}
		else
			lprintf(filter,LBUFSZ-1, "(%s=*%s*@%s*)", ldap_info.search_name, parameter[1],user_domain);
		imsg("Search filter is (%s)",filter);

		ldap_check_server();
		res = ldap_search_s( ldap_info.ldap, ldap_info.search_base, ldap_info.scope, filter, NULL, 0, &result );
		x = ldap_check_codes(res);
		if (!x)
			res = ldap_search_s( ldap_info.ldap, ldap_info.search_base, ldap_info.scope, filter, NULL, 0, &result );

		if ( res == LDAP_SUCCESS ) {
			char *dn = NULL;

		/* Checking each matching entry...*/
			entry = ldap_first_entry( ldap_info.ldap, result );	
			while (entry) {
				if ( count >= start && (count <= (start+max) || max <= 0) ) {

					dn = ldap_get_dn( ldap_info.ldap, entry );
					if (dn) {
						users = ld_user_select(entry,dn, NULL);

						if (users && users->username)
							display_user(users, "+DATA", users->username);
						else
							clear_user(users);

						ldap_memfree( dn );
					}
					else
						imsg("ldap_get_dn failed for %s", filter);

				}
				count++;
				entry = ldap_next_entry( ldap_info.ldap, entry );
			}

			ldap_msgfree( result );
			return count;
		}
		else
			imsg("No users located for (%s)\n" ,filter);		

		if (x == -TRUE)
			return -TRUE;
		return FALSE;
	}


/* Externed LDAP to support user.dat settings stored in LDAP server. */
	enum {	CMD_LD_HELP, CMD_OPEN, CMD_SETVAL, CMD_GETVAL, CMD_CLOSE, NUM_LD_COMMAND };
	char *ld_command_names[] = { "help", "open", "setval", "getval", "close" };

	int ld_extended(char *parameter[MAX_PARAMETERS])
	{
		int i;

	/* Decode username and password space encoding */
		if (parameter[1]) {
			char username[BFSZ], *ptr = username, *str = parameter[1];
			while (*str != '\0') {
				if (*str != '\\')
					*ptr++ = *str++;
				else {
					str++;
					if (*str == 's')
						*ptr++ = ' ';
					else
						*ptr++ = *str;
					str++;
				}
			}
			*ptr = '\0';

			myfree(parameter[1]);
			parameter[1] = strdup(username);
		}
		if (parameter[2]) {
			char username[BFSZ], *ptr = username, *str = parameter[2];
			while (*str != '\0') {
				if (*str != '\\')
					*ptr++ = *str++;
				else {
					str++;
					if (*str == 's')
						*ptr++ = ' ';
					else
						*ptr++ = *str;
					str++;
				}
			}
			*ptr = '\0';

			myfree(parameter[2]);
			parameter[2] = strdup(username);
		}


	/* Locate Command */
		for (i=0;i<NUM_LD_COMMAND;i++) {
			if (ld_command_names[i][0] == parameter[0][0] &&
				!stricmp(ld_command_names[i], parameter[0]))
				break;
		}

	/* Process Command */
		switch (i) {
			case CMD_HELP:
				outmsg("+DATA%s",NEXT_LINE);
				outmsg("+DATA Supported User Commands%s",NEXT_LINE);
				outmsg("+DATA     open <username> <password>%s",NEXT_LINE);
				outmsg("+DATA     setval <variable>%s",NEXT_LINE);
				outmsg("+DATA     getval <variable>%s",NEXT_LINE);
				outmsg("+DATA     close%s",NEXT_LINE);
				return TRUE;

			case CMD_OPEN:
				if (parameter[1] && parameter[2]) {
					if (ld_user_open(parameter))
						outmsg("+OK User open%s",NEXT_LINE);
					else
						outmsg("-ERR Failed to locate User%s",NEXT_LINE);
				}
				else
					outmsg("-ERR No username/password provided%s", NEXT_LINE);
				return TRUE;

			case CMD_SETVAL:
				if (parameter[1]) {
					if (ld_user_set(parameter))
						outmsg("+OK Variable set%s",NEXT_LINE);
					else
						outmsg("-ERR Failed to set variable%s",NEXT_LINE);
				}
				else
					outmsg("-ERR No variabel name defined%s", NEXT_LINE);
				return TRUE;

			case CMD_GETVAL:
				if (parameter[1]) {
					if (ld_user_get(parameter))
						outmsg(".%s",NEXT_LINE);
					else
						outmsg("-ERR Failed to locate variable%s",NEXT_LINE);
				}
				else
					outmsg("-ERR No variabel name defined%s", NEXT_LINE);
				return TRUE;

			case CMD_CLOSE:
				if (ld_user_close(parameter))
					outmsg("+OK User close%s",NEXT_LINE);
				else
					outmsg("-ERR Failed to close user%s",NEXT_LINE);
				return TRUE;
		}

		return FALSE;
	}

	int ld_user_open(char *parameter[MAX_PARAMETERS])
	{
		char *new_parm[MAX_PARAMETERS];
		char sbase[LBUFSZ],user[LBUFSZ];
		char filter[BFSZ];
		int res = -1;
		int x = FALSE;
	
		if (parameter) {
			if (domain && !strchr(parameter[1], '@')) {
				char username[BFSZ], *ptr;
				strcpy(username,parameter[1]);
				ptr = username + strlen(username);
				sprintf(ptr, "@%s", domain);
				myfree(parameter[1]);
				parameter[1] = strdup(username);
				imsg("...Attached default domain {%s}", parameter[1]);
			}
		}
		else if (ldap_info.user && ldap_info.pass) {
			parameter = new_parm;
			new_parm[0] = NULL;
			new_parm[1] = ldap_info.user;
			new_parm[2] = ldap_info.pass;
			new_parm[3] = NULL;
		}
		else
			return FALSE;

		imsg("...Open: User='%s'", parameter[1]);

		ldap_info.set_data = FALSE;
		ld_user_close(NULL);

		if (ld_extend_search(ldap_info.search_base,parameter[1],sbase,user)) {
		/* search for matching entries */
			lprintf(filter, BFSZ-1,"(%s=%s)", ldap_info.search_name,user);

			ldap_check_server();
			res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &ldap_info.result );
			x = ldap_check_codes(res);
			if (!x)
				res = ldap_search_s( ldap_info.ldap, sbase, ldap_info.scope, filter, NULL, 0, &ldap_info.result );
			if ( res == LDAP_SUCCESS ) {
				char *dn = NULL;
				res = -1;

			/* Checking each matching entry...*/
				ldap_info.entry = ldap_first_entry( ldap_info.ldap, ldap_info.result );
				while (ldap_info.entry) {

					if (ldap_info.verbose)
						ld_print_attributes(ldap_info.entry);

					dn = ldap_get_dn( ldap_info.ldap, ldap_info.entry );
					if (dn) {
					/* Try to bind as user (Check password) */
						if ( ldap_simple_bind_s( ldap_info.ldap, dn, parameter[2]) == LDAP_SUCCESS ) {
							ld_manager_auth(ldap_info.ldap);

							myfree(ldap_info.user);
							myfree(ldap_info.pass);
							mystrdup(ldap_info.user, parameter[1]);
							mystrdup(ldap_info.pass, parameter[2]);

							ldap_memfree( dn );
							return TRUE;
						}
						ldap_memfree( dn );
					}

					ldap_info.entry = ldap_next_entry( ldap_info.ldap, ldap_info.entry );
				}

				ldap_msgfree( ldap_info.result );
				ldap_info.result = NULL;
			}
			else
				imsg("No users located for (%s)\n" ,filter);
		}
		else
			imsg("Specify context, not just (%s)\n",parameter[1]);

		if (x == -TRUE)
			return -TRUE;
		return FALSE;
	}

	int ld_user_set(char *parameter[MAX_PARAMETERS])
	{
		int x = FALSE;
		if (ldap_info.entry) {
			char bf[1025] = "";			
			char *label, *end;			
			char **values = NULL;
			int count = 0;
			LDAP_DATA data;

			memset(&data,0, sizeof(LDAP_DATA));
		/* Get All input Fields */

		/* Input from stdin */
			while (bf[0] != '.') {
				if (!fgets(bf,1024,stdin))
					break;

				label = strchr(bf,' ');
				if (label) {
					*label++ = '\0';
					while (*label == ' ' || *label == '\t')
						label++;

					if (!stricmp(bf,"+DATA")) {
						int size = sizeof(char *) * (count+1);

						end = label + strlen(label) - 1;
						while (end > label && (*end == ' ' || *end == '\t' || *end == '\n' || *end == '\t'))
							*end-- = '\0';

						values = (char **)realloc(values, size);
						values[count++] = strdup(label);					
					}
				}
			}

			if (values) {
				char *dn;
				int res;

				dn = ldap_get_dn( ldap_info.ldap, ldap_info.entry );
				if (dn) {
					int size = sizeof(char *) * (count+1);
					values = (char **)realloc(values, size);
					values[count++] = NULL;

					data_add_values(&data, LDAP_MOD_REPLACE, parameter[1], values); 
					data_add(&data, LDAP_MOD_REPLACE, NULL, NULL);

					ldap_check_server();
					res = ldap_modify_s(ldap_info.ldap, dn, data.data);
					x = ldap_check_codes(res);
					if (!x)
						res = ldap_modify_s(ldap_info.ldap, dn, data.data);
					ldap_info.set_data = TRUE;
					
					ldap_memfree( dn );
					data_clear(&data);
					if (res == LDAP_SUCCESS) 
						return TRUE;
				}
			}
		}
		if (x == -TRUE)
			return -TRUE;
		return FALSE;
	}

	int ld_user_get(char *parameter[MAX_PARAMETERS])
	{
		if (ldap_info.entry && ldap_info.set_data) 
			ld_user_open(NULL);

		if (ldap_info.entry) {
			int count = 0;
			char **vals;

		/* get variable */
			if (parameter[1]) {
				vals = ldap_get_values( ldap_info.ldap, ldap_info.entry, parameter[1]);
				if (vals) {
					while (vals[count]) {
						outmsg("+DATA %s%s",vals[count],NEXT_LINE);
						count++;
					}
					ldap_value_free( vals );
					return TRUE;
				}
			}
			
		}
		return FALSE;
	}

	int ld_user_close(char *parameter[MAX_PARAMETERS])
	{
		if (ldap_info.result)
			ldap_msgfree( ldap_info.result );
		ldap_info.result = NULL;
		ldap_info.entry = NULL;
		return TRUE;
	}

#endif
