/*-
 * Copyright (c) 1998-2001 Joao Cabral (jcnc@dhis.org)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *      DHIS(c)  Dynamic Host Information System Release 5.0
 */

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<ctype.h>
#include<time.h>
#include<sys/time.h>
#include<sys/types.h>
#include<netdb.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<arpa/nameser.h>
#include<resolv.h>
#include<sys/utsname.h>
#include<unistd.h>
#include<signal.h>
#include<sys/signal.h>
#include<sys/wait.h>
#include<syslog.h>
#include<varargs.h>

#define	DHISD_DB	"/etc/dhis/db/dhis.db"
#define	DHISE_LOG	"/etc/dhis/log/dhis-mx-sendmail-engine.log"
#define	SENDMAIL	"/usr/sbin/sendmail"

#define	MAX_HOSTNAME	64


typedef struct db_t {
			int id;
			unsigned char fqdn[MAX_HOSTNAME];
			struct db_t *next;
		    } db_t;

/* Function prototypes */

int db_reload(void);
int db_free(void);
int db_add(db_t *);
unsigned char *db_hostname(int);
void strtolower(unsigned char *);
void off_nl(unsigned char *);
unsigned char *line_entry(int,unsigned char *);
int msg_log(unsigned char *);
void send_mx(unsigned char *);

/* Global variables */
db_t *dbase=NULL;
int wait_for_all=1;
int wait_timer=0;

/* Log function */
int msg_log(unsigned char *msg) {

	FILE *fp;

	time_t	tt;
	unsigned char buff[256];

	fp=fopen(DHISE_LOG,"a");
	while(fp==NULL) { sleep(1); fp=fopen(DHISE_LOG,"a"); }
	tt=time(NULL);
	strcpy(buff,ctime(&tt));
	off_nl(buff);
	strcat(buff," : ");
	strcat(buff,msg);
	strcat(buff,"\n");
	fputs(buff,fp);
	fclose(fp);
	return(1);
}
	
/* Misc functions */
void strtolower(unsigned char *s) { while(*s!='\0') { *s=tolower(*s); s++; } }
void off_nl(unsigned char *s) {

	while(*s!='\0' && *s!='\n' && *s!='\r') s++;
	*s='\0';
	return;
}

unsigned char *line_entry(int idx,unsigned char *buff) {

	static unsigned char b2[1024],*pb2;
	int i;

	idx--;
	b2[0]='\0';
	pb2=b2;

	while((*buff==' ' || *buff=='\t') && *buff!='\0' && *buff!='\n')
		buff++;
	if(*buff=='\0' || *buff=='\n') return(b2);
		
	for(i=0;i<idx;i++) {
		while(*buff!=' ' && *buff!='\t' && *buff!='\0' &&
			*buff!='\n') buff++;
		if(*buff=='\0' || *buff=='\n') return(b2);
		while((*buff==' ' || *buff=='\t') && *buff!='\0' && *buff!='\n')
			buff++;
		if(*buff=='\0' || *buff=='\n') return(b2);
	}
	while(*buff!=' ' && *buff!='\t' && *buff!='\0' && *buff!='\n') {
		*pb2 = *buff;
		buff++;
		pb2++;
	}
	*pb2 = '\0';
	return(b2);
}

int db_reload(void) {

	FILE *fp;
	unsigned char str[1024];
	db_t *rec;

	if(dbase!=NULL) db_free();

	fp=fopen(DHISD_DB,"r");
	if(fp==NULL) return(0);
	while(fgets(str,1024,fp)!=NULL) {
	 off_nl(str);
	 if(!strcmp(line_entry(2,str),"{")) {

		unsigned char keyword[256];

		rec=(db_t *)malloc(sizeof(db_t));
		if(rec==NULL) { fclose(fp); return(0); }
		rec->id=atoi(line_entry(1,str));
		rec->fqdn[0]='\0';
		do {
		if(fgets(str,1024,fp)==NULL) { fclose(fp);free(rec);return(0);}
		off_nl(str);
		strcpy(keyword,line_entry(1,str));
		strtolower(keyword);

		if(!strcmp(keyword,"hostname"))
			strcpy(rec->fqdn,line_entry(2,str));

		} while(strcmp(line_entry(1,str),"}"));
		if(rec->fqdn[0]=='\0') continue;
		db_add(rec);
	 } 
	} 
	fclose(fp);
	return(1);
}

int db_free() {

	db_t *p1,*p2;

	if(dbase==NULL) return(1);
	p1=dbase;
	while(p1!=NULL) {
		p2=p1;
		p1=p1->next;
		free(p2);
	}
	dbase=NULL;
	return(1);
}

unsigned char *db_hostname(int id) {

	db_t *p1;
	static unsigned char host[MAX_HOSTNAME];

	p1=dbase;
	while(p1!=NULL) {
		if(p1->id==id) {
			strcpy(host,p1->fqdn);
			return(host);
		}
		p1=p1->next;
	}
	return(NULL);
}

int db_add(db_t *rec) {

	db_t *p;
	p=dbase;
	if(dbase==NULL) {
	dbase=rec;
	dbase->next=NULL;
	return(1);
	}

	while(p->next!=NULL) p=p->next;
	p->next=rec;
	p->next->next=NULL;
	return(1);
}


void send_mx(unsigned char *fqdn) {

	unsigned char str[1024];

	if(wait_for_all) {
		if(time(NULL) < wait_timer) return;
		wait_for_all=0;
	}

	sprintf(str,"-> relay %s",fqdn);
	msg_log(str);

	#ifdef  BSD
        sigsetmask(sigmask(SIGCHLD) | SA_NOCLDWAIT);
#else
        signal(SIGCHLD,SIG_IGN);        /* Let us not create zombies */
#endif

	sprintf(str,"-qR%s",fqdn);
	if(!fork()) {
		close(0);
		close(1);
		close(2);
		db_free();
		sleep(10); /* wait for dns update */
		execlp(SENDMAIL,"sendmail",str,NULL);
		exit(0);
	}
}

int main() {

	unsigned char line[1024];

	db_reload();
	sprintf(line,"-> DHIS Relay Engine Starting");
	msg_log(line);

	wait_timer=(int)time(NULL)+300;	/* Wait for all hosts
					   to become online so we
					   don't fork hundreds of 
					   processes */

	while(fgets(line,1024,stdin)!=NULL) {
		off_nl(line);
		if(!strcmp(line_entry(1,line),"exit")) {
			db_free();
			exit(0);
		}
		if(!strcmp(line_entry(1,line),"reload")) {
			db_reload();
			continue;
		}
		if(!strcmp(line_entry(1,line),"add")) {
			int id;
			id=atoi(line_entry(3,line));
			send_mx(db_hostname(id)); 
			continue;
		}
		if(!strcmp(line_entry(1,line),"update")) {
			int id;
			id=atoi(line_entry(3,line));
			send_mx(db_hostname(id)); 
			continue;
		}
		if(!strcmp(line_entry(1,line),"delete")) {
			continue;
		}
	}
	return(0);	/* Just to keep -Wall quiet */
}
