Files
bitchx/source/irc.c
Kevin Easton 6dbc712046 Rework and improve the scheduling of the server lag check
Previously the server lag check was run by update_clock() if the number of
seconds since client startup was a multiple of 20 (unless it had already been
done this second).  This meant that if update_clock() was not being called
frequently (eg. if your IRC connection was very quiet), it might go a long
time between lag checks.

This commit adds a /set LAG_CHECK_INTERVAL (defaulting to 30).  For each
server, a lag check ping is scheduled for every LAG_CHECK_INTERVAL seconds
after the connection is finalised/registered.  Setting this value to 0
disables the lag check pings entirely.

The old code set the lag to 'unknown' immediately after sending out a ping,
which meant that it was quite unpredictable how long the lag value would
stay around for.  The new code only sets this once the current lag value
has become stale (ie. a ping reply is overdue based on the current lag
value).  If your lag is staying the same or reducing, you shouldn't see
the [Lag ??] at all.
2015-06-12 15:54:43 +10:00

1686 lines
37 KiB
C

/*
* 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
*/
#define __irc_c
#include "irc.h"
#include "struct.h"
static char cvsrevision[] = "$Id$";
CVS_REVISION(irc_c)
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#ifdef USING_CURSES
#include <curses.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 "ircterm.h"
#include "exec.h"
#include "flood.h"
#include "screen.h"
#include "log.h"
#include "server.h"
#include "hook.h"
#include "keys.h"
#include "ircaux.h"
#include "commands.h"
#include "window.h"
#include "history.h"
#include "exec.h"
#include "notify.h"
#include "numbers.h"
#include "mail.h"
#include "debug.h"
#include "newio.h"
#include "timer.h"
#include "whowas.h"
#include "misc.h"
#include "gui.h"
#include "cdns.h"
#include "tcl_bx.h"
#include "ssl.h"
#include <pwd.h>
#define MAIN_SOURCE
#include "modval.h"
#ifdef __EMX__
#include <signame.h>
#endif
const char irc_version[] = VERSION;
/* Format of bitchx_numver: MMmmpp
* MM = major version (eg 10 = 1.0)
* mm = minor version
* pp = patchlevel (00 = development, 01 = release)
*/
const unsigned long bitchx_numver = 120101;
/*
* INTERNAL_VERSION is the number that the special alias $V returns.
* Make sure you are prepared for floods, pestilence, hordes of locusts(
* and all sorts of HELL to break loose if you change this number.
* Its format is actually YYYYMMDD, for the _release_ date of the
* client..
*/
const char internal_version[] = "20141114";
int irc_port = IRC_PORT, /* port of ircd */
strip_ansi_in_echo,
current_on_hook = -1, /* used in the send_text()
* routine */
use_flow_control = USE_FLOW_CONTROL, /* true: ^Q/^S used for flow
* cntl */
current_numeric, /* this is negative of the
* current numeric! */
dumb_mode = 0, /* if true, IRCII is put in
* "dumb" mode */
bflag = 1,
use_input = 1, /* if 0, stdin is never
* checked */
waiting_out = 0, /* used by /WAIT command */
waiting_in = 0, /* used by /WAIT command */
who_mask = 0, /* keeps track of which /who
* switchs are set */
dead = 0,
inhibit_logging = 0,
#ifndef ONLY_STD_CHARS
startup_ansi = 1, /* display startup ansi */
#else
startup_ansi = 0, /* DO NOT display startup ansi */
#endif
auto_connect = 1, /* auto-connect to first server*/
background = 0,
do_check_pid = 0,
do_ignore_ajoin = 0,
#ifdef HAVE_SSL
do_use_ssl = 0,
#endif
run_level = 0,
foreground = 1,
reconnect = 0, /* reconnecting to old process */
use_nat_address = 0, /* use NAT address */
term_initialized = 0;
char zero[] = "0",
one[] = "1",
space[] = " ",
space_plus[] = " +",
space_minus[] = " -",
dot[] = ".",
star[] = "*",
comma[] = ",",
empty_string[] = "",
on[] = "ON",
off[] = "OFF";
const char *unknown_userhost = "<UNKNOWN>@<UNKNOWN>";
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 */
struct sockaddr_foobar MyHostAddr; /* The local machine address */
struct sockaddr_foobar LocalHostAddr;
char *LocalHostName = NULL;
char *channel = NULL;
int inbound_line_mangler = 0,
logfile_line_mangler = 0,
operlog_line_mangler = 0,
outbound_line_mangler = 0;
char *invite_channel = NULL, /* last channel of an INVITE */
*ircrc_file = NULL, /* full path .ircrc file */
*bircrc_file = NULL, /* full path .ircrc file */
*my_path = NULL, /* path to users home dir */
*irc_path = NULL, /* paths used by /load */
*irc_lib = NULL, /* path to the ircII library */
*ircservers_file = NULL, /* name of server file */
nickname[NICKNAME_LEN + 1], /* users nickname */
hostname[NAME_LEN + 1], /* name of current host */
userhost[(NAME_LEN + 1) * 2],
realname[REALNAME_LEN + 1], /* real name of user */
username[NAME_LEN + 1], /* usernameof user */
attach_ttyname[500], /* ttyname for this term */
socket_path[500], /* ttyname for this term */
*forwardnick = NULL, /* used for /forward */
*send_umode = NULL, /* sent umode */
*args_str = NULL, /* list of command line args */
*last_notify_nick = NULL, /* last detected nickname */
*auto_str = NULL, /* auto response str */
*new_script = NULL, /* rephacement for .bitchxrc and .ircrc */
*cut_buffer = NULL; /* global cut_buffer */
int quick_startup = 0; /* set if we ignore .ircrc */
int cpu_saver = 0;
int use_socks = 0; /* do we use socks info to connect */
char *old_tty = NULL, /* re-attach tty and password */
*old_pass = NULL;
extern char *FromUserHost;
int cx_line = 0;
char cx_file[BIG_BUFFER_SIZE/4]; /* debug file info */
char cx_function[BIG_BUFFER_SIZE/4];
extern int doing_privmsg, doing_notice;
time_t idle_time = 0,
now = 0,
start_time;
fd_set readables, writables;
struct in_addr nat_address;
static void quit_response (char *, char *);
static void versionreply (void);
static char *parse_args (char **, int, char **);
static void remove_pid(void);
void do_ansi_logo(int);
extern void set_process_bits(fd_set *rd);
static volatile int cntl_c_hit = 0;
char version[] = _VERSION_;
static char *switch_help[] = {
"Usage: BitchX [switches] [nickname] [server list] \n",
" The [nickname] can be at most 15 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",
#ifndef WINNT
" -H <hostname>\tuses the virtual hostname if possible\n",
#endif
" -N do not auto-connect to the first server\n",
" -A do not display the startup ansi\n",
" -c <channel>\tjoins <channel> on startup. don\'t forget to escape the # using \\\n",
#if defined(WINNT) || defined(__EMX__)
" -b\t\tload bx-rc or irc-rc after connecting to a server\n",
#else
" -b\t\tload .bitchxrc or .ircrc after connecting to a server\n",
#endif
" -p <port>\tdefault server connection port (usually 6667)\n",
#ifndef WINNT
" -f\t\tyour terminal uses flow controls (^S/^Q), so BitchX shouldn't\n",
" -F\t\tyour terminal doesn't use flow control (default)\n",
#endif
" -d\t\truns BitchX in \"dumb\" terminal mode\n",
#if defined(WINNT) || defined(__EMX__)
" -q\t\tdoes not load ~/irc-rc\n",
#else
" -q\t\tdoes not load ~/.ircrc\n",
#endif
" -r file\tload file as list of servers\n",
" -n nickname\tnickname to use\n",
" -a\t\tadds default servers and command line servers to server list\n",
" -x\t\truns BitchX in \"debug\" mode\n",
" -Z\t\tuse NAT address when doing dcc.\n",
" -P\t\ttoggle check pid.nickname for running program.\n",
" -v\t\ttells you about the client's version\n",
#ifdef HAVE_SSL
" -s\t\tservers specified are SSL.\n",
#endif
" -i\t\tignores the autojoin list entries.\n",
#if defined(WINNT) || defined(__EMX__)
" -l <file>\tloads <file> in place of your irc-rc\n\
-L <file>\tloads <file> in place of your irc-rc and expands $ expandos\n",
#else
" -l <file>\tloads <file> in place of your .ircrc\n\
-L <file>\tloads <file> in place of your .ircrc and expands $ expandos\n",
#endif
#if !defined(WINNT) && !defined(__EMX__)
" -B\t\tforce BitchX to fork and return you to shell. pid check on.\n",
#endif
NULL };
char *time_format = NULL; /* XXX Bogus XXX */
#ifdef __EMX__
char *strftime_24hour = "%H:%M";
#else
char *strftime_24hour = "%R";
#endif
char *strftime_12hour = "%I:%M%p";
char time_str[61];
void detachcmd(char *, char *, char *, char *);
/* update_clock: figUres out the current time and returns it in a nice format */
char *BX_update_clock(int flag)
{
static int min = -1,
hour = -1;
static time_t last_minute = -1;
time_t idlet;
static struct tm time_val;
time_t hideous;
int new_minute = 0;
int new_hour = 0;
hideous = now;
#if !defined(NO_CHEATING)
if (hideous / 60 > last_minute)
{
last_minute = hideous / 60;
time_val = *localtime(&hideous);
}
#else
time_val = *localtime(&hideous);
#endif
if (flag == RESET_TIME || time_val.tm_min != min || time_val.tm_hour != hour)
{
int ofs = from_server;
from_server = primary_server;
if (time_format) /* XXXX Bogus XXXX */
strftime(time_str, 60, time_format, &time_val);
else if (get_int_var(CLOCK_24HOUR_VAR))
strftime(time_str, 60, strftime_24hour, &time_val);
else
strftime(time_str, 60, strftime_12hour, &time_val);
lower(time_str);
if ((time_val.tm_min != min) || (time_val.tm_hour != hour))
{
int is_away = 0;
if (time_val.tm_hour != hour)
new_hour = 1;
new_minute = 1;
hour = time_val.tm_hour;
min = time_val.tm_min;
do_hook(TIMER_LIST, "%02d:%02d", hour, min);
if (min == 0 || new_hour)
do_hook(TIMER_HOUR_LIST, "%02d:%02d", hour, min);
idlet = hideous - idle_time;
if (from_server != -1)
is_away = get_server_away(from_server) ? 1 : 0;
if (do_hook(IDLE_LIST, "%lu", (unsigned long)idlet / 60))
{
if (is_away && new_hour && get_int_var(TIMESTAMP_AWAYLOG_HOURLY_VAR))
logmsg(LOG_CRAP, NULL, 4, NULL);
check_auto_away(idlet);
}
check_channel_limits();
}
from_server = ofs;
if (flag != RESET_TIME || new_minute)
return time_str;
else
return NULL;
}
if (flag == GET_TIME)
return time_str;
else
return NULL;
}
void reset_clock(Window *win, char *unused, int unused1)
{
update_clock(RESET_TIME);
update_all_status(win, NULL, 0);
}
/* sig_refresh_screen: the signal-callable version of refresh_screen */
SIGNAL_HANDLER(sig_refresh_screen)
{
refresh_screen(0, NULL);
}
/* irc_exit: cleans up and leaves */
SIGNAL_HANDLER(irc_exit_old)
{
irc_exit(1,NULL, NULL);
}
volatile int dead_children_processes;
/* 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.
*/
SIGNAL_HANDLER(child_reap)
{
dead_children_processes++;
#ifdef __EMX__
my_signal(SIGCHLD, SIG_IGN, 0);
#endif
}
SIGNAL_HANDLER(nothing)
{
/* nothing to do! */
}
SIGNAL_HANDLER(sigpipe)
{
static int sigpipe_hit = 0;
sigpipe_hit++;
}
#if 0
SIGNAL_HANDLER(sigusr2)
{
mtrace();
}
SIGNAL_HANDLER(sigusr3)
{
muntrace();
}
#endif
/* irc_exit: cleans up and leaves */
void BX_irc_exit (int really_quit, char *reason, char *format, ...)
{
char buffer[BIG_BUFFER_SIZE];
logger(current_window, NULL, 0);
if (get_int_var(MSGLOG_VAR))
log_toggle(0, NULL);
if (format)
{
va_list arglist;
va_start(arglist, format);
vsprintf(buffer, format, arglist);
va_end(arglist);
}
else
sprintf(buffer, "%s -- just do it.",irc_version);
if (really_quit)
{
put_it("%s", convert_output_format("$G Signon time : $0-", "%s", my_ctime(start_time)));
put_it("%s", convert_output_format("$G Signoff time : $0-", "%s", my_ctime(now)));
put_it("%s", convert_output_format("$G Total uptime : $0-", "%s", convert_time(now - start_time)));
}
do_hook(EXIT_LIST, "%s", reason ? reason : buffer);
close_all_servers(reason ? reason : buffer);
put_it("%s", !format && reason ? reason : buffer);
clean_up_processes();
if (!dumb_mode && term_initialized)
{
cursor_to_input(); /* Needed so that ircII doesn't gobble
* the last line of the kill. */
term_cr();
term_clear_to_eol();
term_reset();
}
destroy_call_stack();
#if defined(THREAD) && defined(WANT_NSLOOKUP)
kill_dns();
#endif
remove_pid();
if (really_quit)
{
#ifdef GUI
gui_exit();
#else
#if defined(WANT_DETACH) && !defined(GUI)
kill_attached_if_needed(0);
#endif
fprintf(stdout, "\r");
fflush(stdout);
exit(0);
#endif
}
}
#ifndef WANT_CORE
volatile int segv_recurse = 0;
/* sigsegv: something to handle segfaults in a nice way */
/* this needs to be changed to *NOT* use printf(). */
SIGNAL_HANDLER(coredump)
{
#if defined(WINNT)
extern char *sys_siglist[];
#endif
if (segv_recurse)
_exit(1);
segv_recurse = 1;
panic_dump_call_stack();
#ifdef TDEBUG
putlog(LOG_ALL, "*", "Error logged. %s at (%d) %s", cx_file, cx_line, cx_function?cx_function:empty_string);
#endif
printf("\n\r\n\rIRCII has been terminated by a [%s]\n\r", sys_siglist[unused]);
printf("Please email " BUG_EMAIL "\n\r");
printf("with as much detail as possible about what you were doing when it happened.\n\r");
printf("Please include the version of IRCII (%s) and type of system in the report.\n\r", irc_version);
fflush(stdout);
irc_exit(1, "Hmmmm... BitchX error!!!! unusual :)", NULL);
}
#endif
/*
* 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.
*/
static void quit_response(char *dummy, char *ptr)
{
int len;
if ((len = strlen(ptr)) != 0)
if (!my_strnicmp(ptr, "yes", len))
irc_exit(1, NULL, "IRC][ %s: Rest in peace", irc_version);
}
/* irc_quit: prompts the user if they wish to exit, then does the right thing */
void irc_quit(char key, char * ptr)
{
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, 1);
in_it = 0;
}
/*
* cntl_c: emergency exit.... if somehow everything else freezes up, hitting
* ^C five times should kill the program.
*/
SIGNAL_HANDLER(cntl_c)
{
if (cntl_c_hit++ >= 4)
irc_exit(1, "User abort with 5 Ctrl-C's", NULL);
else if (cntl_c_hit > 1)
kill(getpid(), SIGALRM);
}
SIGNAL_HANDLER(sig_user1)
{
bitchsay("Got SIGUSR1, closing DCC connections and EXECed processes");
close_all_dcc();
clean_up_processes();
}
SIGNAL_HANDLER(sig_detach)
{
detachcmd(NULL, NULL, NULL, NULL);
}
void set_detach_on_hup(Window *dummy, char *unused, int value)
{
if(value)
my_signal(SIGHUP, sig_detach, 0);
else
my_signal(SIGHUP, irc_exit_old, 0);
}
/* shows the version of irc */
static void versionreply(void)
{
printf("BitchX version %s (%s)\n\r", irc_version, internal_version);
exit (0);
}
#ifndef RAND_MAX
#define RAND_MAX 2147483647
#endif
void display_bitchx(int j)
{
int i = 0;
int old_strip_ansi = strip_ansi_in_echo;
strip_ansi_in_echo = 0;
if (j == -1)
#ifdef ASCII_LOGO
i = (int) (5.0*rand()/RAND_MAX);
#else
i = (int) (17.0*rand()/RAND_MAX);
#endif
else
i = j;
if (!startup_ansi)
return;
#if !defined(WINNT) && !defined(__EMX__)
charset_ibmpc();
#endif
do_ansi_logo(i);
#if !defined(WINNT) && !defined(__EMX__)
#if defined(LATIN1)
charset_lat1();
#elif defined(CHARSET_CUSTOM)
charset_cst();
#endif
#endif
strip_ansi_in_echo = old_strip_ansi;
}
/*
* 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, -s, -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.
* Exception: the -s flag will be accepted without a argument.
* (ick: backwards compatability sucks. ;-)
* 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 -n flag means "nickname"
*
* Bugs:
* The -s flag is hard to use without an argument unless youre careful.
*/
#ifdef CLOAKED
extern char **Argv;
extern char *LastArgv;
#endif
static char *parse_args (char *argv[], int argc, char **envp)
{
int ac;
int add_servers = 0;
#if !defined(WINNT)
struct passwd *entry;
#endif
char *channel = NULL;
struct hostent * hp;
char *ptr;
*nickname = 0;
#ifdef CLOAKED
Argv = argv;
while (*envp)
envp++;
LastArgv = envp[-1] + strlen(envp[-1]);
#endif
for ( ac = 1; ac < argc; ac++ )
{
if (argv[ac][0] == '-')
{
switch (argv[ac][1]) {
case 'A': /* turn off startup ansi */
startup_ansi = 0;
break;
case 'v': /* Output ircII version */
{
versionreply();
/* 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 parameter after -c\n");
exit(1);
}
malloc_strcpy(&channel, what);
break;
}
case 'p': /* Default port to use */
{
char *what = empty_string;
if (reconnect)
{
what = getpass("Enter password : ");
if (what && *what)
malloc_strcpy(&old_pass, what);
break;
}
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 parameter after -p\n");
exit(1);
}
irc_port = my_atol(what);
break;
}
#ifndef WINNT
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;
}
#endif
case 'd': /* use dumb mode */
{
dumb_mode = 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(&new_script, 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 'r': /* Load list of servers from this file */
{
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 -r\n");
if (*what)
{
add_servers = 1;
malloc_strcpy(&ircservers_file, what);
}
break;
}
case 'R':
fprintf(stderr, "Use \"scr-bx\" to reconnect\r\n");
exit(1);
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 'b':
{
bflag = 0;
break;
}
case 'B':
{
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_mode = 1;*/
/* use_input = 0;*/
background = 1;
}
case 'P':
{
do_check_pid ^= 1;
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);
}
strlcpy(nickname, what, sizeof nickname);
break;
}
case 'N':
{
auto_connect = 0;
break;
}
case 'x': /* set server debug */
{
x_debug = (unsigned long)0xffffffff;
if (argv[ac][2])
fprintf(stderr, "Ignoring junk after -x\n");
break;
}
case 'Z':
{
use_nat_address = 1;
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;
strlcpy(username, what, sizeof username);
break;
}
case 'H':
{
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, "You forgot to specify a hostname\n");
exit(1);
}
malloc_strcpy(&LocalHostName, what);
break;
}
case 'S':
use_socks = 1;
break;
case 'i':
do_ignore_ajoin = 1;
break;
#ifdef HAVE_SSL
case 's':
do_use_ssl = 1;
break;
#endif
case '\0':
break; /* ignore - alone */
default:
fprintf(stderr, "Unknown flag: %s\n",argv[ac]);
case 'h':
{
int t = 0;
while(switch_help[t])
{
fprintf(stderr, "%s", switch_help[t]);
t++;
}
exit(1);
}
} /* End of switch */
}
else
{
if (!strchr(argv[ac], '.') && !strchr(argv[ac], ',') && !*nickname)
strlcpy(nickname, argv[ac], sizeof nickname);
else
build_server_list(argv[ac]);
#ifdef HAVE_SSL
do_use_ssl = 0;
#endif
}
}
if (!*nickname && (ptr = getenv("IRCNICK")))
strlcpy(nickname, ptr, sizeof nickname);
if (!ircservers_file)
#if defined(WINNT) || defined(__EMX__)
malloc_strcpy(&ircservers_file, "irc-serv");
#else
malloc_strcpy(&ircservers_file, ".ircservers");
#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 ((ptr = getenv("IRCUMODE")))
malloc_strcpy(&send_umode, ptr);
if ((ptr = getenv("IRCNAME")))
strlcpy(realname, ptr, sizeof realname);
else if ((ptr = getenv("NAME")))
strlcpy(realname, ptr, sizeof realname);
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);
/*
* 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
*
* Here we determine our username. It may be set above, by the -z command line
* option. If not check IRCUSER, then USER, then the IDENT_HACK file, then
* fallback to gecos below.
*/
if (!*username && (ptr = getenv("IRCUSER"))) strlcpy(username, ptr, sizeof username);
else if (!*username && (ptr = getenv("USER"))) strlcpy(username, ptr, sizeof username);
else if (!*username)
{
#ifdef IDENT_FAKE
char *p = NULL, *q = NULL;
FILE *f;
malloc_sprintf(&p, "~/%s", get_string_var(IDENT_HACK_VAR));
q = expand_twiddle(p);
if ((f = fopen(q, "r")))
{
fgets(username, NAME_LEN, f);
if (*username && strchr(username, '\n'))
username[strlen(username)-1] = 0;
}
fclose(f);
new_free(&p); new_free(&q);
if (!*username)
#endif
strlcpy(username, "Unknown", sizeof username);
}
#ifndef WINNT
if ((entry = getpwuid(getuid())))
{
if (!*realname && entry->pw_gecos && *(entry->pw_gecos))
{
#ifdef GECOS_DELIMITER
if ((ptr = strchr(entry->pw_gecos, GECOS_DELIMITER)))
*ptr = 0;
#endif
/* The first '&' character in pw_gecos is replaced with pw_name */
if ((ptr = strchr(entry->pw_gecos, '&')))
*ptr = 0;
strlcpy(realname, entry->pw_gecos, sizeof realname);
if (ptr)
{
size_t len = ptr - entry->pw_gecos;
strlcat(realname, entry->pw_name, sizeof realname);
strlcat(realname, ptr + 1, sizeof realname);
/* Make the first character of the username uppercase, if
it's preceeded by a space */
if (len < sizeof realname && *(entry->pw_name) &&
(len == 0 || isspace((unsigned char)realname[len - 1])))
{
realname[len] = toupper((unsigned char)realname[len]);
}
}
}
if (entry->pw_name && *(entry->pw_name) && !*username)
strlcpy(username, entry->pw_name, sizeof username);
if (entry->pw_dir && *(entry->pw_dir))
malloc_strcpy(&my_path, entry->pw_dir);
}
#else
{
u_long size=NAME_LEN+1;
if (!(ptr = getenv("IRCUSER")))
strcpy(username, "unknown");
else
strncpy(username,ptr, size);
}
#endif
if ((ptr = getenv("HOME")))
malloc_strcpy(&my_path, ptr);
else if (!my_path || !*my_path)
#ifdef WINNT
malloc_strcpy(&my_path, empty_string);
#else
malloc_strcpy(&my_path, "/");
#endif
#if defined(WINNT) || defined(__EMX__)
convert_unix(my_path);
#endif
if (!*realname)
strlcpy(realname, "* I'm too lame to read BitchX.doc *", sizeof realname);
if (!LocalHostName && ((ptr = getenv("IRC_HOST")) || (ptr = getenv("IRCHOST"))))
LocalHostName = m_strdup(ptr);
if ((gethostname(hostname, sizeof(hostname))))
if (!LocalHostName)
exit(1);
if (LocalHostName)
{
printf("Your hostname appears to be [%s]\n", LocalHostName);
#ifndef IPV6
memset((void *)&LocalHostAddr, 0, sizeof(LocalHostAddr));
if ((hp = gethostbyname(LocalHostName)))
memcpy((void *)&LocalHostAddr.sf_addr, hp->h_addr, sizeof(struct in_addr));
}
else
{
if ((hp = gethostbyname(hostname)))
memcpy((char *) &MyHostAddr.sf_addr, hp->h_addr, sizeof(struct in_addr));
#endif
}
if (!*nickname)
strlcpy(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 == NULL)
{
ircrc_file = (char *) new_malloc(strlen(my_path) + strlen(IRCRC_NAME) + 1);
strcpy(ircrc_file, my_path);
strcat(ircrc_file, IRCRC_NAME);
}
if (bircrc_file == NULL)
#if defined(WINNT) || defined(__EMX__)
malloc_sprintf(&bircrc_file, "%s/bx-rc", my_path);
#else
malloc_sprintf(&bircrc_file, "%s/.bitchxrc", my_path);
#endif
if ((ptr = getenv("IRCPORT")))
irc_port = my_atol(ptr);
if ((ptr = getenv("IRCSERVER")))
build_server_list(ptr);
if (!server_list_size() || add_servers)
{
if (!read_server_file(ircservers_file) && !server_list_size())
{
#ifdef DEFAULT_SERVER
char *ptr = NULL;
malloc_strcpy(&ptr, DEFAULT_SERVER);
build_server_list(ptr);
new_free(&ptr);
#endif
from_server = -1;
}
}
return (channel);
}
/*
* 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. >;-)
*/
extern void set_screens (fd_set *, fd_set *);
void BX_io (const char *what)
{
static int level = 0;
long clock_timeout = 0;
struct timeval my_now,
my_timer,
timeout;
int hold_over, rc;
fd_set rd, wd;
static int old_level = 0;
static const char *caller[51] = { NULL }; /* XXXX */
static int last_warn = 0;
level++;
if (x_debug & DEBUG_WAITS)
{
if (level != old_level)
{
yell("Moving from io level [%d] to level [%d] from [%s]", old_level, level, what);
old_level = level;
}
}
if (level && (level - last_warn == 5))
{
last_warn = level;
yell("io's nesting level is [%d], [%s]<-[%s]<-[%s]<-[%s]<-[%s]<-[%s]", level, what, caller[level-1], caller[level-2], caller[level-3], caller[level-4], caller[level-5]);
if ((level % 30) == 0)
ircpanic("Ahoy there matey! Abandon ship!");
return;
}
else if (level && (last_warn -level == 5))
last_warn -= 5;
caller[level] = what;
get_time(&my_now);
now = my_now.tv_sec;
/* CHECK FOR CPU SAVER MODE */
if (!cpu_saver && get_int_var(CPU_SAVER_AFTER_VAR))
if (now - idle_time > get_int_var(CPU_SAVER_AFTER_VAR) * 60)
cpu_saver_on(0, NULL);
rd = readables;
wd = writables;
FD_ZERO(&wd);
FD_ZERO(&rd);
#ifndef GUI
set_screens(&rd, &wd);
#endif
set_process_bits(&rd);
set_socket_read(&rd, &wd);
set_nslookupfd(&rd);
#ifdef GUI
gui_setfd(&rd);
#endif
clock_timeout = 60 - (my_now.tv_sec % 60);
if (cpu_saver && get_int_var(CPU_SAVER_EVERY_VAR))
clock_timeout += (get_int_var(CPU_SAVER_EVERY_VAR) - 1) * 60;
my_timer.tv_sec = my_now.tv_sec + clock_timeout;
my_timer.tv_usec = 0;
set_server_bits(&rd, &wd, &my_timer);
tclTimerTimeout(&my_timer);
TimerTimeout(&my_timer);
if ((hold_over = unhold_windows()) || time_cmp(&my_timer, &my_now) < 0)
{
timeout.tv_sec = 0;
timeout.tv_usec = 0;
}
else
{
timeout.tv_sec = my_timer.tv_sec - my_now.tv_sec;
timeout.tv_usec = my_timer.tv_usec - my_now.tv_usec;
if (timeout.tv_usec < 0)
{
timeout.tv_sec--;
timeout.tv_usec += 1000000;
}
}
/* GO AHEAD AND WAIT FOR SOME DATA TO COME IN */
switch ((rc = new_select(&rd, &wd, &timeout)))
{
case 0:
do_idle_server();
break;
case -1:
{
/* if we just got a sigint */
if (cntl_c_hit)
{
edit_char('\003');
/* cntl_c_hit = 0; */
}
else if (errno != EINTR)
yell("Select failed with [%s]", strerror(errno));
break;
}
/* we got something on one of the descriptors */
default:
{
cntl_c_hit = 0;
get_time(&my_now);
now = my_now.tv_sec;
make_window_current(NULL);
do_server(&rd, &wd);
do_processes(&rd);
do_screens(&rd);
#ifdef WANT_CDCC
dcc_sendfrom_queue();
#endif
dcc_check_idle();
print_nslookup(&rd);
scan_sockets(&rd, &wd);
#ifdef GUI
scan_gui(&rd);
#endif
break;
}
}
get_time(&my_now);
now = my_now.tv_sec;
ExecuteTimers();
#ifdef WANT_TCL
check_utimers();
#endif
send_from_server_queue();
get_child_exit(-1);
if (!hold_over)
cursor_to_input();
#ifdef WANT_LLOOK
if (get_int_var(LLOOK_VAR) && from_server > -1 && !get_server_linklook(from_server))
{
if (now - get_server_linklook_time(from_server) > get_int_var(LLOOK_DELAY_VAR))
{
set_server_linklook(from_server, get_server_linklook(from_server)+1);
my_send_to_server(from_server, "LINKS");
set_server_linklook_time(from_server, now);
}
}
#endif
if (update_clock(RESET_TIME))
{
do_notify();
#ifdef WANT_TCL
check_timers();
#endif
clean_whowas_chan_list();
clean_whowas_list();
clean_flood_list();
clean_split_server_list(CHAN_SPLIT, 1800);
#ifdef WANT_LLOOK
clean_split_server_list(LLOOK_SPLIT, 3600);
#endif
if (get_int_var(CLOCK_VAR) || check_mail_status())
{
update_all_status(current_window, NULL, 0);
cursor_to_input();
}
check_server_connect(from_server);
}
/* (set in term.c) -- we should redraw the screen here */
if (need_redraw)
refresh_screen(0, NULL);
FromUserHost = empty_string;
alloca(0);
caller[level] = NULL;
level--;
return;
}
char pidfile[80];
void check_pid(void)
{
char *p = NULL;
FILE *t;
if (!do_check_pid)
return;
p = expand_twiddle(DEFAULT_CTOOLZ_DIR);
snprintf(pidfile, sizeof pidfile, "%s/pid.%s", p, nickname);
if ((t = fopen(pidfile, "r")))
{
char buffer[80];
int i;
fgets(buffer, 10, t);
i = atol(buffer);
kill(i, SIGCHLD);
if (errno != ESRCH)
{
fprintf(stderr, "Bitchx already running as %s\n", nickname);
exit(1);
}
}
}
extern int save_ipc;
static void remove_pid(void)
{
#ifdef WANT_DETACH
char *p;
FILE *t;
if (!do_check_pid)
return;
p = expand_twiddle(DEFAULT_CTOOLZ_DIR);
snprintf(pidfile, sizeof pidfile, "%s/pid.%s", p, nickname);
if ((t = fopen(pidfile, "r")))
{
#if !defined(__EMX__) && !defined(WINNT) && !defined(GUI)
SocketList *s;
fclose(t);
unlink(pidfile);
if (save_ipc != -1 && (s = get_socket(save_ipc)))
{
char buf[500];
snprintf(buf, sizeof buf, s->server, s->port);
unlink(buf);
}
#endif
}
#endif
}
void setup_pid(void)
{
#ifdef WANT_DETACH
pid_t pid;
if (!do_check_pid)
return;
if ((pid = getpid()))
{
FILE *t;
unlink(pidfile);
if ((t = fopen(pidfile, "w")))
{
fprintf(t, "%u\n", pid);
fclose(t);
}
}
#endif
}
int main(int argc, char *argv[], char *envp[])
{
srand((unsigned)time(NULL));
time(&start_time);
time(&idle_time);
time(&now);
/* We need to zero these early */
FD_ZERO(&readables);
FD_ZERO(&writables);
/* First thing we need to do is initialize
* the global function table with the default
* function pointers.
*/
init_global_functions();
/* Setup OS/2 */
#if defined(__EMX__)
setvbuf(stdout, NULL, _IOLBF, 80);
#endif
/* Setup the GUIs */
#ifdef GUI
gui_startup(argc, argv);
#endif
#ifdef SOCKS
SOCKSinit(argv[0]);
#endif
#ifdef TDEBUG
*cx_file = 0;
cx_line = 0;
*cx_function = 0;
#endif
#if !defined(GUI)
printf("BitchX - Based on EPIC Software Labs epic ircII (1998).\r\n");
printf("Version (%s) -- Date (%s).\r\n", irc_version, internal_version);
printf("Process [%d]", getpid());
if ((isatty(0) && !background) || (!isatty(0) && background))
{
char s[90];
if (ttyname(0))
{
strncpy(s, ttyname(0), 85);
/* printf(" connected to tty [%s]", s);*/
strncpy(attach_ttyname, s, 400);
}
else if (background)
strcpy(attach_ttyname, "tty1");
}
else
dumb_mode = 1;
printf("\n");
#endif
channel = parse_args(argv, argc, envp);
check_pid();
init_socketpath();
#ifdef WANT_TCL
tcl_interp = Tcl_CreateInterp();
#endif
if (!dumb_mode && term_init(NULL))
_exit(1);
if (background)
{
#ifdef WANT_DETACH
#ifndef PUBLIC_ACCESS
detachcmd(NULL, NULL, NULL, NULL);
#else
exit(0);
#endif
#else
fprintf(stderr, "Try compiling with WANT_DETACH\r\n");
exit(0);
#endif
}
#ifndef WANT_CORE
my_signal(SIGSEGV, coredump, 0);
my_signal(SIGBUS, coredump, 0);
#endif
my_signal(SIGQUIT, SIG_IGN, 0);
#ifdef WANT_DETACH
set_detach_on_hup(NULL, NULL, DEFAULT_DETACH_ON_HUP);
#else
my_signal(SIGHUP, irc_exit_old, 0);
#endif
my_signal(SIGTERM, irc_exit_old, 0);
my_signal(SIGPIPE, SIG_IGN, 0);
my_signal(SIGINT, cntl_c, 0);
my_signal(SIGCHLD, child_reap, 0);
my_signal(SIGALRM, nothing, 0);
my_signal(SIGUSR1, sig_user1, 0);
#ifdef GTK
my_signal(SIGTTOU, SIG_IGN, 0);
#endif
#if 0
my_signal(SIGUSR1, sigusr2, 0);
my_signal(SIGUSR2, sigusr3, 0);
#endif
init_output();
if (!dumb_mode)
{
init_screen();
if (background)
{
use_input = 0;
main_screen->fdin = main_screen->fdout = open("/dev/null", O_RDWR|O_NOCTTY);
new_open(main_screen->fdin);
main_screen->fpout = fdopen(1, "w");
main_screen->fpin = fdopen(0, "r");
}
#ifndef __EMX__
my_signal(SIGCONT, term_cont, 0);
#if !defined(WINNT) && !defined(GTK)
my_signal(SIGWINCH, sig_refresh_screen, 0);
#endif
#endif
}
else
{
if (background)
{
my_signal(SIGHUP, SIG_IGN, 0);
background = 0;
#ifndef WANT_DETACH
term_reset();
fprintf(stderr, "try recompiling the client with WANT_DETACH defined\r\n");
exit(0);
use_input = 0;
#endif
}
create_new_screen();
new_window(main_screen);
}
setup_pid();
init_keys();
init_keys2();
init_variables();
init_dcc_table();
#if defined(THREAD) && defined(WANT_NSLOOKUP)
start_dns();
#endif
if (!dumb_mode)
{
build_status(current_window, NULL, 0);
update_input(UPDATE_ALL);
}
start_identd();
#ifdef WANT_TCL
tcl_init();
#ifdef WANT_TK
Tk_Init(tcl_interp);
#endif
add_tcl_vars();
#endif
#ifdef HAVE_SSL
{
char *entropy = malloc(100);
int i;
for(i=0;i<100;i++)
entropy[i] = (char) getrandom(0, 255);
/* Many systems don't have /dev/random so we seed */
RAND_seed(entropy, 100);
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
free(entropy);
}
#endif
#ifdef CLOAKED
initsetproctitle(argc, argv, envp);
setproctitle("%s", CLOAKED);
#endif
/* We move from run level 0 to run level 1
* signifying that we have completed the initial
* startup and are now ready to load scripts.
*/
run_level = 1;
display_bitchx(-1);
if (bflag)
load_scripts();
#ifdef GUI
gui_font_init();
#endif
reinit_autoresponse(current_window, NULL, 0);
if (auto_connect)
get_connected(0, -1);
else
display_server_list();
start_memdebug();
set_input(empty_string);
set_input_prompt(current_window, get_string_var(INPUT_PROMPT_VAR), 0);
/* We now move from run level 1 to run level 2
* signifying that we are in normal operation mode.
*/
run_level = 2;
for (;;)
io("main");
#ifdef GUI
gui_exit();
#else
ircpanic("get_line() returned");
#endif
return (-((int)0xdead));
}