/* -------------------------------------------------------------------- */
/* SMS Client, send messages to mobile phones and pagers		*/
/*									*/
/* smsd.c								*/
/*									*/
/*  Copyright (C) 1997,1998,1999 Angelo Masci				*/
/*									*/
/*  This library is free software; you can redistribute it and/or	*/
/*  modify it under the terms of the GNU Library General Public		*/
/*  License as published by the Free Software Foundation; either	*/
/*  version 2 of the License, or (at your option) any later version.	*/
/*									*/
/*  This library is distributed in the hope that it will be useful,	*/
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of	*/
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU	*/
/*  Library General Public License for more details.			*/
/*									*/
/*  You should have received a copy of the GNU Library General Public	*/
/*  License along with this library; if not, write to the Free		*/
/*  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.	*/
/*									*/
/*  You can contact the author at this e-mail address:			*/
/*									*/
/*  angelo@styx.demon.co.uk						*/
/*									*/
/* --------------------------------------------------------------------
   $Id$
   -------------------------------------------------------------------- */

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

#if defined(LINUX)
#include <getopt.h>
#endif

#include "server.h"
#include "logfile/logfile.h"
#include "common/common.h"
#include "resource/resource.h"
#include "parser/gs_translate.h"
#include "version.h"
#include "lock/lock.h"
#include "error.h"

/* -------------------------------------------------------------------- */

#if !defined(MVERSION)
#error "MVERSION undefined"
#else
#define VERSION MVERSION
#endif
 
#if !defined(MSMSDLOGFILE)
#error "MSMSDLOGFILE undefined"
#else
#define LOGFILE         MSMSDLOGFILE
#endif

#if !defined(MSMSDLOGLEVEL)
#error "MSMSDLOGLEVEL undefined" 
#else
#define LOGLEVEL	MSMSDLOGLEVEL
#endif

#if !defined(MSERVICEDIR)
#error "MSERVICEDIR undefined"
#else
#define SERVICEDIR      MSERVICEDIR
#endif

#define DAEMONCONFIGFILE (MSERVICEDIR "/sms_daemons")

/* -------------------------------------------------------------------- */

#define ERROR_STATE	-2
#define EXIT_STATE	-1
#define NULL_STATE	 0
#define UNKNOWN_STATE	 1
#define OK_STATE	 2

/* -------------------------------------------------------------------- */

struct sms_control_data_struct
	{
		char 	*user,
			*host,
			*mail,
			*alen,
			*destination_service,
			*destination_id,
			*control_file,
			*data_file,
			*id;

		long	len;

	};

typedef struct sms_control_data_struct SMS_CONTROL_DATA;

struct sms_data_struct
	{
		char 	*data;
	};

typedef struct sms_data_struct SMS_DATA;

/* -------------------------------------------------------------------- */

SMS_DATA sms_data;

/* -------------------------------------------------------------------- */

static	int
	smsd_port,
	smsd_maxid;

static  char	
	smsd_lockfile[512];

static	char 	
	*smsd_spool,
	*smsd_incoming,
	*smsd_services,
	*smsd_locks;

static 	TOKEN_HEAP
	*smsd_services_heap;


static 	RESOURCE resource_list[] = 
	{
		{ RESOURCE_NUMERIC,  "server.port", 		0, 1, NULL, 0,  NULL,    	   4563,  	&smsd_port	},
		{ RESOURCE_NUMERIC,  "server.maxid", 		0, 1, NULL, 0,  NULL,    	   32000,  	&smsd_maxid	},
		{ RESOURCE_STRING,   "directories.spool", 	0, 1, NULL, 0,  "/var/spool/sms",  0,  		&smsd_spool	},
		{ RESOURCE_STRING,   "directories.incoming",	0, 1, NULL, 0,  "incoming",        0,  		&smsd_incoming	},
		{ RESOURCE_STRING,   "directories.services", 	0, 1, NULL, 0,  "services",        0,  		&smsd_services	},
		{ RESOURCE_STRING,   "directories.locks", 	0, 1, NULL, 0,  "locks",           0,  		&smsd_locks	},
		{ RESOURCE_HEAP,     "services", 		0, 1, NULL, 0,  NULL,        	   0,  		&smsd_services_heap	},
		{ RESOURCE_NULL,     NULL, 			0, 1, NULL, 0,  NULL,              0,  		NULL  	}
	};


#define SMS_PORT	smsd_port	/* Standard port number 	*/
					/* assigned for sms traffic	*/
#define SMS_SPOOLDIR	smsd_spool
#define SMS_LOCKDIR	smsd_locks
#define SMS_INCOMINGDIR	smsd_incoming
#define SMS_SERVICESDIR	smsd_services

#define SMS_MAXID	smsd_maxid

/* -------------------------------------------------------------------- */

#define SAFEOUTPUT(X) ((X == NULL)?("<NULL>"):(X))
#define SAFECONTROLOUTPUT(X) ((X == NULL)?(""):(X))

/* -------------------------------------------------------------------- */

void cleanup_files(SMS_CONTROL_DATA *control_data);
void display_current(int fd, SMS_CONTROL_DATA *control_data);
void gateway(int new_fd);
void usage(char *file);

char *get_data(char *buf);

int gen_controlfile(SMS_CONTROL_DATA *control_data);
int copy_data(int dst_fd, int src_fd, long dlen);

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void display_current(int fd, SMS_CONTROL_DATA *control_data)
{
	hprintf(fd, "     Current Control Record info:\r\n");
	hprintf(fd, "         Username: %s\r\n", SAFEOUTPUT(control_data->user));
	hprintf(fd, "         Hostname: %s\r\n", SAFEOUTPUT(control_data->host));
	hprintf(fd, "         Mail on Delivery: %s\r\n", SAFEOUTPUT(control_data->mail));
	hprintf(fd, "         Destination Service: %s\r\n", SAFEOUTPUT(control_data->destination_service));
	hprintf(fd, "         Destination ID: %s\r\n", SAFEOUTPUT(control_data->destination_id));
	hprintf(fd, "         Control File: %s\r\n", SAFEOUTPUT(control_data->control_file));
	hprintf(fd, "         Data File: %s\r\n", SAFEOUTPUT(control_data->data_file));
	hprintf(fd, "         ID: %s\r\n", SAFEOUTPUT(control_data->id));
	hprintf(fd, "         Length: %ld\r\n", control_data->len);
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void write_control_data(SMS_CONTROL_DATA *control_data)
{
	FILE	*fp;


	fp = fopen(control_data->control_file, "w");
	if (fp == NULL)
	{	lprintf(LOG_ERROR, "Opening control_file '%s'\n", control_data->control_file);
		exit(-1);
	}


	fprintf(fp, "{\n");
	fprintf(fp, "    username            = \"%s\"\n", SAFECONTROLOUTPUT(control_data->user));
	fprintf(fp, "    hostname            = \"%s\"\n", SAFECONTROLOUTPUT(control_data->host));
	fprintf(fp, "    mail_on_delivery    = \"%s\"\n", SAFECONTROLOUTPUT(control_data->mail));
	fprintf(fp, "    destination_service = \"%s\"\n", SAFECONTROLOUTPUT(control_data->destination_service));
	fprintf(fp, "    destination_id      = \"%s\"\n", SAFECONTROLOUTPUT(control_data->destination_id));
	fprintf(fp, "    control_file        = \"%s\"\n", SAFECONTROLOUTPUT(control_data->control_file));
	fprintf(fp, "    data_file           = \"%s\"\n", SAFECONTROLOUTPUT(control_data->data_file));
	fprintf(fp, "    id                  = \"%s\"\n", SAFECONTROLOUTPUT(control_data->id));
	fprintf(fp, "    length              = \"%ld\"\n", control_data->len);
	fprintf(fp, "}\n");

	fclose(fp);
}



/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
#define iseol(x) ((x == '\n') || (x == '\r'))

char *get_field(char *buf, char **eptr)
{
	char	*new_buf,
		*ptr,
		*dst;

	int	inword;



	/* ---------------------------------------- */


	new_buf = NULL;
	inword  = FALSE;
	ptr     = buf;
	while(!iseol(*ptr) && (*ptr != '\0'))
	{
		if (!isspace(*ptr) && !inword)
		{
			new_buf = (char *)malloc(sizeof(char) * (strlen(buf) +1));
			if (new_buf == NULL)
			{	
				lprintf(LOG_ERROR, "malloc() failed\n");
				exit(EMALLOC);
			}

			inword = TRUE;
			dst    = new_buf;
		}
		else
		if (isspace(*ptr))
		{
			if (inword)
			{	break;
			}

			inword = FALSE;
		}
		
		if (inword)
		{	*dst++ = *ptr;
		}

		ptr++;
	}

	if (inword)
	{	*dst = '\0';
	}

	*eptr = ptr;
	return new_buf;
}





/* -------------------------------------------------------------------- */
/* Parse string of form:						*/
/*									*/
/*	COMMAND<SP>DATA<EOL>						*/
/* 									*/
/* Copy data to newly allocated buffer and return			*/
/* a pointer to it. If no data available return NULL			*/
/* -------------------------------------------------------------------- */

#define iseol(x) ((x == '\n') || (x == '\r'))

char *get_data(char *buf)
{
	char 	*src, 
		*dst,
		*data;
	int	i, j,
		cmd;


	cmd = TRUE;
	src = buf;
	i   = 0;
	j   = 0;
#if 0
	while ((*src != '\n') && (*src != '\0') && (*src != '\r'))
#else
	while ((!iseol(*src)) && (*src != '\0'))
#endif
	{
		if ((cmd) && (*src == ' '))
		{	cmd = FALSE;	
			j = i+1;
			i = 0;
		} 
		else
		{	i++;
		}

		src++;
	}

	if (j == 0)
	{	return NULL;
	}

	data = (char *)malloc(sizeof(char) * (i +1));
	if (dst == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	dst = data;
	src = &buf[j];
#if 0
	while ((*src != '\n') && (*src != '\0') && (*src != '\r'))
#else
	while ((!iseol(*src)) && (*src != '\0'))
#endif
	{
		*dst++ = *src++;
	}

	*dst = '\0';
	return data;
}


/* -------------------------------------------------------------------- */
/* Return Values:							*/
/*									*/
/* 	0 - If the current control record is incomplete			*/
/* 	1 - if the current control record is complete			*/
/*									*/
/* -------------------------------------------------------------------- */
int is_complete(SMS_CONTROL_DATA *control_data)
{
	if ((control_data->user == NULL) ||
	    (control_data->host == NULL) ||
	    (control_data->mail == NULL) ||
	    (control_data->alen == NULL) ||
	    (control_data->id   == NULL) ||
	    (control_data->destination_service  == NULL) ||
	    (control_data->destination_id  == NULL) ||
	    (control_data->control_file == NULL) ||
	    (control_data->data_file    == NULL))
	{
		return 0;
	}

	return 1;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
SMS_CONTROL_DATA *init_control_data(void)
{
	SMS_CONTROL_DATA
		*control_data;


	control_data = (SMS_CONTROL_DATA *)malloc(sizeof(SMS_CONTROL_DATA));
	if (control_data == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	control_data->user = NULL;
	control_data->host = NULL;
	control_data->mail = NULL;
	control_data->alen = NULL;
	control_data->id   = NULL;

	control_data->destination_service  = NULL;
	control_data->destination_id       = NULL;
	control_data->control_file         = NULL;
	control_data->data_file            = NULL;

	control_data->len  = 0;

	return control_data;
}


/* -------------------------------------------------------------------- */
/* Generate a control file in the incoming directory. 			*/
/* The control file is empty. 						*/
/* The control_data structure is updated to include the full path	*/
/* to the generated control file and it's corresponding data file.	*/
/* Note that memory is allocated for each of control_file, 		*/
/* data_file and id. It is the responsibility of the caller to		*/
/* free this memory.							*/
/*									*/
/* Return Value:							*/
/*									*/
/*	-1 error							*/
/* 	 0 Success							*/
/* -------------------------------------------------------------------- */
int gen_controlfile(SMS_CONTROL_DATA *control_data)
{
	int	n,
		fd;


	cleanup_files(control_data);


	control_data->id = (char *)malloc(sizeof(char) * 8);
	if (control_data->id == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	control_data->control_file = (char *)malloc(sizeof(char) * (strlen(SMS_SPOOLDIR) + 
			                                            strlen(SMS_INCOMINGDIR) + 10));
	if (control_data->control_file == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	control_data->data_file = (char *)malloc(sizeof(char) * (strlen(SMS_SPOOLDIR) + 
			                                         strlen(SMS_INCOMINGDIR) + 10));
	if (control_data->data_file == NULL)
	{
		lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}


  	strcpy(control_data->control_file, SMS_SPOOLDIR);
	libcommon_strfcat(control_data->control_file, SMS_INCOMINGDIR);
	libcommon_strfcat(control_data->control_file, "");

  	strcpy(control_data->data_file, SMS_SPOOLDIR);
	libcommon_strfcat(control_data->data_file, SMS_INCOMINGDIR);
	libcommon_strfcat(control_data->data_file, "");

	for (n = getpid(); n < SMS_MAXID; n++)
	{
		sprintf(control_data->id, "%05d", n);
		sprintf(control_data->control_file, "%scf%s", control_data->control_file, control_data->id);
		sprintf(control_data->data_file, "%sdf%s", control_data->data_file, control_data->id);

		if (access(control_data->control_file, F_OK) == 0)
		{	continue;
		}
		else
		{	fd = open(control_data->control_file, O_CREAT|O_WRONLY|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
			if (fd == -1)
			{	if (errno == EEXIST)
				{	continue;
				}

				lprintf(LOG_WARNING, "Cannot create file %s\n", control_data->control_file);

				free(control_data->control_file);
				free(control_data->data_file);
				free(control_data->id);

				return -1;
			}
			else
			{	close(fd);
				break;
			}

		}
	}

	if (n == SMS_MAXID)
	{	lprintf(LOG_WARNING, "Cannot create file as MAXID exceeded\n");

		free(control_data->control_file);
		free(control_data->data_file);
		free(control_data->id);

		return -1;
	}

	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void cleanup_files(SMS_CONTROL_DATA *control_data)
{
	if (control_data->control_file != NULL)
	{
		unlink(control_data->control_file);

		free(control_data->control_file);
		control_data->control_file = NULL;
	}

	if (control_data->data_file != NULL)
	{
		unlink(control_data->data_file);

		free(control_data->data_file);
		control_data->data_file = NULL;
	}

	if (control_data->id != NULL)
	{
		free(control_data->id);
		control_data->id = NULL;
	}
}


char *strip_eol(char *buf)
{
	char	*ptr;


	ptr = buf;
	while (*ptr != '\0')
	{	
		if (iseol(*ptr))
		{	*ptr = '\0';
			break;
		}

		ptr++;
	}	

	return buf;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void gateway(int new_fd)
{
	char	buf[MAX_STRING_LEN],
		host_name[512],
		ip_address[512],
		*tmp_buf,
		*ptr,
		*cmd,
		*job,
		*queue,
		*command;

	int 	state, 
		data_fd;
	
	SMS_CONTROL_DATA
		*control_data;


	control_data = init_control_data();



	if (get_client_information(new_fd, host_name, ip_address) == 0)
	{	lprintf(LOG_STANDARD, "Connection from %s [%s]\n", host_name, ip_address);
	}

	hprintf(new_fd, "400 SMS Gateway Ready\r\n");

	while ((state != EXIT_STATE) &&
	       (state != ERROR_STATE) &&
	       (hgets(buf, MAX_STRING_LEN, new_fd) != NULL))
	{
		state = NULL_STATE;


		cmd = get_field(buf, &ptr);
		if (cmd == NULL)
		{
			hprintf(new_fd, "502 '%s': command not understood.\r\n", strip_eol(buf));
			continue;
		}


		if (strcasecmp(cmd, "MAIL") == 0)
		{
			control_data->mail = get_field(ptr, &ptr);
			if (control_data->mail == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check this is a valid	*/
					/* email address.		*/

					state = OK_STATE;
				}
			}
		}
		else 
		if (strcasecmp(cmd, "HOST") == 0)
		{
			control_data->host = get_field(ptr, &ptr);
			if (control_data->host == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check that this is a valid	*/
					/* hostname.			*/

					state = OK_STATE;
				}
			}
		}
		else 
		if (strcasecmp(cmd, "DEST") == 0)
		{
			control_data->destination_service = get_field(ptr, &ptr);
			if (control_data->destination_service == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{	/* Check that this is a valid	*/
					/* destination-service.	 	*/

					if (get_varlist(smsd_services_heap, control_data->destination_service) != NULL)
					{	state = OK_STATE;
					}
					else
					{	state = UNKNOWN_STATE;
					}
				}
			}
		}
		else 
		if (strcasecmp(cmd, "ID") == 0)
		{
			control_data->destination_id = get_field(ptr, &ptr);
			if (control_data->destination_id == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{	/* Check that this is a valid	*/
					/* destination-id.			*/

					state = OK_STATE;
				}
			}
		}
		else 
		if (strcasecmp(cmd, "DATA") == 0)
		{

			/* The control file only gets generated when 	*/
			/* data is received. It is placed in incoming	*/
			/* All required fields have been sent and	*/
			/* are included.				*/


			control_data->alen = get_field(ptr, &ptr);
			if (control_data->alen == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					control_data->len = atoi(control_data->alen);
				
					/* WARNING - use strtol and strict 	*/
					/* checks 				*/
									
					/* Read 'len' bytes of data from fd	*/
					/* and copy to data_file		*/

					/* Open up the data_file - 		*/
					/* ensure we have a control file and    */
					/* all locks are setup			*/

					if (gen_controlfile(control_data) == -1)
					{	state = ERROR_STATE;
					}
					else
					{
						data_fd = open(control_data->data_file, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
						if (data_fd < 0)
						{	lprintf(LOG_ERROR, "Opening data_file '%s'\n", control_data->data_file);
							exit(-1);
						}

						if (copy_data(data_fd, new_fd, control_data->len) != 0)
						{	lprintf(LOG_ERROR, "Copying data failed\n");
							exit(-1);
						}

						close(data_fd);

						read(new_fd, &buf[0], 1);
						if (buf[0] == '\n')
						{
							state = OK_STATE;
						}
						else 
						if (buf[0] == '\r')
						{
							read(new_fd, &buf[0], 1);
							if (buf[0] == '\n')
							{
								state = OK_STATE;
							}
							else
							{	state = ERROR_STATE;
							}
						}
						else
						{	state = ERROR_STATE;
						}
					}
				}
			}
		}
		else 
		if (strcasecmp(cmd, "USER") == 0)
		{
			control_data->user = get_field(ptr, &ptr);
			if (control_data->user == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check that this is a valid	*/
					/* user.			*/

					state = OK_STATE;
				}
			}
		}
		else 
		if (strcasecmp(cmd, "SEND") == 0)
		{
			tmp_buf = get_field(ptr, &ptr);
			if (tmp_buf != NULL)
			{	free(tmp_buf);
				state = UNKNOWN_STATE;
			}
			else
 			{	if (is_complete(control_data))
				{
					write_control_data(control_data);
					state = OK_STATE;
				}
				else
				{	state = ERROR_STATE;
				}
			}
		}
		else
		if (strcasecmp(cmd, "SENJ") == 0)
		{
			job = get_field(ptr, &ptr);
			if (job == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check this is a valid	*/
					/* job.				*/
	
					if (is_complete(control_data))
					{
						write_control_data(control_data);
						state = OK_STATE;
					}
					else
					{	state = ERROR_STATE;
					}
				}


				free(job);
			}
		}
		else 
		if (strcasecmp(cmd, "SENQ") == 0)
		{
			queue = get_field(ptr, &ptr);
			if (queue == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check this is a valid	*/
					/* queue.			*/
	
					if (is_complete(control_data))
					{
						write_control_data(control_data);
						state = OK_STATE;
					}
					else
					{	state = ERROR_STATE;
					}
				}


				free(queue);
			}
		}
		else 
		if (strcasecmp(cmd, "SENA") == 0)
		{
			tmp_buf = get_field(ptr, &ptr);
			if (tmp_buf != NULL)
			{	free(tmp_buf);
				state = UNKNOWN_STATE;
			}
			else
			{	if (is_complete(control_data))
				{
					write_control_data(control_data);
					state = OK_STATE;
				}
				else
				{	state = ERROR_STATE;
				}
			}
		}
		else 
		if (strcasecmp(cmd, "STAJ") == 0)
		{
			job = get_field(ptr, &ptr);
			if (job == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	state = UNKNOWN_STATE;
				}
				else
				{
					/* Check this is a valid	*/
					/* job.				*/

					state = OK_STATE;
				}


				free(job);
			}
		}
		else 
		if (strcasecmp(cmd, "STAQ") == 0)
		{
			queue = get_field(ptr, &ptr);
			if (queue == NULL)
			{	state = UNKNOWN_STATE;
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					/* Check this is a valid	*/
					/* queue.			*/

					state = OK_STATE;
				}


				free(queue);
			}
		}
		else 
		if (strcasecmp(cmd, "STAT") == 0)
		{
			tmp_buf = get_field(ptr, &ptr);
			if (tmp_buf != NULL)
			{	free(tmp_buf);
				state = UNKNOWN_STATE;
			}
			else
			{	state = OK_STATE;
			}
		}
		else 
		if (strcasecmp(cmd, "INFO") == 0)
		{
			tmp_buf = get_field(ptr, &ptr);
			if (tmp_buf != NULL)
			{	free(tmp_buf);
				state = UNKNOWN_STATE;
			}
			else
			{
				hprintf(new_fd, "231- SMS Server info:\r\n");
				hprintf(new_fd, "         Version: 0.1\r\n");

				display_current(new_fd, control_data);

				hprintf(new_fd, "231 End of Info.\r\n");
			}
		}
		else 
		if (strcasecmp(cmd, "QUIT") == 0)
		{
			tmp_buf = get_field(ptr, &ptr);
			if (tmp_buf != NULL)
			{	free(tmp_buf);
				state = UNKNOWN_STATE;
			}
			else
			{	hprintf(new_fd, "220 Goodbye.\r\n");
				state = EXIT_STATE;
			}
		}
		else 
		if (strcasecmp(cmd, "HELP") == 0)
		{
			command = get_field(ptr, &ptr);
			if (command == NULL)
			{
				hprintf(new_fd, "230- The following commands are recognized\r\n");
				hprintf(new_fd, "     USER    DATA    MAIL    HOST    DEST\r\n");
				hprintf(new_fd, "     SEND    SENJ    SENQ    SENA        \r\n");
				hprintf(new_fd, "     STAT    STAJ    STAQ    ID          \r\n");
				hprintf(new_fd, "     QUIT    INFO    HELP                \r\n");
				hprintf(new_fd, "230 End of Help.\r\n");
			}
			else
			{
				tmp_buf = get_field(ptr, &ptr);
				if (tmp_buf != NULL)
				{	free(tmp_buf);
					state = UNKNOWN_STATE;
				}
				else
				{
					if (strcasecmp(command, "USER") == 0)
					{
						hprintf(new_fd, "214 Syntax: USER <sp> username\r\n");
					}
					else
					if (strcasecmp(command, "HOST") == 0)
					{
						hprintf(new_fd, "214 Syntax: HOST <sp> hostname\r\n");
					}
					else
					if (strcasecmp(command, "DEST") == 0)
					{
						hprintf(new_fd, "214 Syntax: DEST <sp> service\r\n");
					}
					else
					if (strcasecmp(command, "ID") == 0)
					{
						hprintf(new_fd, "214 Syntax: ID <sp> id\r\n");
					}
					else
					if (strcasecmp(command, "DATA") == 0)
					{
						hprintf(new_fd, "214 Syntax: DATA <sp> len <\\r\\n> data <\\r\\n>\r\n");
					}
					else
					if (strcasecmp(command, "MAIL") == 0)
					{
						hprintf(new_fd, "214 Syntax: MAIL <sp> address\r\n");
					}
					else
					if (strcasecmp(command, "QUIT") == 0)
					{
						hprintf(new_fd, "214 Syntax: QUIT (terminate service)\r\n");
					}
					else
					{	hprintf(new_fd, "502 '%s': command unknown.\r\n", strip_eol(buf));
					}
				}

				free(command);
			}
		}
		else 
		{	state = UNKNOWN_STATE;
		}

		if (state == ERROR_STATE)
		{	hprintf(new_fd, "510 Error Connection Terminated.\r\n");
		}
		else 
		if (state == UNKNOWN_STATE)
		{	hprintf(new_fd, "511 '%s': command not understood.\r\n", strip_eol(buf));
		}
		else 
		if (state == OK_STATE)
		{	hprintf(new_fd, "210 Ok.\r\n");
		}
	}

	if (state == ERROR_STATE)
	{	cleanup_files(control_data);
	}

	free(control_data);
}


/* -------------------------------------------------------------------- */
/* Read 'len' bytes of data from src_fd and copy to 'dst_fd' 		*/
/* Return Values							*/
/*									*/
/*	 0 Success							*/
/* 	-1 Failure							*/
/* -------------------------------------------------------------------- */
int copy_data(int dst_fd, int src_fd, long dlen)
{
	int 	len, 
		sent;

	char	*ptr;
	char	*mbuf;


	mbuf = (char *)malloc(sizeof(char) * 1024);
	if (mbuf == NULL)
	{	lprintf(LOG_ERROR, "malloc() failed\n");
		exit(EMALLOC);
	}

	{
		len = read(src_fd, mbuf, ((dlen<1024)?(dlen):(1024)));
		if (len < 0)
		{	if (errno != EINTR)
			{	lprintf(LOG_ERROR, "read() failed\n");
				return(-1);
			}
		}

	} while (len < 0);


	while(len)
	{
		dlen -= len;

		ptr = mbuf;
		sent = 0;
		while (len - sent)
		{
			len -= sent;
			ptr += sent;
			
			{	
				sent = write(dst_fd, ptr, len);
				if (sent < 0)
				{	if (errno != EINTR)
					{	lprintf(LOG_ERROR, "write() failed\n");
						return(-1);
					}
				}

			} while (len < 0);

			if (sent == 0)
			{	lprintf(LOG_ERROR, "write() failed\n");
				return(-1);
			}
		}
		
		if (dlen <= 0)
		{	break;
		}		

		{
			len = read(src_fd, mbuf, ((dlen<1024)?(dlen):(1024)));
			if (len < 0)
			{	if (errno != EINTR)
				{	lprintf(LOG_ERROR, "read() failed\n");
					return(-1);
				}
			}

		} while (len < 0);
	}

	free(mbuf);
	return 0;
}


/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void usage(char *file)
{
	lprintf(LOG_STANDARD, "Usage: %s [-l loglevel] [-p port]\n", file);
	lprintf(LOG_STANDARD, "       %s -v\n", file);
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
void release_lock(void)
{	resource_unlock(smsd_lockfile);
}

/* -------------------------------------------------------------------- */
/* -------------------------------------------------------------------- */
int main(int argc, char *argv[])
{
	int 	c,
		port;
	char	*ptr,
		*progname;

	struct 	sockaddr 
		sa_client;


	progname = strrchr(argv[0], '/');
	if (progname == NULL)
	{	progname = argv[0];
	}
	else
	{	progname++;
	}

	/* ---------------------------- */

	set_logfile(LOGFILE);
	set_loglevel(LOGLEVEL);
	set_consolelog(TRUE);

	/* ---------------------------- */

	port = 0;

	while ((c = getopt (argc, argv, "p:l:v")) != -1)
        {
                switch (c)
                {
                        case 'p':  
				port = (int)strtol(optarg, &ptr, 10);
                                break;

                        case 'l':  
				set_loglevel((int)strtol(optarg, &ptr, 10));
				if (ptr == optarg)
				{
					lprintf(LOG_ERROR, "Option l requires an argument\n");
	     	                        usage(progname);
                	                exit(-1);
				}
                                
                                break;
                        case 'v':
				lprintf(LOG_STANDARD, "%s %s\n", progname, VERSION);
                                exit(0);

                        case '?':
                                lprintf(LOG_ERROR, "Unknown option `-%c'\n", optopt);
                                usage(progname);
                                exit(-1);

                        default:
                                abort ();
                }
        }


	/* ---------------------------- */

	if (read_resource_file(DAEMONCONFIGFILE, resource_list, 1) == RESOURCE_FILE_ERROR)
	{	lprintf(LOG_ERROR, "Failed to read resource file  '%s'\n", DAEMONCONFIGFILE);
		exit(-1);
	}

	if (port != 0)
	{	SMS_PORT = port;
	}

	/* ---------------------------- */

	if ((argc - optind) != 0)
	{	usage(progname);
		exit(-1);
	}

	/* ---------------------------- */

	c = sizeof(sa_client);
	if (getpeername(fileno(stdin), &sa_client, &c) < 0)
	{
		/* getpeername() fails if fd isn't a 	*/
		/* socket. If this is the case we can 	*/
		/* assume that we aren't running	*/
		/* from inetd and should startup and	*/
		/* run as a daemon ourselves.		*/

		strcpy(smsd_lockfile, SMS_SPOOLDIR);
		libcommon_strfcat(smsd_lockfile, SMS_LOCKDIR);
		libcommon_strfcat(smsd_lockfile, "smsd.pid");

		lprintf(LOG_STANDARD, "Starting SMSD Standalone Server deamon...\n");

		if (server_main(SMS_PORT, gateway, smsd_lockfile) == -1)
		{	lprintf(LOG_STANDARD, "Failed to start SMSD Standalone Server deamon\n");
			exit(-1);
		}
	}
	else
	{	set_consolelog(FALSE);
		lprintf(LOG_STANDARD, "Starting SMSD Server as an INETD service\n");
		gateway(fileno(stdin));
	}

	return 0;
}

