/* * server.c: Things dealing with server connections, etc. * * 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. */ #include "irc.h" IRCII_RCSID("@(#)$eterna: server.c,v 1.183 2006/07/22 03:50:10 mrg Exp $"); #ifdef HAVE_SYS_UN_H # include int connect_to_unix(int, u_char *); #endif /* HAVE_SYS_UN_H */ #include "server.h" #include "screen.h" #include "ircaux.h" #include "whois.h" #include "lastlog.h" #include "exec.h" #include "window.h" #include "output.h" #include "names.h" #include "parse.h" #include "list.h" #include "newio.h" #include "vars.h" #include "hook.h" #include "icb.h" #include "server.h" static void add_to_server_buffer(int, u_char *); static void login_to_server(int); static int connect_to_server_direct(u_char *, int, u_char *, int); static int connect_to_server_process(u_char *, int, u_char *, int); static void irc2_login_to_server(int); static void server_group_get_connected_next(int); static int reconnect_to_server(int, int); /* * Don't want to start ircio by default... */ int using_server_process = 0; /* server_list: the list of servers that the user can connect to,etc */ Server *server_list = (Server *) 0; /* number_of_servers: in the server list */ int number_of_servers = 0; /* server_group_list: list of server groups */ SGroup *server_group_list = (SGroup *) 0; extern WhoisQueue *WQ_head; extern WhoisQueue *WQ_tail; int primary_server = -1; int from_server = -1; int never_connected = 1; /* true until first connection * is made */ int connected_to_server = 0; /* true when connection is * confirmed */ int parsing_server_index = -1; extern int dgets_errno; #define DEFAULT_SERVER_VERSION Server2_8 /* * close_server: Given an index into the server list, this closes the * connection to the corresponding server. It does no checking on the * validity of the index. It also first sends a "QUIT" to the server being * closed */ void close_server(server_index, message) int server_index; u_char *message; { u_char buffer[BIG_BUFFER_SIZE]; int i, min, max; if (server_index == -1) { min = 0; max = number_of_servers; } else { min = server_index; max = server_index + 1; } for (i = min; i < max; i++) { int old_server = from_server; if (server_list[i].flags & CLOSE_PENDING) continue; if (waiting) irc_io_loop = 0; if (i == primary_server) clean_whois_queue(); from_server = -1; mark_not_connected(i); from_server = old_server; server_list[i].operator = 0; server_list[i].connected = 0; server_list[i].buffer = (u_char *) 0; server_list[i].flags = SERVER_2_6_2; if (-1 != server_list[i].write) { if (message && *message) { snprintf(CP(buffer), sizeof buffer, "QUIT :%s\n", message); send(server_list[i].write, CP(buffer), my_strlen(buffer), 0); } new_close(server_list[i].write); if (server_list[i].write == server_list[i].read) server_list[i].read = -1; server_list[i].write = -1; } if (-1 != server_list[i].read) { new_close(server_list[i].read); server_list[i].read = -1; } #ifndef _Windows if (-1 != server_list[i].pid) { kill(server_list[i].pid, SIGKILL); server_list[i].pid = (pid_t) -1; } #endif /* _Windows */ } } /* * set_server_bits: Sets the proper bits in the fd_set structure according to * which servers in the server list have currently active read descriptors. */ void set_server_bits(rd, wd) fd_set *rd, *wd; { int i; for (i = 0; i < number_of_servers; i++) { if (server_list[i].read != -1) FD_SET(server_list[i].read, rd); #ifdef NON_BLOCKING_CONNECTS if (!(server_list[i].flags & (LOGGED_IN|CLOSE_PENDING)) && server_list[i].write != -1) FD_SET(server_list[i].write, wd); #endif /* NON_BLOCKING_CONNECTS */ } } static int reconnect_to_server(si, fi) int si, fi; { return connect_to_server(server_list[si].name, server_list[si].port, server_list[si].nickname, fi); } /* * do_server: check the given fd_set against the currently open servers in * the server list. If one have information available to be read, it is read * and and parsed appropriately. If an EOF is detected from an open server, * one of two things occurs. 1) If the server was the primary server, * get_connected() is called to maintain the connection status of the user. * 2) If the server wasn't a primary server, connect_to_server() is called to * try to keep that connection alive. */ void do_server(rd, wd) fd_set *rd, *wd; { u_char lbuf[BIG_BUFFER_SIZE]; int des, j; static int times = 0; int old_timeout; #ifdef NON_BLOCKING_CONNECTS Win_Trav stuff; Window *tmp; #endif for (j = 0; j < number_of_servers && !break_io_processing; j++) { #ifdef NON_BLOCKING_CONNECTS /* * deraadt@theos.com suggests that every fd awaiting connection * should be run at this point. */ if ((des = server_list[j].write) != -1 && /*FD_ISSET(des, wd) && */ !(server_list[j].flags & LOGGED_IN)) { SOCKADDR_STORAGE sa; socklen_t salen = sizeof sa; if (getpeername(server_list[j].write, (struct sockaddr *) &sa, &salen) != -1) login_to_server((from_server = j)); } #endif /* NON_BLOCKING_CONNECTS */ if ((des = server_list[j].read) != -1 && FD_ISSET(des, rd)) { int junk; u_char *bufptr; u_char *s; int i = j; int old_sep = -1; int is_icb; size_t len; from_server = i; is_icb = get_server_version(from_server) == ServerICB; if (is_icb) old_sep = dgets_set_separator('\0'); old_timeout = dgets_timeout(1); s = server_list[from_server].buffer; bufptr = lbuf; if (s && *s) { len = my_strlen(s); my_strncpy(lbuf, s, len); bufptr += len; } else len = 0; if (len >= sizeof(lbuf)) goto buffer_is_full_hack; /* XXX? */ junk = dgets(bufptr, (int)(sizeof(lbuf) - len), des, (u_char *) 0); (void) dgets_timeout(old_timeout); switch (junk) { case -1: add_to_server_buffer(from_server, lbuf); goto real_continue; case 0: { #ifdef NON_BLOCKING_CONNECTS int old_serv = server_list[i].close_serv; /* Get this here before close_server() clears it -Sol */ int logged_in = server_list[i].flags & LOGGED_IN; #endif /* NON_BLOCKING_CONNECTS */ close_server(i, empty_string); say("Connection closed from %s: %s", server_list[i].name, dgets_errno == -1 ? "Remote end closed connection" : strerror(dgets_errno)); server_list[i].read = server_list[i].write = -1; #ifdef NON_BLOCKING_CONNECTS if (!logged_in && server_list[i].res0) { say("Trying next IP address for %s...", server_list[i].name); if (reconnect_to_server(i, -1)) { say("Connection to server %s failed...", server_list[i].name); clean_whois_queue(); window_check_servers(); } continue; } if (!logged_in && old_serv != -1) { if (old_serv == i) /* a hack? you bet */ goto a_hack; if (server_list[old_serv].flags & CLOSE_PENDING) { say("Connection to server %s resumed...", server_list[old_serv].name); server_list[i].close_serv = -1; server_list[old_serv].flags &= ~(CLOSE_PENDING|CLEAR_PENDING); server_list[old_serv].flags |= LOGGED_IN; server_list[old_serv].connected = 1; stuff.flag = 1; while ((tmp = window_traverse(&stuff))) if (tmp->server == i) { window_set_server(tmp->refnum, old_serv, WIN_ALL); break; } } window_check_servers(); break; } a_hack: #endif /* NON_BLOCKING_CONNECTS */ if (i == primary_server) { if (server_list[i].eof) { say("Unable to connect to server %s", server_list[i].name); if (i == number_of_servers - 1) { clean_whois_queue(); window_check_servers(); if (!connected_to_server) say("Use /SERVER to connect to a server"); times = 0; } else server_group_get_connected_next(i); } else { if (times++ > 1) { clean_whois_queue(); window_check_servers(); if (!connected_to_server) say("Use /SERVER to connect to a server"); times = 0; } else get_connected(i); } } else if (server_list[i].eof) { say("Connection to server %s lost.", server_list[i].name); clean_whois_queue(); window_check_servers(); } else { if (reconnect_to_server(i, -1)) { say("Connection to server %s lost.", server_list[i].name); clean_whois_queue(); window_check_servers(); } } server_list[i].eof = 1; break; } default: buffer_is_full_hack: { int old_psi = parsing_server_index; parsing_server_index = i; server_list[parsing_server_index].parse_server(lbuf); new_free(&server_list[i].buffer); parsing_server_index = old_psi; break; } } real_continue: from_server = primary_server; if (is_icb && old_sep != -1) (void)dgets_set_separator(old_sep); } } } /* * find_in_server_list: given a server name, this tries to match it against * names in the server list, returning the index into the list if found, or * -1 if not found */ extern int find_in_server_list(server, port, nick) u_char *server; int port; u_char *nick; { int i, maybe = -1; size_t len; len = my_strlen(server); for (i = 0; i < number_of_servers; i++) { if (port && server_list[i].port && port != server_list[i].port) continue; if (my_strnicmp(server, server_list[i].name, len) != 0) continue; if (nick) { if (server_list[i].nickname == NULL) { maybe = i; continue; } if (my_stricmp(server_list[i].nickname, nick)) continue; } maybe = i; break; } return (maybe); } /* * parse_server_index: given a string, this checks if it's a number, and if * so checks it validity as a server index. Otherwise -1 is returned */ int parse_server_index(str) u_char *str; { int i; if (is_number(str)) { i = my_atoi(str); if ((i >= 0) && (i < number_of_servers)) return (i); } return (-1); } /* * add_to_server_list: adds the given server to the server_list. If the * server is already in the server list it is not re-added... however, if the * SL_ADD_OVERWRITE flag is true, the port and passwords are updated to the * values passes. If the server is not on the list, it is added to the end. * In either case, the server is made the current server. */ void add_to_server_list(server, port, password, nick, group, type, flags) u_char *server; int port; u_char *password; u_char *nick; int group; int type; int flags; { int i; if (port == -1) port = CHOOSE_PORT(type); if ((from_server = find_in_server_list(server, port, nick)) == -1) { from_server = number_of_servers++; if (server_list) server_list = (Server *) new_realloc(UP(server_list), number_of_servers * sizeof(Server)); else server_list = (Server *) new_malloc(number_of_servers * sizeof(Server)); server_list[from_server].name = (u_char *) 0; server_list[from_server].itsname = (u_char *) 0; server_list[from_server].password = (u_char *) 0; server_list[from_server].away = (u_char *) 0; server_list[from_server].version_string = (u_char *) 0; server_list[from_server].operator = 0; server_list[from_server].read = -1; server_list[from_server].write = -1; server_list[from_server].pid = -1; server_list[from_server].whois = 0; server_list[from_server].flags = SERVER_2_6_2; server_list[from_server].nickname = (u_char *) 0; server_list[from_server].connected = 0; server_list[from_server].eof = 0; server_list[from_server].motd = 1; server_list[from_server].group = (u_char *) 0; server_list[from_server].icbmode = (u_char *) 0; server_list[from_server].chan_list = (ChannelList *) 0; malloc_strcpy(&(server_list[from_server].name), server); if (password && *password) malloc_strcpy(&server_list[from_server].password, password); if (nick && *nick) malloc_strcpy(&server_list[from_server].nickname, nick); server_list[from_server].port = port; server_list[from_server].WQ_head = (WhoisQueue *) 0; server_list[from_server].WQ_tail = (WhoisQueue *) 0; server_list[from_server].whois_stuff.nick = (u_char *) 0; server_list[from_server].whois_stuff.user = (u_char *) 0; server_list[from_server].whois_stuff.host = (u_char *) 0; server_list[from_server].whois_stuff.channel = (u_char *) 0; server_list[from_server].whois_stuff.channels = (u_char *) 0; server_list[from_server].whois_stuff.name = (u_char *) 0; server_list[from_server].whois_stuff.server = (u_char *) 0; server_list[from_server].whois_stuff.server_stuff = (u_char *) 0; server_list[from_server].whois_stuff.away = (u_char *) 0; server_list[from_server].whois_stuff.oper = 0; server_list[from_server].whois_stuff.chop = 0; server_list[from_server].whois_stuff.not_on = 0; server_list[from_server].buffer = (u_char *) 0; server_list[from_server].close_serv = -1; server_list[from_server].localaddr = 0; server_list[from_server].localaddrlen = 0; switch (type) { case ServerICB: server_list[from_server].parse_server = icb_parse_server; break; case -1: /* default */ if (client_default_icb) { type = ServerICB; server_list[from_server].parse_server = icb_parse_server; break; } type = DEFAULT_SERVER_VERSION; /* FALLTHROUGH */ default: server_list[from_server].parse_server = irc2_parse_server; } server_list[from_server].version = type; server_list[from_server].ctcp_last_reply_time = 0; server_list[from_server].ctcp_flood_time = 0; server_list[from_server].ctcp_backlog_size = get_int_var(CTCP_REPLY_BACKLOG_SECONDS_VAR); server_list[from_server].ctcp_send_size = (int *)new_malloc(server_list[from_server].ctcp_backlog_size*sizeof(int)); for(i = 0; i (int) my_strlen(server_list[from_server].name)) malloc_strcpy(&(server_list[from_server].name), server); } } extern void ctcp_reply_backlog_change(s) int s; { int i, j, delta; if (s <= 0) s = 1; if (server_list) { for (i = 0; i < number_of_servers; i++) { delta = s - server_list[i].ctcp_backlog_size; if (delta) { server_list[i].ctcp_send_size = (int *)new_realloc((void *)(server_list[i].ctcp_send_size), s*sizeof(int)); for(j = server_list[i].ctcp_backlog_size; j < s; j++) server_list[i].ctcp_send_size[j] = 0; server_list[i].ctcp_backlog_size = s; } } } } extern void remove_from_server_list(i) int i; { int old_server = from_server, flag = 1; Window *tmp; from_server = i; clean_whois_queue(); from_server = old_server; close_server(i, (u_char *) 0); if (server_list[i].name) new_free(&server_list[i].name); if (server_list[i].itsname) new_free(&server_list[i].itsname); if (server_list[i].password) new_free(&server_list[i].password); if (server_list[i].away) new_free(&server_list[i].away); if (server_list[i].version_string) new_free(&server_list[i].version_string); if (server_list[i].nickname) new_free(&server_list[i].nickname); if (server_list[i].group) new_free(&server_list[i].group); if (server_list[i].icbmode) new_free(&server_list[i].icbmode); if (server_list[i].whois_stuff.nick) new_free(&server_list[i].whois_stuff.nick); if (server_list[i].whois_stuff.user) new_free(&server_list[i].whois_stuff.user); if (server_list[i].whois_stuff.host) new_free(&server_list[i].whois_stuff.host); if (server_list[i].whois_stuff.channel) new_free(&server_list[i].whois_stuff.channel); if (server_list[i].whois_stuff.channels) new_free(&server_list[i].whois_stuff.channels); if (server_list[i].whois_stuff.name) new_free(&server_list[i].whois_stuff.name); if (server_list[i].whois_stuff.server) new_free(&server_list[i].whois_stuff.server); if (server_list[i].whois_stuff.server_stuff) new_free(&server_list[i].whois_stuff.server_stuff); if (server_list[i].ctcp_send_size) new_free(&server_list[i].ctcp_send_size); if (server_list[i].res0) freeaddrinfo(server_list[i].res0); /* update all the structs with server in them */ channel_server_delete(i); /* fix `higher' servers */ clear_channel_list(i); #ifndef _Windows exec_server_delete(i); #endif /* _Windows */ if (i < primary_server) --primary_server; if (i < from_server) --from_server; while ((tmp = traverse_all_windows(&flag)) != NULL) if (tmp->server > i && tmp->server > 0) tmp->server--; bcopy((char *) &server_list[i + 1], (char *) &server_list[i], (number_of_servers - i - 1) * sizeof(Server)); server_list = (Server *) new_realloc(UP(server_list), --number_of_servers * sizeof(Server)); if (from_server >= number_of_servers) from_server = -1; } /* * parse_server_info: This parses a single string of the form * "server:portnum:password:nickname[:icbgroup]". It the points port to the portnum * portion and password to the password portion. This chews up the original * string, so * upon return, name will only point the the name. If portnum * or password are missing or empty, their respective returned value will * point to null. if extra is non NULL, it is set to anything after the * final : after the nickname.. * * Note: this will set *type if it sees * the IRC/ or ICB/ at the start of * the "name". The server group name will be set by prepending ":group:" to * the server, so any of these is valid: * * :group:server:portnum:... * ICB/:group:server:portnum:... * server:portnum:... * ICB/server:portnum:... */ void parse_server_info(name, port, password, nick, group, extra, type) u_char **name, **port, **password, **nick, **group, **extra; int *type; { u_char *ptr, *ename, *savename = (u_char *) 0; *port = *password = *nick = *extra = NULL; if (my_strncmp(*name, "IRC/", 4) == 0) { *type = DEFAULT_SERVER_VERSION; *name += 4; } else if (my_strncmp(*name, "ICB/", 4) == 0) { *type = ServerICB; *name += 4; } /* check for :group: processing */ if (**name == ':') { if ((ename = my_index((*name)+1, ':'))) { *ename = '\0'; if (group) *group = *name + 1; *name = ename + 1; /* now points to empty or : we hope */ } } /* check for [i:p:v:6]:port style */ if (**name == '[') { if ((ename = my_index((*name)+1, ']'))) { *ename = '\0'; savename = *name + 1; *name = ename + 1; /* now points to empty or : we hope */ } } if ((ptr = my_index(*name, ':')) != NULL) { *(ptr++) = '\0'; if (my_strlen(ptr) == 0) *port = (u_char *) 0; else { *port = ptr; if ((ptr = my_index(ptr, ':')) != NULL) { *(ptr++) = '\0'; if (my_strlen(ptr) == 0) *password = '\0'; else { *password = ptr; if ((ptr = my_index(ptr, ':')) != NULL) { *(ptr++) = '\0'; if (!my_strlen(ptr)) *nick = NULL; else { *nick = ptr; if (extra && (ptr = my_index(ptr, ':')) != NULL) { *(ptr++) = '\0'; if (!my_strlen(ptr)) *extra = NULL; else *extra = ptr; } } } } } } } if (savename) *name = savename; } /* * build_server_list: given a whitespace separated list of server names this * builds a list of those servers using add_to_server_list(). Since * add_to_server_list() is used to added each server specification, this can * be called many many times to add more servers to the server list. Each * element in the server list case have one of the following forms: * * servername * * servername:port * * servername:port:password * * servername::password * * Note also that this routine mucks around with the server string passed to it, * so make sure this is ok . * * A new format for ICB and more support is: * * type/ * * eg: * IRC/server:port:pass:nick:#foo:#bar:&baz * means connect to server on port port with pass and nick, and then to join * channels #foo, #bar and &baz. this is not implemented beyond the nick... * * or * ICB/[:group:]server:port:pass:nick:group:mode * which is all the things needed at connection startup. this is done. */ void build_server_list(servers) u_char *servers; { u_char *host, *rest, *extra, *mode, *password = (u_char *) 0, *port = (u_char *) 0, *group = (u_char *) 0, *nick = (u_char *) 0; int port_num, type = -1; if (servers == (u_char *) 0) return; while (servers) { if ((rest = my_index(servers, '\n')) != NULL) *rest++ = '\0'; while ((host = next_arg(servers, &servers)) != NULL) { parse_server_info(&host, &port, &password, &nick, &group, &extra, &type); if (port && *port) { port_num = my_atoi(port); if (!port_num) port_num = CHOOSE_PORT(type); } else port_num = CHOOSE_PORT(type); if (!nick) nick = nickname; add_to_server_list(host, port_num, password, nick, find_server_group(group, 1), type, 0); if (extra) { switch (type) { case ServerICB: if ((mode = my_index(extra, ':')) && mode[1]) *mode++ = 0; else mode = NULL; set_server_icbgroup(from_server, extra); set_server_icbmode(from_server, mode); break; default: break; /* nothing yet */ } } } servers = rest; } } /* * connect_to_server_direct: handles the tcp connection to a server. If * successful, the user is disconnected from any previously connected server, * the new server is added to the server list, and the user is registered on * the new server. If connection to the server is not successful, the * reason for failure is displayed and the previous server connection is * resumed uniterrupted. * * This version of connect_to_server() connects directly to a server */ static int connect_to_server_direct(server_name, port, nick, server_index) u_char *server_name; int port; u_char *nick; int server_index; { SOCKADDR_STORAGE *localaddr; int new_des; struct addrinfo *r = 0, *r0 = 0; socklen_t address_len; oper_command = 0; errno = 0; #ifdef HAVE_SYS_UN_H if (*server_name == '/') new_des = connect_to_unix(port, server_name); else #endif /* HAVE_SYS_UN_H */ { if (server_index >= 0 && server_list[server_index].res && server_list[server_index].res0) new_des = connect_by_number(port, server_name, 1, &server_list[server_index].res, &server_list[server_index].res0); else new_des = connect_by_number(port, server_name, 1, &r, &r0); } if (new_des < 0) { char *e = NULL; switch (new_des) { default: case -2: e = "Unknown host"; errno = 0; break; case -3: e = "socket"; break; case -4: e = "connect"; break; } say("Unable to connect to port %d of server %s: %s%s%s", port, server_name, e, errno ? ": " : "", errno ? strerror(errno) : ""); if (is_server_open(from_server)) say("Connection to server %s resumed...", server_list[from_server].name); return (-1); } if (server_list[from_server].localaddr) new_free(&server_list[from_server].localaddr); server_list[from_server].localaddr = 0; address_len = sizeof *localaddr; localaddr = (SOCKADDR_STORAGE *) new_malloc(sizeof *localaddr); #ifdef HAVE_SYS_UN_H if (*server_name == '/') { server_list[from_server].localaddr = 0; server_list[from_server].localaddrlen = 0; } else #endif /* HAVE_SYS_UN_H */ if (getsockname(new_des, (struct sockaddr *) localaddr, &address_len) >= 0) { server_list[from_server].localaddr = localaddr; server_list[from_server].localaddrlen = address_len; } else { close(new_des); say("Could not getsockname(): %s", strerror(errno)); return -1; } update_all_status(); add_to_server_list(server_name, port, (u_char *) 0, nick, -1, get_server_version(from_server), SL_ADD_OVERWRITE); if (port) { server_list[from_server].read = new_des; server_list[from_server].write = new_des; } else server_list[from_server].read = new_des; if (!server_list[from_server].res0 && r && r0) { server_list[from_server].res = r; server_list[from_server].res0 = r0; } server_list[from_server].operator = 0; return (0); } /* * connect_to_server_process: handles the tcp connection to a server. If * successful, the user is disconnected from any previously connected server, * the new server is added to the server list, and the user is registered on * the new server. If connection to the server is not successful, the * reason for failure is displayed and the previous server connection is * resumed uniterrupted. * * This version of connect_to_server() uses the ircio process to talk to a * server */ static int connect_to_server_process(server_name, port, nick, server_index) u_char *server_name; int port; u_char *nick; int server_index; { #ifdef _Windows return -1; #else int write_des[2], read_des[2], pid, c; u_char *path, *name = (u_char *) 0, *s; u_char buffer[BIG_BUFFER_SIZE]; int old_timeout; path = UP(IRCIO_PATH); if ((s = my_rindex(path, '/')) != NULL) malloc_strcpy(&name, s + 1); if (!name) name = path; if (*path == '\0') return (connect_to_server_direct(server_name, port, nick, server_index)); oper_command = 0; write_des[0] = -1; write_des[1] = -1; if (pipe(write_des) || pipe(read_des)) { if (write_des[0] != -1) { new_close(write_des[0]); new_close(write_des[1]); } say("Couldn't start new process: %s", strerror(errno)); return (connect_to_server_direct(server_name, port, nick, server_index)); } switch (pid = fork()) { case -1: say("Couldn't start new process: %s\n", strerror(errno)); return (-1); case 0: (void) MY_SIGNAL(SIGINT, (sigfunc *)SIG_IGN, 0); dup2(read_des[1], 1); dup2(write_des[0], 0); new_close(read_des[0]); new_close(read_des[1]); new_close(write_des[0]); new_close(write_des[1]); snprintf(CP(buffer), sizeof buffer, "%u", port); setuid(getuid()); execl(CP(path), CP(name), server_name, buffer, (u_char *) 0); printf("-5 0\n"); /* -1 - -4 returned by connect_by_number() */ fflush(stdout); _exit(1); default: new_close(read_des[1]); new_close(write_des[0]); break; } old_timeout = dgets_timeout(3); c = dgets(buffer, sizeof buffer, read_des[0], (u_char *) 0); (void) dgets_timeout(old_timeout); if ((c == 0) || ((c = my_atoi(buffer)) != 0)) { if (c == -5) return (connect_to_server_direct(server_name, port, nick, server_index)); else { u_char *ptr; if ((ptr = my_index(buffer, ' ')) != NULL) { ptr++; if (my_atoi(ptr) > 0) say("Unable to connect to port %d of server %s: %s", port, server_name, strerror(my_atoi(ptr))); else say("Unable to connect to port %d of server %s: Unknown host", port, server_name); } else say("Unable to connect to port %d of server %s: Unknown host", port, server_name); if (is_server_open(from_server)) say("Connection to server %s resumed...", server_list[from_server].name); new_close(read_des[0]); new_close(write_des[1]); return (-1); } } update_all_status(); add_to_server_list(server_name, port, (u_char *) 0, nick, -1, get_server_version(from_server), SL_ADD_OVERWRITE); server_list[from_server].read = read_des[0]; server_list[from_server].write = write_des[1]; server_list[from_server].pid = pid; server_list[from_server].operator = 0; return (0); #endif /* _Windows */ } /* * connect_to_server: Given a name and portnumber, this will attempt to * connect to that server using either a direct connection or process * connection, depending on the value of using_server_process. If connection * is successful, the proper NICK, USER, and PASS commands are sent to the * server. If the c_server parameter is not -1, then the server with that * index will be closed upon successful connection here. Also, if connection * is successful, the attempting_to_connect variable is incremented. This is * checked in the notice.c routines to make sure that connection was truely * successful (and not closed immediately by the server). */ int connect_to_server(server_name, port, nick, c_server) u_char *server_name; int port; u_char *nick; int c_server; { int server_index; SOCKADDR_STORAGE sa; socklen_t salen = sizeof sa; int rv; save_message_from(); message_from((u_char *) 0, LOG_CURRENT); server_index = find_in_server_list(server_name, port, nick); if (server_index < 0) { yell("connect_to_server: server_index returned -1 from find_in_server_list()"); yell("aborting!"); abort(); } server_list[server_index].attempting_to_connect = 1; /* * check if the server doesn't exist, or that we're not already * connected to it. */ if (!is_server_connected(server_index)) { if (is_server_open(server_index)) close_server(server_index, empty_string); if (port == -1) port = server_list[server_index].port; if (port == -1) port = CHOOSE_PORT(server_list[server_index].version); say("Connecting to port %d of server %s", port, server_name); if (!qflag) load_ircquick(); if (using_server_process) rv = connect_to_server_process(server_name, port, nick, server_index); else rv = connect_to_server_direct(server_name, port, nick, server_index); if (rv) { server_list[server_index].attempting_to_connect = 0; restore_message_from(); return -1; } if ((c_server != -1) && (c_server != from_server)) { #ifdef NON_BLOCKING_CONNECTS #if defined(GKM) say("--- server %s will be closed when we connect", server_list[c_server].name); if (server_list[c_server].flags & CLOSE_PENDING) say("--- why are we flagging this for closing a second time?"); #endif /* GKM */ server_list[from_server].close_serv = c_server; server_list[c_server].flags |= CLOSE_PENDING; server_list[c_server].connected = 0; #else close_server(c_server, empty_string); #endif /* NON_BLOCKING_CONNECTS */ } else { server_list[from_server].close_serv = -1; } if (server_list[from_server].nickname == (u_char *) 0) malloc_strcpy(&server_list[from_server].nickname, nickname); server_list[from_server].flags &= ~LOGGED_IN; /* * this used to be an ifndef NON_BLOCKING_CONNECTS .. we want to do this * whenever the connection is valid, it's possible for a connect to be * "immediate". */ if (is_server_open(from_server) && (using_server_process || getpeername(server_list[from_server].read, (struct sockaddr *) &sa, &salen) != -1)) login_to_server(from_server); } else { if (port == -1) { if (server_index != -1) port = server_list[server_index].port; else port = CHOOSE_PORT(get_server_version(server_index)); } say("Connected to port %d of server %s", port, server_name); from_server = server_index; if ((c_server != -1) && (c_server != from_server)) close_server(c_server, empty_string); } update_all_status(); restore_message_from(); return 0; } #ifdef NON_BLOCKING_CONNECTS static void login_to_server_nonnblocking(int); static void login_to_server_nonnblocking(server) int server; { int old_serv = server_list[server].close_serv; if (using_server_process == 0) { set_blocking(server_list[server].read); if (server_list[server].read != server_list[server].write) set_blocking(server_list[server].write); } /* clean up after ourselves */ if (server_list[server].res0) { freeaddrinfo(server_list[server].res0); server_list[server].res0 = 0; } server_list[server].res = 0; if (old_serv != -1) { #if defined(GKM) say("--- closing server %s - changing servers", server_list[server_list[server].close_serv].name); if (!(server_list[server_list[server].close_serv].flags & CLOSE_PENDING)) say("--- uh oh. closing a server that wasn't CLOSE_PENDING"); #endif /* GKM */ if (server_list[old_serv].flags & CLEAR_PENDING) clear_channel_list(old_serv); /* Channels were transfered -Sol */ server_list[old_serv].flags &= ~(CLOSE_PENDING|CLEAR_PENDING); close_server(old_serv, empty_string); server_list[server].close_serv = -1; /* should we pause here to let the net catch up with us? */ } #if defined(GKM) else { say("--- no server to close in login_to_server()"); } #endif /* GKM */ } #endif static void login_to_server(server) int server; { #ifdef NON_BLOCKING_CONNECTS login_to_server_nonnblocking(server); #endif server_list[server].flags |= LOGGED_IN; if (get_server_version(server) == ServerICB) icb_login_to_server(server); else irc2_login_to_server(server); window_set_prev_server(server); } static void irc2_login_to_server(server) int server; { if (get_server_version(server) == ServerICB) { yell("--- ICB called irc2_login_to_server???"); return; } if (server_list[server].password) send_to_server("PASS %s", server_list[server].password); send_to_server("NICK %s", server_list[server].nickname); send_to_server("USER %s %s %s :%s", username, (send_umode && *send_umode) ? send_umode : hostname, server_list[server].name, realname); } /* * get_connected: This function connects the primary server for IRCII. It * attempts to connect to the given server. If this isn't possible, it * traverses the server list trying to keep the user connected at all cost. * oldconn is set if this connection is really an old connection being * resurected (eg. connection to server failed). */ void get_connected(server) int server; { int s, ret = -1; if (server_list) { int already_connected = 0; if (server == number_of_servers) server = 0; else if (server < 0) server = number_of_servers - 1; s = server; if (reconnect_to_server(server, primary_server)) { while (server_list[server].read == -1) { server++; if (server == number_of_servers) server = 0; if (server == s) { clean_whois_queue(); say("Use /SERVER to connect to a server"); break; } from_server = server; already_connected = is_server_connected(server); ret = reconnect_to_server(server, primary_server); } if (!ret) from_server = server; else from_server = -1; } if (from_server != -1) { int flags; flags = (already_connected ? 0 : WIN_TRANSFER); window_set_server(-1, from_server, flags); } } else { clean_whois_queue(); say("Use /SERVER to connect to a server"); } } #ifdef SERVERS_FILE /* * read_server_file: reads hostname:portnum:password server information from * a file and adds this stuff to the server list. See build_server_list()/ */ int read_server_file() { FILE *fp; u_char format[11]; u_char *file_path = (u_char *) 0; u_char buffer[BIG_BUFFER_SIZE]; if ((file_path = my_getenv("IRCSERVERSFILE")) == NULL) { malloc_strcpy(&file_path, irc_lib); malloc_strcat(&file_path, UP(SERVERS_FILE)); } snprintf(CP(format), sizeof format, "%%%ds", (int)sizeof buffer); fp = fopen(CP(file_path), "r"); new_free(&file_path); if ((FILE *) 0 != fp) { while (fscanf(fp, CP(format), buffer) != EOF) build_server_list(buffer); fclose(fp); return (0); } return (1); } #endif /* SERVERS_FILE */ /* display_server_list: just guess what this does */ void display_server_list() { int i; /* XXX */ if (from_server >= number_of_servers) from_server = -1; if (server_list) { if (from_server != -1) say("Current server: %s %d", server_list[from_server].name, server_list[from_server].port); else say("Current server: "); if (primary_server != -1) say("Primary server: %s %d", server_list[primary_server].name, server_list[primary_server].port); else say("Primary server: "); if (client_default_icb) say("Using ICB connections by default"); say("Server list:"); for (i = 0; i < number_of_servers; i++) { u_char *icb_msg, *group_msg, lbuf[BIG_BUFFER_SIZE]; icb_msg = server_list[i].version == ServerICB ? (u_char *) " (ICB connection)" : empty_string; if (server_list[i].server_group) { snprintf(CP(lbuf), sizeof lbuf, " [group: %s]", find_server_group_name(server_list[i].server_group)); group_msg = lbuf; } else group_msg = empty_string; if (!server_list[i].nickname) { say("\t%d) %s %d%s%s%s", i, server_list[i].name, server_list[i].port, server_list[i].read == -1 ? UP(" (not connected)") : empty_string, group_msg, icb_msg); } else { say("\t%d) %s %d (%s%s)%s%s", i, server_list[i].name, server_list[i].port, (server_list[i].read == -1) ? UP("was ") : empty_string, server_list[i].nickname, group_msg, icb_msg); } #ifdef GKM say("\t\tflags: %s%s%s%s%s%s%s", server_list[i].flags & SERVER_2_6_2 ? UP("SERVER_2_6_2 ") : empty_string, server_list[i].flags & USER_MODE_I ? UP("USER_MODE_I ") : empty_string, server_list[i].flags & USER_MODE_W ? UP("USER_MODE_W ") : empty_string, server_list[i].flags & USER_MODE_S ? UP("USER_MODE_S ") : empty_string, server_list[i].flags & CLOSE_PENDING ? UP("CLOSE_PENDING ") : empty_string, server_list[i].flags & CLEAR_PENDING ? UP("CLEAR_PENDING ") : empty_string, server_list[i].flags & LOGGED_IN ? UP("LOGGED_IN ") : empty_string ); say("\t\tclose_serv=%d, connected=%d, read=%d, eof=%d", server_list[i].close_serv, server_list[i].connected, server_list[i].read, server_list[i].eof); #endif /* GKM */ } } else say("The server list is empty"); } void MarkAllAway(command, message) u_char *command; u_char *message; { int old_server; old_server = from_server; for (from_server = 0; from_server < number_of_servers; from_server++) { if (is_server_connected(from_server)) send_to_server("%s :%s", command, message); } from_server = old_server; } /* * set_server_password: this sets the password for the server with the given * index. If password is null, the password for the given server is returned */ u_char * set_server_password(server_index, password) int server_index; u_char *password; { if (server_list) { if (password) malloc_strcpy(&(server_list[server_index].password), password); return (server_list[server_index].password); } else return ((u_char *) 0); } /* * ICB support */ void set_server_icbgroup(server_index, group) int server_index; u_char *group; { malloc_strcpy(&server_list[server_index].group, group); } void set_server_icbmode(server_index, mode) int server_index; u_char *mode; { malloc_strcpy(&server_list[server_index].icbmode, mode); } /* * server: the /SERVER command. Read the SERVER help page about */ /*ARGSUSED*/ void servercmd(command, args, subargs) u_char *command, *args, *subargs; { u_char *server, *port, *extra, *newmode, *password = (u_char *) 0, *nick = (u_char *) 0, *group = (u_char *) 0; int port_num, i, new_server_flags, type = -1; if ((server = next_arg(args, &args)) != NULL) { while (*server == '-') { size_t len; /* * old usage of `/server -' handled here. */ if (*++server == '\0') { get_connected(primary_server - 1); return; } upper(server); len = my_strlen(server); /* * just don't return if you want to perform some action in one of * the flag handling sections. */ if (!my_strncmp(server, "ICB", len)) type = ServerICB; else if (!my_strncmp(server, "IRC", len)) type = DEFAULT_SERVER_VERSION; else if (!my_strncmp(server, "DELETE", len)) { if ((server = next_arg(args, &args)) != NULL) { if ((i = parse_server_index(server)) == -1) { if (-1 == (i = find_in_server_list(server, 0, 0))) { say("No such server in list"); return; } } if (server_list[i].connected) { say("Can not delete server that is already open"); return; } remove_from_server_list(i); return; } say("Need server number for -DELETE"); return; } else if (!my_strncmp(server, "GROUP", len)) { if ((group = next_arg(args, &args)) == NULL) { say("SERVER -GROUP needs and "); return; } } else { say("SERVER: %s is an unknown flag", server); return; } if ((server = next_arg(args, &args)) == NULL) { say("SERVER: need a server name"); return; } } if (my_index(server, ':') != NULL) { parse_server_info(&server, &port, &password, &nick, group ? 0 : &group, &extra, &type); if (!my_strlen(server)) { say("Server name required"); return; } if (port && *port) { port_num = my_atoi(port); if (!port_num) port_num = CHOOSE_PORT(type); } else port_num = CHOOSE_PORT(type); } else { if ((port = next_arg(args, &args)) != NULL) { port_num = my_atoi(port); if (!port_num) port_num = CHOOSE_PORT(type); if ((password = next_arg(args, &args)) != NULL) nick = next_arg(args, &args); } else port_num = CHOOSE_PORT(type); extra = (u_char *) 0; } add_to_server_list(server, port_num, password, nick, -1, type, 0); if (group && *group) server_list[from_server].server_group = find_server_group(group, 1); if (extra && type == ServerICB) { if ((newmode = my_index(extra, ':'))) { *newmode++ = 0; malloc_strcpy(&(server_list[from_server].icbmode), newmode); } malloc_strcpy(&(server_list[from_server].group), extra); } if (*server == '+' || *server == '=' || *server == '~') { if (group) add_server_to_server_group(curr_scr_win->server, group); if (*(server+1)) { u_char servinfo[INPUT_BUFFER_SIZE+1]; if (*server == '+') server++; /* Reconstitute whole server info so window_get_connected can parse it -Sol */ snprintf(CP(servinfo), sizeof servinfo, "%s:%d:%s:%s", server, port_num, password ? password : empty_string, nick ? nick : empty_string); window_get_connected(curr_scr_win, servinfo, -1, (u_char *) 0, group, type); } else get_connected(primary_server + 1); return; } /* * work in progress.. window->prev_server needs to be set for * all windows that used to be associated with a server as it * switches [successfully] to a new server. * this'll be fun since that can happen in server.c and * window.c and non-blocking-connects will throw yet another * wrench into things since we only want it to happen on * a successful connect. - gkm */ else if (*server == '.') { if (*(++server)) { say("syntax error - nothing may be specified after the '.'"); return; } if (current_screen && curr_scr_win && curr_scr_win->prev_server != -1) { if (group) add_server_to_server_group(curr_scr_win->prev_server, group); window_restore_server(curr_scr_win->prev_server); window_get_connected(curr_scr_win, NULL, curr_scr_win->server, (u_char *) 0, group, type); } else say("No server previously in use in this window"); return; } if ((i = parse_server_index(server)) != -1) { server = server_list[i].name; if (server_list[i].port != -1) port_num = server_list[i].port; if (server_list[i].nickname && !nick) nick = server_list[i].nickname; } else i = find_in_server_list(server, port_num, nick); if (group) add_server_to_server_group(i, group); if (is_server_connected(i)) { /* * We reset the log level only if the "new" server * already has windows associated with it : here it's * equivalent to its already being connected. -Sol */ new_server_flags = 0; } else new_server_flags = WIN_TRANSFER; if (connect_to_server(server, port_num, nick, primary_server) != -1) { if (primary_server > -1 && from_server != primary_server && !server_list[from_server].away && server_list[primary_server].away) malloc_strcpy(&server_list[from_server].away, server_list[primary_server].away); window_set_server(-1, from_server, new_server_flags); } } else display_server_list(); } /* * flush_server: eats all output from server, until there is at least a * second delay between bits of servers crap... useful to abort a /links. */ void flush_server() { fd_set rd; struct timeval time_out; int flushing = 1; int des; int old_timeout; u_char buffer[BIG_BUFFER_SIZE]; if ((des = server_list[from_server].read) == -1) return; time_out.tv_usec = 0; time_out.tv_sec = 1; old_timeout = dgets_timeout(1); while (flushing) { FD_ZERO(&rd); FD_SET(des, &rd); switch (new_select(&rd, (fd_set *) 0, &time_out)) { case -1: case 0: flushing = 0; break; default: if (FD_ISSET(des, &rd)) { if (0 == dgets(buffer, sizeof buffer, des, (u_char *) 0)) flushing = 0; } break; } } /* make sure we've read a full line from server */ FD_ZERO(&rd); FD_SET(des, &rd); if (new_select(&rd, (fd_set *) 0, &time_out) > 0) dgets(buffer, sizeof buffer, des, (u_char *) 0); (void) dgets_timeout(old_timeout); } /* * set_server_whois: sets the whois value for the given server index. If the * whois value is 0, it assumes the server doesn't send End of WHOIS commands * and the whois.c routines use the old fashion way of getting whois info. If * the whois value is non-zero, then the server sends End of WHOIS and things * can be done more effienciently */ void set_server_whois(server_index, value) int server_index, value; { server_list[server_index].whois = value; } /* get_server_whois: Returns the whois value for the given server index */ int get_server_whois(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return (server_list[server_index].whois); } void set_server_2_6_2(server_index, value) int server_index, value; { set_server_flag(server_index, SERVER_2_6_2, value); } int get_server_2_6_2(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return (get_server_flag(server_index, SERVER_2_6_2)); } void set_server_flag(server_index, flag, value) int server_index; int flag; int value; { if (server_index == -1) server_index = primary_server; if (value) server_list[server_index].flags |= flag; else server_list[server_index].flags &= ~flag; } int get_server_flag(server_index, value) int server_index; int value; { if (server_index == -1) server_index = primary_server; return server_list[server_index].flags & value; } /* get ICB group */ u_char * get_server_icbgroup(server_index) int server_index; { u_char *group; if (server_index == -1) server_index = primary_server; group = server_list[server_index].group ? server_list[server_index].group : empty_string; return (group); } /* get ICB mode */ u_char * get_server_icbmode(server_index) int server_index; { u_char *mode; if (server_index == -1) server_index = primary_server; mode = server_list[server_index].icbmode ? server_list[server_index].icbmode : empty_string; return (mode); } /* * get_server_password: get the passwor for this server. */ u_char * get_server_password(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return (server_list[server_index].password); } /* * set_server_version: Sets the server version for the given server type. A * zero version means pre 2.6, a one version means 2.6 aso. (look server.h * for typedef) */ void set_server_version(server_index, version) int server_index; int version; { if (server_index == -1) server_index = primary_server; server_list[server_index].version = version; } /* * get_server_version: returns the server version value for the given server * index */ int get_server_version(server_index) int server_index; { if (server_index == -1) server_index = primary_server; if (server_index == -1) return DEFAULT_SERVER_VERSION; else return (server_list[server_index].version); } /* get_server_name: returns the name for the given server index */ u_char * get_server_name(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return (server_list[server_index].name); } /* set_server_itsname: returns the server's idea of its name */ u_char * get_server_itsname(server_index) int server_index; { if (server_index == -1) server_index = primary_server; if (server_list[server_index].itsname) return server_list[server_index].itsname; else if (server_list[server_index].name) return server_list[server_index].name; else return UP(""); } void set_server_itsname(server_index, name) int server_index; u_char *name; { if (server_index == -1) server_index = primary_server; malloc_strcpy(&server_list[server_index].itsname, name); } /* * is_server_open: Returns true if the given server index represents a server * with a live connection, returns false otherwise */ int is_server_open(server_index) int server_index; { if (server_index < 0) return (0); return (server_list[server_index].read != -1); } /* * is_server_connected: returns true if the given server is connected. This * means that both the tcp connection is open and the user is properly * registered */ int is_server_connected(server_index) int server_index; { if (server_index < 0) return (0); return (server_list[server_index].connected && (server_list[server_index].flags & LOGGED_IN)); } /* get_server_port: Returns the connection port for the given server index */ int get_server_port(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return (server_list[server_index].port); } /* * get_server_nickname: returns the current nickname for the given server * index */ u_char * get_server_nickname(server_index) int server_index; { if ((server_index != -1) && server_list[server_index].nickname) return (server_list[server_index].nickname); else return (nickname); } /* get_server_qhead - get the head of the whois queue */ WhoisQueue * get_server_qhead(server_index) int server_index; { if (server_index != -1) return server_list[server_index].WQ_head; else return WQ_head; } /* get_server_whois_stuff */ WhoisStuff * get_server_whois_stuff(server_index) int server_index; { if (server_index == -1) server_index = primary_server; return &server_list[server_index].whois_stuff; } /* get_server_qtail - get the tail of the whois queue */ WhoisQueue * get_server_qtail(server_index) int server_index; { if (server_index !=-1) return server_list[server_index].WQ_tail; else return WQ_tail; } /* set_server_qhead - set the head of the whois queue */ void set_server_qhead(server_index, value) int server_index; WhoisQueue *value; { if (server_index != -1) server_list[server_index].WQ_head = value; else WQ_head = value; } /* set_server_qtail - set the tail of the whois queue */ void set_server_qtail(server_index, value) int server_index; WhoisQueue *value; { if (server_index !=-1) server_list[server_index].WQ_tail = value; else WQ_tail = value; } /* * get_server_operator: returns true if the user has op privs on the server, * false otherwise */ int get_server_operator(server_index) int server_index; { return (server_list[server_index].operator); } /* * set_server_operator: If flag is non-zero, marks the user as having op * privs on the given server. */ void set_server_operator(server_index, flag) int server_index; int flag; { server_list[server_index].operator = flag; } /* * set_server_nickname: sets the nickname for the given server to nickname. * This nickname is then used for all future connections to that server * (unless changed with NICK while connected to the server */ void set_server_nickname(server_index, nick) int server_index; u_char *nick; { if (server_index != -1) { malloc_strcpy(&(server_list[server_index].nickname), nick); if (server_index == primary_server) malloc_strcpy(&nickname, nick); } update_all_status(); } void set_server_motd(server_index, flag) int server_index; int flag; { if (server_index != -1) server_list[server_index].motd = flag; } int get_server_motd(server_index) int server_index; { if (server_index != -1) return(server_list[server_index].motd); return (0); } void server_is_connected(server_index, value) int server_index, value; { server_list[server_index].connected = value; if (value) server_list[server_index].eof = 0; } extern int in_redirect; /* send_to_server: sends the given info the the server */ void send_to_server(char *format, ...) { static int in_send_to_server = 0; u_char lbuf[BIG_BUFFER_SIZE]; /* make this buffer *much* * bigger than needed */ u_char *buf = lbuf; int des; size_t len; int server = from_server; va_list vlist; va_start(vlist, format); if (in_send_to_server) return; bzero(lbuf, sizeof(lbuf)); in_send_to_server = 1; if (server == -1) server = primary_server; if (server != -1 && ((des = server_list[server].write) != -1) && (server_list[server].flags & LOGGED_IN) ) { /* save space for the packet length */ if (get_server_version(server) == ServerICB) buf++; server_list[server].sent = 1; vsnprintf(CP(buf), sizeof lbuf, format, vlist); va_end(vlist); len = my_strlen(buf); if (len > (IRCD_BUFFER_SIZE - 2)) lbuf[IRCD_BUFFER_SIZE - 2] = '\0'; /* * for ICB, we send a final nul, and for IRC, we have * a final newline. */ len++; if (do_hook(RAW_SEND_LIST, "%s", lbuf)) { if (get_server_version(server) == ServerICB) { /* * we depend on our caller to split things * up for the ICB server */ if (len > 254) len = 254; lbuf[len] = 0; lbuf[0] = (u_char)len; lbuf[++len] = 0; } else my_strmcat(buf, "\n", IRCD_BUFFER_SIZE); send(des, CP(lbuf), len, 0); } } else if (!in_redirect && !connected_to_server) say("You are not connected to a server, use /SERVER to connect."); in_send_to_server = 0; } #ifdef HAVE_SYS_UN_H /* * Connect to a UNIX domain socket. Only works for servers. * submitted by Avalon for use with server 2.7.2 and beyond. */ int connect_to_unix(port, path) int port; u_char *path; { struct sockaddr_un un; int sock; sock = socket(AF_UNIX, SOCK_STREAM, 0); un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof un.sun_path, "%-.100s/%-.6d", path, port); if (connect(sock, (struct sockaddr *)&un, (int)my_strlen(path)+2) == -1) { new_close(sock); return -1; } return sock; } #endif /* HAVE_SYS_UN_H */ /* * close_all_server: Used whn creating new screens to close all the open * server connections in the child process... */ extern void close_all_server() { int i; for (i = 0; i < number_of_servers; i++) { if (server_list[i].read != -1) new_close(server_list[i].read); if (server_list[i].write != -1) new_close(server_list[i].write); } } extern u_char * create_server_list() { int i; u_char *value = (u_char *) 0; u_char buffer[BIG_BUFFER_SIZE]; *buffer = '\0'; for (i = 0; i < number_of_servers; i++) if (server_list[i].read != -1) { my_strmcat(buffer, get_server_itsname(i), sizeof buffer); my_strmcat(buffer, " ", sizeof buffer); } malloc_strcpy(&value, buffer); return value; } static void add_to_server_buffer(server, buf) int server; u_char *buf; { if (buf && *buf) { if (server_list[server].buffer) malloc_strcat(&server_list[server].buffer, buf); else malloc_strcpy(&server_list[server].buffer, buf); } } void disconnectcmd(command, args, subargs) u_char *command, *args, *subargs; { u_char *server; u_char *message; int i; int old_serv; if ((server = next_arg(args, &args)) != NULL && server[0] != '*' && server[1] != '\0') { i = parse_server_index(server); if (-1 == i) { say("No such server!"); return; } } else i = get_window_server(0); /* * XXX - this is a major kludge. i should never equal -1 at * this point. we only do this because something has gotten * *really* confused at this point. .mrg. */ if (i == -1) { for (i = 0; i < number_of_servers; i++) { server_list[i].eof = -1; server_list[i].connected = 0; new_close(server_list[i].read); new_close(server_list[i].write); } goto done; } if (!args || !*args) message = UP("Disconnecting"); else message = args; if (-1 == server_list[i].write) { say("That server isn't connected!"); return; } server = server_list[i].itsname ? server_list[i].itsname : server_list[i].name ? server_list[i].name : (u_char *) "unknown?"; say("Disconnecting from server %s", server); old_serv = server_list[i].close_serv; close_server(i, message); server_list[i].eof = 1; if (old_serv != -1 && old_serv != i) { Window *tmp; Win_Trav stuff; say("Connection to server %s resumed...", server_list[old_serv].name); server_list[i].close_serv = -1; server_list[old_serv].flags &= ~(CLOSE_PENDING|CLEAR_PENDING); server_list[old_serv].flags |= LOGGED_IN; server_list[old_serv].connected = 1; stuff.flag = 1; while ((tmp = window_traverse(&stuff))) if (tmp->server == i) { window_set_server(tmp->refnum, old_serv, WIN_ALL); break; } } done: clean_whois_queue(); window_check_servers(); if (!connected_to_server) say("You are not connected to a server. Use /SERVER to connect."); } void set_server_server_group(server_index, group) int server_index; int group; { if (server_index < 0 && server_index >= number_of_servers) server_index = from_server; server_list[server_index].server_group = group; } static void server_group_get_connected_next(si) int si; { int i, group; group = server_list[si].server_group; for (i = si + 1; i != si; i = (i + 1) % number_of_servers) if (server_list[i].server_group == group) { say("Ok, trying server %s...", get_server_itsname(si)); get_connected(i); return; } say("No servers available."); } void add_server_to_server_group(server, group) int server; u_char *group; { int i = find_server_group(group, 1); server_list[server].server_group = i; say("Server %s's server group is now %s", get_server_itsname(server), group); } int find_server_group(group, add) u_char *group; int add; { static int next = 1; SGroup *g; if (!group || !*group) return 0; g = (SGroup *) find_in_list((List **)(void *)&server_group_list, group, 0); if (g) goto end; if (!add) return 0; g = (SGroup *) new_malloc(sizeof(SGroup)); g->name = (u_char *) 0; malloc_strcpy(&g->name, group); g->number = next++; add_to_list((List **)(void *)&server_group_list, (List *) g); end: return g->number; } u_char * find_server_group_name(number) int number; { SGroup *g = server_group_list; for (; g; g = g->next) if (g->number == number) return g->name; return empty_string; } int active_server_group(int sgroup) { int i; /* kinda blah - returns first active server in that group */ for (i = 0; i < number_of_servers; i++) if (server_list[i].connected) return i; return -1; } SOCKADDR_STORAGE * get_server_localaddr(server) int server; { if (server >= number_of_servers) return 0; return server_list[server].localaddr; } int get_server_localaddrlen(server) int server; { if (server >= number_of_servers) return 0; return server_list[server].localaddrlen; }