/*
 * ircII: a new irc client.  I like it.  I hope you will too!
 *
 * Written By Michael Sandrof
 * Copyright(c) 1990 
 * See the COPYRIGHT file, or do a HELP IRCII COPYRIGHT 
 *
 * Reformatted by Matthew Green, November 1992.
 */
#include "irc.h"


/*
 * irc_version is what $J returns, its the common-name for the version.
 */
const char irc_version[] = "2.8.2-EPIC3.004";

/*
 * internal_version is what $V returns, its the integer-id for the
 * version, and corresponds to the date of release, YYYYMMDD.
 */ 
const char internal_version[] = "19970129";


#include <sys/stat.h>
#include <pwd.h>
 
#ifdef ISC22
# include <sys/bsdtypes.h>
#endif /* ISC22 */
 
#ifdef ESIX
# include <lan/net_types.h>
#endif /* ESIX */
 
/* typo reported by Antti-Pekka Liedes */
#ifdef USING_CURSES
# include <curses.h>
#endif /* POSIX */
 
#if DO_USER2
# include <setjmp.h>
#endif

#include <stdarg.h>

#include "status.h"
#include "dcc.h"
#include "names.h"
#include "vars.h"
#include "input.h"
#include "alias.h"
#include "output.h"
#include "term.h"
#include "exec.h"
#include "screen.h"
#include "log.h"
#include "server.h"
#include "hook.h"
#include "keys.h"
#include "ircaux.h"
#include "edit.h"
#include "window.h"
#include "history.h"
#include "exec.h"
#include "notify.h"
#include "mail.h"
#include "timer.h"

int		irc_port = IRC_PORT,	/* port of ircd */
		current_on_hook = -1,		/* Used to check for ON */
	use_flow_control = USE_FLOW_CONTROL,	/* true: ^Q/^S used for flow
						 * cntl */
		current_numeric,	/* this is negative of the
					 * current numeric! */
		dumb = 0,		/* if true, IRCII is put in
					 * "dumb" mode */
		no_fork = 0,		/* if true, IRCII won't with
					 * -b or -e */
		background = 0,         /* if true, ircII will fork */
		use_input = 1,		/* if 0, stdin is never
					 * checked */
		waiting = 0,		/* used by /WAIT command */
		key_pressed = 0,	/* set to one when a key is
					 * pressed.  if you set this
					 * to zero and call io(),
					 * when it pops back to one,	
					 * you know a key was pressed
					 * ok? */
		who_mask = 0;		/* keeps track of which /who
					 * switchs are set */

char		oper_command = 0;	/* true just after an oper() command is
					 * given.  Used to tell the difference
					 * between an incorrect password generated by
					 * an oper() command and one generated when
					 * connecting to a new server */
char		global_all_off[2];	/* lame kludge to get around lameness */
char		MyHostName[80];		/* The local machine name. Used by
					 * DCC TALK */
struct	in_addr	MyHostAddr;		/* The local machine address */
struct	in_addr	local_ip_address; 	/* Sometimes the same, sometimes not */
char		*LocalHostName = NULL;	/* Virtual host name */
struct	in_addr	LocalHostAddr;

extern	char	*last_away_nick;

char		*invite_channel = (char *) 0,	/* last channel of an INVITE */
		*ircrc_file = (char *) 0,	/* full path .ircrc file */
		*my_path = (char *) 0,		/* path to users home dir */
		*irc_path = (char *) 0,		/* paths used by /load */
		*irc_lib = (char *) 0,		/* path to the ircII library */
#ifdef ALLOW_LONG_NICKNAMES
		nickname[LONG_NICKNAME_LEN + 1],
#else
		nickname[NICKNAME_LEN + 1],	/* users nickname */
#endif
		hostname[NAME_LEN + 1],		/* name of current host */
		realname[REALNAME_LEN + 1],	/* real name of user */
		username[NAME_LEN + 1],		/* usernameof user */
		*send_umode = NULL,		/* sent umode */
		*last_notify_nick = (char *) 0,	/* last detected nickname */
		empty_string[] = "",		/* just an empty string */
		space[] = " ",			/* just a lonely space */
		*who_name = (char *) 0,		/* extra /who switch info */
		*who_file = (char *) 0,		/* extra /who switch info */
		*who_server = (char *) 0,	/* extra /who switch info */
		*who_host = (char *) 0,		/* extra /who switch info */
		*who_nick = (char *) 0,		/* extra /who switch info */
		*who_real = (char *) 0,		/* extra /who switch info */
		*cannot_open = (char *) 0,	/* extra /who switch info */
		*cut_buffer = (char *) 0;	/* global cut_buffer */

int 		away_set = 0,			/* set if there is an away
						 * message anywhere */
		quick_startup = 0;		/* set if we ignore .ircrc */

time_t		idle_time = 0,			/* amount of time we've been idle */
		start_time;			/* the time when we booted up */
fd_set		readables, writables;
int		child_dead = 0;


/* global function delcarations */
	RETSIGTYPE	cntl_c();
	RETSIGTYPE	sig_user1() ;
	RETSIGTYPE	coredump() ;

static volatile int 	cntl_c_hit = 0;
		int 	x_debug = 0;
extern		char	*compile_info;

static		char	switch_help[] =
"Usage: irc [switches] [nickname] [server list] \n\
  The [nickname] can be at most 9 characters long\n\
  The [server list] is a whitespace separate list of server name\n\
  The [switches] may be any or all of the following\n\
   -c <channel>\tjoins <channel> on startup\n\
   -p <port>\tdefault server connection port (usually 6667)\n\
   -f\t\tyour terminal uses flow controls (^S/^Q), so IRCII shouldn't\n\
   -F\t\tyour terminal doesn't use flow control (default)\n\
   -s\t\tdon't use separate server processes (ircserv) (default)\n\
   -S\t\tuse separate server processes (ircserv)\n\
   -d\t\truns IRCII in \"dumb\" terminal mode\n\
   -q\t\tdoes not load ~/.ircrc\n\
   -a\t\tadds default servers and command line servers to server list\n\
   -x\t\truns IRCII in \"debug\" mode\n\
   -v\t\ttells you about the client's version\n";

static		char	switch_help_l[] =
"   -l <file>\tloads <file> in place of your .ircrc\n\
   -L <file>\tloads <file> in place of your .ircrc and expands $ expandos\n";

/* Someone took this out... I stuck it back in -- Jake [WinterHawk] Khuon */
static char	switch_help_b[] =
	"   -b\t\tforce ircII to fork and return you to shell\n";

RETSIGTYPE irc_exit_old _((int unused))
{
	irc_exit (1, NULL);
}

/* irc_exit: cleans up and leaves */
#if defined(__STDC__) && defined(HAVE_STDARG_H)
void irc_exit (int really_quit, char *format, ...)
#else
void irc_exit (really_quit, format, arg0, arg1, arg2, arg3, arg4, arg5)
int really_quit;
char *format, *arg0, *arg1, *arg2, *arg3, *arg4, *arg5;
#endif
{
	char buffer[BIG_BUFFER_SIZE];
	int old_window_display = window_display;

	if (format)
	{
#if defined(__STDC__) && defined(HAVE_STDARG_H)
		va_list arglist;
		va_start(arglist, format);
		vsprintf(buffer, format, arglist);
		va_end(arglist);
#else
		sprintf(buffer, format, arg0, arg1, arg2, arg3, arg4, arg5);
#endif
	}
	else
	{
		sprintf(buffer, 
#ifdef WINTRHAWK
	"ircII %s taking fire! Mayday Mayday Mayday!!! Eject Eject Eject!!!", 
#else
	"%s -- just do it.",
#endif
				irc_version);
	}

	do_hook(EXIT_LIST, "%s", buffer);
	close_server(-1, buffer);
	logger(0);
	set_history_file((char *) 0);
	clean_up_processes();
	if (!dumb)
	{
		cursor_to_input();	/* Needed so that ircII doesn't gobble
					 * the last line of the kill. */
		term_cr();
		if (term_clear_to_eol())
			term_space_erase(0);
		term_reset();
#if defined(_HPUX_SOURCE) || defined(ESIX)
		endwin();		/* Added for curses */
#ifdef ESIX
		system("tput reset");
		new_stty("sane");
#endif /* ESIX */
#endif /* defined(_HPUX_SOURCE) || defined(ESIX) */
	}

	/* Try to free as much memory as possible */
	window_display = 0;
	flush_aliases(NULL, NULL, NULL);
	set_lastlog_size(0);
	set_history_size(0);
	remove_channel(NULL, 0);
	really_free(0);
#ifdef WIND_STACK
	destroy_call_stack();
#endif
	window_display = old_window_display;
	fprintf(stdout, "\r");
	fflush(stdout);

	if (really_quit)
		exit(0);
}

/* This is needed so that the fork()s we do to read compressed files dont
 * sit out there as zombies and chew up our fd's while we read more.
 */
#ifdef __STDC__
RETSIGTYPE child_reap (int sig)
#else
RETSIGTYPE child_reap (sig)
int sig;
#endif
{
	child_dead++;
}

static int segv_recurse = 0;

/* sigsegv: something to handle segfaults in a nice way */
#ifdef __STDC__
RETSIGTYPE coredump (int sig)
#else
RETSIGTYPE coredump(sig)
	int	sig;
#endif
{
	if (segv_recurse)
		_exit(1); /* recursive coredumps are bad. */

	segv_recurse = 1;
	printf("\
									\n\
									\n\
									\n\
* * * * * * * * * * * * * * * * * * * * * * * *				\n\
IRC-II has trapped a critical protection error.				\n\
This is probably due to a bug in the program.				\n\
									\n\
If you have access to the 'BUG_FORM' in the ircII source distribution,	\n\
we would appreciate your filling it out if you feel doing so would	\n\
be helpful in finding the cause of your problem.			\n\
									\n\
If you do not know what the 'BUG_FORM' is or you do not have access	\n\
to it, please dont worry about filling it out.  You might try talking	\n\
to the person who is in charge of IRC at your site and see if you can	\n\
get them to help you.							\n\
									\n\
This version of IRC II is  --->[%s]					\n\
The date of release is     --->[%s]					\n\
									\n\
* * * * * * * * * * * * * * * * * * * * * * * *				\n\
The program will now terminate.						\n", irc_version, internal_version);

	fflush(stdout);
#ifdef WIND_STACK
	panic_dump_call_stack();
#endif
	irc_exit(1, "Hmmm. EPIC has another bug. Go figure...");
}

/*
 * quit_response: Used by irc_io when called from irc_quit to see if we got
 * the right response to our question.  If the response was affirmative, the
 * user gets booted from irc.  Otherwise, life goes on. 
 */
#ifdef __STDC__
static void quit_response (char *dummy, char *ptr)
#else
static	void quit_response(dummy, ptr)
	char	*dummy;
	char	*ptr;
#endif
{
	int	len;

	if ((len = strlen(ptr)) != 0)
		if (!my_strnicmp(ptr, "yes", len))
			irc_exit(1, "ircII %s -- Bigger and better then they said it could be done!", irc_version);
}

/* irc_quit: prompts the user if they wish to exit, then does the right thing */
#ifdef __STDC__
extern void irc_quit (char unused, char *not_used)
#else
extern void irc_quit (unused, not_used)
char unused, *not_used;
#endif
{
	static	int in_it = 0;

	if (in_it)
		return;
	in_it = 1;
	add_wait_prompt("Do you really want to quit? ", quit_response, empty_string, WAIT_PROMPT_LINE);
	in_it = 0;
}

/*
 * cntl_c: emergency exit.... if somehow everything else freezes up, hitting
 * ^C five times should kill the program.   Note that this only works when
 * the program *is* frozen -- if it doesnt die when you do this, then the
 * program is functioning correctly (ie, something else is wrong)
 */
RETSIGTYPE
cntl_c _((int unused))
{
#ifdef SYSVSIGNALS
	(void) MY_SIGNAL(SIGINT, cntl_c, 0);
#endif /* SYSVSIGNALS */


	/* after 5 hits, we stop whatever were doing */
	if (cntl_c_hit++ >= 4)
		irc_exit(1, "User pressed ^C five times.");
	else if (cntl_c_hit > 1)
		kill(getpid(), SIGALRM);
}

RETSIGTYPE
nothing _((int unused))
{
#ifdef SYSVSIGNALS
	(void) MY_SIGNAL(SIGALRM, nothing, 0);
#endif
	/* nothing to do! */
}

RETSIGTYPE
sig_user1 _((int unused))
{
#ifdef SYSVSIGNALS
	(void) MY_SIGNAL(SIGUSR1, sig_user1, 0);
#endif /* SYSVSIGNALS */
	say("Got SIGUSR1, closing DCC connections and EXECed processes");
	close_all_dcc();
	close_all_exec();
}

#if DO_USER2
RETSIGTYPE
sig_user2 _((int unused))
{
#ifdef SYSVSIGNALS
	(void) MY_SIGNAL(SIGUSR2, sig_user2, 0);
#endif /* SYSVSIGNALS */
	/* Nothing to do -- this isnt needed since the main loop cant get
	 * hung up any more */
}
#endif 

#ifdef MUNIX
/* A characteristic of PCS MUNIX - Ctrl-Y produces a SIGQUIT */
RETSIGTYPE
cntl_y _((int unused))
{
	(void) MY_SIGNAL(SIGQUIT, cntl_y, 0);
	edit_char((char) 25); /* Ctrl-Y */
}
#endif

static	void	show_version _((void))
{
#ifdef WINTRHAWK
	printf("ircII %s [Internal Release Code: %s]\n\r", irc_version, internal_version);
#else
	printf("ircII %s (Date of release: %s)\n\r",irc_version,internal_version);
#endif
	exit (0);
}

/*
 * parse_args: parse command line arguments for irc, and sets all initial
 * flags, etc. 
 *
 * major rewrite 12/22/94 -jfn
 *
 *
 * Im going to break backwards compatability here:  I think that im 
 * safer in doing this becuase there are a lot less shell script with
 * the command line flags then there are ircII scripts with old commands/
 * syntax that would be a nasty thing to break..
 *
 * Sanity check:
 *   Supported flags: -b, -l, -v, -c, -p, -f, -F, -L, -a, -S, -z
 *   New (changed) flags: -s, -I, -i, -n
 *
 * Rules:
 *   Each flag must be included by a hyphen:  -lb <filename> is not the
 * 		same as -l <filename> -b  any more...
 *   Each flag may or may not have a space between the flag and the argument.
 *   		-lfoo  is the same as -l foo
 *   Anything surrounded by quotation marks is honored as one word.
 *   The -c, -p, -L, -l, -z flags all take arguments.  If no arguments
 *		are given between the flag and the next flag, an error
 * 		message is printed and the program is halted.
 *   Arguments occuring after a flag that does not take an argument
 * 		will be parsed in the following way: the first instance
 *		will be an assumed nickname, and the second instance will
 *		will be an assumed server. (some semblance of back compat.)
 *   The -bl sequence will emit a depreciated feature warning.
 *   The -I flag forces you to become invisible <NOT YET SUPPORTED>
 *   The -i flag forces you to become visible <NOT YET SUPPORTED>
 *   The -X flag forces ircII to become an X application <NOT YET SUPPORTED>
 *   The -s flag means "no ircserv"
 *   The -n flag means "nickname"
 *
 * Bugs:
 *   The -s flag is hard to use without an argument unless youre careful.
 */
#ifdef __STDC__
static	char	*parse_args (char *argv[], int argc)
#else
static	char	*
parse_args(argv, argc)
	char	*argv[];
	int	argc;
#endif
{
	int ac;
	int add_servers = 0;
	struct passwd *entry;
	struct hostent *hp;

	char *channel = (char *) 0;
	char *ptr = (char *) 0;
	*nickname = 0;

	for ( ac = 1; ac < argc; ac++ )
	{
		if (argv[ac][0] == '-')
		{
		    switch (argv[ac][1]) {

			case 'v': /* Output ircII version */
			{
				show_version();
				/* NOTREACHED */
			}

			case 'c': /* Default channel to join */
			{
				char *what = empty_string;

				if (argv[ac][2])
					what = &(argv[ac][2]);
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
				{
					fprintf(stderr, "Missing paramater after -c\n");
					exit(1);
				}
				malloc_strcpy(&channel, what);
				break;
			}

			case 'p': /* Default port to use */
			{
				char *what = empty_string;

				if (argv[ac][2])
					what = &argv[ac][2];
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
				{
					fprintf(stderr, "Missing paramater after -p\n");
					exit(1);
				}
				irc_port = atoi(what);
				break;
			}

			case 'f': /* Use flow control */
			{
				use_flow_control = 1;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -f\n");
				break;
			}

			case 'F': /* dont use flow control */
			{
				use_flow_control = 0;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -F\n");
				break;
			}

			case 'd': /* use dumb mode */
			{
				dumb = 1;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -d\n");
				break;
			}

			case 'l': /* Load some file instead of ~/.ircrc */
			{
				char *what = empty_string;

				if (argv[ac][2])
					what = &argv[ac][2];
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
				{
					fprintf(stderr, "Missing argument to -l\n");
					exit(1);
				}
				malloc_strcpy(&ircrc_file, what);
				break;
			}

			case 'L': /* load and expand */
			{
				char *what = empty_string;

				if (argv[ac][2])
					what = &argv[ac][2];
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
				{
					fprintf(stderr, "Missing argument to -L\n");
					exit(1);
				}
				malloc_strcpy(&ircrc_file, what);
				malloc_strcat(&ircrc_file, " -");
				break;
			}

			case 'a': /* add server, not replace */
			{
				add_servers = 1;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -a\n");
				break;
			}

			case 'q': /* quick startup -- no .ircrc */
			{
				quick_startup = 1;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -q\n");
				break;
			}

			case 's':
			{
				using_server_process = 0;
				break;
			}
			case 'S':
			{
				using_server_process = 1;
				break;
			}
			case 'b':
			{
/* siiiiiiiigh */
#ifndef NO_BOTS
				if (argv[ac][2] && argv[ac][2] != 'l')
					fprintf(stderr, "Ignoring junk after -b\n");
				else if (argv[ac][2] == 'l')
				{
					fprintf(stderr, "Usage of -bl is decprecated: use -b -l instead.\n");
					exit(1);
				}
				dumb = 1;
				use_input = 0;
				background = 1;
#else
				fprintf("This client was compiled to not support the -b flag. Tough for you.\n");
				exit(1);
#endif
				break;
			}

			case 'n':
			{
				char *what = empty_string;

				if (argv[ac][2])
					what = &(argv[ac][2]);
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
				{
					fprintf(stderr,"Missing argument for -n\n");
					exit(1);
				}
				strcpy(nickname, what);
				break;
			}

			case 'x': /* add server, not replace */
			{
				x_debug = 1;
				if (argv[ac][2])
					fprintf(stderr, "Ignoring junk after -a\n");
				break;
			}

			case 'z':
			{
				char *what;
				if (argv[ac][2])
					what = &argv[ac][2];
				else if (argv[ac+1] && argv[ac+1][0] != '-')
				{
					what = argv[ac+1];
					ac++;
				}
				else
					break;
				strmcpy(username, what, NAME_LEN);
				break;
			}

			case '\0': break;	/* ignore - alone */

			default:
			{
				fprintf(stderr, "Unknown flag: %s\n",argv[ac]);
				fprintf(stderr, "%s%s%s", switch_help, switch_help_b, switch_help_l);
				exit(1);
			}
		   } /* End of switch */
		}
		else
		{
			if (*nickname)
				build_server_list(argv[ac]);
			else
				strmcpy(nickname, argv[ac], 
#ifdef ALLOW_LONG_NICKNAMES
							   LONG_NICKNAME_LEN
#else
							   NICKNAME_LEN
#endif
									    );
		}
	}
	/* v-- right there was a '!' that should not have been there. */
	if ((ptr = getenv("IRCLIB")))
	{
		malloc_strcpy(&irc_lib, ptr);
		malloc_strcat(&irc_lib, "/");
	}
	else
		malloc_strcpy(&irc_lib, IRCLIB);

	if (!ircrc_file && (ptr = getenv("IRCRC")))
		malloc_strcpy(&ircrc_file, ptr);

	if (!*nickname && (ptr = getenv("IRCNICK")))
		strmcpy(nickname, ptr, 
#ifdef ALLOW_LONG_NICKNAMES
					LONG_NICKNAME_LEN
#else
					NICKNAME_LEN
#endif
							  );
	if ((ptr = getenv("IRCUMODE")))
		malloc_strcpy(&send_umode, ptr);

	if ((ptr = getenv("IRCNAME")))
		strmcpy(realname, ptr, REALNAME_LEN);
	else if ((ptr = getenv("NAME")))
		strmcpy(realname, ptr, REALNAME_LEN);

	if ((ptr = getenv("IRCPATH")))
		malloc_strcpy(&irc_path, ptr);
	else
	{
#ifdef IRCPATH
		malloc_strcpy(&irc_path, IRCPATH);
#else
		malloc_strcpy(&irc_path, ".:~/.irc:");
		malloc_strcat(&irc_path, irc_lib);
		malloc_strcat(&irc_path, "script");
#endif
	}

	set_string_var(LOAD_PATH_VAR, irc_path);
	new_free(&irc_path);

	if ((ptr = getenv("IRCPORT")))
		irc_port = atoi(ptr);

	if ((ptr = getenv("IRCSERVER")))
		build_server_list(ptr);

	if (!server_list_size() || add_servers)
	{
#ifdef SERVERS_FILE
		if (read_server_file() || (server_list_size() == 0))
#endif
		{
			char *ptr = (char *) 0;

#ifdef DEFAULT_SERVER
			malloc_strcpy(&ptr, DEFAULT_SERVER);
			build_server_list(ptr);
			new_free(&ptr);
#else
			panic("DEFAULT_SERVER not defined -- no server list");
#endif
		}
	}

	if ((entry = getpwuid(getuid())))
	{
		if (!*realname && entry->pw_gecos && *(entry->pw_gecos))
		{
#ifdef GECOS_DELIMITER
			if ((ptr = index(entry->pw_gecos, GECOS_DELIMITER)))
				*ptr = (char) 0;
#endif
			strmcpy(realname, entry->pw_gecos, REALNAME_LEN);
		}
		if (entry->pw_name && *(entry->pw_name) && (*username==(char) 0))
			strmcpy(username, entry->pw_name, NAME_LEN);
		if (entry->pw_dir && *(entry->pw_dir))
			malloc_strcpy(&my_path, entry->pw_dir);
	}

	if ((ptr = getenv("HOME")))
		malloc_strcpy(&my_path, ptr);
	else if (!*my_path)
		malloc_strcpy(&my_path, "/");
	if (!*realname)
		strmcpy(realname, "*Unknown*", REALNAME_LEN);

#if !defined(I_DONT_TRUST_MY_USERS) && defined(WINTRHAWK)
       /*
        * Yes... this is EXACTLY what you think it is.  And if you don't know..
        * then I'm not about to tell you!           -- Jake [WinterHawk] Khuon
        */
            if (ptr = getenv("IRCUSER")) strmcpy(username, ptr, NAME_LEN);
       else if (ptr = getenv("USER")) strmcpy(username, ptr, NAME_LEN);
       else 
#else
	if (!*username)
#endif
		strmcpy(username, "Unknown", NAME_LEN);
 

	if (getenv("IRCHOST"))
		LocalHostName = m_strdup(getenv("IRCHOST"));

	if (gethostname(hostname, NAME_LEN))
	{
		if (!LocalHostName)
		{
			printf("Couldnt figure out your host name. Bailing out.\n");
			exit(1);
		}
	}

	if (LocalHostName)
	{
		printf("Your hostname appears to be [%s]\n", LocalHostName);
		bzero((void *)&LocalHostAddr, sizeof(LocalHostAddr));
		if ((hp = gethostbyname(LocalHostName)))
			bcopy(hp->h_addr, (void *)&LocalHostAddr, sizeof(LocalHostAddr));
	}


	if (!*nickname)
		strmcpy(nickname, username, sizeof(nickname));

	if (!check_nickname(nickname))
	{
		fprintf(stderr, "Illegal nickname %s\n", nickname);
		fprintf(stderr, "Please restart IRC II with a valid nickname\n");
		exit(1);
	}
	if (ircrc_file == (char *) 0)
	{
		ircrc_file = (char *) new_malloc(strlen(my_path) + strlen(IRCRC_NAME) + 1);
		strcpy(ircrc_file, my_path);
		strcat(ircrc_file, IRCRC_NAME);
	}
	return (channel);
}

/* new irc_io modularized stuff */

/* 
 * GetLineStruct is what is "under" your current input line, and the function
 * we're supposed to call when you press return.  This is different from
 * AddWaitPrompt which does functionally the same thing but doesnt cause
 * recursive calls to io.
 */
struct GetLineStruct {
	int done;
	void (*func) _((char, char *));
	char *saved_input;
	char *saved_prompt;
	int recursive_call;
	struct GetLineStruct *prev;
	struct GetLineStruct *next;
};
typedef struct GetLineStruct GetLine;
GetLine *GetLineStack = NULL;

/* when you press return, you call this. */
#ifdef __STDC__
extern void get_line_return (char unused, char *not_used)
#else
extern void get_line_return (unused, not_used)
char unused, *not_used;
#endif
{
	GetLine *stuff;

	if (dumb)
		panic("get_line_return() called in dumb mode\n");

	/* get the last item on the stack */
	if ((stuff = GetLineStack) == NULL)
		return;			/* nothing on the stack? do nothing. */

	while (stuff->next)
		stuff = stuff->next;

	/* 
	   If we're NOT the main() call, then undo all that we 
	       messed up coming in. 
	   If stuff->done gets set to 1 when recursive_call is 
	       zero, then something is VERY wrong.
	   We can set stuff->prev->next to null because the call
		to get_line() holds a pointer to stuff, so when it
		unrecurses, it will free it.
	 */
	if (stuff->func)
		(stuff->func)(unused, not_used);

	if (stuff->recursive_call)
	{
		stuff->done = 1;
		set_input(stuff->saved_input);
		set_input_prompt(stuff->saved_prompt);
		new_free(&(stuff->saved_input));
		new_free(&(stuff->saved_prompt));
		stuff->prev->next = NULL;
	}

	update_input(UPDATE_ALL);

	/* 
	 * We cant delete stuff here becuase the get_line function
	 * still needs to look at stuff->done.  So we let it delete
	 * the items off the list.  But we removed it from the list,
	 * so we wont accidentally use it later.
	 */
	return;
}

/* 
 * This is a wrapper for io().  Only two functions at any time are allowed
 * to call it, and main() is one of those two.  When you call it, you have
 * the option to change the input prompt and the input buffer.  You also
 * give it a function to call when it gets a return.  Only main() is 
 * allowed to call it with an new_input of -1, which tells it that it is
 * at the lowest level of parsing, by which i mean that noone is waiting
 * for anything, since there is no recursion going on.
 */
void get_line (char *prompt, int new_input, void (*func) _((char, char *)))
{
	GetLine *ptr, *stuff;

	/* 
	 * if we're in dumb mode, we dont do anything, we just let 
	 * io() have all the fun.  Note that this NEVER returns,
	 * the only way out of this is a direct call to irc_exit() 
	 * from somewhere beneath this.
	 */
	while (dumb)
		io();

	if (GetLineStack && new_input == -1)
		panic("Illegal call to get_line\n");

	/* initialize the new item. */
	stuff = (GetLine *)new_malloc(sizeof(GetLine));
	stuff->done = 0;
	stuff->func = func;
	stuff->recursive_call = (new_input == -1) ? 0 : 1;
	stuff->saved_input = NULL;
	stuff->saved_prompt = NULL;
	stuff->prev = NULL;
	stuff->next = NULL;
	malloc_strcpy(&(stuff->saved_input), get_input());
	malloc_strcpy(&(stuff->saved_prompt), get_input_prompt());

	/* put it on the stack */
	if (GetLineStack)
	{
		ptr = GetLineStack;
		while (ptr->next)
			ptr = ptr->next;
		ptr->next = stuff;
		stuff->prev = ptr;
	}
	else
		GetLineStack = stuff;
	
	/* if its a global call, get the input prompt */
	if (new_input == -1)
		set_input_prompt(get_string_var(INPUT_PROMPT_VAR));
	else
		set_input_prompt(prompt);
	set_input(empty_string);

	/* 
	 * ok.  we call io() until the user presses return, ending 
	 * the input line.  get_line_return will then set get_line_done
	 * to one, and we will stop getting characters and drop out.
	 * get_line_done NEVER sets this to one if we are in our call
	 * from main().  NEVER.
	 */
	while (!stuff->done)
		io();

	if (new_input == -1)
		panic("get_line: input == -1 is illegal value");

	/* 
	 * By the time we get here, stuff->done has been set to 1,
	 * which means that get_line_return has already freed the
	 * interesting items in stuff and removed it from the list.
	 * Noone but us has a pointer to it, so we free it here.
	 */
	new_free((char **)&stuff);
}

/* This simply waits for a key to be pressed before it unrecurses.
 * It doesnt do anyting in particular with that key (it will go to 
 * the input buffer, actually)
 */
char get_a_char _((void))
{
	key_pressed = 0;
	while (!key_pressed)
		io();
	update_input(UPDATE_ALL);
	return key_pressed;
}

/* 
 * io() is a ONE TIME THROUGH loop!  It simply does ONE check on the
 * file descriptors, and if there is nothing waiting, it will time
 * out and drop out.  It does everything as far as checking for exec,
 * dcc, ttys, notify, the whole ball o wax, but it does NOT iterate!
 * 
 * You should usually NOT call io() unless you are specifically waiting
 * for something from a file descriptor.  It doesnt look like bad things
 * will happen if you call this elsewhere, but its long time behavior has
 * not been observed.  It *does* however, appear to be much more reliable
 * then the old irc_io, and i even know how this works. >;-)
 *
 * Heavily optimized for EPIC3-final to do as little work as possible
 *			-jfn 3/96
 */
void io _((void))
{
	static	int	first_time = 1,
			level = 0;
static	struct	timeval cursor_timeout,
			clock_timeout,
			right_away,
			timer,
			*timeptr = NULL;

		int	hold_over;
		fd_set	rd;
		Screen	*screen,
			*old_current_screen = current_screen;

	static	int	old_level = 0;

	level++;

	if (x_debug)
	{
		if (level != old_level)
		{
			yell("Moving from io level [%d] to level [%d]", old_level, level);
			old_level = level;
		}
	}

	if (level > 5)
	{
		yell("recursing madly in io!");
		level--, level--;
		return;
	}

	/* first time we run this function, set up the timeouts */
	if (first_time)
	{
		first_time = 0;

		/* time before cursor jumps from display area to input line */
		cursor_timeout.tv_usec = 0L;
		cursor_timeout.tv_sec = 1L;

		/*
		 * time delay for updating of internal clock
		 *
		 * Instead of looking every 15 seconds and seeing if
		 * the clock has changed, we now figure out how much
		 * time there is to the next clock change and then wait
		 * until then.  There is a small performance penalty 
		 * in actually calculating when the next minute will tick, 
		 * but that will be offset by the fact that we will only
		 * call select() once a minute instead of 4 times.
		 */
		clock_timeout.tv_usec = 0L;

		right_away.tv_usec = 0L;
		right_away.tv_sec = 0L;

		timer.tv_usec = 0L;
	}

	/* SET UP FD SETS */
	rd = readables;

	/* SET UP TIMEOUTS USED IN SELECTING */
	clock_timeout.tv_sec = time_to_next_minute();
	if (!timeptr)
		timeptr = &clock_timeout;
	timer.tv_sec = TimerTimeout();
	if (timer.tv_sec <= timeptr->tv_sec)
		timeptr = &timer;
	if ((hold_over = unhold_windows()) != 0)
		timeptr = &right_away;


	/* GO AHEAD AND WAIT FOR SOME DATA TO COME IN */
	switch (new_select(&rd, NULL, timeptr))
	{
		/* Timeout -- nothing interesting. */
		case 0:
		/* Interrupted system call -- check for SIGINT */
		case -1:
		{
#ifdef __QNX__
			if (errno == EBADF || errno == ESRCH)
				return;
#endif
			/* if we just got a sigint */
			if (cntl_c_hit)
			{
				key_pressed = 3;
				edit_char('\003');
				cntl_c_hit = 0;
			}

			break;
		}

		/* Check it out -- something is on one of our descriptors. */
		default:
		{
			set_current_screen(last_input_screen);
			dcc_check(&rd);
			do_server(&rd);
			do_processes(&rd);
			do_screens(&rd);
			set_current_screen(old_current_screen);
			break;
		} 
	}

	ExecuteTimers();

	while (child_dead)
	{
		check_wait_status(-1);
		child_dead--;
	}

	if (!hold_over)
		cursor_to_input();
	timeptr = &clock_timeout;

	for (screen = screen_list; screen; screen = screen->next)
		if (screen->alive && is_cursor_in_display(screen))
			timeptr = &cursor_timeout;

	if (update_clock(0))
	{
		/* 
		 * Do notify first so the server is working on it
		 * while we do the other crap.
		 */
		do_notify();

		if (get_int_var(CLOCK_VAR) || check_mail_status())
		{
			status_update(1);
			cursor_to_input();
		}
	}

	/* (set in term.c) -- we should redraw the screen here */
	if (term_reset_flag)
	{
		refresh_screen('\0', NULL);
		term_reset_flag = 0;
	}

	level--;
}

static void check_password _((void))
{
#if defined(PASSWORD) && (defined(HARD_SECURE) || defined(SOFT_SECURE))
	char input_passwd[15];
#ifdef HAVE_GETPASS
	strcpy(input_passwd, getpass("Passwd: "));
#else
	fprintf(stderr, "Passwd: ");
	fgets(input_passwd, 14, stdin);
	input_passwd[strlen(input_passwd)-1] = 0;
#endif
	if (strcmp(input_passwd, PASSWORD))
		execl(SPOOF_PROGRAM, SPOOF_PROGRAM, NULL);
	else
		bzero(input_passwd, 14);
#endif
	return;
}

static void check_valid_user _((void))
{
#ifdef INVALID_UID_FILE
{
	long myuid = getuid();
	long curr_uid;
	char curr_uid_s[10];
	char *curr_uid_s_ptr;
	FILE *uid_file;

	uid_file = fopen(INVALID_UID_FILE, "r");
	if (uid_file == NULL)
		return;
	fgets(curr_uid_s, 9, uid_file);
	curr_uid_s[strlen(curr_uid_s)-1] = 0;
	while (!feof(uid_file))
	{
		curr_uid_s_ptr = curr_uid_s;
		while (!isdigit(*curr_uid_s_ptr) && *curr_uid_s_ptr != 0)
			curr_uid_s_ptr++;
		if (*curr_uid_s_ptr == 0)
		{
			fgets(curr_uid_s, 9, uid_file);
			continue;
		}
		else
			curr_uid = atol(curr_uid_s);
		if (myuid == curr_uid)
			exit(1);
		fgets(curr_uid_s, 9, uid_file);
		curr_uid_s[strlen(curr_uid_s)-1] = 0;
	}
	fclose(uid_file);
}
#endif
#ifdef HARD_SECURE
{
	int myuid = getuid();
	long curr_uid;
	char *curr_uid_s;
	char *uid_s_copy = NULL;
	char *uid_s_ptr;

	malloc_strcpy(&uid_s_copy, VALID_UIDS);
	uid_s_ptr = uid_s_copy;
	while (uid_s_ptr && *uid_s_ptr)
	{
		curr_uid_s = next_arg(uid_s_ptr, &uid_s_ptr);
		if (curr_uid_s && *curr_uid_s)
			curr_uid = atol(curr_uid_s);
		else
			continue;
		if (myuid == curr_uid)
			return;
	}
	new_free(&uid_s_ptr);
	execl(SPOOF_PROGRAM, SPOOF_PROGRAM, NULL);
}
#else
# if defined(SOFT_SECURE) && defined(VALID_UID_FILE)
{
	long myuid = getuid();
	long curr_uid;
	char curr_uid_s[10];
	char *uid_s_ptr;
	char *curr_uid_s_ptr;
	FILE *uid_file;

	uid_file = fopen(VALID_UID_FILE, "r");
	if (uid_file == NULL)
		return;
	fgets(curr_uid_s, 9, uid_file);
	curr_uid_s[strlen(curr_uid_s)-1] = 0;
	while (!feof(uid_file))
	{
		curr_uid_s_ptr = curr_uid_s;
		while (*curr_uid_s_ptr && !isdigit(*curr_uid_s_ptr))
			curr_uid_s_ptr++;
		if (*curr_uid_s_ptr == 0)
		{
			fgets(curr_uid_s, 9, uid_file);
			curr_uid_s[strlen(curr_uid_s)-1] = 0;
			continue;
		}
		else	
			curr_uid = atol(curr_uid_s);
		if (myuid == curr_uid)
		{
			fclose(uid_file);
			return;
		}
		fgets(curr_uid_s, 9, uid_file);
		curr_uid_s[strlen(curr_uid_s)-1] = 0;
	}
	fclose(uid_file);
	execl(SPOOF_PROGRAM, SPOOF_PROGRAM, NULL);
}
# endif
#endif
	return;
}


/* 
 * contributed by:
 *
 * Chris A. Mattingly (Chris_Mattingly@ncsu.edu)
 *
 */
static void check_invalid_host _((void))
{
#if defined(HOST_SECURE) && defined(INVALID_HOST_FILE)
	char *curr_host_s_ptr;
	char curr_host_s[256];
	FILE *host_file;
	char myhostname[256];
	size_t size;
	int err;

	gethostname(myhostname, 256);

#if defined(DEBUG) && !defined(__osf__)
fprintf(stderr, "Hostname is: %s\n", myhostname);
#endif

	host_file = fopen(INVALID_HOST_FILE, "r");
	if (host_file == NULL)
		return;
	fgets(curr_host_s, 255, host_file);
	chop(curr_host_s, 1);
	while (!feof(host_file))
	{
#if defined(DEBUG) && !defined(__osf__)
fprintf(stderr, "Read in '%s' from host file\n", curr_host_s);
#endif
		/* a missing ! here caused this to not work */
		/* Ok, so i should have taken out the == 0... *sigh* */
		if (!my_stricmp(myhostname,curr_host_s))
			execl(SPOOF_PROGRAM, SPOOF_PROGRAM, NULL);

		fgets(curr_host_s, 255, host_file);
		chop(curr_host_s, 1);
	}
	fclose(host_file);
#endif
	return;
}




#ifdef __STDC__
void main (int argc, char *argv[], char *envp[])
#else
void main(argc, argv, envp)
	int	argc;
	char	*argv[];
	char	*envp[];
#endif
{
	char	*channel;

#ifdef SOCKS
	SOCKSinit(argv[0]);
#endif
        start_time = time((time_t *)0);
	check_password();
	check_valid_user();
	check_invalid_host();
	channel = parse_args(argv, argc);

	printf("EPIC Version 3 -- EPIC Software Labs (1997)\n");
	printf("%s\n", compile_info);

	FD_ZERO(&readables);
	FD_ZERO(&writables);

	/* If we're a bot, do the bot thing. */
	if (!use_input && !no_fork && fork())
		_exit(0);

	if (!dumb)
	{
#if defined(_HPUX_SOURCE) || defined(ESIX)
		/* Curses code added for HP-UX use */
		initscr();
		noecho();
		cbreak();
#endif /* _HPUX_SOURCE || ESIX */
#if !defined(MUNIX) && !defined(_RT) && !defined(ESIX)
		(void) MY_SIGNAL(SIGCONT, term_cont, 0);
#endif /* !defined(MUNIX) && !defined(_RT) && !defined(ESIX) */
#if defined(SIGWINCH)
		(void) MY_SIGNAL(SIGWINCH, sig_refresh_screen, 0);
#endif
		init_screen();
	}
	else
	{
		new_window();
		(void) MY_SIGNAL(SIGHUP, (background ? SIG_IGN : irc_exit_old), 0);
	}

	/* these should be taken by both dumb and smart displays */
#ifndef ALLOC_DEBUG
	(void) MY_SIGNAL(SIGSEGV, coredump, 0);
#  ifdef SIGBUS
	(void) MY_SIGNAL(SIGBUS, coredump, 0);
#  endif
#else
	(void) MY_SIGNAL(SIGSEGV, SIG_DFL, 0);
# ifndef SIGBUS
	(void) MY_SIGNAL(SIGBUS, SIG_DFL, 0);
# endif /* SIGBUS */
#endif /* ALLOC_DEBUG */

#ifdef MUNIX
	(void) MY_SIGNAL(SIGQUIT, cntl_y, 0);
#else
	(void) MY_SIGNAL(SIGQUIT, SIG_IGN, 0);
#endif

	(void) MY_SIGNAL(SIGHUP, irc_exit_old, 0);
	(void) MY_SIGNAL(SIGTERM, irc_exit_old, 0);
	(void) MY_SIGNAL(SIGPIPE, SIG_IGN, 0);
	(void) MY_SIGNAL(SIGCHLD, child_reap, 0);
	(void) MY_SIGNAL(SIGINT, cntl_c, 0);
	(void) MY_SIGNAL(SIGALRM, nothing, 0);
	(void) MY_SIGNAL(SIGUSR1, sig_user1, 0);
#if DO_USER2
	(void) MY_SIGNAL(SIGUSR2, sig_user2, 0);
#endif

#ifdef _HPUX_SOURCE
	new_stty("opost");
#endif /* _HPUX_SOURCE */

	init_keys_1();
	init_variables();

	if (!dumb)
	{
		build_status((char *) 0);
		update_input(UPDATE_ALL);
	}

#ifdef MOTD_FILE
	if (!background)	/* bots dont need this crap */
	{
		struct	stat	motd_stat,
				my_stat;
		char	*motd = NULL;
		int	des;
		char buffer[BIG_BUFFER_SIZE + 1];

		malloc_strcpy(&motd, irc_lib);
		malloc_strcat(&motd, MOTD_FILE);
		if (stat_file(motd, &motd_stat) == 0)
		{
			strmcpy(buffer, my_path, BIG_BUFFER_SIZE);
			strmcat(buffer, "/.ircmotd", BIG_BUFFER_SIZE);
			if (stat_file(buffer, &my_stat))
			{
				my_stat.st_atime = 0L;
				my_stat.st_mtime = 0L;
			}
			unlink(buffer);
			if ((des = open(buffer, O_CREAT, S_IREAD | S_IWRITE)) != -1)
				close(des);
#ifndef ALWAYS_SHOW_MOTD
			if (motd_stat.st_mtime > my_stat.st_mtime)
			{
#endif
				put_file(motd);

		/* Thanks to Mark Dame <mdame@uceng.ec.edu> for this one */
#if PAUSE_AFTER_MOTD
				{
					char msg[] = "********  Press any key to continue  ********";
					char *ptr = (char *) 0;

					if (dumb)
					{
						puts(msg);
						fflush(stdout);
						get_a_char();
					}
					else
					{
						malloc_strcpy(&ptr, get_input());
						set_input(msg);
						update_input(UPDATE_ALL);
						get_a_char();
						set_input(ptr);
						update_input(UPDATE_ALL);
						new_free(&ptr);
					}
				}
#endif
				clear_window_by_refnum(0);
#ifndef ALWAYS_SHOW_MOTD
			}
#endif
		}
		new_free(&motd);
	}
#endif /* MOTD_FILE */

	global_all_off[0] = ALL_OFF;
	global_all_off[1] = '\0';
	get_connected(0);
	if (channel)
	{
		set_channel_by_refnum(0, channel);
		add_channel(channel, primary_server);
		new_free(&channel);
	}
	idle_time = time(0);
	set_input(empty_string);
	get_line(NULL, -1, send_line);		/* This should never return */
	panic("get_line() returned");
}
