/* * ircII: a new irc client. I like it. I hope you will too! * * Written By Michael Sandrof * * Copyright (c) 1990 Michael Sandrof. * Copyright (c) 1991, 1992 Troy Rollo. * Copyright (c) 1992-2006 Matthew R. Green. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define IRCII_VERSION "20060725" /* YYYYMMDD */ #include "irc.h" IRCII_RCSID("@(#)$eterna: irc.c,v 1.298 2006/07/25 11:14:19 mrg Exp $"); #include #ifndef __MSDOS__ #include #endif /* __MSDOS__ */ #ifdef ISC22 # include #endif /* ISC22 */ #ifdef DO_USER2 # include #endif /* DO_USER2 */ #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 "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 "debug.h" #include "newio.h" #include "ctcp.h" #include "parse.h" #include "strsep.h" int irc_port = IRC_PORT, /* port of ircd */ icb_port = ICB_PORT, /* port of icbd */ send_text_flag = -1, /* used in the send_text() * routine */ use_flow_control = USE_FLOW_CONTROL, /* true: ^Q/^S used for flow * cntl */ irc_io_loop = 1, /* see irc_io below */ break_io_processing = 0, /* more forceful than * irc_io_loop */ 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 */ use_input = 1, /* if 0, stdin is never * checked */ waiting = 0, /* used by /WAIT command */ who_mask = 0, /* keeps track of which /who * switchs are set */ cur_signal = -1, /* head of below */ occurred_signals[16]; /* got signal which may require /ON action */ int client_default_icb = 0; /* default to irc server connections */ u_char *zero = UP("0"); u_char *one = UP("1"); u_char oper_command; /* 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 */ extern u_char *last_away_nick; u_char *invite_channel, /* last channel of an INVITE */ *ircrc_file, /* full path .ircrc file */ *ircquick_file, /* full path .ircquick file */ *my_path, /* path to users home dir */ *irc_path, /* paths used by /load */ *irc_lib, /* path to the ircII library */ *nickname, /* users nickname */ FAR hostname[NAME_LEN + 1], /* name of current host */ FAR realname[REALNAME_LEN + 1], /* real name of user */ FAR username[NAME_LEN + 1], /* usernameof user */ *source_host, /* specify a specific source host * for multi-homed machines */ *dcc_source_host, /* source host to use for DCC */ *send_umode, /* sent umode */ *args_str, /* list of command line args */ empty_string[] = "", /* just an empty string */ *who_name, /* extra /who switch info */ *who_file, /* extra /who switch info */ *who_server, /* extra /who switch info */ *who_host, /* extra /who switch info */ *who_nick, /* extra /who switch info */ *who_real, /* extra /who switch info */ *cannot_open, /* extra /who switch info */ *cut_buffer, /* global cut_buffer */ *last_notify_nick; /* last detected nickname */ int away_set; /* set if there is an away * message anywhere */ int qflag; /* set if we ignore .ircrc */ int bflag; /* set if we load .ircrc before connecting */ int tflag = 1; /* don't use termcap ti/te sequences */ time_t idle_time; /* last time the user hit a key */ time_t start_time; /* epoch time we started */ static RETSIGTYPE cntl_c(int); static RETSIGTYPE sig_user1(int); static RETSIGTYPE sig_refresh_screen(int); static RETSIGTYPE sig_on(int); static void real_sig_user1(void); static int do_sig_user1; #ifdef DO_USER2 static RETSIGTYPE sig_user2(int) ; #endif /* DO_USER2 */ #ifdef MUNIX static RETSIGTYPE cntl_y(int); #endif /* MUNIX */ #ifdef CORECATCH static RETSIGTYPE coredump(int) ; #endif /* CORECATCH */ static int irc_do_a_screen(Screen *, fd_set *, int); static void TimerTimeout(struct timeval *tv); static void quit_response(u_char *, u_char *); static void show_version(void); static u_char *get_arg(u_char *, u_char *, int *); static u_char *parse_args(u_char **, int); static int cntl_c_hit = 0; #ifdef DO_USER2 jmp_buf outta_here; #endif /* DO_USER2 */ u_char irc_version[] = IRCII_VERSION; static char FAR switch_help[] = "Usage: irc [switches] [nickname] [server list] \n\ The [nickname] can be at most 9 characters long on some server\n\ The [server list] is a whitespace separated list of server names\n\ The [switches] may be any or all of the following:\n\ -c \tjoins on startup\n\ -p \tdefault IRC server connection port (usually 6667)\n\ -P \tdefault ICB server connection port (usually 7326)\n\ -f\t\tyour terminal uses flow control (^S/^Q), so IRCII shouldn't\n\ -F\t\tyour terminal doesn't use flow control (default)\n\ -r\t\treverse terminal colours (only if colours are activated)\n\ -s\t\tdon't use separate server processes (ircio)\n\ -S\t\tuse separate server processes (ircio)\n\ -h \tsource host, for multihomed machines\n\ -H \toriginating address for dcc requests (to work behind NATs etc)\n\ -d\t\truns IRCII in \"dumb\" terminal mode\n\ -q\t\tdoes not load .ircrc nor .ircquick\n\ -a\t\tadds default servers and command line servers to server list\n\ -b\t\tload .ircrc before connecting to a server\n\ -l \tloads in place of your .ircrc\n\ -I \tloads in place of your .ircquick\n\ -t\t\tdo not use termcap ti and te sequences at startup\n\ -T\t\tuse termcap ti and te sequences at startup (default)\n\ -icb\t\tuse ICB connections by default\n\ -irc\t\tuse IRC connections by default\n\ icb [same switches] (default to -icb)\n"; #ifdef _Windows extern unsigned int hwndMain; #endif /* _Windows */ /* irc_exit: cleans up and leaves */ RETSIGTYPE irc_exit() { do_hook(EXIT_LIST, "Exiting"); close_server(-1, empty_string); logger(0); set_history_file((u_char *) 0); #ifndef _Windows clean_up_processes(); #endif /* _Windows */ 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(); } #ifdef _Windows DestroyWindow(hwndMain); #else exit(0); #endif /* _Windows */ } #ifdef CORECATCH /* sigsegv: something to handle segfaults in a nice way */ /* this needs to be changed to *NOT* use printf(). */ RETSIGTYPE coredump(signo) int signo; { printf("IRCII has been terminated by a SIG%s\n\r", signals[signo]); printf("Please inform the ircii bugs list (ircii-bugs@eterna.com.au) of this\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(); } #endif /* CORECATCH */ /* * 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(dummy, ptr) u_char *dummy; u_char *ptr; { size_t len; int old_irc_io_loop; old_irc_io_loop = irc_io_loop; irc_io_loop = 0; if ((len = my_strlen(ptr)) != 0) { if (!my_strnicmp(ptr, UP("yes"), len)) { send_to_server("QUIT"); irc_exit(); } } irc_io_loop = old_irc_io_loop; } /* irc_quit: prompts the user if they wish to exit, then does the right thing */ void irc_quit(key, ptr) u_int key; u_char *ptr; { static int in_it = 0; if (in_it) return; in_it = 1; add_wait_prompt(UP("Do you really want to quit? "), quit_response, empty_string, WAIT_PROMPT_LINE); in_it = 0; } void on_signal_occurred(signo) int signo; { if (cur_signal > 16) return; occurred_signals[++cur_signal] = signo; } /* * cntl_c: emergency exit.... if somehow everything else freezes up, hitting * ^C five times should kill the program. */ static RETSIGTYPE cntl_c(signo) int signo; { #ifdef SYSVSIGNALS (void) MY_SIGNAL(SIGINT, (sigfunc *) cntl_c, 0); #endif /* SYSVSIGNALS */ on_signal_occurred(signo); if (cntl_c_hit++ >= 4) irc_exit(); } static RETSIGTYPE sig_on(signo) int signo; { #ifdef SYSVSIGNALS (void) sigfunc *(signo, (sigfunc *) sig_on, 0); #endif /* SYSVSIGNALS */ on_signal_occurred(signo); } static RETSIGTYPE sig_user1(signo) int signo; { #ifdef SYSVSIGNALS (void) sigfunc *(SIGUSR1, (sigfunc *) sig_user1, 0); #endif /* SYSVSIGNALS */ do_sig_user1++; on_signal_occurred(signo); } static void real_sig_user1() { say("Got SIGUSR1, closing DCC connections and EXECed processes"); close_all_dcc(); #ifndef _Windows close_all_exec(); #endif /* _Windows */ } #ifdef DO_USER2 static RETSIGTYPE sig_user2(signo) int signo; { #ifdef SYSVSIGNALS (void) MY_SIGNAL(SIGUSR2, (sigfunc *) sig_user2, 0); #endif /* SYSVSIGNALS */ on_signal_occurred(signo); say("Got SIGUSR2, jumping to normal loop"); /* unsafe */ longjmp(outta_here); } #endif #ifdef MUNIX /* A characteristic of PCS MUNIX - Ctrl-Y produces a SIGQUIT */ static RETSIGTYPE cntl_y(signo) int signo; { (void) MY_SIGNAL(SIGQUIT, (sigfunc *) cntl_y, 0); on_signal_occurred(signo); edit_char(25); /* Ctrl-Y */ /* unsafe */ } #endif /* MINUX */ static RETSIGTYPE sig_refresh_screen(signo) int signo; { on_signal_occurred(signo); do_refresh_screen++; } /* shows the version of irc */ static void show_version() { printf("ircII version %s\n\r", irc_version); exit (0); } /* get_arg: used by parse_args() to get an argument after a switch */ static u_char * get_arg(arg, next, ac) u_char *arg; u_char *next; int *ac; { (*ac)++; if (*arg) return (arg); else { if (next) return (next); fprintf(stderr, "irc: missing parameter\n"); exit(1); return (0); /* cleans up a warning */ } } /* * parse_args: parse command line arguments for irc, and sets all initial * flags, etc. */ static u_char * parse_args(argv, argc) u_char **argv; int argc; { u_char *arg, *ptr; int ac; int add_servers = 0; u_char *channel = (u_char *) 0; struct passwd *entry; int minus_minus = 0; #ifdef _Windows u_char buffer[BIG_BUFFER_SIZE]; #endif if (my_strncmp(argv[0], "icb", 3) == 0 || strstr(CP(argv[0]), "/icb") != 0) client_default_icb = 1; *realname = '\0'; ac = 1; malloc_strcpy(&args_str, argv[0]); malloc_strcat(&args_str, UP(" ")); while ((arg = argv[ac++]) != (u_char *) 0) { malloc_strcat(&args_str, argv[ac-1]); malloc_strcat(&args_str, UP(" ")); if ((*arg == '-') != '\0') { ++arg; while (*arg) { switch (*(arg++)) { case 'v': show_version(); break; case 'b': if (qflag) { fprintf(stderr, "Can not use -b with -q\n"); exit(1); } bflag = 1; break; case 'c': malloc_strcpy(&channel, get_arg(arg, argv[ac], &ac)); break; case 'p': irc_port = my_atoi(get_arg(arg, argv[ac], &ac)); break; case 'P': icb_port = my_atoi(get_arg(arg, argv[ac], &ac)); break; case 'f': use_flow_control = 1; break; case 'F': use_flow_control = 0; break; case 'd': dumb = 1; break; case 'H': malloc_strcpy(&dcc_source_host, get_arg(arg, argv[ac], &ac)); break; case 'h': malloc_strcpy(&source_host, get_arg(arg, argv[ac], &ac)); break; case 'i': if (arg[0] == 'r' && arg[1] == 'c') client_default_icb = 0; else if (arg[0] == 'c' && arg[1] == 'b') client_default_icb = 1; else { printf("irc: invalid -i option; use -irc or -icb"); exit(-1); } arg += 2; break; #ifdef DEBUG case 'D': setdlevel(my_atoi(get_arg(arg, argv[ac], &ac))); break; case 'o': { FILE *fp; u_char *file = get_arg(arg, argv[ac], &ac); if (!file) { printf("irc: need filename for -o\n"); exit(-1); } fp = freopen(CP(file), "w", stderr); if (!fp) { printf("irc: can not open %s: %s\n", file, errno ? "" : strerror(errno)); exit(-1); } } break; #endif /* DEBUG */ case 'l': malloc_strcpy(&ircrc_file, get_arg(arg, argv[ac], &ac)); break; case 'I': malloc_strcpy(&ircquick_file,get_arg(arg, argv[ac], &ac)); break; case 'a': add_servers = 1; break; case 's': using_server_process = 0; break; case 'S': using_server_process = 1; break; case 't': tflag = 1; break; case 'T': tflag = 0; break; case 'q': if (bflag) { fprintf(stderr, "Can not use -q with -b\n"); exit(1); } qflag = 1; break; case 'r': { int oldfg = get_int_var(FOREGROUND_COLOUR_VAR); set_int_var(FOREGROUND_COLOUR_VAR, get_int_var(BACKGROUND_COLOUR_VAR)); set_int_var(BACKGROUND_COLOUR_VAR, oldfg); } break; case '-': if (argv[ac]) { int first = 1; while ((arg = argv[ac++]) != NULL) { malloc_strcat(&command_line, arg); if (first) first = 0; else malloc_strcat(&command_line, UP(" ")); } } minus_minus = 1; break; default: fputs(switch_help, stderr); exit(1); } if (minus_minus) break; } } else { if (nickname && *nickname) build_server_list(arg); else malloc_strcpy(&nickname, arg); } if (minus_minus) break; } if ((u_char *) 0 != (ptr = my_getenv("IRCLIB"))) { malloc_strcpy(&irc_lib, ptr); malloc_strcat(&irc_lib, UP("/")); } else malloc_strcpy(&irc_lib, UP(IRCLIB)); /* -h overrides environment variable */ if ((u_char *) 0 == source_host && (u_char *) 0 != (ptr = my_getenv("IRCHOST"))) malloc_strcpy(&source_host, ptr); if ((u_char *) 0 == ircrc_file && (u_char *) 0 != (ptr = my_getenv("IRCRC"))) malloc_strcpy(&ircrc_file, ptr); if ((u_char *) 0 == ircquick_file && (u_char *) 0 != (ptr = my_getenv("IRCQUICK"))) malloc_strcpy(&ircquick_file, ptr); if ((nickname == 0 || *nickname == '\0') && (u_char *) 0 != (ptr = my_getenv("IRCNICK"))) malloc_strcpy(&nickname, ptr); if ((u_char *) 0 != (ptr = my_getenv("IRCUMODE"))) malloc_strcpy(&send_umode, ptr); if ((u_char *) 0 != (ptr = my_getenv("IRCNAME"))) my_strmcpy(realname, ptr, REALNAME_LEN); if ((u_char *) 0 != (ptr = my_getenv("IRCPATH"))) malloc_strcpy(&irc_path, ptr); else { #ifdef IRCPATH malloc_strcpy(&irc_path, UP(IRCPATH)); #else #ifdef __MSDOS__ malloc_strcpy(&irc_path, ".:~/irc:"); #else malloc_strcpy(&irc_path, ".:~/.irc:"); #endif /* __MSDOS__ */ malloc_strcat(&irc_path, irc_lib); malloc_strcat(&irc_path, "script"); #endif /* IRCPATH */ } set_string_var(LOAD_PATH_VAR, irc_path); new_free(&irc_path); if ((u_char *) 0 != (ptr = my_getenv("IRCSERVER"))) build_server_list(ptr); if (0 == number_of_servers || add_servers) { #ifdef SERVERS_FILE if (read_server_file() || (number_of_servers == 0)) #endif /* SERVERS_FILE */ { u_char *s = (u_char *) 0; #ifdef _Windows GetProfileString("IRC", "Server", "Choose.File->Setup.From.Menu", buffer, sizeof(buffer)); malloc_strcpy(&s, buffer); #else malloc_strcpy(&s, UP(DEFAULT_SERVER)); #endif /* _Windows */ build_server_list(s); new_free(&s); } } #ifdef _Windows if (nickname == 0 || !*nickname) { GetProfileString("IRC", "Nick", "ircuser", buffer, sizeof buffer); malloc_strcpy(&nickname, buffer); } GetProfileString("IRC", "UserName", "ircuser", username, NAME_LEN + 1); GetProfileString("IRC", "RealName", "ircuser", realname, REALNAME_LEN + 1); GetProfileString("IRC", "StartDir", get_path(4), buffer, sizeof buffer); malloc_strcpy(&my_path, buffer); #else /* _Windows */ if ((struct passwd *) 0 != (entry = getpwuid(getuid()))) { if ((*realname == '\0') && entry->pw_gecos && *(entry->pw_gecos)) { #ifdef GECOS_DELIMITER if ((ptr = my_index(entry->pw_gecos, GECOS_DELIMITER)) != NULL) *ptr = '\0'; #endif /* GECOS_DELIMITER */ if ((ptr = my_index(entry->pw_gecos, '&')) == NULL) my_strmcpy(realname, entry->pw_gecos, REALNAME_LEN); else { size_t len = ptr - (u_char *) entry->pw_gecos; if (len < REALNAME_LEN && *(entry->pw_name)) { u_char *q = realname + len; my_strmcpy(realname, entry->pw_gecos, len); my_strmcat(realname, entry->pw_name, REALNAME_LEN); my_strmcat(realname, ptr + 1, REALNAME_LEN); if (islower(*q) && (q == realname || isspace(*(q - 1)))) *q = toupper(*q); } else my_strmcpy(realname, entry->pw_gecos, REALNAME_LEN); } } if (entry->pw_name && *(entry->pw_name)) my_strmcpy(username, entry->pw_name, NAME_LEN); if (entry->pw_dir && *(entry->pw_dir)) malloc_strcpy(&my_path, UP(entry->pw_dir)); } #endif /* _Windows */ if ((u_char *) 0 != (ptr = my_getenv("HOME"))) malloc_strcpy(&my_path, ptr); else if (*my_path == '\0') malloc_strcpy(&my_path, UP("/")); if ('\0' == *realname) my_strmcpy(realname, "*Unknown*", REALNAME_LEN); if ('\0' == *username) { if ((ptr = my_getenv("USER")) != NULL) my_strmcpy(username, ptr, NAME_LEN); else my_strmcpy(username, "Unknown", NAME_LEN); } if ((u_char *) 0 != (ptr = my_getenv("IRCUSER"))) my_strmcpy(username, ptr, REALNAME_LEN); if (nickname == 0 || *nickname == '\0') malloc_strcpy(&nickname, username); if ((u_char *) 0 == ircrc_file) { ircrc_file = (u_char *) new_malloc(my_strlen(my_path) + my_strlen(IRCRC_NAME) + 1); my_strcpy(ircrc_file, my_path); my_strcat(ircrc_file, IRCRC_NAME); } if ((u_char *) 0 == ircquick_file) { ircquick_file = (u_char *) new_malloc(my_strlen(my_path) + my_strlen(IRCQUICK_NAME) + 1); my_strcpy(ircquick_file, my_path); my_strcat(ircquick_file, IRCQUICK_NAME); } return (channel); } /* * TimerTimeout: Called from irc_io to help create the timeout * part of the call to select. */ static void TimerTimeout(tv) struct timeval *tv; { struct timeval current; tv->tv_usec =0; tv->tv_sec =0; if (!PendingTimers) { tv->tv_sec = 70; /* Just larger than the maximum of 60 */ return; } gettimeofday(¤t, NULL); if (PendingTimers->time < current.tv_sec || (PendingTimers->time == current.tv_sec && PendingTimers->microseconds < current.tv_usec)) { /* No time to lose, the event is now or was */ return; } tv->tv_sec = PendingTimers->time - current.tv_sec; if (PendingTimers->microseconds >= current.tv_usec) tv->tv_usec = PendingTimers->microseconds - current.tv_usec; else { tv->tv_usec = current.tv_usec - PendingTimers->microseconds; tv->tv_sec -= 1; } } /* * generally processes one screen's input. this used to be part of irc_io() * but has been split out because that function was too large and too nested. * it returns 0 if IO processing should continue and 1 if it should stop. */ static int irc_do_a_screen(screen, rd, one_key) Screen *screen; fd_set *rd; int one_key; { if (!screen->alive) return 0; set_current_screen(screen); if (!is_main_screen(screen) && FD_ISSET(screen->wservin, rd)) screen_wserv_message(screen); if (FD_ISSET(screen->fdin, rd)) { /* buffer much bigger than IRCD_BUFFER_SIZE */ u_char lbuf[BIG_BUFFER_SIZE + 1]; /* * This section of code handles all in put from the terminal(s) * connected to ircII. Perhaps the idle time *shouldn't* be * reset unless its not a screen-fd that was closed.. */ idle_time = time(0); if (dumb) { int old_timeout; old_timeout = dgets_timeout(1); if (dgets(lbuf, INPUT_BUFFER_SIZE, screen->fdin, (u_char *) 0)) { (void) dgets_timeout(old_timeout); if (one_key) { irc_io_loop = 0; return 1; } *(lbuf + my_strlen(lbuf) - 1) = '\0'; if (get_int_var(INPUT_ALIASES_VAR)) parse_line(NULL, lbuf, empty_string, 1, 0, 0); else parse_line(NULL, lbuf, NULL, 1, 0, 0); } else { say("IRCII exiting on EOF from stdin"); irc_exit(); } } else { int server; u_char loc_buffer[BIG_BUFFER_SIZE + 1]; int n, i; server = from_server; from_server = get_window_server(0); last_input_screen = screen; if (one_key) { if (read(screen->fdin, lbuf, 1)) { irc_io_loop = 0; return 1; } /* * Following Fizzy's remark below, if we * don't use window create, we can't kill * then, can we? --FlashMan, October 1994 */ #ifdef WINDOW_CREATE else { #ifndef _Windows if (!is_main_screen(screen)) kill_screen(screen); else #endif /* _Windows */ irc_exit(); } #endif /* WINDOW_CREATE */ } else if ((n = read(screen->fdin, loc_buffer, BIG_BUFFER_SIZE)) != 0) for (i = 0; i < n; i++) edit_char((u_int)loc_buffer[i]); /* * if the current screen isn't the main screen, * then the socket to the current screen must have * closed, so we call kill_screen() to handle * this - phone, jan 1993. * but not when we arent running windows - Fizzy, may 1993 * if it is the main screen we got an EOF on, we exit.. * closed tty -> chew cpu -> bad .. -phone, july 1993. */ #ifdef WINDOW_CREATE else { if (!is_main_screen(screen)) kill_screen(screen); else irc_exit(); } #endif /* WINDOW_CREATE */ cntl_c_hit = 0; from_server = server; } } return 0; } /* * irc_io: the main irc input/output loop. Handles all io from keyboard, * server, exec'd processes, etc. If a prompt is specified, it is displayed * in the input line and cannot be backspaced over, etc. The func is a * function which will take the place of the SEND_LINE function (which is * what happens when you hit return at the end of a line). This function must * decide if it's ok to exit based on anything you really want. It can then * set the global irc_io_loop to false to cause irc_io to exit. */ int irc_io(prompt, func, my_use_input, loop) u_char *prompt; void (*func)(u_int, u_char *); int my_use_input; int loop; { static int level = 0; fd_set rd, wd; struct timeval cursor_timeout, clock_timeout, right_away, timer, *timeptr; int hold_over; int old_loop; u_char *last_input = NULL; u_char *last_prompt = NULL; void (*last_func)(u_int, u_char *); int one_key = 0; Screen *screen, *old_current_screen; int i; last_func = get_send_line(); if (my_use_input == -1) one_key = 1, prompt = NULL; /* 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 */ clock_timeout.tv_usec = 0L; clock_timeout.tv_sec = 30L; right_away.tv_usec = 0L; right_away.tv_sec = 0L; old_loop = irc_io_loop; irc_io_loop = loop; /* * irc_io has been recursive to date. * with multiple xterms and screen * windows, this has to change */ if (level++ > 5) { level--; irc_io_loop = old_loop; return (1); } if (!dumb) { if (my_use_input) { malloc_strcpy(&last_input, get_input()); set_input(empty_string); last_func = get_send_line(); change_send_line(func); } if (prompt) { malloc_strcpy(&last_prompt, get_input_prompt()); set_input_prompt(prompt); } } /* * Here we work out if this has been called recursively or * not.. and if not so.. -phone */ #if defined(DEBUG) || defined(DO_USER2) if (level != 1) { #ifdef DEBUG yell("--- Recursive call to irc_io() - careful"); #endif /* DEBUG */ } else { #ifdef DO_USER2 if (setjmp(outta_here)) yell("*** Got SIGUSR2, Aborting"); #endif /* DO_USER2 */ } #endif /* DEBUG || DO_USER2 */ timeptr = &clock_timeout; do { break_io_processing = 0; ctcp_was_crypted = 0; FD_ZERO(&rd); FD_ZERO(&wd); set_process_bits(&rd); set_server_bits(&rd, &wd); if (my_use_input) for (screen = screen_list; screen; screen = screen->next) if (screen->alive) { FD_SET(screen->fdin, &rd); if (!is_main_screen(screen)) FD_SET(screen->wservin, &rd); } set_dcc_bits(&rd, &wd); if (term_reset_flag) { refresh_screen(0, (u_char *) 0); term_reset_flag = 0; } TimerTimeout(&timer); if (timer.tv_sec <= timeptr->tv_sec) timeptr = &timer; if ((hold_over = unhold_windows()) != 0) timeptr = &right_away; Debug((7, "irc_io: selecting with %ld:%ld timeout", timeptr->tv_sec, timeptr->tv_usec)); switch (new_select(&rd, &wd, timeptr)) { case 0: case -1: /* * yay for the QNX socket manager... drift here, drift there, oops, * i fell down a hole.. */ #ifdef __QNX__ if (errno == EBADF || errno == ESRCH) irc_io_loop = 0; #endif /* __QNX__ */ if (cntl_c_hit) { if (one_key) { irc_io_loop = 0; break; } edit_char((u_int)'\003'); cntl_c_hit = 0; } if (do_status_alarmed) { real_status_alarmed(); do_status_alarmed = 0; } if (do_refresh_screen) { refresh_screen(0, (u_char *) 0); do_refresh_screen = 0; } if (do_sig_user1) { real_sig_user1(); do_sig_user1 = 0; } for (i = 0; i <= cur_signal; i++) if (occurred_signals[i] != 0) do_hook(OS_SIGNAL_LIST, "%s", signals[occurred_signals[i]]); cur_signal = -1; if (!hold_over) cursor_to_input(); break; default: if (term_reset_flag) { refresh_screen(0, (u_char *) 0); term_reset_flag = 0; } old_current_screen = current_screen; set_current_screen(last_input_screen); if (!break_io_processing) dcc_check(&rd, &wd); if (!break_io_processing) do_server(&rd, &wd); set_current_screen(old_current_screen); for (screen = screen_list; screen && !break_io_processing; screen = screen->next) if (irc_do_a_screen(screen, &rd, one_key)) break; set_current_screen(old_current_screen); if (!break_io_processing) do_processes(&rd); break; } execute_timer(); check_process_limits(); while (check_wait_status(-1) >= 0) ; if ((primary_server == -1) && !never_connected) do_hook(DISCONNECT_LIST, "%s", nickname); timeptr = &clock_timeout; old_current_screen = current_screen; for (screen = screen_list; screen; screen = screen->next) { set_current_screen(screen); if (screen->alive && is_cursor_in_display()) timeptr = &cursor_timeout; } set_current_screen(old_current_screen); if (update_clock(0, 0, UPDATE_TIME)) { Debug((8, "update_clock(0,0,0) returned true; updating clock")); if (get_int_var(CLOCK_VAR) || check_mail_status()) { status_update(1); cursor_to_input(); } if (primary_server != -1) do_notify(); } } while (irc_io_loop); level--; irc_io_loop = old_loop; if (! dumb) { if (my_use_input) { set_input(last_input); new_free(&last_input); change_send_line(last_func); } if (prompt) { if (level == 0) set_input_prompt(get_string_var(INPUT_PROMPT_VAR)); else set_input_prompt(last_prompt); new_free(&last_prompt); } } update_input(UPDATE_ALL); return (0); } int /*ARGSUSED*/ main(int, char *[], char *[]); int main(argc, argv, envp) int argc; char *argv[]; char *envp[]; { u_char *channel; srandom(time(NULL) ^ getpid()); /* something */ start_time = time((time_t *)0); #ifdef SOCKS SOCKSinit(argv[0]); #endif /* SOCKS */ channel = parse_args((u_char **) argv, argc); if ((use_input == 0) && !no_fork) { if (fork()) _exit(0); } if (gethostname(CP(hostname), NAME_LEN)) { fprintf(stderr, "irc: couldn't figure out the name of your machine!\n"); exit(1); } term_set_fp(stdout); if (dumb) new_window(); else { init_screen(); #if !defined(MUNIX) && !defined(_RT) (void) MY_SIGNAL(SIGCONT, (sigfunc *) term_cont, 0); #elif defined(SIGCONT) (void) MY_SIGNAL(SIGCONT, (sigfunc *) sig_on, 0 ); #endif /* !defined(MUNIX) && !defined(_RT) */ #if !defined(_RT) && defined(SIGWINCH) (void) MY_SIGNAL(SIGWINCH, (sigfunc *) sig_refresh_screen, 0); #elif defined(SIGWINCH) (void) MY_SIGNAL(SIGWINCH, (sigfunc *) sig_on, 0 ); #endif /* _RT */ #ifndef ALLOC_DEBUG # ifdef CORECATCH (void) MY_SIGNAL(SIGSEGV, (sigfunc *) coredump, 0); # ifdef SIGBUS (void) MY_SIGNAL(SIGBUS, (sigfunc *) coredump, 0); # endif /* SIGBUS */ # else (void) MY_SIGNAL(SIGSEGV, (sigfunc *) SIG_DFL, 0); /* Linux doesn't have SIGBUS */ # ifdef SIGBUS (void) MY_SIGNAL(SIGBUS, (sigfunc *) SIG_DFL, 0); # endif /* SIGBUS */ # endif /* CORECATCH */ #endif /* ALLOC_DEBUG */ #ifdef MUNIX (void) MY_SIGNAL(SIGQUIT, (sigfunc *) cntl_y, 0); #endif /* MINUX */ (void) MY_SIGNAL(SIGHUP, (sigfunc *) irc_exit, 0); (void) MY_SIGNAL(SIGTERM, (sigfunc *) irc_exit, 0); (void) MY_SIGNAL(SIGPIPE, (sigfunc *) sig_on, 0); (void) MY_SIGNAL(SIGINT, (sigfunc *) cntl_c, 0); #ifdef SIGSTOP (void) MY_SIGNAL(SIGSTOP, (sigfunc *) sig_on, 0); #endif /* SIGSTOP */ (void) MY_SIGNAL(SIGUSR1, (sigfunc *) sig_user1, 0); #ifdef DO_USER2 (void) MY_SIGNAL(SIGUSR2, (sigfunc *) sig_user2, 0); #else (void) MY_SIGNAL(SIGUSR2, (sigfunc *) sig_on, 0); #endif /* DO_USER2 */ #ifdef SIGPWR (void) MY_SIGNAL(SIGPWR, (sigfunc *) sig_on, 0); #endif #ifdef SIGCHLD (void) MY_SIGNAL(SIGCHLD, (sigfunc *) sig_on, 0); #endif #ifdef SIGURG (void) MY_SIGNAL(SIGURG, (sigfunc *) sig_on, 0); #endif #ifdef SIGTRAP (void) MY_SIGNAL(SIGTRAP, (sigfunc *) sig_on, 0); #endif #ifdef SIGTTIN (void) MY_SIGNAL(SIGTTIN, (sigfunc *) sig_on, 0); #endif #ifdef SIGTTOU (void) MY_SIGNAL(SIGTTOU, (sigfunc *) sig_on, 0); #endif #ifdef SIGXCPU (void) MY_SIGNAL(SIGXCPU, (sigfunc *) sig_on, 0); #endif #ifdef SIGXFSZ (void) MY_SIGNAL(SIGXFSZ, (sigfunc *) sig_on, 0); #endif #ifdef SIGPOLL (void) MY_SIGNAL(SIGPOLL, (sigfunc *) sig_on, 0); #endif #ifdef SIGINFO (void) MY_SIGNAL(SIGINFO, (sigfunc *) sig_on, 0); #endif #ifdef SIGWAITING (void) MY_SIGNAL(SIGWAITING, (sigfunc *) sig_on, 0); #endif #ifdef SIGLWP (void) MY_SIGNAL(SIGLWP, (sigfunc *) sig_on, 0); #endif #ifdef SIGSYS (void) MY_SIGNAL(SIGSYS, (sigfunc *) sig_on, 0); #endif #ifdef SIGIO (void) MY_SIGNAL(SIGIO, (sigfunc *) sig_on, 0); #endif /* More signals could probably be added, perhaps some should be removed */ } init_variables(); if (!dumb) { build_status((u_char *) 0); update_input(UPDATE_ALL); } #ifdef MOTD_FILE { struct stat motd_stat, my_stat; u_char *motd = NULL; int des; malloc_strcpy(&motd, irc_lib); malloc_strcat(&motd, UP(MOTD_FILE)); if (stat(CP(motd), &motd_stat) == 0) { u_char *s = (u_char *) 0; malloc_strcpy(&s, my_path); #ifdef __MSDOS__ malloc_strcat(&s, UP("/ircmotd.red")); #else malloc_strcat(&s, UP("/.ircmotd")); #endif /* __MSDOS__ */ if (stat(CP(s), &my_stat)) { my_stat.st_atime = 0L; my_stat.st_mtime = 0L; } unlink(CP(s)); if ((des = open(CP(s), O_CREAT, S_IREAD | S_IWRITE)) != -1) new_close(des); new_free(&s); if (motd_stat.st_mtime > my_stat.st_mtime) { put_file(motd); /* Thanks to Mark Dame for this one */ #if defined(PAUSE_AFTER_MOTD) && PAUSE_AFTER_MOTD input_pause(UP("******** Press any key to continue ********")); #endif /* PAUSE_AFTER_MOTD */ clear_window_by_refnum(0); } } new_free(&motd); } #endif /* MOTD_FILE */ if (bflag) { loading_global = 1; load(empty_string, UP("global"), empty_string); loading_global = 0; load_ircrc(); } get_connected(0); if (channel) { if (get_server_version(primary_server) == ServerICB) set_server_icbgroup(primary_server, channel); else { u_char *ptr; ptr = my_strsep(&channel, UP(",")); if (is_channel(ptr)) add_channel(ptr, 0, 0, CHAN_LIMBO, (ChannelList *) 0); while ((ptr = my_strsep(&channel, UP(","))) != NULL) if (is_channel(ptr)) add_channel(ptr, 0, 0, CHAN_LIMBO, (ChannelList *) 0); new_free(&channel); } } idle_time = time(0); set_input(empty_string); #ifndef _Windows irc_io(get_string_var(INPUT_PROMPT_VAR), NULL, use_input, irc_io_loop); irc_exit(); #endif /* _Windows */ return 0; } /* * set_irchost: This sets the source host for subsequent connections. */ void set_irchost(irchost) u_char *irchost; { if (!irchost || !*irchost) new_free(&source_host); else malloc_strcpy(&source_host, irchost); } /* * set_dcchost: This sets the source host for subsequent connections. */ void set_dcchost(dcchost) u_char *dcchost; { if (!dcchost || !*dcchost) new_free(&dcc_source_host); else malloc_strcpy(&dcc_source_host, dcchost); }