/*
 *
 * Multi server by Dave Stewart, KE4UWL
 *
 * Adapted to FBB 7.02 by F6FBB 11/2000
 * - added fbb.conf management
 * - added the default multi functions as defined by LA6CU
 * - dynamic allocation of the message buffer
 * - added R (remove) command
 * - accept lowercase commands
 *
 */

#define VERSION "1.25"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#include <fbb_conf.h>

int end = 0;
int pos = 0;
int begin = 0;
FILE *fptr, *msg_ptr, *data_ptr, *out_ptr, *hlp_ptr;
FILE *cfg_ptr;
char listname[80];
char buffer[80];
char datafile[10];
char sento[80];
char sender[80];
char service[80];
char subject[80];
char mail_in[80];
char bbs_addr[80];
char bbs_call[80];
char tocall[80];
char to_call[80];
char command[80];
char *message;
char helpline[80];
char config[80];
char list_status[10];
char up[80];

char *upper(char *up)
{
	int i;
	char *ptr = up;

	for (i = 0; i < strlen(up); i++)
	{
		up[i] = toupper(up[i]);
	}

	return ptr;
}

/* return a line from "message" and update begin accordingly */
char *getline(void)
{
	char *ptr;

	if (begin >= pos)
		return NULL;

	for (ptr = message + begin; message[begin] && message[begin] != '\n'; begin++)
	{
		if (begin == pos)
		{
			break;
		}
	}

	message[begin++] = '\0';
	return ptr;
}

typedef struct
{
	char type;
	char address[80];
}
list_t;

list_t *list_data;
int list_pos;
int list_size;

void init_dest_list(void)
{
	list_pos = 0;
	list_size = 20;
	list_data = malloc(list_size * sizeof(list_t));
}

void add_dest_list(char type, char *address)
{
	if (list_pos >= list_size)
	{
		list_size += 20;
		list_data = realloc(list_data, list_size * sizeof(list_t));
	}

	list_data[list_pos].type = type;
	strcpy(list_data[list_pos].address, address);
	++list_pos;
}

void print_dest_list(FILE * out_ptr, int hide)
{
	int i;
	int ofs = 0;
	int len;
	char *ptr;

	for (i = 0; i < list_pos; i++)
	{
		ptr = list_data[i].address;

		if (hide && list_data[i].type == 'H')
			continue;

		if (strcmp(ptr, sender) == 0)
			continue;			/* Sender ignored */

		len = strlen(ptr);
		if (list_data[i].type == 'H')
			len +=2;
			
		if ((ofs + len) > 76)
		{
			fprintf(out_ptr, ",\n");
			ofs = 0;
		}
		else if (ofs != 0)
		{
			fprintf(out_ptr, ", ");
			ofs += 2;
		}
		if (list_data[i].type == 'H')
		{
			fprintf(out_ptr, "(%s)", ptr);
		}
		else
		{
			fprintf(out_ptr, "%s", ptr);
		}
		ofs += len;
	}
	fputc('\n', out_ptr);
}

void footer(FILE *out_ptr)
{
	fprintf(out_ptr, "73 de %s (MULTI %s for Linux FBB by KE4UWL/F6FBB)\n/EX\n",
			bbs_call, VERSION);
}

void sent_by(FILE *out_ptr)
{
	fprintf(out_ptr, "\nMessage sent by server %s@%s\n\n", service, bbs_addr);
}

int main(int argc, char **argv)
{
	int allocsize;
	int c;
	int i;
	int orig;
	char *ptr;

	if (argc != 2)
	{
		printf("MULTI %s for Linux FBB by KE4UWL/F6FBB\n", VERSION);
		exit(1);
	}

	msg_ptr = fopen(argv[1], "r");	/* Open the received message */
	if (msg_ptr == NULL)
		exit(1);


	fgets(buffer, 80, msg_ptr);	/* Read the command line */
	sscanf(buffer, "%*s %s %*s %s\n", sento, sender);

	/* keep the name of the service */
	strcpy(service, sento);

	fgets(subject, 80, msg_ptr);	/* Read the subject line */

	init_dest_list();
	
	/* Read entire message into memory  */
	allocsize = 4096;
	message = malloc(allocsize + 1);
	if (message == NULL)
		exit(1);
	while ((c = fgetc(msg_ptr)) != EOF)
	{
		if (pos >= allocsize)
		{
			allocsize += 4096;
			message = realloc(message, allocsize + 1);
			if (message == NULL)
				exit(1);
		}
		message[pos++] = c;
	}
	/* Close the message file  */
	fclose(msg_ptr);

	/* Convert the 'to' address to lower case  */
	for (i = 0; i <= strlen(sento); i++)
	{
		datafile[i] = tolower(sento[i]);
	}

	/* Build filename from lowercase 'to' address  */
	strcpy(listname, sento);
	strcpy(sento, datafile);
	strcat(sento, ".dat");

	/* Test "multi" address */
	if (strcmp("multi", datafile) == 0)
	{
		int ndesti = 0;

		/* Get the list of callsigns */
		while ((ptr = getline()) != NULL)
		{
			int nb;
			char call[80];

			if (strncmp(ptr, "---", 3) == 0)
				break;

			if (strlen(upper(ptr)) > 80)
				ptr[80] = '\0';
			nb = sscanf(ptr, "%s %s", command, call);
			if ((nb > 1) && (strlen(command) == 1))
			{
				add_dest_list(*command, call);
				++ndesti;
			}
			else if (nb == 1)
			{
				add_dest_list('I', command);
				++ndesti;
			}
		}

		if (ndesti == 0 || ptr == NULL)
		{
			/* No destination or "---" */
			out_ptr = fopen(mail_in, "a");

			fprintf(out_ptr, "SP %s < MULTI\nMessage error\n", sender);
			fprintf(out_ptr,
					"\nSorry, no destination or \"---\" separator found.\n\n");
			footer(out_ptr);
			fclose(out_ptr);
			exit(0);
		}

		strcpy(list_status, "CLOSED");
	}
	else
	{
		strcpy(config, datafile);
		strcat(config, ".cfg");

		strcpy(list_status, "UNKNOWN");

		/* Read config file */
		cfg_ptr = fopen(config, "r");
		if (cfg_ptr)
		{

			fgets(buffer, 9, cfg_ptr);
			sscanf(upper(buffer), "%s", list_status);

			fclose(cfg_ptr);
		}

		printf("\nConfig file: %s\nList Status: %s\n", config, list_status);
	}

	/* Get some configuration values */
	if (read_fbb_conf(NULL) > 0)
		exit(1);

	/* Get the BBS callsign */
	ptr = find_fbb_conf("call", 0);
	if (ptr == NULL)
		exit(1);
	sscanf(ptr, "%[0-9A-Za-z]", bbs_call);

	/* Get the H address of the BBS */
	strcpy(bbs_addr, ptr);

	/* Get the mail.in file */
	ptr = find_fbb_conf("impo", 0);
	if (ptr == NULL)
		ptr = def_fbb_conf("impo");
	if (ptr == NULL)
		exit(1);
	sscanf(ptr, "%s", mail_in);

	if ((strlen(subject) == 2) && (toupper(*subject) == 'L'))
	{
		data_ptr = fopen(sento, "r");
		out_ptr = fopen(mail_in, "a");


		fprintf(out_ptr, "SP %s < MULTI\nCallsign List\n", sender);
		fprintf(out_ptr, "\nThese callsigns are on the %s mailing list:\n\n",
				listname);

		if (data_ptr)
		{
			while (fgets(tocall, 80, data_ptr) != NULL)
			{
				if (*tocall == '#')
					continue;	/* Comments ignored */
				if (*tocall == '\n')
					continue;	/* Blank lines ignored */
				sscanf(tocall, "%s", to_call);

				fprintf(out_ptr, "%s\n", to_call);
			}

			fclose(data_ptr);
		}
		else
		{
			fprintf(out_ptr, "None\n");
		}

		footer(out_ptr);

		fclose(out_ptr);
		exit(0);
	}

	if ((strlen(subject) == 2) && (toupper(*subject) == 'H'))
	{
		hlp_ptr = fopen("multi.hlp", "r");
		out_ptr = fopen(mail_in, "a");


		fprintf(out_ptr, "SP %s < MULTI\nHELP for Multi %s\n", sender,
				VERSION);
		while (fgets(helpline, 80, hlp_ptr) != NULL)
		{
			if (*helpline == '#')
				continue;		/* Comments ignored */
			fprintf(out_ptr, "%s", helpline);
		}

		fclose(hlp_ptr);

		footer(out_ptr);

		fclose(out_ptr);
		exit(0);
	}

	if ((strlen(subject) == 2) && (toupper(*subject) == 'A'))
	{
		printf("List state: %s\n", list_status);

		if (strncmp("UNKNOWN", list_status, 7) == 0)
		{
			out_ptr = fopen(mail_in, "a");

			fprintf(out_ptr, "SP %s < MULTI\nAuto-add result\n", sender);
			fprintf(out_ptr,
					"\nSorry, the %s list is not correctly configured.\n\n",
					listname);
			footer(out_ptr);
			fclose(out_ptr);
			exit(0);
		}

		/* Check if the callsign is not already in the list */
		data_ptr = fopen(sento, "r");
		if (data_ptr)
		{
			printf("data_ptr\n");
			while (fgets(tocall, 80, data_ptr) != NULL)
			{
				printf("tocall = %s\n", tocall);
				if (*tocall == '#')
					continue;	/* Comments ignored */
				if (*tocall == '\n')
					continue;	/* Blank lines ignored */

				sscanf(tocall, "%s", to_call);
				printf("to_call = %s\n", to_call);
				if (strcmp(upper(to_call), sender) == 0)
				{
					/* Already on the list. Do not add ! */
					fclose(data_ptr);

					printf("trouve to_call = %s\n", to_call);
					out_ptr = fopen(mail_in, "a");

					fprintf(out_ptr, "SP %s < MULTI\nAuto-add confirmation\n",
							sender);
					fprintf(out_ptr,
							"\nYou already are in the %s mailing list\n",
							listname);
					footer(out_ptr);

					fclose(out_ptr);
					exit(0);
				}
			}
			fclose(data_ptr);
		}

		if (strncmp("OPEN", list_status, 4) == 0)
		{

			data_ptr = fopen(sento, "a");
			fprintf(data_ptr, "\n%s # Callsign auto-added by Multi %s\n",
					sender, VERSION);
			fclose(data_ptr);

			out_ptr = fopen(mail_in, "a");

			fprintf(out_ptr, "SP %s < MULTI\nAuto-add confirmation\n",
					sender);
			fprintf(out_ptr, "\nYou have been added to the %s mailing list\n",
					listname);
			footer(out_ptr);

			fclose(out_ptr);
			exit(0);
		}

		out_ptr = fopen(mail_in, "a");

		fprintf(out_ptr, "SP %s < MULTI\nAuto-add result\n", sender);
		fprintf(out_ptr,
				"\nSorry, the %s list is a closed list.  Please ask\n",
				listname);
		fprintf(out_ptr, "the system operator for access to this list\n\n");
		footer(out_ptr);
		fclose(out_ptr);
		exit(0);

	}

	if ((strlen(subject) == 2) && (toupper(*subject) == 'R'))
	{
		char sentbck[80];

		strcpy(sentbck, datafile);
		strcat(sentbck, ".bck");

		/* Remove from list */
		rename(sento, sentbck);

		data_ptr = fopen(sentbck, "r");
		out_ptr = fopen(sento, "w");

		while (fgets(tocall, 80, data_ptr) != NULL)
		{
			if (*tocall == '#')
			{
				fprintf(out_ptr, "%s", tocall);
				continue;		/* Comments ignored and copied */
			}
			if (*tocall == '\n')
			{
				continue;		/* Blank lines ignored and not copied */
			}

			sscanf(tocall, "%s", to_call);
			if (strcmp(upper(to_call), sender) == 0)
				continue;		/* Sender deleted */

			/* write to the config file */
			fprintf(out_ptr, "%s\n", to_call);

		}
		fclose(data_ptr);
		fclose(out_ptr);

		unlink(sentbck);

		out_ptr = fopen(mail_in, "a");

		fprintf(out_ptr, "SP %s < MULTI\nAuto-remove result\n", sender);
		fprintf(out_ptr, "\nYou have been removed from the %s mailing list\n",
				listname);
		footer(out_ptr);
		fclose(out_ptr);
		exit(0);
	}

	/* Open the data.in and mail.in files */

	out_ptr = fopen(mail_in, "a");

	if (list_pos == 0 && (data_ptr = fopen(sento, "r")) != NULL)
	{
		/* We'll read each call from the input data file, and 
		   write the message mail.in for each, since FBB needs 
		   a specific format in order to import the mail      */

		while (fgets(tocall, 80, data_ptr) != NULL)
		{
			if (*tocall == '#')
				continue;			/* Comments ignored */
			if (*tocall == '\n')
				continue;			/* Blank lines ignored */

			sscanf(tocall, "%s %s", command, to_call);
			if (strlen(command) > 1)
			{
				strcpy(to_call, command);
				strcpy(command, "I");
			}
			add_dest_list(*command, to_call);
		}
		fclose(data_ptr);
	}

	orig = begin;
	for (i = 0 ; i < list_pos ; i++)
	{
		char *ptr = list_data[i].address;

		if (strcmp(ptr, sender) == 0)
			continue;			/* Sender ignored */

		/* Send command line to mail.in */
		fprintf(out_ptr, "SP %s < %s\n", ptr, sender);

		/* Send subject line to mail.in */
		fprintf(out_ptr, "%s", subject);

		/* Send text to mail.in */
		begin = orig;
		while ((ptr = getline()) != NULL)
		{
			if (strncmp(ptr, "/EX", 2) == 0)
				break;
			fprintf(out_ptr, "%s\n", ptr);
		}
		
		fprintf(out_ptr, "---\nMessage via MULTI %s by KE4UWL/F6FBB copied to :\n",
				VERSION);

		print_dest_list(out_ptr, 1);

		sent_by(out_ptr);
		
		/* Send /ex to the mail.in file, making sure there is a newline before it */
		fprintf(out_ptr, "\n/ex\n");
	}

	/* Now we'll send a confirmation message to the sender, 
	   just to let them know their message has been copied */

	fprintf(out_ptr, "SP %s < MULTI\n", sender);
	fprintf(out_ptr, "Message confirmation\n");


	fprintf(out_ptr,
			"Your message, titled : %shas been copied to :\n", subject);

	print_dest_list(out_ptr, 0);

	footer(out_ptr);

	fclose(out_ptr);

	return 0;
}
