#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/time.h>

#include <mysql/mysql.h>

#include <dotconf.h>

#include <auth-client.h>
#include <libesmtp.h>

#include "smtp.h"
#include "global.h"
#include "config_file.h"

#define IGW_WEB_VALIDATE_URL "http://www.iglobalwall.com/?rub=validate&randomwallid="

int extern errno;
extern char **environ;
extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;

/////////////////////////
// Mysql connect stuff //
/////////////////////////
MYSQL mysql,*sock;
MYSQL_RES *res;
void do_ze_mysql_connect(){
//	char query[256]="";
	mysql_init(&mysql);
	if (!(sock = mysql_real_connect(&mysql,igw_config.mysql_hostname,
				igw_config.mysql_user,igw_config.mysql_pass,igw_config.mysql_db,0,NULL,0))){
		fprintf(stderr,"Couldn't connect to mysql engine!\n%s\n\n",mysql_error(&mysql));
		exit(2);
	}
	if(mysql_query(sock,"SELECT pop3_email FROM mailbox WHERE igw_login='zigo' AND pop3_server='gplhost.com' AND pop3_login='zigo@pplchat.com';")){
		fprintf(stderr,"Query: failed ! %s\n",mysql_error(sock));
	}
	return;
}

/////////////////////////////////////////////////////
// Fetch all the message into an array of strings. //
/////////////////////////////////////////////////////
#define MAX_MESSAGE_LINE_SIZE 4096
#define MAX_MESSAGE_LINES 64*1024	// This must be enough lines for all message with attachement less than 3Mo

char** msg;		// The message itself
int msg_line_nbr;	// The number of line
int msg_size;		// Message size
int lines_size[MAX_MESSAGE_LINES];

char** getMessage(int* msg_line_nbr){
	char** msg;
 	char inbuf[MAX_MESSAGE_LINE_SIZE];
	char* ret;
	int cur_line=0;
	int line_size;

	msg = (char**) malloc(MAX_MESSAGE_LINE_SIZE);
	msg_size = 0;
	if(msg == NULL){
		fprintf(stderr,"Couldn't not malloc line buffer!!!\n");
		exit(2);
	}
	while((ret = fgets(inbuf,MAX_MESSAGE_LINE_SIZE,stdin)) != NULL){
		printf("%d:%s\n",cur_line,inbuf);
		line_size=strlen(inbuf);
		lines_size[cur_line] = line_size;
		msg_size += line_size+1;
		msg[cur_line] = (char*) malloc(line_size + 1);
		strcpy(msg[cur_line],inbuf);
		cur_line++;
	}
	*msg_line_nbr = cur_line;
	return msg;
}

void cleanUpMsgMem(char** msg,int nbr_line){
	int i;
	for(i=0;i<nbr_line;i++){
		free(msg[0]);
	}
	free(msg);
}

void debugLog(char* logLine){
	FILE* fp;

	fp = fopen("/root/debug","a+");
	fprintf(fp,"%s",logLine);
	fclose(fp);
	return;
}
#define debug 1
#ifdef debug
#define LOG(a) {debugLog(a);printf("%s",a);}
#else
#define LOG(a) {}
#endif
// Error codes:
// 11 : address too long
// 31 : Permanentely refuse to send the message
// All other: temporary errors
// 51 : out of memeory
// 52 : timeout
// 53 : write error
// 55 : enable to read a configuration file
// 56 : problem to make a network connection from this host
// 61 : problem with the qmail home directory
// 62 : problem with the queue directory
// 63 : problem with the queue/pid directory
// 64 : problem with the queue/mess
// 65 : problem with the queue/intd
// 66 : problem with the queue/todo
// 71 : Mail  server  temporarily refuses to send the message to any recipients.
// 72 : Connection to mail server timed out.   (Not  used  by qmail-queue.)
// 73 : Connection  to  mail  server  rejected.  (Not used by qmail-queue.)
// 74 : Connection to mail server succeeded,  but  communication failed.  (Not used by qmail-queue.)
// 81 : Internal bug; e.g., segmentation fault.
// 91 : Envelope format error.

int getHeaderNumOfLine(char** msg,int nbr_line){
	int i;
	char* lineP;
	for(i=0;i<nbr_line;i++){
		lineP = msg[i];
		if(*lineP == '\n' || *lineP == '\r'){
			return i;
		}
	}
	exit(31);
	return -1;
}

void cleanUp(){
	LOG("Freeing memory..............");
	cleanUpMsgMem(msg,msg_line_nbr);
	LOG("done!\n");

	LOG("Disconnect from MySQL.......");
	mysql_close(sock);
	LOG("done!\n");
}

void reject(char* message){
	cleanUp();

	LOG("Denying message.............done!\n\n");
	fprintf(stderr,"iGlobalWall spam checker rejected message %s",message);
	exit(31);
}

void deferal(char* message){
	cleanUp();

	LOG("Deffering message...........done!\n\n");
	fprintf(stderr,"iGlobalWall spam checker defered message %s\n",message);
	exit(2);
}

void autorize(){
	cleanUp();

	LOG("Autorising message..........done!\n\n");
	fprintf(stderr,"iGlobalWall spam checker autorised message\n");
	exit(0);
}

int isStartingWith(char* toSearch,char* textContent){
	while(*toSearch && *textContent && (*toSearch == *textContent)){
		toSearch++;
		textContent++;
	}
	if(!*toSearch)
		return 1;
	else
		return 0;
}

int findHeaderLine(char** msg,int nbr_line,char* fieldName){
	int i;
	char* lineP;
	for(i=0;i<nbr_line;i++){
		lineP = msg[i];
		if(isStartingWith(fieldName,lineP)){
			return i;
		}
	}
	return -1;
}
#define DELIVER_TO "Delivered-To: "
int findUserAndDomain(int deliveredto_line_num,char** rcpt_to_domain,char** rcpt_to_user){
	char* deliveredto_domain_copy;
	char* deliveredto_domain_p;
	char* p;
	deliveredto_domain_p = strtok(msg[deliveredto_line_num],"@");
	deliveredto_domain_p = strtok(NULL,"@");

	strtok(deliveredto_domain_p,"\n");

	if(deliveredto_domain_p == NULL){
		LOG("not found!\n");
		reject("Cannot find Delivered-To domain in the delivered line.");
		return 0;
	}

	deliveredto_domain_copy = (char*) malloc(strlen(deliveredto_domain_p)+2+strlen(DELIVER_TO));
	if(deliveredto_domain_copy == NULL){
		LOG("Cannot malloc delivered domain buffer!\n");
		deferal("Cannot malloc!");
		return 0;
	}
	strcpy(deliveredto_domain_copy,DELIVER_TO);
	strcat(deliveredto_domain_copy,deliveredto_domain_p);
	strcat(deliveredto_domain_copy,"-");

	// Change all "." caracters into "-" (that is qmail stuff...)
	p = deliveredto_domain_copy + strlen(DELIVER_TO);
	while(*p){
		if(*p == '.'){
			*p = '-';
		}
		p++;
	}

	if(!isStartingWith(deliveredto_domain_copy,msg[deliveredto_line_num])){
		free(deliveredto_domain_copy);
		LOG("Cannot find user!!!\n");
		reject("Cannot find user!!!");
		return 0;
	}

	*rcpt_to_user = msg[deliveredto_line_num] + strlen(deliveredto_domain_copy);
	*rcpt_to_domain = deliveredto_domain_p;
	free(deliveredto_domain_copy);
	return 1;
}

char* createValidateURL(char* igw_login,char* mailbox){
	long number;
	static char buff[32];
	char q[1024];
	struct timeval tv;
	struct timezone tz;

	// Generate a random number for the URL
	gettimeofday(&tv,&tz);

	srand(tv.tv_usec);
	number = rand();
	sprintf(buff,"%ld%ld",tv.tv_sec,number);
	LOG("Calculated random URL value: ");
	LOG(buff);

	sprintf(q,"INSERT INTO validate_url (random_val,igw_login,mailbox)VALUES('%s','%s','%s')",buff,igw_login,mailbox);
	if(mysql_query(sock,q)){
        	fprintf(stderr,"Query: \"%s\" failed ! %s\n",q,mysql_error(sock));
	}
	return buff;
}

char* recreateMessage(){
// lines_size
// msg_line_nbr;       // The number of line
// msg_size
	int i;
	char* rebuild;
	char* p;

	rebuild = (char*) malloc(msg_size);
	p = rebuild;
	for(i=0;i<msg_line_nbr;i++){
		strcpy(p,msg[i]);
		p += lines_size[i];
		*p++ = '\n';
	}
	return rebuild;
}

char defaultBounceMsg[]=
"Pour eviter les spams, j'utilise un systeme de verification des messages\r\n\
entrants. Merci de vous inscrire sur le web a l'adresse ci-dessous pour optenir\r\n\
l'autorisation de m'ecrire.\r\n\
\r\n\
!!!URL!!!\r\n\
\r\n\
To avoid spam, I use an incoming message verification system. Please\r\n\
register yourself a the following web addr to get the autorization to write me.\r\n\
\r\n\
!!!URL!!!";

int bounceNotInWhitelist(char* subject,char* from_user,char* to_mailbox,char* rcpt_to_user){
	char q[1024];
	int num_rows;
	char* url;
	MYSQL_ROW row;
	char* bncTxt;
	char *p,*next,*cur,*temp;
	char* newSubject;

	// Get message from MySQL or do a copy of default text
	sprintf(q,"SELECT bounce_msg FROM mailbox WHERE igw_login='%s' AND pop3_email='%s';",rcpt_to_user,to_mailbox);
	fprintf(stderr,"Querying for bounce msg: \"%s\" !\n",q);
	if(mysql_query(sock,q)){
		fprintf(stderr,"Query: \"%s\" failed ! %s\n",q,mysql_error(sock));
	}
	LOG("Getting result...\n");
	if (!(res=mysql_store_result(sock))){
		fprintf(stderr,"Couldn't get result from %s\n",mysql_error(sock));
		return -1;
	}
	LOG("Getting num rows...\n");
	num_rows = mysql_num_rows(res);
	if(num_rows != 1){
		LOG("No row found error: using default text...\n");
		bncTxt = strdup(defaultBounceMsg);
	}else{
		row = mysql_fetch_row(res);
		if(row[0]){
			LOG("Bounce text found: inserting URL in custom text...\n");
			bncTxt = row[0];
		}else{
			LOG("Bounce text not found: using default text...\n");
			bncTxt = strdup(defaultBounceMsg);
		}
	}
	mysql_free_result(res);
	temp = createValidateURL(rcpt_to_user,to_mailbox);
	url = (char*)malloc( strlen(IGW_WEB_VALIDATE_URL)+strlen(temp)+1);
	strcpy(url,IGW_WEB_VALIDATE_URL);
	strcat(url,temp);

	// Replace url token into bounce message.
	cur = (char*)malloc(strlen(bncTxt)+1);
	sprintf(cur,"%s",bncTxt);
	printf("\nURL will be: %s\n",url);
	while( ( next = strstr(cur,"!!!URL!!!") ) ){
//		printf("%s",next);
		p = next;
		next = next + strlen("!!!URL!!!");
		*p = 0;
		temp = (char*)malloc(strlen(cur)+strlen(url)+strlen(next)+1);
		strcpy(temp,cur);
		strcat(temp,url);
		strcat(temp,next);
		free(cur);
		cur = temp;
	}

	newSubject = (char*)malloc(strlen(subject)+strlen("Re: ")+strlen("[iGlobalWall antispam check]")+1);
	strcpy(newSubject,"Re: ");
	strcat(newSubject,"[iGlobalWall antispam check]");
	strncat(newSubject,subject,strlen(subject)-1);

	ESMTP_send_message("iGlobalWall Daemon","iglobalwalldaemon@iglobalwall.com",
		from_user,newSubject,cur);

	free(newSubject);
	free(cur);
	free(bncTxt);
	free(url);
	return 0;
}

int main(int argc, char **argv){
	int header_line_nbr;
	char size[256];
	int to_line_num;
	int from_line_num;
	int subject_line_num;
	int deliveredto_line_num;
	char* rcpt_to_domain;
	char* rcpt_to_user;
	char* subject;
	char q[1024];
	MYSQL_RES *res;
	unsigned int num_rows;

	LOG("Reading conf file...........");
	read_config_file();
	LOG("done!\n");
	
	LOG("Connecting to mysql.........");
	do_ze_mysql_connect();
	LOG("done!\n");

	LOG("Parsing message.............");
	msg = getMessage(&msg_line_nbr);
	sprintf(size,"%d",msg_line_nbr);
	LOG(size);
	LOG(" lines!\n");

	LOG("Get header size.............");
	header_line_nbr = getHeaderNumOfLine(msg,msg_line_nbr);
	if(header_line_nbr == -1)	reject("Cannot find header size!");
	sprintf(size,"%d",header_line_nbr);
	LOG(size);
	LOG(" line!\n");

	LOG("Get To: header line.........");
	to_line_num = findHeaderLine(msg,header_line_nbr,"To: ");
	if(to_line_num == -1){
		LOG("not found!\n");
		reject("Cannot find To line in header");
	}
	strtok(msg[to_line_num],"\n");
	sprintf(size,"%d-> '%s'\n",to_line_num,msg[to_line_num]+strlen("To: "));
	LOG(size);

	LOG("Get From: header line.......");
	from_line_num = findHeaderLine(msg,header_line_nbr,"From:");
	if(from_line_num == -1){
		LOG("not found!\n");
		reject("Cannot find From line in header");
	}
	strtok(msg[from_line_num],"\n");
	sprintf(size,"%d-> '%s'\n",from_line_num,msg[from_line_num]+strlen("From: "));
	LOG(size);

	LOG("Get Delivered-To: header....");
	deliveredto_line_num = findHeaderLine(msg,header_line_nbr,"Delivered-To:");
	if(deliveredto_line_num == -1){
		LOG("not found!\n");
		reject("Cannot find Delivered-To line in header");
	}
	sprintf(size,"%d-> %s",deliveredto_line_num,msg[deliveredto_line_num]);
	LOG(size);

	LOG("Finding domain and user.....");
	findUserAndDomain(deliveredto_line_num,&rcpt_to_domain,&rcpt_to_user);
	sprintf(size,"Domain: '%s' User: '%s'\n",rcpt_to_domain,rcpt_to_user);
	LOG(size);

	sprintf(q,"SELECT * FROM whitelist WHERE igw_login='%s' AND mail_from_user='%s' AND mailbox='%s';",
			rcpt_to_user,msg[from_line_num] + strlen("From: "),msg[to_line_num] + strlen("To: "));
	printf("Querying for whitelist: \"%s\"\n",q);
 
	if(mysql_query(sock,q)){
		sprintf(size,"Query: \"%s\" failed ! %s\n",q,mysql_error(sock));
		deferal(size);
	}
	LOG("Getting result...\n");
	if (!(res=mysql_store_result(sock))){
		fprintf(stderr,"Couldn't get result from %s\n",mysql_error(sock));
		exit(2);
	}

	LOG("Getting num rows...\n");
	num_rows = mysql_num_rows(res);

	if(num_rows < 1){
		LOG("Not found in whitelist: pusshing message to mysql and bouncing...\n");
		mysql_free_result(res);

		LOG("Get Subject: header.........");
		subject_line_num = findHeaderLine(msg,header_line_nbr,"Subject:");
		if(subject_line_num == -1){
			LOG("not found!\n");
			reject("Cannot find Subject line in header");
		}
		sprintf(size,"%d-> %s",subject_line_num,msg[subject_line_num]);
		LOG(size);
		subject = msg[subject_line_num] + strlen("Subject:");
		bounceNotInWhitelist(subject,msg[from_line_num] + strlen("From: "),msg[to_line_num] + strlen("To: "),rcpt_to_user);

		reject("User not in whitelist !\n");
	}

	mysql_free_result(res);
	mysql_close(sock);
	autorize();

	return 0;
}
