/*
* 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 <sys/un.h>
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<server_list[from_server].ctcp_backlog_size; i++)
server_list[from_server].ctcp_send_size[i] = 0;
if (group == -1)
server_list[from_server].server_group = 0;
else
server_list[from_server].server_group = group;
server_list[from_server].res = 0;
server_list[from_server].res0 = 0;
}
else
{
if (flags & SL_ADD_OVERWRITE)
{
server_list[from_server].port = port;
if (password)
{
if (*password)
malloc_strcpy(&(server_list[from_server].password), password);
else
new_free(&(server_list[from_server].password));
}
if (nick && *nick)
malloc_strcpy(&(server_list[from_server].nickname), nick);
if (group != -1)
server_list[from_server].server_group = group;
}
if (server_list[from_server].res0)
{
freeaddrinfo(server_list[from_server].res0);
server_list[from_server].res0 = 0;
}
server_list[from_server].res = 0;
if ((int) my_strlen(server) > (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/<type-specifc-format>
*
* 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: <None>");
if (primary_server != -1)
say("Primary server: %s %d",
server_list[primary_server].name,
server_list[primary_server].port);
else
say("Primary server: <None>");
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 <group> and <server>");
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("<None>");
}
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;
}
syntax highlighted by Code2HTML, v. 0.9.1