/*
* 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( ¤t_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;
}