/*
FILE
	$Id: mail.c 21 2005-12-21 22:20:02Z ggw $
PURPOSE
	System modification functions	
LEGAL
	(C) 2002, Gary Wallis. GPL License Applies.
TODO
	Create functions for fixing uid problems and conflicts with single server
	apache and sendmail accounts.
	On deletion create at job for new mysqlSendmail.cgi command line option
	for removing mail files (ex. rm /var/mail/jasons in 60 days if no current jasons tUser)

	See TODO comments throughout code

WORK IN PROGRESS
	uMailFilter system...using tConfiguration for individual user storage
	Also code is based on tMailFilter.cLabel
	Pretty much a hack on a hack for now...See uMailFilter everywhere...

	uServer=1 is another wacked area...

*/

#include <unistd.h>
#include "mysqlrad.h"
#include "mail.h"
#include <ctype.h>


//website group and admin uid space
//#define VH_UID_BASE 1000
//additional website user space
//#define VU_UID_BASE 20000

//#define SU_BASE_ID 70000 mail.h

typedef struct {
	char cPasswd[65];
	char cLogin[64];
	char cEnterPasswd[16];	
	char cDomain[100];
	char cVUTDomain[100];
	char cVUTEntry[33];
	char cVUTTarget[100];

	unsigned uClearText;

	unsigned uParamPasswd;
	unsigned uParamLogin;
	unsigned uParamDomain;
	unsigned uParamVUTDomain;
	unsigned uParamVUTEntry;
	unsigned uParamVUTTarget;
	unsigned uFakeISPJob;

	//tClient
	unsigned uISPClient; 
	char cClientName[33];//cLabel
	unsigned uParamClientName;

	//tMailFilter
	char cMailFilter[33];//cLabel
	unsigned uParamMailFilter;

} structExtJobParameters;
void InitializeParams(structExtJobParameters *structExtParam);
void CreateNewClient(structExtJobParameters *structExtParam);
void DeleteClient(structExtJobParameters *structExtParam);
unsigned uGetMailFilterId(const char *cMailFilter);
unsigned uGetMailFilterFromUser(const char *cLogin);


//Local
void GetConfiguration(const char *cName, char *cValue, unsigned uHtml);
void ScheduleJob(unsigned uUser, unsigned uServer, const char *cJobName, const char *cJobData);
void EncryptPasswd(char *cClearTextPasswd);
int ChangeSystemPasswd(char *cLogin, char *cPasswd);
#ifdef RADIUSIMPORT
void mysqlRadiusImport(void);
#endif
unsigned GetuServer(char *cServer);
void UpdateESMTPJobStatus(unsigned uJob,unsigned uJobStatus,char *cRemoteMsg,char *cServer);
char *LoginCleanUp(char *cInput);
void TextConnectDb(void);
static unsigned guDebug=0;

//mysqlISP constants
//tJob.uJobStatus
#define mysqlISP_RemotelyQueued 7
#define mysqlISP_Waiting 10
//tInstance.uStatus:
#define mysqlISP_Deployed 4
#define mysqlISP_Canceled 5
#define mysqlISP_OnHold 6



MYSQL mysqlext;
int SubmitExtSingleJob(char *cJobName,char *cJobData,char *cServer,unsigned uJobDate,
		unsigned uJob, unsigned uUser);
int SubmitISPJob(const char *cJobName,const char *cJobData,const char *cServer,unsigned uJobDate);
int InformExtJob(char *cRemoteMsg,char *cServer,unsigned uJob,unsigned uJobStatus);
void ExtConnectDb(unsigned uHtml);
void ProcessExtJobQueue(char *cServer);

//Job queue
int NewUser(unsigned uUser);
int DeleteUser(unsigned uUser);

int ModUserAPopOff(unsigned uUser);
int ModUserAPopOn(unsigned uUser);

int ModUserTrafficQuotaOff(unsigned uUser);
int ModUserTrafficQuotaOn(unsigned uUser);

int ModUserHDQuotaOff(unsigned uUser);
int ModUserHDQuotaOn(unsigned uUser);

int ModUserMailFilterOff(unsigned uUser);
int ModUserMailFilterOn(unsigned uUser);

int ModUserPwd(unsigned uUser);
int ProcessJobQueue(unsigned uDebug, char *cServer);
int ProcessESMTPJobQueue(unsigned uDebug, char *cServer);
int NewUserJob(unsigned uJob,unsigned uUser);
int DeleteUserJob(unsigned uJob,unsigned uUser);
int ModUserPwdJob(unsigned uJob,unsigned uUser);
int ModUserAPopOnJob(unsigned uJob,unsigned uUser);
int ModUserAPopOffJob(unsigned uJob,unsigned uUser);
int ModUserMailFilterOffJob(unsigned uJob,unsigned uUser);
int ModUserMailFilterOnJob(unsigned uJob,unsigned uUser);
int ModUserHDQuotaOffJob(unsigned uJob,unsigned uUser);
int ModUserHDQuotaOnJob(unsigned uJob,unsigned uUser);
int ModUserTrafficQuotaOffJob(unsigned uJob,unsigned uUser);
int ModUserTrafficQuotaOnJob(unsigned uJob,unsigned uUser);
void MakeVUTJob(unsigned uJob,unsigned uServer);
void MakeLocalJob(unsigned uJob,unsigned uServer);
void MakeAccessJob(unsigned uJob,unsigned uServer);
void MakeNewaliasesJob(unsigned uJob,unsigned uServer);
int MakeAliasFile(unsigned uServer,unsigned uHtml);
int MakeVUTFile(unsigned uServer,unsigned uHtml);
int MakeAccessFile(unsigned uServer,unsigned uHtml);
int MakeLocalFile(unsigned uServer,unsigned uHtml);
int MakeHomeDir(unsigned uUser);
int MakeHomeDirJob(unsigned uJob,unsigned uUser);
void CleanLocalAccess(void);
void CleanDoneOkJobQueue(void);//mail.c
void FixLocalUIDs(unsigned uMode);


int ModUserAPopOn(unsigned uUser)
{
	fprintf(stderr,"ModUserAPopOn(%u) not implemented\n",uUser);
	return(0);
}

int ModUserAPopOff(unsigned uUser)
{
	fprintf(stderr,"ModUserAPopOff(%u) not implemented\n",uUser);
	return(0);
}

int ModUserTrafficQuotaOff(unsigned uUser)
{
	fprintf(stderr,"ModUserTrafficQuotaOff(%u) not implemented\n",uUser);
	return(0);
}

int ModUserTrafficQuotaOn(unsigned uUser)
{
	fprintf(stderr,"ModUserTrafficQuotaOn(%u) not implemented\n",uUser);
	return(0);
}

int ModUserHDQuotaOff(unsigned uUser)
{
	fprintf(stderr,"ModUserHDQuotaOff(%u) not implemented\n",uUser);
	return(0);
}

int ModUserHDQuotaOn(unsigned uUser)
{
	fprintf(stderr,"ModUserHDQuotaOn(%u) not implemented\n",uUser);
	return(0);
}


//Note that this function ends cValue and cComment strings at [0]!
void GetExtConfiguration(const char *cName, char *cValue, char *cComment);

int ModUserMailFilterOff(unsigned uUser)
{
	char cLogin[256]={""};
	char cFileSpec[256]={""};
	char cHomeDirPreFix[256]={"/home"};
	/*
	char cValue[256]={""};
	char cJobData[256]={""};
	char cServer[256]={"Any"};
	char cPasswd[256]={""};
	char cEnterPasswd[256]={""};
	char cComment[4096]={""};
	char cMailFilter[33]={""};
	unsigned uMailFilter=0;
	time_t clock;
	char *cp;
	*/

	sprintf(cLogin,"%.99s",ForeignKey("tUser","cLogin",uUser));
	if(!cLogin[0])
	{
		fprintf(stderr,"Could not determine cLogin from uUser=%u\n",uUser);
		return(1);
	}

	GetConfiguration("cHomeDirPreFix",cHomeDirPreFix,0);
	sprintf(cFileSpec,"%s/%s/.procmailrc",cHomeDirPreFix,cLogin);

	if(unlink(cFileSpec))
		//If after delete job this is logical
		fprintf(stderr,"Could not remove: '%s'. After del job? Then ignore.\n",cFileSpec);

	//We need to get uMailFilter from cJobData to determine if spam-iso method used
	//and if so get server and create job for it.

	return(0);

}//int ModUserMailFilterOff(unsigned uUser)


int ModUserMailFilterOn(unsigned uUser)
{
	char cValue[256]={""};
	char cLogin[256]={""};
	char cJobData[256]={""};
	char cServer[256]={"Any"};
	char cHomeDirPreFix[256]={"/home"};
	char cPasswd[256]={""};
	char cEnterPasswd[256]={""};
	char cFileSpec[256]={""};
	char cComment[4096]={""};
	char cMailFilter[33]={""};
	FILE *fp;
	unsigned uMailFilter=0;
	time_t clock;
	char *cp;

	//Get from tConfiguration this users uMailFilter.cLabel ex. procmail if it exists
	//if not use default procmail also in tConfiguration supposedly if not abort
	//must edit it for user

	//write it into ~user dir
	//create user dir if not exists: usermod -d required also
	//char *ForeignKey(char *cTableName, char *cFieldName, unsigned uKey);
	sscanf(ForeignKey("tUser","uMailFilter",uUser),"%u",&uMailFilter);
	if(!uMailFilter)
	{
		fprintf(stderr,"Could not determine uMailFilter from uUser=%u\n",uUser);
		return(0);
	}

//debug only
//fprintf(stderr,"d1 uUser=%u uMailFilter=%u\n",uUser,uMailFilter);

	sprintf(cLogin,"%.99s",ForeignKey("tUser","cLogin",uUser));
	if(!cLogin[0])
	{
		fprintf(stderr,"Could not determine cLogin from uUser=%u\n",uUser);
		return(0);
	}

//debug only
//fprintf(stderr,"d2 cLogin=%s\n",cLogin);

	sprintf(cEnterPasswd,"%.99s",ForeignKey("tUser","cEnterPasswd",uUser));
	if(!cEnterPasswd[0])
	{
		sprintf(cPasswd,"%.99s",ForeignKey("tUser","cPasswd",uUser));
		if(!cPasswd[0])
		{
			fprintf(stderr,"Could not determine cEnterPasswd/cPasswd from uUser=%u\n"
				,uUser);
			return(0);
		}
	}

//debug only
//fprintf(stderr,"d3 cPasswd=%s cEnterPasswd=%s\n",cPasswd,cEnterPasswd);

	sprintf(cMailFilter,"%.32s-%.99s",
				ForeignKey("tMailFilter","cLabel",uMailFilter),
				cLogin);
	GetExtConfiguration(cMailFilter,cValue,cComment);

//debug only
//fprintf(stderr,"d3a1 cValue='%s' cComment='%s' cMailFilter='%s'\n",cValue,cComment,cMailFilter);

	if(!cComment[0])
	{

		//Try specific first then if not found default.
		sprintf(cMailFilter,"%.15s-%.16s",
				ForeignKey("tMailFilter","cLabel",uMailFilter),
				cLogin);
		GetExtConfiguration(cMailFilter,cValue,cComment);
//debug only
//fprintf(stderr,"d3a cValue='%s' cComment='%s'\n",cValue,cComment);

		if(!cComment[0])
		{
			sprintf(cMailFilter,"%.32s-default",
				ForeignKey("tMailFilter","cLabel",uMailFilter));
			GetExtConfiguration(cMailFilter,cValue,cComment);
//debug only
//fprintf(stderr,"d3b cValue='%s' cComment='%s'\n",cValue,cComment);
		}

	}

	if(!cComment[0])
	{
		fprintf(stderr,"Could not determine cComment from uUser=%u and uMailFilter=%u\n",
				uUser,uMailFilter);
		return(0);
	}

//debug only
//fprintf(stderr,"d4 cValue='%s' cComment='%s'\n",cValue,cComment);

	//this is very system centric. Another tConfiguration pair?
	GetConfiguration("cHomeDirPreFix",cHomeDirPreFix,0);
	sprintf(cFileSpec,"%s/%s/.procmailrc",cHomeDirPreFix,cLogin);

        umask(022);
	if((fp=fopen(cFileSpec,"w"))==NULL)
	{
		fprintf(stderr,"Could not open file for write: '%s'. Need NewUser job with home dir creation beforehand?\n",cFileSpec);
		return(0);
	}
	else
	{
		fprintf(fp,"#ModUserMailFilterOn() $Id: mail.c 21 2005-12-21 22:20:02Z ggw $\n%s\n",cComment);
		fclose(fp);
	}

	//Done with local part. Now we need to setup an external job for spam-isolation webmail
	//server

	//1-. Get cServer from cValue of configuration
	if((cp=strstr(cValue,"server=")))
	{
		char *cp2;

		if((cp2=strchr(cp+7,' ')))
			*cp2=0;
		else if((cp2=strchr(cp+7,'\n')))
			*cp2=0;
		else if((cp2=strchr(cp+7,';')))
			*cp2=0;

		sprintf(cServer,"%.31s",cp+7);
	}
	//else default "Any" for cServer

	//Skip slaved jobs if cLabel has server=None
	if(!strncasecmp(cServer,"None",4))
		return(0);

	//2-. Create tJob entry cJobData as a mysqlISP.tJob would have.
	if(cEnterPasswd[0])
		sprintf(cJobData,"Login=%s\nClearPasswd=%s\nMakeHomeDir=Yes\nFakeISPJob\n",
							cLogin,cEnterPasswd);
	else
		sprintf(cJobData,"Login=%s\nPasswd=%s\nMakeHomeDir=Yes\nFakeISPJob\n",
							cLogin,cPasswd);

	//3-. Create tJob entry for remote iso-spam server w/mysqlSendmail ext job queue processing
	time(&clock);
        sprintf(query,"INSERT INTO tJob SET cServer='%s',cLabel='Priority=Normal',cJobData='%s',cJobName='mysqlSendmail.Pop.New',uUser=0,uJobDate=%lu,uJobStatus=%u,uOwner=%u,uCreatedBy=%u,uCreatedDate=%lu",
			cServer,
			cJobData,
			(long unsigned)clock,
			mysqlISP_Waiting,
			uLoginClient,
			uLoginClient,
			(long unsigned)clock);
	mysql_query(&mysql,query);
	if(mysql_errno(&mysql))
		fprintf(stderr,"Could not create job: %s\n",mysql_error(&mysql));


	return(0);

}//int ModUserMailFilterOn(unsigned uUser)


void GetExtConfiguration(const char *cName, char *cValue, char *cComment)
{
        MYSQL_RES *res;
        MYSQL_ROW field;

        char query[256];

        sprintf(query,"SELECT cValue,cComment FROM tConfiguration WHERE cLabel='%s'",cName);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		exit(1);
	}
        res=mysql_store_result(&mysql);
	cValue[0]=0;
	cComment[0]=0;
        if((field=mysql_fetch_row(res)))
	{
                sprintf(cValue,"%.254s",field[0]);
		sprintf(cComment,"%.4095s",field[1]);
	}
        mysql_free_result(res);

}//void GetExtConfiguration()


void GetConfiguration(const char *cName, char *cValue, unsigned uHtml)
{
        MYSQL_RES *res;
        MYSQL_ROW field;

        char query[256];

        sprintf(query,"SELECT cValue FROM tConfiguration WHERE cLabel='%s'",cName);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
	{
		if(uHtml)
		{
        	        tConfiguration(mysql_error(&mysql));
		}
		else
		{
			fprintf(stderr,"%s\n",mysql_error(&mysql));
			exit(1);
		}
	}
        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
                strcpy(cValue,field[0]);
        mysql_free_result(res);

}//void GetConfiguration()


void ScheduleJob(unsigned uUser, unsigned uServer, const char *cJobName, const char *cJobData)
{
	time_t clock;
	
	time(&clock);

        sprintf(query,"INSERT INTO tJob (cServer,cLabel,cJobData,cJobName,uUser,uJobDate,uJobStatus,uOwner,uCreatedBy,uCreatedDate) SELECT cLabel,'%s','%s','%s',%u,%lu,%u,%u,%u,%lu FROM tServer WHERE uServer=%u",
			"Priority=Normal",
			cJobData,
			cJobName,
			uUser,
			(unsigned long)clock,
			1,//uJobStatus 1=waiting
			uLoginClient,
			uLoginClient,
			(unsigned long)clock,
			uServer);
	mysql_query(&mysql,query);
	if(mysql_errno(&mysql))
		mysqlSendmail(mysql_error(&mysql));

}//void ScheduleJob()


//Passwd stuff
static unsigned char itoa64[] =         /* 0 ... 63 => ascii - 64 */
"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

void to64(s, v, n)
	register char *s;
	register long v;
	register int n;
{
	while (--n >= 0) {
	*s++ = itoa64[v&0x3f];
	v >>= 6;
	}
}

//cClearTextPasswd is written over must have enough space for MD5 + Salt header
void EncryptPasswd(char *cClearTextPasswd)
{
        char cSalt[16];
        char *cp;
	char cMethod[16] ={""}; 

	GetConfiguration("cCryptMethod",cMethod,0);

	//Pseudo random (time based) should be ok for salt! But...check.
    	(void)srand((int)time((time_t *)NULL));

	if(!strcmp(cMethod,"MD5"))
	{
		char cSubSalt[16];

		//Make pseudo random Linux/BSD style PAM MD5 salt
    		to64(&cSubSalt[0],rand(),8);
		sprintf(cSalt,"$1$%.8s$",cSubSalt);

		cp=crypt(cClearTextPasswd,cSalt);
		// error not verified, str NULL ("") returned	
	}
	else
	{
		// default DES method

		//Make DES 2 char salt
    		to64(&cSalt[0],rand(),2);

		cp=crypt(cClearTextPasswd,cSalt);
		// error not verified, str NULL ("") returned	
	}	
	strcpy(cClearTextPasswd,cp);

}//void EncryptPasswd(char *cClearTextPasswd)


int ProcessESMTPJobQueue(unsigned uDebug, char *cServer)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
        time_t clock;
        unsigned uErrNum=0;
        unsigned uJob=0;
        unsigned uUser=0;
        unsigned uServer=0;
        unsigned uMakeLocal=0;
        unsigned uMakeAccess=0;

        ConnectDb();

        time(&clock);

        uServer=GetuServer(cServer);
        if(!uServer)
        {
                fprintf(stderr,"Must specify valid uServer: cServer=%s unknown\n",cServer);
                exit(1);
        }

        sprintf(query,"SELECT uJob,cJobName,uUser,cJobData FROM tJob WHERE cServer='%s' AND uJobStatus=1 AND uJobDate<%lu ORDER BY uUser,uJob LIMIT 200",
                        cServer,
                        (unsigned long)clock);

        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
                return(1);
        }

        res=mysql_store_result(&mysql);
        if(uDebug) printf("\nNo preprocessing of jobqueue for ESMTP ocurrs...moving forward...\n");
        while((field=mysql_fetch_row(res)))
        {
                sscanf(field[0],"%u",&uJob);
                sscanf(field[2],"%u",&uUser);
                uErrNum=0;

                if(uDebug)
                printf("uJob:%s cJobName:%s cJobData:%s\n",
                                field[0],field[1],field[3]);

                if(     strstr(field[1],"NewAccess") ||
                        strstr(field[1],"ModAccess") ||
                        strstr(field[1],"DelAccess") )
                {
                        uMakeAccess=1;
                        UpdateJobStatus(uJob,JOBSTATUS_OK,"");
                }
                else if(strstr(field[1],"NewLocal") ||
                        strstr(field[1],"ModLocal") ||
                        strstr(field[1],"DelLocal") )
                {
                        uMakeLocal=1;
                        UpdateJobStatus(uJob,JOBSTATUS_OK,"");
                }
        }
        mysql_free_result(res);
        if(uDebug) printf("\n");

        if(uMakeLocal)
                MakeLocalJob(uJob,uServer);
        if(uMakeAccess)
                MakeAccessJob(uJob,uServer);

        return(0);

}//int ProcessESMTPJobQueue()


int ProcessJobQueue(unsigned uDebug, char *cServer)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	char cPrevUser[100]={""};
	time_t clock;
	unsigned uErrNum=0;
	unsigned uFirst=1;
	unsigned uIgnore=0;
	unsigned uJob=0;
	unsigned uUser=0;
	unsigned uFirstNSJob=0;
	unsigned uServer=0;
	unsigned uMakeVUT=0;
	unsigned uMakeLocal=0;
	unsigned uMakeAccess=0;
	unsigned uNewaliases=0;
	unsigned uOnlyOnce=1;
	char *cp;
	char cESMTPServer[256]={""};
	
	ConnectDb();
	
	time(&clock);
	
	uServer=GetuServer(cServer);
	if(!uServer)
	{
		fprintf(stderr,"Must specify valid uServer: cServer=%s unknown\n",cServer);
		exit(1);
	}

	//First pass eliminate NewUser-DeleteUser
	sprintf(query,"SELECT uJob,cJobName,uUser,cJobData FROM tJob WHERE cServer='%s' AND uJobStatus=1 AND uJobDate<%lu ORDER BY uUser,uJob",
			cServer,
			(unsigned long)clock);

        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
	if(uDebug) printf("\nFirst pass\n");
        while((field=mysql_fetch_row(res)))
	{
		if(strcmp(field[2],cPrevUser))
		{
			if(uDebug && !uFirst) printf("\n");
			strcpy(cPrevUser,field[2]);
			if(uDebug) printf("(uUser:%s)\n",field[2]);
			uFirst=0;
			uIgnore=0;
			uFirstNSJob=0;
		}

		//Eliminate NewUser/DeleteUser
		if(strstr(field[1],"NewUser"))
		{
			sscanf(field[0],"%u",&uJob);
			uFirstNSJob=uJob;
		}
		if( strstr(field[1],"DeleteUser") && uFirstNSJob
				&& !strcmp(field[2],cPrevUser) ) 
		{
			sscanf(field[0],"%u",&uJob);
			//Should update all jobs in this set
			//JOBSTATUS_REDUNDANT
			sprintf(query,"UPDATE tJob SET uJobStatus=6,uModBy=1,uModDate=%lu WHERE uJob>=%u AND uJob<=%u AND uUser=%s",(unsigned long)clock,
					uFirstNSJob,uJob,field[2]);
        		mysql_query(&mysql,query);
        		if(mysql_errno(&mysql))
        		{
                		fprintf(stderr,"%s\n",mysql_error(&mysql));
               			return(1);
        		}
			uIgnore=1;

			sprintf(query,"DELETE FROM tUser WHERE uUser=%s",field[2]);
        		mysql_query(&mysql,query);
        		if(mysql_errno(&mysql))
        		{
                		fprintf(stderr,"%s\n",mysql_error(&mysql));
               			return(1);
        		}
		}

		if(uDebug)
		{
			if(uIgnore)
			{
				printf("Canceling this DeleteUser and prev NewUser\n");
				printf("uJob:%s cJobName:%s cJobData:%s\n",
						field[0],field[1],field[3]);
				
			}
			else
			{
				printf("uJob:%s cJobName:%s cJobData:%s\n",
					field[0],field[1],field[3]);
			}
		}
	}
        mysql_free_result(res);
	if(uDebug) printf("\n");

	GetConfiguration("cESMTPServer",cESMTPServer,0);
	
	//Now run remaining jobs. Limit is for crontab period adjust for your
	//situacion
	sprintf(query,"SELECT uJob,cJobName,uUser,cJobData FROM tJob WHERE cServer='%s' AND uJobStatus=1 AND uJobDate<%lu ORDER BY uUser,uJob LIMIT 100",
			cServer,
			(unsigned long)clock);

        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
	if(uDebug) printf("\nSecond pass\n");
        while((field=mysql_fetch_row(res)))
	{
		sscanf(field[0],"%u",&uJob);
		sscanf(field[2],"%u",&uUser);
		uErrNum=0;

		if(uDebug)
		printf("uJob:%s cJobName:%s cJobData:%s\n",
				field[0],field[1],field[3]);
		
		//Pop mailbox user add/mod/del operations
		if(strstr(field[1],"NewUser"))
		{
			uErrNum=NewUserJob(uJob,uUser);
		}
		else if(strstr(field[1],"DeleteUser"))
		{
			uErrNum=DeleteUserJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserPwd"))
		{
			uErrNum=ModUserPwdJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserAPopOn"))
		{
			ModUserAPopOnJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserAPopOff"))
		{
			ModUserAPopOffJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserTrafficQuotaOn"))
		{
			ModUserTrafficQuotaOnJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserTrafficQuotaOff"))
		{
			ModUserTrafficQuotaOffJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserHDQuotaOn"))
		{
			ModUserHDQuotaOnJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserHDQuotaOff"))
		{
			ModUserHDQuotaOffJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserMailFilterOn"))
		{
			ModUserMailFilterOnJob(uJob,uUser);
		}
		else if(strstr(field[1],"ModUserMailFilterOff"))
		{
			ModUserMailFilterOffJob(uJob,uUser);
		}
		else if(!strcmp(field[1],"MakeHomeDir"))
		{
			MakeHomeDirJob(uJob,uUser);
		}
		
		//Sendmail configuration file ops
		else if(strstr(field[1],"NewAlias") ||
			strstr(field[1],"ModAlias") ||
			strstr(field[1],"DelAlias") )
		{
			uNewaliases=1;
			UpdateJobStatus(uJob,JOBSTATUS_OK,"");
		}
		else if(strstr(field[1],"NewAccess") ||
			strstr(field[1],"ModAccess") ||
			strstr(field[1],"DelAccess") )
		{
			uMakeAccess=1;
			if(!cESMTPServer[0])	
				UpdateJobStatus(uJob,JOBSTATUS_OK,"");
			else
				UpdateESMTPJobStatus(uJob,JOBSTATUS_WAITING,"MainServerOk",cESMTPServer);
		}
		else if(strstr(field[1],"NewLocal") ||
			strstr(field[1],"ModLocal") ||
			strstr(field[1],"DelLocal") )
		{
			uMakeLocal=1;
			UpdateJobStatus(uJob,JOBSTATUS_OK,"");
		}
		else if(strstr(field[1],"NewVUTEntry") ||
			strstr(field[1],"ModVUTEntry") ||
			strstr(field[1],"DeleteVUTEntry") )
		{
			uMakeVUT=1;
			UpdateJobStatus(uJob,JOBSTATUS_OK,"");
		}

		//mysqlISP Ext jobs
		if(!strncmp(field[1],"Ext",3) && (cp=strstr(field[3],"uJob=")))
		{
			char cMsg[33];
			unsigned uExtJob=0;

			//This is in a loop...
			if(uOnlyOnce)
			{
				ExtConnectDb(0);
				uOnlyOnce=0;
			}
			sprintf(cMsg,"mysqlSendmail.%.20s %u",
					field[1],uErrNum);
			sscanf(cp+5,"%u",&uExtJob);
			if(uErrNum)
			{
				InformExtJob(cMsg,cServer,uExtJob,mysqlISP_Waiting);
				fprintf(stderr,"uErrNum=%u\n\n",uErrNum);
			}
			else
			{
				InformExtJob(cMsg,cServer,uExtJob,mysqlISP_Deployed);
			}
		}

	}
        mysql_free_result(res);
	if(uDebug) printf("\n");
	
	if(uMakeVUT)
		MakeVUTJob(uJob,uServer);
	if(uMakeLocal)
		MakeLocalJob(uJob,uServer);
	if(uMakeAccess)
		MakeAccessJob(uJob,uServer);
	if(uNewaliases)
		MakeNewaliasesJob(uJob,uServer);
	
	return(0);

}//int ProcessJobQueue()


void MakeVUTJob(unsigned uJob,unsigned uServer)
{
	//Redhat 7.3 default with our other defaults
	char cMakeVUT[256]={"/usr/bin/makemap hash /etc/mail/virtusertable.db < /etc/mail/virtusertable  "};
	unsigned uErrNum;
	char cErrorNum[16];
	
	GetConfiguration("cMakeVUT",cMakeVUT,0);
	printf("MakeVUTJob() %s\n",cMakeVUT);
	//Make file
	if(MakeVUTFile(uServer,0))
	{
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,"MakeVUTFile()");
		return;
	}
	if((uErrNum=system(cMakeVUT)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}

}//void MakeVUTJob(unsigned uJob,unsigned uServer)


void MakeLocalJob(unsigned uJob,unsigned uServer)
{
	//Redhat 7.3 default
	char cMakeLocal[256]={"/etc/rc.d/init.d/sendmail restart"};
	unsigned uErrNum;
	char cErrorNum[16];

	GetConfiguration("cMakeLocal",cMakeLocal,0);
	printf("MakeLocalJob() %s\n",cMakeLocal);
	//Make file
	if(MakeLocalFile(uServer,0))
	{
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,"MakeLocalFile()");
		return;
	}
	if((uErrNum=system(cMakeLocal)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}

}//void MakeLocalJob(unsigned uJob,unsigned uServer)


void MakeAccessJob(unsigned uJob,unsigned uServer)
{
	//Redhat 7.3 default with our other defaults
	char cMakeAccess[256]={"/usr/bin/makemap hash /etc/mail/access.db < /etc/mail/access"};
	unsigned uErrNum;
	char cErrorNum[16];

	GetConfiguration("cMakeAccess",cMakeAccess,0);
	printf("MakeAccessJob() %s\n",cMakeAccess);
	//Make file
	if(MakeAccessFile(uServer,0))
	{
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,"MakeAccessFile()");
		return;
	}
	if((uErrNum=system(cMakeAccess)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}

}//void MakeAccessJob(unsigned uJob,unsigned uServer)


void MakeNewaliasesJob(unsigned uJob,unsigned uServer)
{
	//Redhat 7.3 default
	char cNewaliases[256]={"/usr/bin/newaliases"};
	unsigned uErrNum;
	char cErrorNum[16];

	GetConfiguration("cNewaliases",cNewaliases,0);
	printf("MakeNewaliasesJob() %s\n",cNewaliases);
	//Make file
	if(MakeAliasFile(uServer,0))
	{
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,"MakeAliasFile()");
		return;
	}
	if((uErrNum=system(cNewaliases)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}

}//void MakeNewaliasesJob(unsigned uJob,unsigned uServer)


int NewUserJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("NewUserJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=NewUser(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_ACTIVE);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int NewUserJob(unsigned uJob,unsigned uUser)


int DeleteUserJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];
	char query[256];

	printf("DeleteUserJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=DeleteUser(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		//Remove from tUser table
		sprintf(query,"DELETE FROM tUser WHERE uUser=%u",uUser);
        	mysql_query(&mysql,query);
        	if(mysql_errno(&mysql))
			fprintf(stderr,"%s\n",mysql_error(&mysql));
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int DeleteUserJob(unsigned uJob,unsigned uUser)


int ModUserPwdJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserPwdJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserPwd(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserPwdJob(unsigned uJob,unsigned uUser)


int ModUserAPopOnJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserAPopOnJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserAPopOn(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserAPopOnJob(unsigned uJob,unsigned uUser)


int ModUserAPopOffJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserAPopOffJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserAPopOff(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserAPopOffJob(unsigned uJob,unsigned uUser)


int ModUserTrafficQuotaOnJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserTrafficQuotaOnJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserTrafficQuotaOn(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserTrafficQuotaOnJob(unsigned uJob,unsigned uUser)


int ModUserTrafficQuotaOffJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserTrafficOffJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserTrafficQuotaOff(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserTrafficQuotaOffJob(unsigned uJob,unsigned uUser)


int ModUserHDQuotaOnJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserHDQuotaOnJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserHDQuotaOn(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserHDQuotaOnJob(unsigned uJob,unsigned uUser)


int ModUserHDQuotaOffJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserHDOffJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserHDQuotaOff(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserHDQuotaOffJob(unsigned uJob,unsigned uUser)


int ModUserMailFilterOnJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserMailFilterOnJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserMailFilterOn(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserMailFilterOnJob(unsigned uJob,unsigned uUser)


int ModUserMailFilterOffJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("ModUserMailFilterOffJob(%u)\n",uUser);
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=ModUserMailFilterOff(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_MODIFIED);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int ModUserMailFilterOffJob(unsigned uJob,unsigned uUser)


void UpdateJobStatus(unsigned uJob,unsigned uJobStatus,char *cRemoteMsg)
{
	char qstr[256];
	time_t clock;

	time(&clock);
	sprintf(qstr,"UPDATE tJob SET uJobStatus=%u,uModBy=1,uModDate=%lu,cRemoteMsg='%.32s' WHERE uJob=%u",uJobStatus,(long unsigned)clock,TextAreaSave(cRemoteMsg),uJob);
        mysql_query(&mysql,qstr);
        if(mysql_errno(&mysql))
                fprintf(stderr,"%s\n",mysql_error(&mysql));

}//void UpdateJobStatus(unsigned uJob,unsigned uJobStatus,char *cRemoteMsg)


//Usually changes status to waiting and the server to the ESMTP remote server
void UpdateESMTPJobStatus(unsigned uJob,unsigned uJobStatus,char *cRemoteMsg,char *cServer)
{
	char qstr[256];
	time_t clock;

	time(&clock);
	sprintf(qstr,"UPDATE tJob SET uJobStatus=%u,uModBy=1,uModDate=%lu,cRemoteMsg='%.32s',cServer='%.32s' WHERE uJob=%u",uJobStatus,(long unsigned)clock,TextAreaSave(cRemoteMsg),cServer,uJob);
        mysql_query(&mysql,qstr);
        if(mysql_errno(&mysql))
                fprintf(stderr,"%s\n",mysql_error(&mysql));

}//UpdateESMTPJobStatus(unsigned uJob,unsigned uJobStatus,char *cRemoteMsg,char *cServer)


void UpdateUserStatus(unsigned uUser,unsigned uStatus)
{
	time_t clock;
	char qstr[256];

	time(&clock);
	sprintf(qstr,"UPDATE tUser SET uStatus=%u,uModBy=1,uModDate=%lu WHERE uUser=%u",uStatus,(long unsigned)clock,uUser);
        mysql_query(&mysql,qstr);
        if(mysql_errno(&mysql))
                fprintf(stderr,"%s\n",mysql_error(&mysql));

}//void UpdateUserStatus()


int DeleteUser(unsigned uUser)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	char query[256];
	char cLogin[100]={""};
	struct passwd *structPasswd;
	unsigned uAPop=0,uHDQuota=0,uTrafficQuota=0,uMailFilter=0,uRetVal=0;

	sprintf(query,"SELECT cLogin,uAPop,uHDQuota,uTrafficQuota,uMailFilter FROM tUser WHERE uUser=%u",uUser);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        if(!mysql_num_rows(res))
        {
        	mysql_free_result(res);
                fprintf(stderr,"User not found in tUser\n");
               	return(2);
        }
        if((field=mysql_fetch_row(res)))
	{
		sprintf(cLogin,"%.99s",field[0]);
		sscanf(field[1],"%u",&uAPop);
		sscanf(field[2],"%u",&uHDQuota);
		sscanf(field[3],"%u",&uTrafficQuota);
		sscanf(field[4],"%u",&uMailFilter);
	}
        mysql_free_result(res);

	//Ideas TODO
	//Create at job to remove cLogin mailbox in 30 (x)days
	//Use tConfiguration values for userdel and number of days
	//Can use cUserDelCommand /usr/sbin/userdel -r (etc) to remove homedir but NOT mailbox
	//
	//Check uid if less than SU_BASE_ID do not userdel!
	if((structPasswd=getpwnam(cLogin)))
	{
		char cUserDelCommand[256]={"/usr/sbin/userdel"};

		if(structPasswd->pw_uid<SU_BASE_ID)
		{
			fprintf(stderr,"Can't delete pw_uid<%u!\n",SU_BASE_ID);
			return(3);
		}

		GetConfiguration("cUserDelCommand",cUserDelCommand,0);

		sprintf(query,"%s %s",cUserDelCommand,cLogin);
		if(system(query))
		{
			fprintf(stderr,"System error:\n%s\n",query);
			return(4);
		}

		if(uAPop)
			uRetVal=ModUserAPopOff(uUser);
		if(uHDQuota)
			uRetVal=ModUserHDQuotaOff(uUser);
		if(uTrafficQuota)
			uRetVal=ModUserTrafficQuotaOff(uUser);
		if(uMailFilter)
			uRetVal=ModUserMailFilterOff(uUser);

		return(uRetVal);

	}
	//Keep info in logfile but allow normal Deletion op.
	fprintf(stderr,"No such user %s in system passwd file\n",cLogin);
	//return(5);
	return(0);

}//int DeleteUser(char *cLogin)


//Change to mod everything ASAP.
int ModUserPwd(unsigned uUser)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	char cPasswd[65]={""};
	char cLogin[64]={""};
	unsigned uRetVal=0;

	char query[1024];

	sprintf(query,"SELECT cLogin,cPasswd FROM tUser WHERE uUser=%u",uUser);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
	{
		sprintf(cLogin,"%.63s",field[0]);
		sprintf(cPasswd,"%.64s",field[1]);
	}
	else
	{
        	mysql_free_result(res);
                fprintf(stderr,"No such tUser.uUser: %u\n",uUser);
               	return(2);
	}
        mysql_free_result(res);
	
	if((uRetVal=ChangeSystemPasswd(cLogin,cPasswd)))
	{
		fprintf(stderr,"Error changing passwd. uRetVal=%u\n",uRetVal);
		return(uRetVal);
	}

	return(0);

}//int ModUserPwd()


int NewUser(unsigned uUser)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	char cShell[256]={"/bin/false"};
	char cGID[16]={"nobody"};
	char cPasswd[65]={""};
	char cHomeDir[256]={"No"};
	char query[254];
	unsigned uUID=0;
	unsigned uRetVal=0;
	struct passwd *structPasswd;

	unsigned uAPop=0,uHDQuota=0,uTrafficQuota=0,uMailFilter=0;
	char cLogin[64]={""};


	sprintf(query,"SELECT cLogin,cPasswd,uAPop,uHDQuota,uTrafficQuota,uMailFilter FROM tUser WHERE uUser=%u",uUser);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
	{
		sprintf(cLogin,"%.62s",field[0]);
		sprintf(cPasswd,"%.64s",field[1]);
		sscanf(field[2],"%u",&uAPop);
		sscanf(field[3],"%u",&uHDQuota);
		sscanf(field[4],"%u",&uTrafficQuota);
		sscanf(field[5],"%u",&uMailFilter);
	}
	else
	{
        	mysql_free_result(res);
                fprintf(stderr,"No such tUser.uUser: %u\n",uUser);
               	return(2);
	}
        mysql_free_result(res);

	GetConfiguration("cPopShell",cShell,0);
	GetConfiguration("cGID",cGID,0);

	//Adjust uid
	uUID=uUser+SU_BASE_ID;
	if((structPasswd=getpwuid(uUID)))
	{
		fprintf(stderr,"User with uid:%u already exists\n",uUID);
		return(uUID);
	}
	if((structPasswd=getpwnam(cLogin)))
	{
		fprintf(stderr,"User with name:%s already exists\n",cLogin);
		return(uUID);
	}

	GetConfiguration("cHomeDir",cHomeDir,0);
	if(cHomeDir[0]=='Y')
	{
		char cUserAddWithHomeDir[256]={"/usr/sbin/useradd -m -u %u -g %s -s %s %s"};
		
		GetConfiguration("cUserAddWithHomeDir",cUserAddWithHomeDir,0);

		sprintf(query,cUserAddWithHomeDir,
				uUID,cGID,cShell,cLogin);
	}
	else
	{
		char cUserAdd[256]={"/usr/sbin/useradd -u %u -g %s -d /nodir -s %s %s"};
		
		GetConfiguration("cUserAdd",cUserAdd,0);

		sprintf(query,cUserAdd,
			uUID,cGID,cShell,cLogin);
	}

	if(system(query))
	{
		fprintf(stderr,"System error:\n%s\n",query);
		return(4);
	}

	if((uRetVal=ChangeSystemPasswd(cLogin,cPasswd)))
	{
		fprintf(stderr,"Error changing passwd (%u).\n",uRetVal);
		return(5);
	}

	//If we get past main hurdle do secondary setup items
	//unsigned uAPop=0,uHDQuota=0,uTrafficQuota=0,uMailFilter=0;
	//Each one of these should handle error messages but not abort NewUser
	if(uAPop)
		uRetVal=ModUserAPopOn(uUser);
	if(uHDQuota)
		uRetVal=ModUserHDQuotaOn(uUser);
	if(uTrafficQuota)
		uRetVal=ModUserTrafficQuotaOn(uUser);
	if(uMailFilter)
		uRetVal=ModUserMailFilterOn(uUser);

	return(uRetVal);

}//int NewUser()

#ifdef Linux

//Find .h please
int lckpwdf(void);
int ulckpwdf(void);
int ChangeSystemPasswd(char *cLogin, char *cPasswd)
{
        FILE *fp,*ofp;
        char cSearch[100]={""};
	struct passwd *userinfo;
	register int ok=0;

	if((userinfo=getpwnam(cLogin))==NULL)
		return(16);

        if(userinfo->pw_uid<500)
		return(17);

        umask(~(S_IRUSR | S_IWUSR));

        if(lckpwdf()) return(8);

        if((fp=fopen("/etc/shadow","r"))==NULL)
                return(1);

        if((ofp=fopen("/etc/shadow.mail","w"))==NULL)
                return(2);

        sprintf(cSearch,"%s:",cLogin);

	//This is slow when server has thousands of users.
	//What other way is there? File position operations?
        while(fgets(query,1024,fp)!=NULL)
        {
		
		//Please fix this 10556 (=days since jan 1st 1970
		// password was last changed) thing soon.
		//
                if(!strncmp(query,cSearch,strlen(cSearch)))
                {
			ok++;
			//MD5 change
                        fprintf(ofp,"%.63s:%.64s:10556::::::\n",cLogin,cPasswd);
                }
                else
                {
                        fprintf(ofp,"%s",query);
                }
        }

        fclose(ofp);
        fclose(fp);

	if((fp=fopen("/etc/shadow.mail","r"))==NULL)
                return(3);
        if((ofp=fopen("/etc/shadow","w"))==NULL)
                return(4);
        while(fgets(query,1024,fp)!=NULL)
                        fprintf(ofp,"%s",query);

        fclose(ofp);
        fclose(fp);

        ulckpwdf();

        if(ok)
		return(0);
	else
		return(5);//Not found in shadow

}//int ChangeSystemPasswd(char *cLogin, char *cPasswd)

#elif defined(FreeBSD)

int ChangeSystemPasswd(char *cLogin, char *cPasswd)
{
        char cQuery[256];

        sprintf(query,"/usr/bin/chpass -p %.32s %.64s",cPasswd,cLogin);

        return(system(cQuery));

}//int ChangeSystemPasswd(char *cLogin, char *cPasswd)

#endif


#ifdef RADIUSIMPORT
extern MYSQL mysql2;
void mysqlRadiusImport(void)
{
        MYSQL_RES *res;
        MYSQL_ROW field;

	unsigned uUser;
        char query[256];
        char cPasswd[65];
	
	ConnectDb();

        sprintf(query,"SELECT cLogin,cPasswd,uClearText,cEnterPasswd FROM tUser");
        mysql_query(&mysql2,query);
        if(mysql_errno(&mysql2))
	{
			fprintf(stderr,"%s\n",mysql_error(&mysql2));
			exit(1);
	}
        res=mysql_store_result(&mysql2);
        while((field=mysql_fetch_row(res)))
	{
		printf("%s,%s,%s,%s\n",field[0],field[1],field[2],field[3]);
		if(field[2][0]=='1')
		{
			strcpy(cPasswd,field[3]);
			EncryptPasswd(cPasswd);
		}
		else
		{
			strcpy(cPasswd,field[1]);
		}
		
		//Note: !mysql2
        	sprintf(query,"INSERT INTO tUser SET cLogin='%s', cPasswd='%s', uServer=1, uHDQuota=1, uTrafficQuota=1, uOwner=1, uCreatedBy=1, uStatus=%u",
				field[0],cPasswd,US_AWAITACT);
        	mysql_query(&mysql,query);
	        if(mysql_errno(&mysql))
		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				exit(1);
		}
		uUser=mysql_insert_id(&mysql);
		ScheduleJob(uUser,1,"NewUser",field[0]);
	}
        mysql_free_result(res);

}//void mysqlRadiusImport(void)
#endif


unsigned GetuServer(char *cServer)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
        char query[254];
	unsigned uRetVal=0;

        sprintf(query,"SELECT uServer FROM tServer WHERE cLabel='%s'",cServer);

        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		exit(1);
	}
        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
                sscanf(field[0],"%u",&uRetVal);
        mysql_free_result(res);
	
	return(uRetVal);

}//unsigned GetuServer(char *cServer)


int MakeLocalFile(unsigned uServer,unsigned uHtml)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	FILE *fp;
	char cFile[256]={"/etc/mail/sendmail.cw"};
	
	GetConfiguration("cLocalFile",cFile,0);
	
	if(!(fp=fopen(cFile,"w")))
        {
		char cMsg[256];
		sprintf(cMsg,"Can't open %.230s for write",cFile);
		if(uHtml)
			tAlias(cMsg);
		else
                	printf("%s\n",cMsg);
               	return(1);
        }

	fprintf(fp,"#OpenISP mysqlSendmail.MakeLocalFile() $Id: mail.c 21 2005-12-21 22:20:02Z ggw $\n");

	sprintf(query,"SELECT cDomain FROM tLocal WHERE uServer=%u",uServer);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
		if(uHtml)
			mysqlSendmail(mysql_error(&mysql));
		else
                	printf("%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        while((field=mysql_fetch_row(res)))
                fprintf(fp,"%s\n",field[0]);
        mysql_free_result(res);
	fclose(fp);

	return(0);

}//int MakeLocalFile()


int MakeAccessFile(unsigned uServer,unsigned uHtml)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	FILE *fp;
	char cFile[256]={"/etc/mail/access"};
	char cESMTPServer[256]={""};
	unsigned uESMTPServer=1;
	
	GetConfiguration("cAccessFile",cFile,0);
	
	if(!(fp=fopen(cFile,"w")))
        {
		char cMsg[256];
		sprintf(cMsg,"Can't open %.230s for write",cFile);
		if(uHtml)
			tAlias(cMsg);
		else
                	printf("%s\n",cMsg);
               	return(1);
        }

	fprintf(fp,"#OpenISP mysqlSendmail.MakeAccessFile() $Id: mail.c 21 2005-12-21 22:20:02Z ggw $\n");

	
	GetConfiguration("cESMTPServer",cESMTPServer,0);
	if(cESMTPServer[0])
	{
		uESMTPServer=GetuServer(cESMTPServer);
		sprintf(query,"SELECT DISTINCT cDomainIP,cRelayAttr FROM tAccess WHERE uServer=%u OR uServer=%u",uServer,uESMTPServer);
		//Check this out against other uses of cESMTPServer
		//Prev notes:
		//uServer=1 hack cESMTPMasterServer? TODO: Master server always uServer=1?
	}
	else
	{
		sprintf(query,"SELECT DISTINCT cDomainIP,cRelayAttr FROM tAccess WHERE uServer=%u",uServer);
	}
		
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
		if(uHtml)
			mysqlSendmail(mysql_error(&mysql));
		else
                	printf("%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        while((field=mysql_fetch_row(res)))
                fprintf(fp,"%s %s\n",field[0],field[1]);
        mysql_free_result(res);
	fclose(fp);

	return(0);

}//int MakeAccessFile()


int MakeVUTFile(unsigned uServer,unsigned uHtml)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	FILE *fp;
	char cFile[256]={"/etc/mail/virtusertable"};
	
	GetConfiguration("cVUTFile",cFile,0);
	
	if(!(fp=fopen(cFile,"w")))
        {
		char cMsg[256];
		sprintf(cMsg,"Can't open %.230s for write",cFile);
		if(uHtml)
			tAlias(cMsg);
		else
                	printf("%s\n",cMsg);
               	return(1);
        }

	fprintf(fp,"#OpenISP mysqlSendmail.MakeVUTFile() $Id: mail.c 21 2005-12-21 22:20:02Z ggw $\n");

	sprintf(query,"SELECT tVUTEntries.cVirtualEmail,tVUT.cDomain,tVUTEntries.cTargetEmail FROM tVUT,tVUTEntries WHERE tVUT.uServer=%u AND tVUT.uVUT=tVUTEntries.uVUT ORDER BY tVUTEntries.uVUT,tVUTEntries.cVirtualEmail",uServer);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
		if(uHtml)
			mysqlSendmail(mysql_error(&mysql));
		else
                	printf("%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        while((field=mysql_fetch_row(res)))
	{
		if(field[0][0]=='D')
                	fprintf(fp,"@%s %s\n",field[1],field[2]);
		else
                	fprintf(fp,"%s@%s %s\n",field[0],field[1],field[2]);
	}
        mysql_free_result(res);
	fclose(fp);

	return(0);

}//int MakeVUTFile()


int MakeAliasFile(unsigned uServer,unsigned uHtml)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	FILE *fp;
	char cFile[256]={"/etc/mail/aliases"};
	
	GetConfiguration("cAliasFile",cFile,0);
	
	if(!(fp=fopen(cFile,"w")))
        {
		char cMsg[256];
		sprintf(cMsg,"Can't open %.230s for write",cFile);
		if(uHtml)
			tAlias(cMsg);
		else
                	printf("%s\n",cMsg);
               	return(1);
        }

	fprintf(fp,"#OpenISP mysqlSendmail.MakeAliasFile() $Id: mail.c 21 2005-12-21 22:20:02Z ggw $\n");

	sprintf(query,"SELECT cUser,cTargetEmail FROM tAlias WHERE uServer=%u",uServer);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
		if(uHtml)
			mysqlSendmail(mysql_error(&mysql));
		else
                	printf("%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        while((field=mysql_fetch_row(res)))
                fprintf(fp,"%s: %s\n",field[0],field[1]);
        mysql_free_result(res);
	fclose(fp);

	return(0);

}//int MakeAliasFile()


void ParseExtParams(structExtJobParameters *structExtParam, char *cJobData)
{
	char *cp,*cp2;

	if((cp=strstr(cJobData,"ClearPasswd=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+12,'\n'))) *cp2=0;
		sprintf(structExtParam->cPasswd,"%.15s",cp+12);
		if(cp2) *cp2='\n';
		structExtParam->uParamPasswd=1;
		//Can have emtpy ClearPasswd for migration from only des3 passwd
		//imports. 
		if(structExtParam->cPasswd[0])
			structExtParam->uClearText=1;
	}

	//ClearText passwd has precedence
	if( !structExtParam->uClearText && (cp=strstr(cJobData,".Passwd=")) )
	{
		cp2=NULL;
		if((cp2=strchr(cp+8,'\n'))) *cp2=0;
		//MD5 Change
		sprintf(structExtParam->cPasswd,"%.64s",cp+8);
		if(cp2) *cp2='\n';
		structExtParam->uParamPasswd=1;
	}

	if((cp=strstr(cJobData,"Login=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+6,'\n'))) *cp2=0;
		sprintf(structExtParam->cLogin,"%.63s",cp+6);
		if(cp2) *cp2='\n';
		structExtParam->uParamLogin=1;
		LoginCleanUp(structExtParam->cLogin);
	}

	if((cp=strstr(cJobData,"VUTDomain=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+10,'\n'))) *cp2=0;
		sprintf(structExtParam->cVUTDomain,"%.99s",cp+10);
		if(cp2) *cp2='\n';
		structExtParam->uParamVUTDomain=1;
	}

	if((cp=strstr(cJobData,".Domain=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+8,'\n'))) *cp2=0;
		sprintf(structExtParam->cDomain,"%.99s",cp+8);
		if(cp2) *cp2='\n';
		structExtParam->uParamDomain=1;
	}

	if((cp=strstr(cJobData,"VUTEntry=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+9,'\n'))) *cp2=0;
		sprintf(structExtParam->cVUTEntry,"%.99s",cp+9);
		if(cp2) *cp2='\n';
		structExtParam->uParamVUTEntry=1;
	}

	if((cp=strstr(cJobData,"VUTTarget=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+10,'\n'))) *cp2=0;
		sprintf(structExtParam->cVUTTarget,"%.99s",cp+10);
		if(cp2) *cp2='\n';
		structExtParam->uParamVUTTarget=1;
	}

	if(strstr(cJobData,"FakeISPJob"))
		structExtParam->uFakeISPJob=1;


	//Service can have both Passwd type params and have override
	//This not known to be of any use so far...but you never know
	if((cp=strstr(cJobData,"ClearText=")))
	{
		cp+=10;
		if( *cp=='Y' || *cp=='y')
			structExtParam->uClearText=1;
		else
			structExtParam->uClearText=0;
	}
			
	//tClient
	if((cp=strstr(cJobData,"ISPClient=")))
	{
		sscanf(cp+10,"%u",&structExtParam->uISPClient);
	}
	if((cp=strstr(cJobData,"ClientName=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+11,'\n'))) *cp2=0;
		sprintf(structExtParam->cClientName,"%.32s",cp+11);
		if(cp2) *cp2='\n';
		structExtParam->uParamClientName=1;
	}

	//tMailFilter
	if((cp=strstr(cJobData,"MailFilter=")))
	{
		cp2=NULL;
		if((cp2=strchr(cp+11,'\n'))) *cp2=0;
		sprintf(structExtParam->cMailFilter,"%.32s",cp+11);
		if(cp2) *cp2='\n';
		structExtParam->uParamMailFilter=1;
	}
	
	

	//Debug only	
	printf("cLogin=%s cPasswd=%s cVUTDomain=%s cVUTEntry=%s cVUTTarget=%s uClearText=%u\n"
		"uISPClient=%u\ncClientName=%s\ncMailFilter=%s\n",
		structExtParam->cLogin,structExtParam->cPasswd,
		structExtParam->cVUTDomain,structExtParam->cVUTEntry,
		structExtParam->cVUTTarget,structExtParam->uClearText,
		structExtParam->uISPClient,structExtParam->cClientName,
		structExtParam->cMailFilter);

}//void ParseExtParams(structExtJobParameters *structExtParam)


//mysqlSendmail.Pop.New/Mod/Hold/Cancel: standard mail box
//mysqlSendmail.Website.New/Mod/Hold/Cancel: tDomain,tAccess,tLocal,tVUT,tVUTEntries TODO
//mysqlSendmail.Domain.New/Mod/Hold/Cancel: tDomain,tAccess,tLocal
//mysqlSendmail.VUT.New/Mod/Hold/Cancel: tVUT only ism|3 can edit the entries directly
void ProcessExtJobQueue(char *cServer)
{
	MYSQL_RES *res;
	MYSQL_ROW field;

	structExtJobParameters structExtParam;

	unsigned uJob=0;
	unsigned uUser=0;
	unsigned uMailFilter=0;

	time_t clock;
	char query[1024];
	
	ExtConnectDb(0);

	time(&clock);

	sprintf(query,"SELECT cJobName,cJobData,uJob FROM tJob WHERE (cServer='Any' OR cServer='%s') AND uJobStatus=%u AND uJobDate<=%lu AND cJobName LIKE 'mysqlSendmail.%%'",cServer,mysqlISP_Waiting,(long unsigned)clock);

	//Debug only	
	//printf("%s\n",query);

	mysql_query(&mysqlext,query);
        if(mysql_errno(&mysqlext))
		fprintf(stderr,"%s\n",mysql_error(&mysqlext));


	res=mysql_store_result(&mysqlext);
        while((field=mysql_fetch_row(res)))
	{
		InitializeParams(&structExtParam);
		
		sscanf(field[2],"%u",&uJob);

		if(!strcmp("mysqlSendmail.Pop.New",field[0]))
		{
			
			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);
		
			if(structExtParam.uClearText)
			{
				strcpy(structExtParam.cEnterPasswd,
					structExtParam.cPasswd);
				EncryptPasswd(structExtParam.cPasswd);
			}
			//else cPasswd should have crypt() passwd and
			//cEnterPasswd will be empty.

			if(structExtParam.uParamMailFilter)
				uMailFilter=uGetMailFilterId(structExtParam.cMailFilter);


		//Note moved margin left			
sprintf(query,"SELECT uUser FROM tUser WHERE cLogin='%s' AND uServer=1 AND uStatus=1"
				,structExtParam.cLogin);
		mysql_query(&mysql,query);
		if(mysql_errno(&mysql))
       		{
			fprintf(stderr,"%s\n",mysql_error(&mysql));
			InformExtJob("Select tUser failed",cServer,uJob,
					mysqlISP_Waiting);
		}
       		res=mysql_store_result(&mysql);
       		if(mysql_num_rows(res)==0)
		{

sprintf(query,"INSERT INTO tUser SET  cLogin='%s',cEnterPasswd='%s',cPasswd='%s',uOwner=%u, uCreatedBy=1, uCreatedDate=%lu, uMailFilter=%u, uServer=1, uStatus=6"
			,structExtParam.cLogin
			,structExtParam.cEnterPasswd
			,structExtParam.cPasswd
			,structExtParam.uISPClient
			,(long unsigned)clock
			,uMailFilter);

			//Debug only	
			//printf("%s\n",query);
	
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				//TODO
				//If cLogin already in use, and we guestimate that
				//we should update information. Then do so and let mysqlISP
				//know that it is deployed.
				//How do we guestimate?
				//Danger issues:
				//This login belongs to someother customer
				//Some other mysqlSendmail<-->mysqlISP sync and ISP wide
				//consistency rule is being violated.
				//
				//Temp solution suppose for most cases we should update but
				//we set the mysqlISP job status cRemoteMsg to "Warning Review"
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("tUser Insert failed",cServer,uJob,
						mysqlISP_Waiting);
			}
			else
			{
			   uUser=mysql_insert_id(&mysql);

			   if(!structExtParam.uFakeISPJob)
			   	SubmitExtSingleJob("ExtNewUser",structExtParam.cLogin,
						cServer,(long unsigned)clock,uJob,uUser);
			   else
			   	SubmitExtSingleJob("NewUser",structExtParam.cLogin,
						cServer,(long unsigned)clock,uJob,uUser);

			   if(!structExtParam.uFakeISPJob)
			   {
				InformExtJob("mysqlSendmail.ExtNewUser",cServer,uJob,
							mysqlISP_RemotelyQueued);
			   }
			   else
			   {
				//TODO this violates protocol we are assuming job will
				//be done...need another remotely queued status and
				//and a special local job handler to set to uJobStatus=3
				sprintf(query,"UPDATE tJob SET uJobStatus=3 WHERE uJob=%u",uJob);
				mysql_query(&mysqlext,query);
				if(mysql_errno(&mysqlext))
					fprintf(stderr,"%s\n",mysql_error(&mysqlext));
			   }

			   CreateNewClient(&structExtParam);

			   printf("Ok\n");

			}

		}
		else
		{
			//Update case
		}
		mysql_free_result(res);

		}//Pop.NewUser

		else if(!strcmp("mysqlSendmail.Pop.Cancel",field[0]))
		{
			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

sprintf(query,"SELECT uUser FROM tUser WHERE cLogin='%s' AND uServer=1",
							structExtParam.cLogin);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Select query failed",cServer,uJob,
						mysqlISP_Waiting);
			}
			else
			{
				MYSQL_RES *res;
				MYSQL_ROW field;

        			res=mysql_store_result(&mysql);
        			if(mysql_num_rows(res))
				{
        				if((field=mysql_fetch_row(res)))
						sscanf(field[0],"%u",&uUser);
//int SubmitExtSingleJob(char *cJobName,char *cJobData,char *cServer,unsigned uJobDate,
//		unsigned uJob, unsigned uUser);
			   		if(!structExtParam.uFakeISPJob)
						SubmitExtSingleJob("ExtDeleteUser",
							structExtParam.cLogin,cServer,
								(long unsigned)clock,uJob,uUser);
					else
						SubmitExtSingleJob("DeleteUser",
							structExtParam.cLogin,cServer,
								(long unsigned)clock,uJob,uUser);


			   		if(!structExtParam.uFakeISPJob)
					{
						InformExtJob("mysqlSendmail.ExtDeleteUser",
							cServer,uJob,mysqlISP_RemotelyQueued);
			   			DeleteClient(&structExtParam);
					}
					else
					{	
				//TODO this violates protocol we are assuming job will
				//be done...need another remotely queued status and
				//and a special local job handler to set to uJobStatus=3
				sprintf(query,"UPDATE tJob SET uJobStatus=3 WHERE uJob=%u",uJob);
				mysql_query(&mysqlext,query);
				if(mysql_errno(&mysqlext))
					fprintf(stderr,"%s\n",mysql_error(&mysqlext));
					}
				}	
				else
				{
			   		if(!structExtParam.uFakeISPJob)
					{
						InformExtJob("No such user/server",
							cServer,uJob,mysqlISP_Waiting);
					}
					else
					{	
				//TODO this violates protocol we are assuming job will
				//be done...need another remotely queued status and
				//and a special local job handler to set to uJobStatus=4
				sprintf(query,"UPDATE tJob SET uJobStatus=4 WHERE uJob=%u",uJob);
				mysql_query(&mysqlext,query);
				if(mysql_errno(&mysqlext))
					fprintf(stderr,"%s\n",mysql_error(&mysqlext));
					}
				}
				mysql_free_result(res);
			}
		}//Pop.Cancel

		//Implict AWAITING MOD status
		else if(!strcmp("mysqlSendmail.Pop.Mod",field[0]))
		{
			unsigned uPrevMailFilter=0;
			uMailFilter=0;

			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.uParamMailFilter)
			{
				uMailFilter=uGetMailFilterId(structExtParam.cMailFilter);
				uPrevMailFilter=uGetMailFilterFromUser(structExtParam.cLogin);
			}

			if(structExtParam.uClearText)
			{
				strcpy(structExtParam.cEnterPasswd,
					structExtParam.cPasswd);
				EncryptPasswd(structExtParam.cPasswd);
			}

sprintf(query,"UPDATE tUser SET cPasswd='%s',cEnterPasswd='%s',uModBy=1,uModDate=%lu,uStatus=4,uOwner=%u,uMailFilter=%u WHERE cLogin='%s' AND uServer=1",
					structExtParam.cPasswd,
					structExtParam.cEnterPasswd,
					(long unsigned)clock,
					structExtParam.uISPClient,
					uMailFilter,
					structExtParam.cLogin);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Update failed",cServer,uJob,
						mysqlISP_Waiting);
			}
			else
			{
        			if(mysql_affected_rows(&mysql))
				{
sprintf(query,"SELECT uUser FROM tUser WHERE cLogin='%s' AND uServer=1",
							structExtParam.cLogin);
					mysql_query(&mysql,query);
					if(mysql_errno(&mysql))
        				{
						fprintf(stderr,"%s\n",
							mysql_error(&mysql));
						InformExtJob("Select query failed",
							cServer,uJob,mysqlISP_Waiting);
					}
					else
					{
						MYSQL_RES *res;
						MYSQL_ROW field;

        					res=mysql_store_result(&mysql);
        					if((field=mysql_fetch_row(res)))
							sscanf(field[0],"%u",&uUser);
						mysql_free_result(res);
					}
					SubmitExtSingleJob("ExtModUserPwd",
						structExtParam.cLogin,
							cServer,(long unsigned)clock,uJob,uUser);
					InformExtJob("mysqlSendmail.ExtModUserPwd",
						cServer,uJob,mysqlISP_RemotelyQueued);

					//This is wierd but should handle changing
					//mail filters also
					if(uPrevMailFilter!=uMailFilter)
					{
						SubmitExtSingleJob("ModUserMailFilterOff",
								structExtParam.cLogin,
								cServer,(long unsigned)clock+300,uJob,uUser);
						if(uMailFilter)
						{
							SubmitExtSingleJob("ModUserMailFilterOn",
								structExtParam.cLogin,
								cServer,(long unsigned)clock+600,uJob,uUser);
						}
					}

					CreateNewClient(&structExtParam);
				}
				else
				{
					InformExtJob("No such user/server",
						cServer,uJob,mysqlISP_Waiting);
				}
			}
		}//Pop.Mod

		else if(!strcmp("mysqlSendmail.Pop.Hold",field[0]))
		{	
			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.uClearText)
			{
				strcpy(structExtParam.cEnterPasswd,
					structExtParam.cPasswd);
				EncryptPasswd(structExtParam.cPasswd);
			}

			//TODO locked shadow entry via tConfiguation value
			//This is very BOGUS!
sprintf(query,"UPDATE tUser SET uStatus=2,uModBy=1,uModDate=%lu,cPasswd='jsyel6yyehWex',cEnterPasswd='l6yyxxehWe',uOwner=%u WHERE cLogin='%s' AND uServer=1",(long unsigned)clock,
				structExtParam.uISPClient,structExtParam.cLogin);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Update failed",cServer,uJob,
						mysqlISP_Waiting);
			}
			else
			{
        			if(mysql_affected_rows(&mysql))
				{
sprintf(query,"SELECT uUser FROM tUser WHERE cLogin='%s' AND uServer=1",
							structExtParam.cLogin);
					mysql_query(&mysql,query);
					if(mysql_errno(&mysql))
        				{
						fprintf(stderr,"%s\n",
							mysql_error(&mysql));
						InformExtJob("Select query failed",
							cServer,uJob,mysqlISP_Waiting);
						continue;
					}
					else
					{
						MYSQL_RES *res;
						MYSQL_ROW field;

        					res=mysql_store_result(&mysql);
        					if((field=mysql_fetch_row(res)))
							sscanf(field[0],"%u",&uUser);
						mysql_free_result(res);
					}
					SubmitExtSingleJob("ExtModUserPwd",
						structExtParam.cLogin,
							cServer,(long unsigned)clock,uJob,uUser);
				      InformExtJob("mysqlSendmail.ExtModUserPwd Hold",
						cServer,uJob,mysqlISP_RemotelyQueued);

				      CreateNewClient(&structExtParam);
				}
				else
				{
					InformExtJob("No such user/server",
						cServer,uJob,mysqlISP_Waiting);
				}
			}
		}//Pop.Hold
		
		//
		//mysqlSendmail.Website job set New/Mod/Hold/Cancel
		//mysqlSendmail.Domain job set New/Mod/Hold/Cancel with minor mods
		//This is also for mysqlSendmail.VUT TODO
		//
		else if(!strcmp("mysqlSendmail.Website.New",field[0]) ||
				!strcmp("mysqlSendmail.Domain.New",field[0]))
		{	
			MYSQL_RES *res2;
			MYSQL_ROW field2;

			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.cVUTDomain[0] && !structExtParam.cDomain[0])
				strcpy(structExtParam.cDomain,
					structExtParam.cVUTDomain);
			if(!structExtParam.cDomain[0])
        		{
				fprintf(stderr,"c[VUT]Domain empty\n");
				InformExtJob("c[VUT]Domain empty",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}

			CreateNewClient(&structExtParam);
			
			//Setup tDomain, tLocal, tAccess

			//Setup tDomain
sprintf(query,"SELECT cDomain FROM tDomain WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.New select1 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
sprintf(query,"INSERT INTO tDomain SET cDomain='%s',uOwner=%u,uCreatedBy=1,uCreatedDate=%lu",
			structExtParam.cDomain,structExtParam.uISPClient,(long unsigned)clock);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New insert1 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
			}
			else
			{
sprintf(query,"UPDATE tDomain SET uModBy=1,uModDate=%lu,uOwner=%u WHERE cDomain='%s'"
				,(long unsigned)clock,structExtParam.uISPClient,structExtParam.cDomain);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New update1 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
			}
			mysql_free_result(res2);

			//Setup tLocal
sprintf(query,"SELECT cDomain FROM tLocal WHERE cDomain='%s' AND uServer=1",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.New select2 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
sprintf(query,"INSERT INTO tLocal SET cDomain='%s',uOwner=%u,uServer=1,uCreatedBy=1,uCreatedDate=%lu",structExtParam.cDomain,structExtParam.uISPClient,(long unsigned)clock);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New insert2 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtNewLocal",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);
			}
			else
			{
sprintf(query,"UPDATE tLocal SET uModBy=1,uModDate=%lu,uOwner=%u WHERE cDomain='%s'"
				,(long unsigned)clock,structExtParam.uISPClient,structExtParam.cDomain);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New update2 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtModLocal",
						structExtParam.cDomain,
							cServer,(long unsigned)clock,uJob,0);
			}
			mysql_free_result(res2);

			//Setup tAccess
sprintf(query,"SELECT cDomainIP FROM tAccess WHERE cDomainIP='%s' AND uServer=1",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.New select3 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
sprintf(query,"INSERT INTO tAccess SET cDomainIP='%s',uOwner=%u,uServer=1,uCreatedBy=1,uCreatedDate=%lu,cRelayAttr='RELAY'",structExtParam.cDomain,structExtParam.uISPClient,(long unsigned)clock);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New insert3 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtNewAccess",
					structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);
			}
			else
			{
sprintf(query,"UPDATE tAccess SET uModBy=1,uModDate=%lu,cRelayAttr='RELAY',uOwner=%u WHERE cDomainIP='%s' AND uServer=1",(long unsigned)clock,structExtParam.uISPClient,structExtParam.cDomain);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New update3 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtModAccess",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);
			}
			mysql_free_result(res2);
	
			//Don't need Website/Domain.New diff if tProduct tService setup
			//correctly	
			if(structExtParam.cVUTDomain[0])
        		{
				unsigned uVUT=0;

				//Setup tVUT
sprintf(query,"SELECT uVUT FROM tVUT WHERE cDomain='%s' AND uServer=1",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.New select4 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
sprintf(query,"INSERT INTO tVUT SET cDomain='%s',uOwner=%u,uServer=1,uCreatedBy=1,uCreatedDate=%lu",structExtParam.cDomain,structExtParam.uISPClient,(long unsigned)clock);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New insert4 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				uVUT=mysql_insert_id(&mysql);
			}
			else
			{
				sscanf(field[0],"%u",&uVUT);

sprintf(query,"UPDATE tVUT SET uModBy=1,uModDate=%lu,uOwner=%u WHERE cDomain='%s' AND uServer=1",(long unsigned)clock,structExtParam.uISPClient,structExtParam.cDomain);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New update4 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
			}
			mysql_free_result(res2);

			if( structExtParam.cVUTEntry[0]&&
					structExtParam.cVUTTarget[0])
			{
				//Setup tVUTEntries
sprintf(query,"SELECT tVUTEntries.uVUTEntries FROM tVUTEntries,tVUT WHERE tVUT.uVUT=tVUTEntries.uVUT AND tVUT.cDomain='%s' AND tVUT.uServer=1 AND tVUTEntries.cVirtualEmail='%s' AND tVUTEntries.cTargetEmail='%s'",structExtParam.cDomain,structExtParam.cVUTEntry,structExtParam.cVUTTarget);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.New select5 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{

sprintf(query,"INSERT INTO tVUTEntries SET cVirtualEmail='%s',cTargetEmail='%s',uOwner=%u,uCreatedBy=1,uCreatedDate=%lu,uVUT=%u",
				structExtParam.cVUTEntry,
				structExtParam.cVUTTarget,
				structExtParam.uISPClient,
				(long unsigned)clock,uVUT);

				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New insert5 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtNewVUTEntry",
					structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);
			}
			else
			{
				unsigned uVUTEntries=0;

				sscanf(field[0],"%u",&uVUTEntries);
sprintf(query,"UPDATE tVUTEntries SET uModBy=1,uModDate=%lu,uOwner=%u WHERE AND uVUTEntries=%u",(long unsigned)clock,structExtParam.uISPClient,uVUTEntries);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
        			{
					fprintf(stderr,"%s\n",
							mysql_error(&mysql));
					InformExtJob("Website/Domain.New update5 failed",
						cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				SubmitExtSingleJob("ExtModVUTEntry",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);
			}
			}//If tVUTEntries
			mysql_free_result(res2);

			}//if cVUTDomain
			
			//Finally
			InformExtJob(field[0],cServer,uJob,mysqlISP_RemotelyQueued);

		}//Website/Domain.New

		else if(!strcmp("mysqlSendmail.Website.Mod",field[0]) ||
				!strcmp("mysqlSendmail.Domain.Mod",field[0]))
		{	
			MYSQL_RES *res2;
			MYSQL_ROW field2;

			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.cVUTDomain[0])
				strcpy(structExtParam.cDomain,
					structExtParam.cVUTDomain);
			if(!structExtParam.cDomain[0])
        		{
				fprintf(stderr,"c[VUT]Domain empty\n");
				InformExtJob("c[VUT]Domain empty",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			
sprintf(query,"SELECT cDomain FROM tDomain WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Mod select1 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
				InformExtJob("Website/Domain.Mod no such domain",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			else
			{
				InformExtJob("Website/Domain.Mod NOP",
						cServer,uJob,mysqlISP_Deployed);
			}
			mysql_free_result(res2);

		}//Website/Domain.Mod

		else if(!strcmp("mysqlSendmail.Website.Hold",field[0]) ||
				!strcmp("mysqlSendmail.Domain.Hold",field[0]))
		{	
			MYSQL_RES *res2;
			MYSQL_ROW field2;

			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.cVUTDomain[0])
				strcpy(structExtParam.cDomain,
					structExtParam.cVUTDomain);
			if(!structExtParam.cDomain[0])
        		{
				fprintf(stderr,"c[VUT]Domain empty\n");
				InformExtJob("c[VUT]Domain empty",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			
sprintf(query,"SELECT cDomain FROM tDomain WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Hold select1 failed",cServer,uJob,
						mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if(!(field2=mysql_fetch_row(res2)))
			{
				InformExtJob("Website/Domain.Hold no such domain",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			else
			{
				InformExtJob("Website/Domain.Hold NOP",
						cServer,uJob,mysqlISP_Deployed);
			}
			mysql_free_result(res2);

		}//Website/Domain.Hold


		else if(!strcmp("mysqlSendmail.Website.Cancel",field[0])||
				!strcmp("mysqlSendmail.Domain.Cancel",field[0]))
		{	
			MYSQL_RES *res2;
			MYSQL_ROW field2;

			printf("\n%s(%s):\n",field[0],field[2]);
			ParseExtParams(&structExtParam,field[1]);

			if(structExtParam.cVUTDomain[0])
				strcpy(structExtParam.cDomain,
					structExtParam.cVUTDomain);
			if(!structExtParam.cDomain[0])
        		{
				fprintf(stderr,"c[VUT]Domain empty\n");
				InformExtJob("c[VUT]Domain empty",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			
sprintf(query,"DELETE FROM tDomain WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Cancel delete1 failed",
							cServer,uJob,mysqlISP_Waiting);
				continue;
			}

sprintf(query,"DELETE FROM tAccess WHERE cDomainIP='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Cancel delete2 failed",
							cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			if(mysql_affected_rows(&mysql))
			SubmitExtSingleJob("ExtDelAccess",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);

sprintf(query,"DELETE FROM tLocal WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Cancel delete3 failed",
							cServer,uJob,mysqlISP_Waiting);
				continue;
			}
			if(mysql_affected_rows(&mysql))
			SubmitExtSingleJob("ExtDelLocal",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);

			if(structExtParam.cVUTDomain[0])
			{

sprintf(query,"SELECT uVUT FROM tVUT WHERE cDomain='%s'",structExtParam.cDomain);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
        		{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				InformExtJob("Website/Domain.Cancel select1 failed",
						cServer,uJob,mysqlISP_Waiting);
				continue;
			}
        		res2=mysql_store_result(&mysql);
        		if((field2=mysql_fetch_row(res2)))
			{
				unsigned uVUT=0;

				sscanf(field2[0],"%u",&uVUT);
sprintf(query,"DELETE FROM tVUTEntries WHERE uVUT=%u",uVUT);
				mysql_query(&mysql,query);
				if(mysql_errno(&mysql))
				{
					fprintf(stderr,"%s\n",mysql_error(&mysql));
					InformExtJob("Website/Domain.Cancel delete4 failed",
							cServer,uJob,mysqlISP_Waiting);
					continue;
				}
				if(mysql_affected_rows(&mysql))
				SubmitExtSingleJob("ExtDeleteVUTEntry",
						structExtParam.cDomain,cServer,
								(long unsigned)clock,uJob,0);

			}
			}//if cVUTDomain[0]

			mysql_free_result(res2);
			InformExtJob(field[0],cServer,uJob,mysqlISP_RemotelyQueued);

		}//Website/Domain.Cancel

		else if(1)
		{
			fprintf(stderr,"Unknown mysqlSendmail job:%s\n",field[0]);
		}
	}
	mysql_free_result(res);

}//void ProcessExtJobQueue(char *cServer)
		

//Uses MYSQL mysqlext
void ExtConnectDb(unsigned uHtml)
{
	char cDbIp[256]={""};
	char *cEffectiveDbIp=NULL;
	char cDbName[256]={"mysqlisp"};
	char cDbPwd[256]={"wsxedc"};
	char cDbLogin[256]={"mysqlisp"};
	char cDbPort[256]={"0"};
	unsigned ucDbPort=0;
	char cDbSocket[256]={""};
	char *cUseDbSocket=NULL;

	GetConfiguration("cExtJobQueueDbIp",cDbIp,0);
	GetConfiguration("cExtJobQueueDbName",cDbName,0);
	GetConfiguration("cExtJobQueueDbPwd",cDbPwd,0);
	GetConfiguration("cExtJobQueueDbLogin",cDbLogin,0);
	GetConfiguration("cExtJobQueueDbPort",cDbPort,0);
	GetConfiguration("cExtJobQueueDbSocket",cDbSocket,0);

	if(cDbPort[0])
		sscanf(cDbPort,"%u",&ucDbPort);

	if(!cDbSocket[0])
		cUseDbSocket=NULL;
	else
		cUseDbSocket=cDbSocket;

	//Debug only	
	//printf("%s %s %s %s\n",cDbIp,cDbName,cDbPwd,cDbLogin);

	if(cDbIp[0]) cEffectiveDbIp=cDbIp;

        mysql_init(&mysqlext);
        if (!mysql_real_connect(&mysqlext,cEffectiveDbIp,
				cDbLogin,cDbPwd,cDbName,ucDbPort,cUseDbSocket,0))
        {
		sprintf(query,"ExtConnectDb failed for %s.%s",cDbIp,cDbName);
		if(uHtml)
                	mysqlSendmail(query);
		else
			fprintf(stderr,"%s\n",query);
		exit(1);
        }

}//end of ExtConnectDb()


int InformExtJob(char *cRemoteMsg,char *cServer,unsigned uJob,unsigned uJobStatus)
{
	MYSQL_RES *res;
	MYSQL_ROW field;
	unsigned uJobGroup=0;
	unsigned uInstance=0;
	time_t clock;

	time(&clock);

	sprintf(query,"UPDATE tJob SET cServer='%s',cRemoteMsg='%.32s',uModBy=1,uModDate=%lu,uJobStatus=%u WHERE uJob=%u",cServer,cRemoteMsg,(long unsigned)clock,uJobStatus,uJob);

	mysql_query(&mysqlext,query);
	if(mysql_errno(&mysqlext))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysqlext));
		SubmitISPJob("mysqlSendmail.InformExtJob.Failed",
				mysql_error(&mysqlext),cServer,(long unsigned)clock);
		return(1);
	}

	//Do not continue unless closing ext job	
	if(uJobStatus!=mysqlISP_Deployed)
		return(0);
	
	//See if all services of client product instance (now grouped via unique 
	//uJobGroup by mysqlISP SubmitJob routines) are now deployed. If they are 
	//then change mysqlISP.tInstance.uStatus to mysqlISP_Deployed, 
	//				mysqlISP_Canceled or mysqlISP_OnHold
	//
	sprintf(query,"SELECT uJobGroup,uInstance FROM tJob WHERE uJob=%u",uJob);
	mysql_query(&mysqlext,query);
	if(mysql_errno(&mysqlext))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysqlext));
		return(1);
	}
	res=mysql_store_result(&mysqlext);
        if((field=mysql_fetch_row(res)))
	{
		sscanf(field[0],"%u",&uJobGroup);
		sscanf(field[1],"%u",&uInstance);
	}
	mysql_free_result(res);

	if(!uJobGroup || !uInstance)
	{
		fprintf(stderr,"Unexpected missing uJobGroup/uInstance for uJob=%u\n"
						,uJob);
		return(0);
	}

	sprintf(query,"select uJobStatus=%u,(max(uJobStatus)=min(uJobStatus)),cJobName FROM tJob WHERE uJobGroup=%u GROUP BY uJobGroup",mysqlISP_Deployed,uJobGroup);

	mysql_query(&mysqlext,query);
	if(mysql_errno(&mysqlext))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysqlext));
	}
	else
	{
		res=mysql_store_result(&mysqlext);
        	if((field=mysql_fetch_row(res)))
		{
			unsigned uInstanceStatus=mysqlISP_Deployed;

			if(field[0][0]=='1' && field[1][0]=='1')
			{
				if(strstr(field[2],".Cancel"))
					uInstanceStatus=mysqlISP_Canceled;
				else if(strstr(field[2],".Hold"))
					uInstanceStatus=mysqlISP_OnHold;

				sprintf(query,"UPDATE tInstance SET uStatus=%u,uModBy=1,uModDate=%lu WHERE uInstance=%u",uInstanceStatus,(long unsigned)clock,uInstance);
				mysql_query(&mysqlext,query);
				if(mysql_errno(&mysqlext))
					fprintf(stderr,"%s\n",mysql_error(&mysqlext));
			}
		}
		mysql_free_result(res);
	}

	return(0);

}//int InformExtJob()

//Uses mysqlext
int SubmitISPJob(const char *cJobName,const char *cJobData,const char *cServer,unsigned uJobDate)
{
	time_t clock;

	time(&clock);
	
	sprintf(query,"INSERT INTO tJob SET  cServer='%s', cJobName='%s', cJobData='%.1024s', uJobDate=%u, uOwner=1, uCreatedBy=1, uCreatedDate=%lu, uJobStatus=%u, cLabel='mysqlSendmail.SubmitISPJob'",
			cServer
			,cJobName
			,TextAreaSave(cJobData)
			,uJobDate
			,(long unsigned)clock
			,mysqlISP_Waiting);
	mysql_query(&mysqlext,query);
	if(mysql_errno(&mysqlext))
	{
                fprintf(stderr,"%s\n",mysql_error(&mysql));
		return(1);
	}

	return(0);

}//int SubmitISPJob()


int SubmitExtSingleJob(char *cJobName,char *cJobData,char *cServer,unsigned uJobDate,
		unsigned uJob, unsigned uUser)
{
	MYSQL_RES *res;

	sprintf(query,"SELECT uJob FROM tJob WHERE cJobName='%s' AND cJobData='%.1024s\nmysqlISP.tJob.uJob=%u' AND cServer='%s' AND uUser=%u",cJobName,TextAreaSave(cJobData),uJob,cServer,uUser);
	mysql_query(&mysql,query);
	if(mysql_errno(&mysql))
	{	
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		return(1);
	}
	res=mysql_store_result(&mysql);
	
	if(mysql_num_rows(res)==0)
	{
		time_t clock;

		time(&clock);

		//waiting uStatus=1
		sprintf(query,"INSERT INTO tJob SET  cServer='%s', cJobName='%s', cJobData='%.1024s\nmysqlISP.tJob.uJob=%u', uJobDate=%u, uOwner=1, uCreatedBy=1, uCreatedDate=%lu, uUser=%u, uJobStatus=1",
			cServer
			,cJobName
			,TextAreaSave(cJobData)
			,uJob
			,uJobDate
			,(long unsigned)clock,
			uUser);
		mysql_query(&mysql,query);
		if(mysql_errno(&mysql))
			fprintf(stderr,"%s\n",mysql_error(&mysql));
	}
	mysql_free_result(res);

	return(0);

}//int SubmitExtSingleJob()


int MakeHomeDirJob(unsigned uJob,unsigned uUser)
{
	unsigned uErrNum=0;
	char cErrorNum[8];

	printf("MakeHomeDirJob()\n");
	UpdateJobStatus(uJob,JOBSTATUS_RUNNING,"");
	if((uErrNum=MakeHomeDir(uUser)))
	{
		sprintf(cErrorNum,"%u",uErrNum);
		UpdateJobStatus(uJob,JOBSTATUS_ERROR,cErrorNum);
	}
	else
	{
		UpdateUserStatus(uUser,US_ACTIVE);
		UpdateJobStatus(uJob,JOBSTATUS_OK,"");
	}
	
	return(uErrNum);

}//int MakeHomeDirJob(unsigned uJob,unsigned uUser)


int MakeHomeDir(unsigned uUser)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
	char query[256];
	char cLogin[100]={""};
	char cUserModForHomeDir[256]={"/usr/sbin/usermod -d /home/%.32s %.32s"};
	unsigned uRetVal=0;

	sprintf(query,"SELECT cLogin FROM tUser WHERE uUser=%u",uUser);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
                fprintf(stderr,"%s\n",mysql_error(&mysql));
               	return(1);
        }

        res=mysql_store_result(&mysql);
        if(!mysql_num_rows(res))
        {
        	mysql_free_result(res);
                fprintf(stderr,"User not found in tUser\n");
               	return(2);
        }
        if((field=mysql_fetch_row(res)))
		sprintf(cLogin,"%.99s",field[0]);
        mysql_free_result(res);

	GetConfiguration("cUserModForHomeDir",cUserModForHomeDir,0);

	sprintf(query,cUserModForHomeDir,
				cLogin,cLogin);
	uRetVal+=system(query);

	sprintf(query,"/bin/mkdir ~%.32s",cLogin);
	uRetVal+=system(query);

	sprintf(query,"/bin/chown %.32s  ~%.32s",cLogin,cLogin);
	uRetVal+=system(query);

	sprintf(query,"/bin/chmod 700 ~%.32s",cLogin);
	uRetVal+=system(query);


	return(uRetVal);

}//int MakeHomeDir(unsigned uUser)


void InitializeParams(structExtJobParameters *structExtParam)
{
	static unsigned uOnlyOnce=1;

	if(uOnlyOnce)
	{
		//Load presets example
		//GetConfiguration("cHostmaster",cHostmaster,0);
		uOnlyOnce=0;
	}

	structExtParam->cPasswd[0]=0;
	structExtParam->cLogin[0]=0;
	structExtParam->cEnterPasswd[0]=0;
	structExtParam->cDomain[0]=0;
	structExtParam->cVUTDomain[0]=0;
	structExtParam->cVUTEntry[0]=0;
	structExtParam->cVUTTarget[0]=0;

	structExtParam->uClearText=0;

	structExtParam->uParamPasswd=0;
	structExtParam->uParamLogin=0;
	structExtParam->uParamDomain=0;
	structExtParam->uParamVUTDomain=0;
	structExtParam->uParamVUTEntry=0;
	structExtParam->uParamVUTTarget=0;
	structExtParam->uFakeISPJob=0;

	structExtParam->uISPClient=1;//Root
	structExtParam->cClientName[0]=0;
	structExtParam->uParamClientName=0;

	structExtParam->cMailFilter[0]=0;
	structExtParam->uParamMailFilter=0;

}//void InitializeParams(structExtJobParameters *structExtParam)


void CleanDoneOkJobQueue(void)
{
	time_t luClock;
	register unsigned uRows;


	time(&luClock);

	luClock-=(3600*24*15);//fifteen days ago

	ConnectDb();

	sprintf(query,"DELETE FROM tJob WHERE uJobStatus=3 AND uModDate<%lu AND uModDate>0",
			(unsigned long)luClock);
        mysql_query(&mysql,query);
	printf("CleanDoneOkJobQueue(): %s\n",query);
        if(mysql_errno(&mysql))
        {
		fprintf(stderr,"%s\n",mysql_error(&mysql));
			return;
        }

	if((uRows=mysql_affected_rows(&mysql)))
	{
		printf("%u tJob 'Done Ok' jobs removed. Over fifteen days old.\n",uRows);
	}
	printf("Done\n");

}//void CleanDoneOkJobQueue(void)


//Removes both uSource=2 modified qualcomm qpopper and our mysqlRadacct Cistron 1.6.6 radius
//This last is uSource=3
//NukeSpam is 4,5,6. Only 4 used for now 4=signature 1: misses.
//tDUL uSource=7
//So we use the eExpireDate diff from 0 as the rule for flexibility.
void CleanLocalAccess(void)
{
	time_t luClock;
	char cESMTPServer[256]={""};


	time(&luClock);

	ConnectDb();

	sprintf(query,"DELETE FROM tAccess WHERE uServer=1 AND uExpireDate!=0 AND uExpireDate<=%lu",
			(unsigned long)luClock);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
        {
		fprintf(stderr,"%s\n",mysql_error(&mysql));
			return;
        }
	if(mysql_affected_rows(&mysql))
	{
		ScheduleJob(1,1,"DelAccess","CleanLocalAccess()");
		GetConfiguration("cESMTPServer",cESMTPServer,0);
		if(cESMTPServer[0])
			ScheduleJob(1,GetuServer(cESMTPServer),"DelAccess","CleanLocalAccessESMTP()");
	}

}//void CleanLocalAccess(void)


//uMode==1 fix stuff else just report what would be done and
//any conflicts if possible. Active status only
//uMode==2 any status
//uMode==3 any status but active
//uMode==13 fix any status but active
void FixLocalUIDs(unsigned uMode)
{
	//first report stuff for develop model
	//change uid report conflicts
	//check/change gid
	//change mail file ownership
	//
	MYSQL_RES *res;
	MYSQL_ROW field;
	struct passwd *structPassInfo;
	//nobody group usually 99 RH 7.3?
	unsigned uGID=99;//nobody standard default?

	//mail group usually 12 RH 7.3/Mandrake etc...?
	unsigned uMailGID=12;//nobody standard default?

	char cFixAnswer[2]={""};
	char cUserModForUIDError[256]={"/usr/sbin/usermod -u %u %s"};
	char cUserModForGIDError[256]={"/usr/sbin/usermod -g %u %s"};


	ConnectDb();
	printf("\nFixLocalUIDs(");
	GetConfiguration("cUserModForUIDError",cUserModForUIDError,0);

	switch(uMode)
	{
		case 0:
			printf("Report-Active");
		break;
		case 1:
			printf("Repair-Active");
		break;
		case 2:
			printf("Report-All");
		break;
		case 3:
			printf("Report-Non-Active");
		break;
		case 13:
			printf("Repair-Non-Active");
		break;

		default:
			printf("Unknown uMode");

	}
	printf(") (uMode=%u)\n\n",uMode);

	query[0]=0;
	GetConfiguration("cGID",query,0);
	if(query[0])
		sscanf(query,"%u",&uGID);

	query[0]=0;
	GetConfiguration("cMailGID",query,0);
	if(query[0])
		sscanf(query,"%u",&uMailGID);

	if(uMode==1 || uMode==0)
		sprintf(query,"SELECT tUser.uUser,tUser.cLogin,tStatus.cLabel FROM tUser,tStatus WHERE tUser.uStatus=1 AND tUser.uStatus=tStatus.uStatus ORDER BY cLogin");
	else if(uMode==2)
		sprintf(query,"SELECT tUser.uUser,tUser.cLogin,tStatus.cLabel FROM tUser,tStatus WHERE tUser.uStatus=tStatus.uStatus ORDER BY cLogin");
	else if(uMode==3 || uMode==13)
		sprintf(query,"SELECT tUser.uUser,tUser.cLogin,tStatus.cLabel FROM tUser,tStatus WHERE tUser.uStatus!=1 AND tUser.uStatus=tStatus.uStatus ORDER BY cLogin");

	mysql_query(&mysql,query);
	if(mysql_errno(&mysql))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		exit(1);
	}
	res=mysql_store_result(&mysql);
        while((field=mysql_fetch_row(res)))
        {
		if((structPassInfo=getpwnam(field[1])))
		{
			unsigned uUID=0;
			unsigned uError=0;
			unsigned uUIDError=0;
			unsigned uGIDError=0;
			unsigned uFixMailBox=0;
			unsigned uFixHomeDir=0;
			unsigned uDoIt=1;//debug only

			sscanf(field[0],"%u",&uUID);

			printf("uUser=%u cLogin=%s uStatus=%s\n",uUID,field[1],field[2]);
			printf("uid=%u, gid=%u\n",
					structPassInfo->pw_uid,
					structPassInfo->pw_gid);
			printf("shell=%s, dir=%s, name=%s\n",
					structPassInfo->pw_shell,
					structPassInfo->pw_dir,
					structPassInfo->pw_gecos);

			uError=0;
			uUIDError=0;
			uGIDError=0;
			uFixMailBox=0;
			uFixHomeDir=0;
			//Error one
                	if(structPassInfo->pw_uid<=SU_BASE_ID)
			{
				printf("uid<=%u\n",SU_BASE_ID);
				uError++;
				uUIDError++;
			}

			//Error two
                	if(structPassInfo->pw_uid!=(SU_BASE_ID+uUID))
			{
				printf("uid!=%u\n",SU_BASE_ID+uUID);
				uError++;
				uUIDError++;
			}

			//Error three check into this more. Custom servicoopsa popusers group 231
                	if(structPassInfo->pw_gid!=uGID)
			{
				printf("gid!=uGID\n");
				uError++;
				uGIDError++;
			}

			//Is this an error should we make empty mail file?
			sprintf(query,"ls -l /var/mail/%s",field[1]);
			if(system(query))
			{
				printf("!No /var/mail/%s file\n",field[1]);
			}
			else
			{
				printf("/var/mail/%s file\n",field[1]);
				if(uError) uFixMailBox=1;
			}

			sprintf(query,"ls -l ~%s",field[1]);
			if(system(query))
			{
				printf("No ~%s file\n",field[1]);
			}
			else
			{
				printf("~%s file\n",field[1]);
				if(uError) uFixHomeDir=1;
			}

			if( (uMode==1 || uMode==13) && uError)
			{


				if(cFixAnswer[0]!='a')
				{
					printf("Intent repair if needed? [y/n/a/q]: ");
					cFixAnswer[0]=(char) getchar();

					if(cFixAnswer[0]=='n')
					{
						printf("\n");
						continue;
					}
					else if(cFixAnswer[0]=='q')
						exit(1);
					else if(cFixAnswer[0]=='y')
						;
					else if(1)
					{
						printf("\n");
						continue;
					}
				}

				if(uUIDError)
				{
					sprintf(query,cUserModForUIDError,
                						(SU_BASE_ID+uUID),
								field[1]);
					printf("fixing uid: %s\n",query);
					if(uDoIt) system(query);
				}

				if(uGIDError)
				{
					sprintf(query,cUserModForGIDError,
                						(uGID),field[1]);
					printf("fixing gid: %s\n",query);
					if(uDoIt) system(query);
				}


				if(uFixMailBox)
				{
					sprintf(query,"/bin/chown %u /var/mail/%s",
                						(SU_BASE_ID+uUID),
								field[1]);
					printf("fixing mailbox owner: %s\n",query);
					if(uDoIt) system(query);

					sprintf(query,"/bin/chgrp %u /var/mail/%s",
                						(uMailGID),field[1]);
					printf("fixing mailbox group: %s\n",query);
					if(uDoIt) system(query);
				}

				if(uFixHomeDir)
				{
					sprintf(query,"/bin/chown -R %u ~%s",
                						(SU_BASE_ID+uUID),
								field[1]);
					printf("fixing homedir: %s\n",query);
					if(uDoIt) system(query);

					if(uGIDError)
					{
					sprintf(query,"/bin/chgrp -R %u ~%s",
                						(SU_BASE_ID+uUID),
								field[1]);
					printf("fixing mailbox: %s\n",query);
					if(uDoIt) system(query);
					}
				}
			}
			printf("\n");
		}
		else
		{
                	printf("uUser=%s cLogin=%s Not in system!\n",field[0],field[1]);
		}

	}
	mysql_free_result(res);
	printf("\n");
	mysql_close(&mysql);
	exit(0);

}//void FixLocalUIDs(unsigned uMode)


char *LoginCleanUp(char *cInput)
{
	register int i;

	for(i=0;cInput[i];i++)
	{
	
		if(!isalnum(cInput[i]) && 
			cInput[i]!='_' &&
			cInput[i]!='-' &&
			cInput[i]!='.' ) break;
		if(isupper(cInput[i])) cInput[i]=tolower(cInput[i]);
	}
	cInput[i]=0;

	return(cInput);

}//char *LoginCleanUp(char *cInput)


void TextConnectDb(void)
{
        mysql_init(&mysql);
	if(!mysql_real_connect(&mysql,DBIP,DBLOGIN,DBPASSWD,DBNAME,DBPORT,DBSOCKET,0))
	{
		if(guDebug)
                	printf("DBLOGIN,DBPASSWD,DBNAME %s,%.3s...,%s\n",
					DBLOGIN,DBPASSWD,DBNAME);
                fprintf(stderr,"Database server unavailable.\n");
		exit(1);
	}

}//end of TextConnectDb()


void CreateNewClient(structExtJobParameters *structExtParam)
{
	char query[256];
	time_t luClock;
	MYSQL_RES *res;

	//Create new tClient if needed
	time(&luClock);
	if(structExtParam->uISPClient>1)
	{
		sprintf(query,"SELECT uClient FROM tClient WHERE uClient=%u",
			structExtParam->uISPClient);
		mysql_query(&mysql,query);
		if(mysql_errno(&mysql))
		{
			fprintf(stderr,"%s\n",mysql_error(&mysql));
			return;
		}

		res=mysql_store_result(&mysql);
		if(!mysql_num_rows(res)) 
		{
			sprintf(query,"INSERT INTO tClient SET cLabel='%s',cCompanyName='(uISPClient)',uClient=%u,uOwner=1,uCreatedBy=1,uCreatedDate=%lu",structExtParam->cClientName,structExtParam->uISPClient,(unsigned long)luClock);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
			{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				mysql_free_result(res);
				return;
			}
		}
		else
		{
			sprintf(query,"UPDATE tClient SET cLabel='%s',cCompanyName='(uISPClient)',uModBy=1,uModDate=%lu WHERE uClient=%u",structExtParam->cClientName,(long unsigned)luClock,structExtParam->uISPClient);
			mysql_query(&mysql,query);
			if(mysql_errno(&mysql))
			{
				fprintf(stderr,"%s\n",mysql_error(&mysql));
				mysql_free_result(res);
				return;
			}
		}
		mysql_free_result(res);
	}

}//void CreateNewClient(structExtJobParameters *structExtParam)


//TODO check this all out.		
void DeleteClient(structExtJobParameters *structExtParam)
{
	if(structExtParam->uISPClient<2) return;

	sprintf(query,"DELETE FROM tClient WHERE uClient=%u AND cCompanyName='(uISPClient)'",structExtParam->uISPClient);
	mysql_query(&mysql,query);
	if(mysql_errno(&mysql))
		fprintf(stderr,"%s\n",mysql_error(&mysql));

}//void DeleteClient(structExtJobParameters *structExtParam)


unsigned uGetMailFilterId(const char *cMailFilter)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
        char query[256];
	unsigned uRetVal=0;

        sprintf(query,"SELECT uMailFilter FROM tMailFilter WHERE cLabel='%s'",cMailFilter);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		exit(1);
	}
        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
		sscanf(field[0],"%u",&uRetVal);
        mysql_free_result(res);

	return(uRetVal);

}//unsigned uGetMailFilterId(const char *cMailFilter)


unsigned uGetMailFilterFromUser(const char *cLogin)
{
        MYSQL_RES *res;
        MYSQL_ROW field;
        char query[256];
	unsigned uRetVal=0;

        sprintf(query,"SELECT uMailFilter FROM tUser WHERE cLogin='%s' AND uServer=1",cLogin);
        mysql_query(&mysql,query);
        if(mysql_errno(&mysql))
	{
		fprintf(stderr,"%s\n",mysql_error(&mysql));
		exit(1);
	}
        res=mysql_store_result(&mysql);
        if((field=mysql_fetch_row(res)))
		sscanf(field[0],"%u",&uRetVal);
        mysql_free_result(res);

	return(uRetVal);

}//unsigned uGetMailFilterFromUser(const char *cLogin)
//sedall patch1
//sedall patch2
