/*
 * This file contains all of the OS-dependent stuff:
 *   startup, signals, BSD sockets for tcp/ip, i/o, timing.
 *
 * The data flow for input is:
 *    Game_loop ---> Read_from_descriptor ---> Read
 *    Game_loop ---> Read_from_buffer
 *
 * The data flow for output is:
 *    Game_loop ---> Process_Output ---> Write_to_descriptor -> Write
 *
 * The OS-dependent functions are Read_from_descriptor and Write_to_descriptor.
 * -- Furey  26 Jan 1993
 */

#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <crypt.h>
#include <mysql/mysql.h>
#include "include.h"
#include "channel.h"

/*
 * Malloc debugging stuff.
 */
#if defined(sun)
#undef MALLOC_DEBUG
#endif

#if defined(MALLOC_DEBUG)
#include <malloc.h>
extern int malloc_debug args((int));
extern int malloc_verify args((void));
#endif



#include <signal.h>

#if defined(apollo)
#undef __attribute
#endif



#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/telnet.h>
const char echo_off_str[] = { IAC, WILL, TELOPT_ECHO, '\0' };
const char echo_on_str[] = { IAC, WONT, TELOPT_ECHO, '\0' };
const char go_ahead_str[] = { IAC, GA, '\0' };



/*
 * Global variables.
 */
DESCRIPTOR_DATA *descriptor_free;	/* Free list for descriptors    */
DESCRIPTOR_DATA *descriptor_list;	/* All open descriptors         */
DESCRIPTOR_DATA *d_next;	/* Next descriptor in loop      */
bool merc_down;			/* Shutdown                     */
time_t current_time;		/* Time of this pulse           */
ACCOUNT			*account_list;
ACNT_CMD		*acnt_cmd_list;
ACNT_CMD		*acnt_cmd_last;
std::list<Attendant *> attendant_list;
std::list<CodeBlob *> codeList;

bool	iCopyover = false; //A bool to tell if the muds in copyover mode for the new process
bool	oCopyover = false;  //A bool to tell if the muds in copyover mode for the old process
bool	dCopyover = false; //A bool for the copyover.done file
void game_loop_unix args((int control));
int init_socket args((int port));
void new_descriptor args((int control));
bool read_from_descriptor args((DESCRIPTOR_DATA * d));
bool write_to_descriptor args((int desc, char *txt, int length));




/*
 * Other local functions (OS-independent).
 */
bool check_parse_name args((char *name));
bool check_playing args((DESCRIPTOR_DATA * d, char *name));
int main args((int argc, char **argv));
void nanny args((DESCRIPTOR_DATA * d, char *argument));
bool process_output args((DESCRIPTOR_DATA * d, bool fPrompt));
void read_from_buffer args((DESCRIPTOR_DATA * d));
void initMysql(char *name, char *database, char *passwd, char *host);
MYSQL *db;

/* Made globle for Copyover */
int port, control, tempcont;

int main(int argc, char **argv)
{
    struct timeval now_time;


    /*
     * Memory debugging if needed.
     */
#if defined(MALLOC_DEBUG)
    malloc_debug(2);
#endif
    initMysql("mudcon", "user", "password", "localhost");
    Channel::loadChannels();
    /*
     * Init time.
     */
    gettimeofday(&now_time, NULL);
    current_time = (time_t) now_time.tv_sec;

    /*
     * Get the port number.
     */
    port = 1234;
    if (argc > 1)
    {
	if (!is_number(argv[1]))
	{
	    fprintf(stderr, "Usage: %s [port #]\n", argv[0]);
	    exit(1);
	}
	else if ((port = atoi(argv[1])) <= 1024)
	{
	    fprintf(stderr, "Port number must be above 1024.\n");
	    exit(1);
	}
    }

	/* Are we recovering from a copyover? */
 	if (argv[2] && argv[2][0])
 	{
 		iCopyover = true;
 		/* Can't start listening on a port untill the other pid dies */
		if(!dCopyover)
			control = atoi(argv[3]);
 		
	}

    /*
     * Run the game.
     */
	if(!iCopyover)
		control = init_socket(port);
    boot_db();
	if(iCopyover)
	{	fclose(fopen("./copyover.save", "w" ) );
		tempcont = atoi(argv[3]);
		copyover_loop(); /* This is a loop that checks for the files written by other process to insta copyover! For the new process*/
	}
    game_loop_unix(control);
    close(control);
    /*
     * That's all, folks.
     */
    exit(0);
    return 0;
}



int init_socket(int port)
{
    static struct sockaddr_in sa_zero;
    struct sockaddr_in sa;
    int x = 1;
    int fd;

    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
	perror("Init_socket: socket");
	exit(1);
    }

    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &x, sizeof(x)) < 0)
    {
	perror("Init_socket: SO_REUSEADDR");
	close(fd);
	exit(1);
    }

#if defined(SO_DONTLINGER) && !defined(SYSV)
    {
	struct linger ld;

	ld.l_onoff = 1;
	ld.l_linger = 1000;

	if (setsockopt(fd, SOL_SOCKET, SO_DONTLINGER,
		       (char *) &ld, sizeof(ld)) < 0)
	{
	    perror("Init_socket: SO_DONTLINGER");
	    close(fd);
	    exit(1);
	}
    }
#endif

    sa = sa_zero;
    sa.sin_family = AF_INET;
    sa.sin_port = htons(port);

    if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0)
    {
	perror("Init_socket: bind");
	close(fd);
	exit(1);
    }

    if (listen(fd, 3) < 0)
    {
	perror("Init_socket: listen");
	close(fd);
	exit(1);
    }

    return fd;
}

void copyover_loop()
{	while(!dCopyover)
	{	if(file_exists("./copyover.ready" ) )
		{	unlink("./copyover.ready");
			recover_accounts();
			control = tempcont;
			dCopyover = true;

			fclose(fopen("./copyover.done", "w" ) );
		}

	}
	return;
}


void game_loop_unix(int control)
{
    static struct timeval null_time;
    struct timeval last_time;
    signal(SIGPIPE, SIG_IGN);
    gettimeofday(&last_time, NULL);
    current_time = (time_t) last_time.tv_sec;

    /* Main loop */
    while (!merc_down)
    {
	fd_set in_set;
	fd_set out_set;
	fd_set exc_set;
	DESCRIPTOR_DATA *d;
	int maxdesc;

#if defined(MALLOC_DEBUG)
	if (malloc_verify() != 1)
	    abort();
#endif
		if(oCopyover)
		{	if(file_exists("./copyover.save" ) )
			{	 for(d=descriptor_list; d;d=d->next )
				 {	ACCOUNT *wch;
					if(d->connected == CON_OOC_CHAT )
					{	wch = d->account;
						save_account(wch);
					}
					unlink("./copyover.save" ); /* Make sure it doesn't keep readin it */
				 }
				 fclose(fopen("./copyover.ready", "w" ));
			}
			if(file_exists("./copyover.done" ) )
			{	unlink("./copyover.done" );
				exit(1);
			}
		}
	/*
	 * Poll all active descriptors.
	 */
	FD_ZERO(&in_set);
	FD_ZERO(&out_set);
	FD_ZERO(&exc_set);
	FD_SET(control, &in_set);
	maxdesc = control;
	for (d = descriptor_list; d; d = d->next)
	{
	    maxdesc = UMAX(maxdesc, d->descriptor);
	    FD_SET(d->descriptor, &in_set);
	    FD_SET(d->descriptor, &out_set);
	    FD_SET(d->descriptor, &exc_set);
	}

	if (select(maxdesc + 1, &in_set, &out_set, &exc_set, &null_time) < 0)
	{
	    perror("Game_loop: select: poll");
	    exit(1);
	}

	/*
	 * New connection?
	 */
	if (FD_ISSET(control, &in_set))
	    new_descriptor(control);

	/*
	 * Kick out the freaky folks.
	 */
	for (d = descriptor_list; d != NULL; d = d_next)
	{
	    d_next = d->next;
	    if (FD_ISSET(d->descriptor, &exc_set))
	    {
		FD_CLR(d->descriptor, &in_set);
		FD_CLR(d->descriptor, &out_set);
		d->outtop = 0;
		close_socket(d);
	    }
	}

	/*
	 * Process input.
	 */
	for (d = descriptor_list; d != NULL; d = d_next)
	{
	    d_next = d->next;
	    d->fcommand = false;

	    if (FD_ISSET(d->descriptor, &in_set))
	    {
		if (!read_from_descriptor(d))
		{
		    FD_CLR(d->descriptor, &out_set);
		    d->outtop = 0;
		    if(d->account)
		    {	infoChan("%s has left the MUD-Con.", d->account->name);
			free_account(d->account);
		    }
		    close_socket(d);
		    continue;
		}
	    }

	    read_from_buffer(d);
	    if (d->incomm[0] != '\0')
	    {
		d->fcommand = true;

		if (d->connected == CON_OOC_CHAT)
		{
			if ( d->pString )
				string_add( d->account, d->incomm );
			else if(d->account && d->account->buffer )
				d->account->page();
			else if (d->showstr_point)
				show_string(d, d->incomm);
			else 
				interp_acnt_cmd( d->account, d->incomm );
		}
		else
			account_gen(d, d->incomm);

		d->incomm[0] = '\0';
	    }
	}



	/*
	 * Autonomous game motion.
	 */


	/*
	 * Output.
	 */
	for (d = descriptor_list; d != NULL; d = d_next)
	{
	    d_next = d->next;

	    if ((d->fcommand || d->outtop > 0)
		&& FD_ISSET(d->descriptor, &out_set))
	    {
		if (!process_output(d, true))
		{
		    d->outtop = 0;
		    close_socket(d);
		}
	    }
	}



	/*
	 * Synchronize to a clock.
	 * Sleep( last_time + 1/PULSE_PER_SECOND - now ).
	 * Careful here of signed versus unsigned arithmetic.
	 */
	{
	    struct timeval now_time;
	    long secDelta;
	    long usecDelta;

	    gettimeofday(&now_time, NULL);
	    usecDelta = ((int) last_time.tv_usec) - ((int) now_time.tv_usec)
	      + 1000000 / PULSE_PER_SECOND;
	    secDelta = ((int) last_time.tv_sec) - ((int) now_time.tv_sec);
	    while (usecDelta < 0)
	    {
		usecDelta += 1000000;
		secDelta -= 1;
	    }

	    while (usecDelta >= 1000000)
	    {
		usecDelta -= 1000000;
		secDelta += 1;
	    }

	    if (secDelta > 0 || (secDelta == 0 && usecDelta > 0))
	    {
		struct timeval stall_time;

		stall_time.tv_usec = usecDelta;
		stall_time.tv_sec = secDelta;
		if (select(0, NULL, NULL, NULL, &stall_time) < 0)
		{
		    perror("Game_loop: select: stall");
		    exit(1);
		}
	    }
	}

	gettimeofday(&last_time, NULL);
	current_time = (time_t) last_time.tv_sec;
    }

    return;
}



void new_descriptor(int control)
{
    static DESCRIPTOR_DATA d_zero;
    char buf[MAX_STRING_LENGTH];
    DESCRIPTOR_DATA *dnew;
    struct sockaddr_in sock;
    struct hostent *from;
    int desc;
    int size;

    size = sizeof(sock);
    getsockname(control, (struct sockaddr *) &sock, (socklen_t *) & size);
    if (
	(desc =
	 accept(control, (struct sockaddr *) &sock,
		(socklen_t *) & size)) < 0)
    {
	perror("New_descriptor: accept");
	return;
    }

#if !defined(FNDELAY)
#define FNDELAY O_NDELAY
#endif

    if (fcntl(desc, F_SETFL, FNDELAY) == -1)
    {
	perror("New_descriptor: fcntl: FNDELAY");
	return;
    }

    /*
     * Cons a new descriptor.
     */
    if (descriptor_free == NULL)
    {
	dnew = (DESCRIPTOR_DATA *) alloc_perm(sizeof(*dnew));
    }
    else
    {
	dnew = descriptor_free;
	descriptor_free = descriptor_free->next;
    }

    *dnew = d_zero;
    dnew->descriptor = desc;
    dnew->connected = CON_GET_ACCOUNT_NAME;
    dnew->showstr_head = NULL;
    dnew->showstr_point = NULL;
    dnew->outsize = 2000;
    dnew->outbuf = (char *) alloc_mem(dnew->outsize);
	dnew->pEdit = NULL;
	dnew->pString = NULL;
    size = sizeof(sock);
    if (getpeername(desc, (struct sockaddr *) &sock, (socklen_t *) & size) <
	0)
    {
	perror("New_descriptor: getpeername");
	dnew->host = str_dup("(unknown)");
    }
    else
    {
	/*
	 * Would be nice to use inet_ntoa here but it takes a struct arg,
	 * which ain't very compatible between gcc and system libraries.
	 */
	int addr;

	addr = ntohl(sock.sin_addr.s_addr);
	sprintf(buf, "%d.%d.%d.%d",
		(addr >> 24) & 0xFF, (addr >> 16) & 0xFF,
		(addr >> 8) & 0xFF, (addr) & 0xFF);
	from = gethostbyaddr((char *) &sock.sin_addr,
			     sizeof(sock.sin_addr), AF_INET);
	dnew->host = str_dup(from ? from->h_name : buf);
    }

    /*
     * Init descriptor data.
     */
    dnew->next = descriptor_list;
    descriptor_list = dnew;

    /*
     * Send the greeting.
     */
    {
		write_to_buffer(dnew, help_greeting, 0);
    }

    return;
}



void close_socket(DESCRIPTOR_DATA * dclose)
{
    if (dclose->outtop > 0)
		process_output(dclose, false);

    if (dclose->snoop_by != NULL)
    	write_to_buffer(dclose->snoop_by, "Your victim has left the game.\n\r", 0);
    
    {
		DESCRIPTOR_DATA *d;

		for (d = descriptor_list; d != NULL; d = d->next)
		{	if (d->snoop_by == dclose)
				d->snoop_by = NULL;
		}
	}

    if (d_next == dclose)
		d_next = d_next->next;

    if (dclose == descriptor_list)
    {	descriptor_list = descriptor_list->next;
	}
    else
    {	DESCRIPTOR_DATA *d;
		for (d = descriptor_list; d && d->next != dclose; d = d->next);
		
		if (d != NULL)
			d->next = dclose->next;
    }

    close(dclose->descriptor);
    free_string(dclose->host);
    dclose->next = descriptor_free;
    descriptor_free = dclose;
    return;
}



bool read_from_descriptor(DESCRIPTOR_DATA * d)
{
    int iStart;

    /* Hold horses if pending command already. */
    if (d->incomm[0] != '\0')
	return true;

    /* Check for overflow. */
    iStart = strlen(d->inbuf);
    if (iStart >= (int) sizeof(d->inbuf) - 10)
    {
	write_to_descriptor(d->descriptor,
			    "\n\r*** PUT A LID ON IT!!! ***\n\r", 0);
	return false;
    }

    /* Snarf input. */
    for (;;)
    {
	int nRead;

	nRead = read(d->descriptor, d->inbuf + iStart,
		     sizeof(d->inbuf) - 10 - iStart);
	if (nRead > 0)
	{
	    iStart += nRead;
	    if (d->inbuf[iStart - 1] == '\n' || d->inbuf[iStart - 1] == '\r')
		break;
	}
	else if (nRead == 0)
	    return false;
	else if (errno == EWOULDBLOCK)
	    break;
	else
	{
	    perror("Read_from_descriptor");
	    return false;
	}
    }

    d->inbuf[iStart] = '\0';
    return true;
}



/*
 * Transfer one line from input buffer to input line.
 */
void read_from_buffer(DESCRIPTOR_DATA * d)
{
    int i, j, k;

    /*
     * Hold horses if pending command already.
     */
    if (d->incomm[0] != '\0')
	return;

    /*
     * Look for at least one new line.
     */
    for (i = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++)
    {
	if (d->inbuf[i] == '\0')
	    return;
    }

    /*
     * Canonical input processing.
     */
    for (i = 0, k = 0; d->inbuf[i] != '\n' && d->inbuf[i] != '\r'; i++)
    {
	if (k >= MAX_INPUT_LENGTH - 2)
	{
	    write_to_descriptor(d->descriptor, "Line too long.\n\r", 0);

	    /* skip the rest of the line */
	    for (; d->inbuf[i] != '\0'; i++)
	    {
		if (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
		    break;
	    }
	    d->inbuf[i] = '\n';
	    d->inbuf[i + 1] = '\0';
	    break;
	}

	if (d->inbuf[i] == '\b' && k > 0)
	    --k;
	else if (isascii(d->inbuf[i]) && isprint(d->inbuf[i]))
	    d->incomm[k++] = d->inbuf[i];
    }

    /*
     * Finish off the line.
     */
    if (k == 0)
	d->incomm[k++] = ' ';
    d->incomm[k] = '\0';

    /*
     * Deal with bozos with #repeat 1000 ...
     */
    if (k > 1 || d->incomm[0] == '!')
    {
	if (d->incomm[0] != '!' && strcmp(d->incomm, d->inlast))
	{
	    d->repeat = 0;
	}
	else
	{
	    if (++d->repeat >= 20)
	    {
		write_to_descriptor(d->descriptor,
				    "\n\r*** PUT A LID ON IT!!! ***\n\r", 0);
		strcpy(d->incomm, "quit");
	    }
	}
    }

    /*
     * Do '!' substitution.
     */
    if (d->incomm[0] == '!')
	strcpy(d->incomm, d->inlast);
    else
	strcpy(d->inlast, d->incomm);

    /*
     * Shift the input buffer.
     */
    while (d->inbuf[i] == '\n' || d->inbuf[i] == '\r')
	i++;
    for (j = 0; (d->inbuf[j] = d->inbuf[i + j]) != '\0'; j++)
	;
    return;
}



/*
 * Low level output function.
 */
bool process_output(DESCRIPTOR_DATA * d, bool fPrompt)
{
    extern bool merc_down;
    ACCOUNT *pAcnt;
    /*
     * Bust a prompt.
     */
    if (fPrompt && !merc_down )
    {	if ( ( pAcnt = d->account ) && pAcnt->buffer )
	{	if(!pAcnt->buffer->isBlob)
		ptc(pAcnt, "{r[{WPress Enter to Continue{r]---[{W%d{r/{D%d{r]{x\n\r", pAcnt->buffer->pos, pAcnt->buffer->lines.size() );
	} 

    	else if( d->connected == CON_OOC_CHAT )
	{	if(d->pString )
		{	write_to_buffer(d, ">", 0 );
			write_to_buffer(d, go_ahead_str, 0);
		}
		else
		{	write_to_buffer(d, "\n\r\n\r--> ", 0);
			write_to_buffer(d, go_ahead_str, 0);
		}
	}
    }
    /*
     * Short-circuit if nothing to write.
     */
    if (d->outtop == 0)
	return true;

    /*
     * Snoop-o-rama.
     */
    if (d->snoop_by != NULL)
    {
	write_to_buffer(d->snoop_by, "% ", 2);
	write_to_buffer(d->snoop_by, d->outbuf, d->outtop);
    }

    /*
     * OS-dependent output.
     */
    if (!write_to_descriptor(d->descriptor, d->outbuf, d->outtop))
    {
	d->outtop = 0;
	return false;
    }
    else
    {
	d->outtop = 0;
	return true;
    }
}

/*
 * Append onto an output buffer.
 */
void write_to_buffer(DESCRIPTOR_DATA * d, const char *txt, int length)
{	const char *tmp;
	tmp = colour_string(txt);
	length = strlen(tmp);

    /*
     * Initial \n\r if needed.
     */
    if (d->outtop == 0 && !d->fcommand)
    {
	d->outbuf[0] = '\n';
	d->outbuf[1] = '\r';
	d->outtop = 2;
    }

    /*
     * Expand the buffer as needed.
     */
    while (d->outtop + length >= d->outsize)
    {
	char *outbuf;

	outbuf = (char *) alloc_mem(2 * d->outsize);
	strncpy(outbuf, d->outbuf, d->outtop);
	free_mem(d->outbuf, d->outsize);
	d->outbuf = outbuf;
	d->outsize *= 2;
    }

    /*
     * Copy.
     */
    strcpy(d->outbuf + d->outtop, tmp);
    d->outtop += length;
    return;
}



/*
 * Lowest level output function.
 * Write a block of text to the file descriptor.
 * If this gives errors on very long blocks (like 'ofind all'),
 *   try lowering the max block size.
 */
bool write_to_descriptor(int desc, char *txt, int length)
{	
    int iStart;
    int nWrite;
    int nBlock;

    if (length <= 0)
	length = strlen(txt);

    for (iStart = 0; iStart < length; iStart += nWrite)
    {
	nBlock = UMIN(length - iStart, 4096);
	if ((nWrite = write(desc, txt + iStart, nBlock)) < 0)
	{
	    logfp(LOG_BUG, "Write_to_descriptor: Bad Descriptor - %s", txt);
	    return false;
	}
    }

    return true;
}



/*
 * Parse a name for acceptability.
 */
bool check_parse_name(char *name)
{
    /*
     * Length restrictions.
     */
    if (strlen(name) < 3)
	return false;

    if (strlen(name) > 12)
	return false;

    /*
     * Alphanumerics only.
     * Lock out IllIll twits.
     */
    {
	char *pc;
	bool fIll;

	fIll = true;
	for (pc = name; *pc != '\0'; pc++)
	{
	    if (!isalpha(*pc))
		return false;
	    if (LOWER(*pc) != 'i' && LOWER(*pc) != 'l')
		fIll = false;
	}

	if (fIll)
	    return false;
    }

    return true;
}

/* The heart of the pager.  Thanks to N'Atas-Ha, ThePrincedom
   for porting this SillyMud code for MERC 2.0 and laying down the groundwork.
   Thanks to Blackstar, hopper.cs.uiowa.edu 4000 for which
   the improvements to the pager was modeled from.  - Kahn */

void show_string(struct descriptor_data *d, char *input)
{
    char buffer[MAX_STRING_LENGTH];
    char buf[MAX_INPUT_LENGTH];
    register char *scan, *chk;
    int lines = 0, toggle = 1;

    one_argument(input, buf);

    switch (UPPER(buf[0]))
    {
    case '\0':
    case 'C':			/* show next page of text */
	lines = 0;
	break;

    case 'R':			/* refresh current page of text */
	lines = -1 - (d->pagelen);
	break;

    case 'B':			/* scroll back a page of text */
	lines = -(2 * d->pagelen);
	break;

    case 'H':			/* Show some help */
	write_to_buffer(d,
			"C, or Return = continue, R = redraw this page,\n\r",
			0);
	write_to_buffer(d,
			"B = back one page, H = this help, Q or other keys = exit.\n\r\n\r",
			0);
	lines = -1 - (d->pagelen);
	break;

    default:			/*otherwise, stop the text viewing */
	if (d->showstr_head)
	{
	    free_string(d->showstr_head);
	    d->showstr_head = str_dup("");
	}
	free_string(d->showstr_point);
	d->showstr_point = str_dup("");
	return;

    }

    /* do any backing up necessary */
    if (lines < 0)
    {
	for (scan = d->showstr_point; scan > d->showstr_head; scan--)
	    if ((*scan == '\n') || (*scan == '\r'))
	    {
		toggle = -toggle;
		if (toggle < 0)
		    if (!(++lines))
			break;
	    }
	d->showstr_point = scan;
    }

    /* show a chunk */
    lines = 0;
    toggle = 1;
    for (scan = buffer;; scan++, d->showstr_point++)
	if (((*scan = *d->showstr_point) == '\n' || *scan == '\r')
	    && (toggle = -toggle) < 0)
	    lines++;
	else if (!*scan || (lines >= d->pagelen))
	{

	    *scan = '\0';
	    write_to_buffer(d, buffer, strlen(buffer));

	    /* See if this is the end (or near the end) of the string */
	    for (chk = d->showstr_point; isspace(*chk); chk++);
	    if (!*chk)
	    {
		if (d->showstr_head)
		{
		    free_string(d->showstr_head);
		    d->showstr_head = 0;
		}
		d->showstr_point = 0;
	    }
	    return;
	}

    return;
}
/*
 * Returns an initial-capped string.
 */
char *capitalize( const char *str )
{
    static char strcap[MAX_STRING_LENGTH];
    int i;

    for ( i = 0; str[i] != '\0'; i++ )
	strcap[i] = LOWER(str[i]);
    strcap[i] = '\0';
    strcap[0] = UPPER(strcap[0]);
    return strcap;
}

const	struct log_type log_table[] =
{	{ LOG_BUG,  "[BUG] - ",	"../log/bug.log"	},
	{ LOG_SITE, "[SITE] - ",  "../log/connect.log"	},
	{ LOG_CMD,  "[CMD] - ", "../log/command.log"	},
	{ LOG_TYPO, "[TYPO] - ", "../log/typo.log"	},
	{ LOG_BANS, "[BANS] - ", "../log/bans.log"	},
	{ LOG_MAX, NULL, NULL }
};

void logfp(int log_value, char *fmt, ... )
{	FILE *fp;
    va_list args;
    char buf[MSL];
	char string[MSL];
	int log_entry = 0;
	bool logfpound = false;
    struct tm *time_str;
	char crs_time[MSL];

	va_start(args, fmt);
    vsprintf(string, fmt, args);
    va_end(args);
	
	for( log_entry = 0; log_entry < LOG_MAX; log_entry++ )
	{
	    if( log_table[log_entry].log_value == log_value )
	    {
		logfpound = true;
		break;
	    }
	}

	if( !logfpound )
	{
	    sprintf( buf, "Invalid log type - %d", log_value );
	    perror(buf);
	    return;
	}
	 
	if(!file_exists(log_table[log_value].path ) )
	{	if( ( fp = fopen( log_table[log_value].path, "w" )  ) == NULL )
		{	perror(log_table[log_value].path);
			return;
		}
	}
	else
	{	if( ( fp = fopen( log_table[log_value].path, "a" ) ) == NULL )
		{	perror(log_table[log_value].path);
			return;
		}
	}
	time_str = localtime( &current_time );
   	strftime (crs_time, 256, "[%B %d %Y %l:%M %p] ", time_str);
	fprintf(fp, "%s%s%s\n", crs_time, log_table[log_value].string, string );
	fclose(fp);
	return;
}

bool file_exists( const char *path )
{
    FILE *fp = NULL;

    if((fp = fopen(path, "r")) != NULL)
    {
	fclose(fp);
	return true;
    }

    return false;
}


/* I know! I know! A cheap hack. But eh. Bite me :P */
void acnt_act(ACCOUNT *ch, void *vo, int to_flag, char *fmt, ... ) 
{	ACCOUNT *victim = (ACCOUNT *) vo; 
 	DESCRIPTOR_DATA *d; 
	va_list ap; /* points to each unnamed arg in turn */ 
	char *p, *sval; 
 	char buf[MSL], buf2[MSL], tmp[MSL]; 
	char *string; 


	va_start(ap, fmt);
    vsprintf(buf, fmt, ap);
    va_end(ap);
	tmp[0] = '\0';

	string = tmp; 
       

 
	if(!ch) 
	{	logfp(LOG_BUG, "APRINTF: NULL ch pointer passed through command" );
		return; 
	}
	for(p = buf; *p; p++)  
	{	sval = NULL;
		if(*p != '$')  
        {	*string++ = *p; 
            continue; 
		} 
		switch (*++p)  
        {	case 'n': 
				sval = buf2;
				sprintf(sval, "%s", ch->name ); 
                break; 
			case 'N': 
				sval = buf2;
                if(!victim) 
					sprintf(sval, "%c", *p); 
				else
					sprintf(sval, "%s", victim->name); 
					break; 
			default: 
				sval = buf2;
				sprintf(sval, "$%c", *p);
				break; 
		} 
		while(*sval && sval)
		*string++ = *sval++;
	} 
	*string = '\0';
	sprintf(buf, "%s", tmp);
    switch(to_flag ) 
    {	case TO_CHAR: 
			if(!ch) 
				return; 
			write_to_buffer(ch->desc, buf, 0); 
            break; 
		case NOTCHAR_WORLD: 
			for( d = descriptor_list; d; d = d->next ) 
			{	ACCOUNT *wch; 
				if(d->connected != CON_OOC_CHAT ) 
					continue; 
                wch = d->account; 
                if(wch == ch ) 
					continue; 
                write_to_buffer(wch->desc, buf, 0); 
            } 
            break; 
		case NOTVICT_WORLD: 
				for( d= descriptor_list;d; d = d->next ) 
                {	ACCOUNT *wch; 
                    if( d->connected != CON_OOC_CHAT ) 
						continue; 
                    wch = d->account; 
                    if( wch == victim ) 
						continue; 
                    write_to_buffer(wch->desc, buf, 0); 
                } 
                break;
		case NOTARGET_WORLD:
				for( d= descriptor_list;d; d = d->next ) 
                {	ACCOUNT *wch; 
                    if( d->connected != CON_OOC_CHAT ) 
						continue; 
                    wch = d->account;
                    if( wch == victim || wch == ch) 
						continue; 
                    write_to_buffer(wch->desc, buf, 0); 
                } 
                break;
		case TO_VICT: 
                if(!victim) 
                     return; 
                write_to_buffer(victim->desc, buf, 0); 
                break; 
		case TO_WORLD:
				for( d= descriptor_list;d; d = d->next ) 
                {	ACCOUNT *wch; 
                    if( d->connected != CON_OOC_CHAT ) 
						continue; 
                    wch = d->account;
                    write_to_buffer(wch->desc, buf, 0); 
                }
				break;
		default: break;
      } 

      return; 
}

void recover_accounts()
{	DESCRIPTOR_DATA *d;
	FILE *fp;
	int desc = -1;
	char name [MSL];
	char host[MSL];
	ACCOUNT *wch;
	
	if( ( fp = fopen("./copyover.acnt", "r" ) ) == NULL )
	{	perror("./copyover.acnt" );
		fclose(fopen("./copyover.fail", "w" ) ); /* If the dat file aint there. We gotta abort! */
		exit(1);
	}
	for(;;)
	{	fscanf (fp, "%d %s %s\n", &desc, name, host);

		if (desc == -1)
			break;
		/* Little test to make sure that the character's there */
		if (!write_to_descriptor (desc, "The Darkness shifts in the realm. It is renewed.\n\r",0))
		{	close (desc); /* nope */
			continue;
		}
		d = copyover_desc();
		d->descriptor = desc;
		
		d->host = str_dup (host);
		d->next = descriptor_list;
		descriptor_list = d;
		d->connected = CON_OOC_CHAT; 
		if( ( wch = load_account(d, name ) ) == NULL )
		{	write_to_descriptor( desc, "Your player was not found. Sorry. Try re-creating!\n\r",0 );
			close_socket(d);
			continue;
		}
		write_to_buffer(d, go_ahead_str, 0);		
	}

	unlink("./copyover.acnt"); /* Make sure we don't read it if it crashes */
	return;
}

void ptc (ACCOUNT *ch, char *fmt, ...)
{
          char buf [MAX_STRING_LENGTH];
          va_list args;
          va_start (args, fmt);
          vsprintf (buf, fmt, args);
          va_end (args);

          write_to_buffer(ch->desc, buf, 0);
       
}

void seperateArgs(char *string, char *arg1, char *arg2)
{	bool after = false;

	for( ; *string != '\0' ; ++string )
	{	if(*string == ':' && !after)
		{	after = true;
			continue;
		}
		if(after)
		{	*arg2 = *string;
			++arg2;
		}
		else
		{	*arg1 = *string;
			++arg1;
		}
	}
	*arg1 = '\0';
	*arg2 = '\0';
}

void disarmString(char *string)
{	for( ;*string != '\0' ; ++string)
	{	if(*string == '`' || *string == '\'')
			*string = '\"';
	}
}

void addAttendant(ACCOUNT *pAcnt)
{	Attendant *attnd = new Attendant();
	free_string(attnd->name);
	free_string(attnd->mud);
	free_string(attnd->url);
	attnd->name = str_dup(pAcnt->name);
	attnd->url = str_dup(pAcnt->url);
	attnd->mud = str_dup(pAcnt->mud);
	attendant_list.push_back(attnd);
	char query[MSL];
	sprintf(query, "INSERT INTO `Attendants` ( `name`, `mud`, `url`, `id` ) VALUES ('%s', '%s', '%s', '');", attnd->name, attnd->mud, attnd->url );
	if( mysql_real_query(db, query, strlen(query) ) )
		logfp(LOG_BUG, "addAttendant: %s", mysql_error(db) );

}

void initMysql(char *name, char *database, char *passwd, char *host)
{	db = mysql_init(db);
	if( !( db = mysql_real_connect(db, host, name, passwd, database, 0, NULL, 0 ) ) )
	{	logfp(LOG_BUG, "initMysql: %s", mysql_error(db) );
		abort();
	}

	logfp(LOG_BUG, "initMysql: Database Enabled");
	return;
}
char *getline( char *str, char *buf );
/*Buffer Stuffer */
void Buffer::Add(char *txt, ... )
{	char buf[MAX_STRING_LENGTH], *ptr;
	char line[MSL];
	va_list args;
	va_start (args, txt);
	vsprintf (buf, txt, args);
	va_end (args);
	isBlob = false;
	ptr = buf;
	finish = 0;
	while(*ptr)
	{	std::string *str;
		ptr = getline(ptr, line);
		str = new std::string(line);
		lines.push_back(str);
	}
}

void Buffer::AddBlob( char *blob )
{	char line[MSL];
	isBlob = true;
	while(*blob)
	{	std::string *str;
		blob = getline(blob, line);
		str = new std::string(line);
		lines.push_back(str);
	}
	return;
}

void Buffer::Empty()
{	std::list<std::string *>::iterator i, last;
	std::string *ptr;
	for( i = lines.begin() ; i != lines.end() ; )
	{	ptr = (*i);
		last = i;
		++i;
		lines.erase(last);
		delete ptr;
	}
}

void Buffer::Send(ACCOUNT *pAcnt)
{	int atATime = 20;
	int start = 0;
	int length;
	if(!isBlob)
		length = (finish ? finish : lines.size());
	else
		length = pAcnt->bufEnd;
	std::list<std::string *>::iterator i;
	i = lines.begin();
	for(start = 0; start < (isBlob ? pAcnt->bufPos : pos) ; start++, i++);
	for(;(isBlob ? pAcnt->bufPos : pos) < start+atATime && (isBlob ? pAcnt->bufPos : pos) < length ; (isBlob ? ++pAcnt->bufPos : ++pos), i++)
		if(!isBlob)
			ptc(pAcnt, "%s\n\r", (*(i))->c_str());
		else
			ptc(pAcnt, "{D%-4d{r|{x %s\n\r", pAcnt->bufPos+1, (*(i))->c_str());

}

CodeBlob::CodeBlob(MYSQL_ROW row)
{	id = atoi(row[0]);
	language = str_dup(row[1]);
	subject = str_dup(row[2]);
	poster = str_dup(row[3]);
	blob = parseHtmlString(row[4]);
	codeList.push_back(this);
	buffer = new Buffer();
	buffer->AddBlob(blob);
}

void infoChan(char *txt, ...)
{	char buf[MAX_STRING_LENGTH];
	va_list args;
	va_start (args, txt);
	vsprintf (buf, txt, args);
	va_end (args);
	
	for(ACCOUNT *pAcnt = account_list ; pAcnt ; pAcnt = pAcnt->next)
		if(pAcnt->desc->connected == CON_OOC_CHAT)
			ptc(pAcnt, "{r[{WInfo{r] {D%s\n\r", buf);
}

void ACCOUNT::sendChanHelp()
{	INIT_BUFFER(this);
buffer->Add("                           {r[{D==={r]{WPlease Read{r[{D==={r]                                \n\r");
buffer->Add("{WWell! I hope you take that little warning into consideration, and do read this! \n\r");
buffer->Add("or you'll be totally lost once you enter this MudCon and realize that the channels\n\r");
buffer->Add("don't work for you to well! Now, first off let me go off by saying welcome to   \n\r");
buffer->Add("MudCon V! This is my(Davion) first time hosting one of these things, and I hope \n\r");
buffer->Add("it goes off without a hitch! Now, lets just get to the point so you can go on in\n\r");
buffer->Add("and begin chattin away!                                                         \n\r");
buffer->Add("                         {r[{D==={r]{WThe Channel Code{r[{D==={r]                             \n\r");
buffer->Add("{WMany hours went into the creation of these channels. They are slightly more     \n\r");
buffer->Add("sophisticated than your normal code. If you've talked to me, or read somethings \n\r");
buffer->Add("I've posted, you've heard me refer to them as a living forum, if you haven't, you\n\r");
buffer->Add("have now! What I mean by this is it flows like a forum. Every thing you say over\n\r");
buffer->Add("the 11 designated channels is a reply to something on a topic. Does it have to be?\n\r");
buffer->Add("Yes. Why? Because it's the only solution I could come up with! Now don't quit!  \n\r");
buffer->Add("I have set things up so it's not to hard to flow into a conversation without a  \n\r");
buffer->Add("bunch of annoying syntax to bend your fingers around. This is going to be a bit \n\r");
buffer->Add("long, but bare with me, it'll be easier in the end. I will show you all the little\n\r");
buffer->Add("shortcuts I've put in to help you out. These might not be so obvious and easy to\n\r");
buffer->Add("pick up as you go! So lets get started! We're going to be using the Legal channel\n\r");
buffer->Add("as the example for this. Why? Because it has the shortest name!                 \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("First off, lets show you how to start a new topic! It's gunna be used frequently\n\r");
buffer->Add("(I hope). First off you need a subject, and the content for the first post (forum\n\r");
buffer->Add("like!) So! I have a question on the Diku license. That will be the subject, and \n\r");
buffer->Add("the content will be my question. To do that                                     \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal !new Diku Licence Question:Do I have to leave the credits in?!            \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WYou seperate the subject, and the content of your first post with a colon(:).  \n\r");
buffer->Add("Once that is executed, everyone on the MUD with the legal channel turned on will\n\r");
buffer->Add("recieve a notice that says you started a new Topic on the Legal channel, state  \n\r");
buffer->Add("the subject, and show the first post. It'll look something like                 \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{r[{WLegal{r] {WDavion has started a new Topic{r[{WT{D:{W1{r]{D:{W Diku Licence Question\n\r");
buffer->Add("{r[{WLegal{r] {r[{WT{D:{W1{r][{WP{D:{W1{r] {WDavion {Dsays{r:{W Do I have to leave the credits in?!\n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WNow with this information, you have enough to reply to this thread! The [T:1][P:1]\n\r");
buffer->Add("part tells you the ID's of the topic (the 'T') and the post (the 'P'). These are\n\r");
buffer->Add("a vital part to start talking. To reply to this thread, we're once again going to\n\r");
buffer->Add("use that ever so useful colon(:) and use it to seperate the topic ID(tid) and  \n\r");
buffer->Add("the post ID(pid). So! Lets reply to this. To do this                            \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal 1:1 Of course you do! Read the license in full!                           \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WAnd you'll get something along the lines of                                     \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{r[{WLegal{r] [{WT{D:{W1{r][{WP{D:{W2{r]{W In reply to {DDavion{r[{WP{D:{W1{r]{W %s {Dsays{r:{W Of course you do! Read\n\r",name);
buffer->Add("the license in full!                                                            \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WOk! I know I said there'd be no cumbersum syntax to bend your fingers around, so\n\r");
buffer->Add("I bet your thinking, 'Then wtf is 1:1! Cumbersom syntax you lying..', and ya,   \n\r");
buffer->Add("that'd be some syntax, and it is a half lie! I have spend a few hours going over\n\r");
buffer->Add("the idea and came up with some automated features to sort them based on your last\n\r");
buffer->Add("reply. So now! Davion has some stupid rebuddle to make you hate him, and for him\n\r");
buffer->Add("to do that, he mearly types                                                     \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal But I really don't want the credits in!                                   \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WAnd the idiots reply is auto-sorted and thrown into reply to you!              \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{r[{WLegal{r] [{WT{D:{W1{r][{WP{r:{W3{r]{W In reply to {D%s{r[{WP{D:{W2{r] {WDavion {Dsays{r:{W But I really don't want\n\r", name);
buffer->Add("{Wthe credits in!                                                                 \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WNow I don't want to go on like this and show you an entire conversation with many\n\r");
buffer->Add("threads running off this one, but that'd get boring. I'll just tell you the other\n\r");
buffer->Add("short cut I implemented, and that is, you do not have to have both a PID and a  \n\r");
buffer->Add("TID. You can simply use one or the other if you want to aim your post alittle   \n\r");
buffer->Add("better. But make sure to insert the colon(:) so the MUD knows which one your   \n\r");
buffer->Add("providing. So for no TID but a PID, it's                                        \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal :3 I'm out of things to say. Stfu.                                        \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WThat will reply to the 3rd post, on the last topic you replied too. If you use  \n\r");
buffer->Add("the reverse, it'll simply reply to the last post made on that topic. I hope you \n\r");
buffer->Add("don't need an example of that one, cause I aint giving one! It's getting long   \n\r");
buffer->Add("enough and your probably loosing interest! But only two more things! Well, two  \n\r");
buffer->Add("and a half. So lets get to them. Quite obviously if your reading this you have  \n\r");
buffer->Add("just arrived to MudCon for the first time (this should only pop up on character \n\r");
buffer->Add("creation. If your not in character creation, be scared. Be very scared. Anyways,\n\r");
buffer->Add("your going to want to get caught up. There's two ways to do this. The logs are  \n\r");
buffer->Add("live, meaning they are sent directly to the website, or, simply use the history \n\r");
buffer->Add("command implemented directly into the mud. It has two levels two it. One shows  \n\r");
buffer->Add("the last X (default 10) topics which is                                         \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal !history                                                                  \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WAnd you'll get                                                                  \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{WThese are the last 10 topics made on Legal                                      \n\r");
buffer->Add("{r[{WLegal{r][{WT{D:{W1{r] {DDavion{r:{WDiku Licence Question                                       \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WClearly there's only one topic, cause I'm far to lazy to flesh the thing out by \n\r");
buffer->Add("myself, but that's why your reading this, right? Anyways. The next step. You got\n\r");
buffer->Add("the topics, now lets see the posts!                                             \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal 1 !history                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WAnd you get                                                                     \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{WThese are the last 10 posts made on Legal under Diku Licence Question           \n\r");
buffer->Add("{r[{WLegal{r][{WT{D:{W1{r]{r[{WP{D:{W1{r] {DDavion says{r:{W Do I have to leave the credits in?!\n\r");
buffer->Add("{r[{WLegal{r][{WT{D:{W1{r]{r[{WP{D:{W2{r] {WIn reply to {DDavion{r[{WP{D:{W1{r] {D%s says{r: {WOf course you do! Read\n\r", name);
buffer->Add("the license in full!                                                            \n\r");
buffer->Add("{r[{WLegal{r][{WT{D:{W1{r][{WP{D:{W3{r] {WIn reply to {D%s{r[{WP{D:{W2{r]{D Davion says{r:{W But I really don't want\n\r", name);
buffer->Add("the credits in!                                                                 \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WYa, not 10. Bah! Sue me!... Wait. Please don't! Ok! That's one and a half things\n\r");
buffer->Add("down! Last thing! Now I know you don't want to be spammed with useless topics   \n\r");
buffer->Add("and all that jazz, so for your sanity (and mine!) I have implemented a way to   \n\r");
buffer->Add("ignore entire topics. And this is really simple!                                \n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("{dlegal !ignore 1                                                                 \n\r");
buffer->Add("{r[{D=============================================================================={r]\n\r");
buffer->Add("                                                                                \n\r");
buffer->Add("{WThen you'll get some generic message telling you which topic your ignoring. I'd \n\r");
buffer->Add("bore you with it here, but I think I've done enough of that. {DFor some at-a-glance\n\r");
buffer->Add("{Dsyntax help, you can type the channel name with no arguments{W. Anyways. This is   \n\r");
buffer->Add("the end of my little channel tutorial. I hope this system makes sense to you, if\n\r");
buffer->Add("it doesn't, I have a 'chat' channel that works like basic channels! I promise!  \n\r");
buffer->Add("Thank you for reading!                                                          \n\r");
buffer->Add("                             {r[{D==={r]{WEnd[{D==={r{D]                                      \n\r");
return;
}