/*
* server.c: interactions with servers, tcp code, and DNS resolver
*
* Copyright(c) 1997-2000 - All Rights Reserved
*
* See the COPYRIGHT file.
*/
#ifndef lint
static char rcsid[] = "@(#)$Id: server.c,v 1.83 2001/08/01 01:21:42 kalt Exp $";
#endif
#include "os.h"
#if HAVE_SYS_FILIO_H
# include <sys/filio.h>
#endif
#include "struct.h"
#include "term.h"
#include "window.h"
#include "option.h"
#include "config.h"
#include "utils.h"
#include "server.h"
extern char need_collect;
extern struct window_ *current;
extern void sic_format(char *, char *, char *, char *);
static struct server_ *slist = NULL;
struct server_ *server = NULL;
static int
sic_nextaddr(srv)
struct server_ *srv;
{
#if defined(HAVE_GETADDRINFO)
struct addrinfo *last;
assert(srv->address);
assert(srv->ip);
last = srv->addrip;
do
{
srv->addrip = srv->addrip->ai_next;
if (srv->addrip == NULL)
srv->addrip = srv->address;
}
while (srv->addrip->ai_family != PF_INET &&
srv->addrip->ai_family != PF_INET6);
free(srv->ip);
srv->ip = aitoip(srv->addrip);
vsic_slog(LOG_DEBUG, "sic_nextaddr: srv->addrip %x, last %x",
srv->addrip, last);
if (srv->addrip != last)
return 1;
#endif
vsic_slog(LOG_DEBUG, "sic_nextaddr returns 0");
return 0;
}
/* sic_connect: connect() to a server (given a struct server_ pointer) */
void
sic_connect(srv)
struct server_ *srv;
{
struct sockaddr_in sock_sin;
int fd, opt = 1, rc;
char *vif;
assert (srv->ip); /* even if srv->addrip is defined */
bzero((char *)&sock_sin, sizeof(sock_sin));
sock_sin.sin_family = AF_INET;
#if defined(HAVE_GETADDRINFO)
if (srv->addrip)
fd = socket(srv->addrip->ai_family, SOCK_STREAM,
srv->addrip->ai_protocol);
else
#endif
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0)
vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
if (fd > 0 && (vif = getenv("SICIP")))
{
/* the following should be fixed rather than simply reporting an error */
sock_sin.sin_addr.s_addr = inet_addr(vif);
if (sock_sin.sin_addr.s_addr == INADDR_NONE)
vsic_slog(LOG_CLIENT, "--- Bad IP address: SICIP = %s", vif);
else
if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
vsic_slog(LOG_CLIENT, "--- Unable to bind to IP %s: %s", vif,
strerror(errno));
}
if (fd > 0 && ioctl(fd, FIONBIO, &opt) < 0)
vsic_slog(LOG_CLIENT, "--- ioctl() for %s:%d failed: %s", srv->sname,
srv->port, strerror(errno));
#if defined(HAVE_GETADDRINFO)
if (srv->addrip)
rc = connect(fd, srv->addrip->ai_addr, srv->addrip->ai_addrlen);
else
#endif
{
sock_sin.sin_port = htons(srv->port);
sock_sin.sin_addr.s_addr = inet_addr(srv->ip);
rc = connect(fd, (struct sockaddr *)&sock_sin, sizeof(sock_sin));
}
if (fd > 0 && rc < 0 && errno != EINPROGRESS)
{
rc = errno;
vsic_slog(LOG_CLIENT, "--- Connection to %s:%d failed: %s", srv->sname,
srv->port, strerror(errno));
close(fd);
if (rc == EHOSTUNREACH && !option(srv->sopt, S_DCC))
fd = -2;
else
fd = -1;
}
unset_option(srv->sopt, S_RECONNECT);
unset_option(srv->sopt, S_QUIT);
switch (fd)
{
case -2: /* no route to host, may be there's another address to try? */
unset_option(srv->sopt, S_CONNECTING);
if (sic_nextaddr(srv))
{
set_option(srv->sopt, S_RECONNECT);
srv->ts = time(NULL) - 4;
break;
}
srv->ts = time(NULL);
break;
case -1: /* other type of failures */
unset_option(srv->sopt, S_CONNECTING);
if (!option(srv->sopt, S_DCC)) /* needed? */
srv->ts = time(NULL);
break;
default: /* connection in progress */
set_option(srv->sopt, S_CONNECTING);
if (!option(srv->sopt, S_CONFIG))
cfg_read(srv->sname);
set_option(srv->sopt, S_CONFIG);
break;
}
srv->fd = fd;
srv->lastr = 0;
srv->readbuf[0] = '\0';
}
/* sic_listen: create a new socket, and bind it */
static void
sic_listen(dcc)
struct server_ *dcc;
{
struct sockaddr_in sock_sin;
int fd, sz = sizeof(struct sockaddr_in);
char *vif;
if (getsockname(server->fd, (struct sockaddr *)&sock_sin, &sz) < 0)
{
vsic_slog(LOG_CLIENT, "--- Unable to get my own IP: %s",strerror(errno));
dcc->fd = -1;
return;
}
else
sz = sizeof(struct sockaddr_in);
dcc->ip = strdup((char *)inet_ntoa(sock_sin.sin_addr));
bzero((char *)&sock_sin, sizeof(sock_sin));
sock_sin.sin_family = AF_INET;
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
vsic_slog(LOG_CLIENT, "--- socket() call failed: %s", strerror(errno));
if (vif = getenv("SICIP"))
sock_sin.sin_addr.s_addr = inet_addr(vif);
else
sock_sin.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *) &sock_sin, sizeof(sock_sin)) < 0)
{
vsic_slog(LOG_CLIENT, "--- bind() call failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (fd > 0 && listen(fd, 1) < 0)
{
vsic_slog(LOG_CLIENT, "--- listen() call failed: %s", strerror(errno));
close(fd);
fd = -1;
}
if (fd > 0)
if (getsockname(fd, (struct sockaddr *)&sock_sin, &sz) < 0)
{
vsic_slog(LOG_CLIENT, "--- getsockname() call failed: %s",
strerror(errno));
free(dcc->ip);
close(fd);
}
else
{
dcc->port = sock_sin.sin_port;
dcc->sname = strdup(server->sname);
set_option(dcc->sopt, S_LISTEN);
}
dcc->fd = fd;
}
/* select_server: set a different server */
struct server_ *
select_server(name)
char *name;
{
struct server_ *stmp = slist;
assert(name);
while (stmp && (strcasecmp(stmp->sname, name)
|| !option(stmp->sopt, S_CONNECTED)))
stmp = stmp->nexts;
return server = stmp;
}
/* sic_server: adds a server to the list, does NOT connect */
struct server_ *
sic_server(nick, name, port, pass)
char *nick, *name, *pass;
int port;
{
struct server_ **stmp = &slist;
while (*stmp)
stmp = &((*stmp)->nexts);
server = (struct server_ *) malloc(sizeof(struct server_));
bzero(server, sizeof(struct server_));
*stmp = server;
server->nick = strdup(nick);
server->sname = strdup(name);
server->port = port;
if (pass)
server->pass = strdup(pass);
#if defined(HAVE_GETADDRINFO)
{
struct addrinfo *address, hints;
char port_str[10];
bzero((char *)&hints, sizeof(hints));
hints.ai_flags = AI_CANONNAME|AI_NUMERICHOST;
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
sprintf(port_str, "%d", port);
if (getaddrinfo(name, port_str, &hints, &address) == 0)
{
server->ip = strdup(name);
server->address = address;
server->addrip = address;
}
}
#else
if (inet_addr(name) != INADDR_NONE)
server->ip = strdup(name);
#endif
if (server->ip == NULL)
{
dns_lookup(name, server);
set_option(server->sopt, S_DNS);
}
set_option(server->sopt, S_NOTCONNECTED);
set_option(server->sopt, S_QUIT);
server->fd = -1;
return server;
}
/* sic_dserver: removes a server from the list */
void
sic_dserver(del)
struct server_ *del;
{
struct server_ **stmp = &slist;
assert(sic_scnt(del) == 0);
assert(option(del->sopt, S_CONNECTED) == 0);
assert(option(del->sopt, S_DNS) == 0);
while (*stmp != del)
stmp = &((*stmp)->nexts);
*stmp = del->nexts;
opt_free(&(del->custs));
free(del->sname);
if (del->ip)
free(del->ip);
#if defined(HAVE_GETADDRINFO)
if (del->address)
freeaddrinfo(del->address);
#endif
if (del->pass)
free(del->pass);
free(del->nick);
if (del->uname)
free(del->uname);
if (del->rname)
free(del->rname);
if (del->readf)
close(del->readf);
if (del->sendf)
close(del->sendf);
free(del);
}
static void
sic_write(str)
char *str;
{
if (server && server->fd >= 0)
{
if (write(server->fd, str, strlen(str)) == -1
&& (errno == EAGAIN || errno == EWOULDBLOCK))
/* should be dealt with in a better way, not worth my time unless
it happens */
vsic_slog(LOG_CLIENT, "--- write() to server failed: %s",
strerror(errno));
str[strlen(str)-1] = '\0'; /* hmmpf */
sic_slog(LOG_OSNIF, str);
}
else
sic_slog(LOG_CLIENT, "--- No server");
}
void
vsic_write(char *format, ...)
{
char buffer[1024];
va_list va;
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
strcat(buffer, "\n");
sic_write(buffer);
}
static int
readln(fd, buffer, length)
int fd, length;
char *buffer;
{
int status, ptr;
ptr = -1;
while (buffer[++ptr]);
while (((status = read(fd, &buffer[ptr], 1)) > 0) && (ptr < length - 1))
{
if (buffer[ptr++] == '\n')
break;
}
if (status == -1 && (errno == EWOULDBLOCK || errno == EAGAIN))
{
/* incomplete line */
buffer[ptr] = '\0';
return -2;
}
if (status > 0)
{
if (buffer[ptr-2] == '\r')
buffer[ptr-2] = '\0';
else
buffer[ptr-1] = '\0';
sic_slog(LOG_ISNIF, buffer);
}
return (status);
}
/* this function is totally buggy, but it's simple and works most of the time*/
static void
parse(str)
char *str;
{
char *sender, *cmd, *dest, *para;
if (option(server->sopt, S_DCC))
{
char snder[267];
sprintf(snder, "%s@%s", server->nick, server->sname);
sic_format(snder, NULL, NULL, str);
return;
}
if (*str == ':')
{
sender = ++str;
while (*++str != ' ');
*str++ = '\0';
}
else
sender = server->sname;
if (!strncmp(str, "ERROR ", 6))
{
cmd = "ERROR";
dest = server->nick;
para = str;
}
else
{
cmd = str;
while (*++str != ' ');
*str++ = '\0';
if (*str == ':')
{
vsic_slog(LOG_DEBUG, "dest begins with : for `%s %s %s'",sender,cmd,str);
dest = str+1;
para = "";
}
else
{
dest = str;
while (*++str != ' ' && *str);
if (*str)
{
*str++ = '\0';
if (*str == ':')
para = str + 1;
else
para = str;
}
else
para = "";
}
}
if (strcmp(cmd, "PONG"))
sic_format(sender, cmd, dest, para);
}
/* sic_select: big fat io loop */
int
sic_select()
{
fd_set rfd, wfd, efd;
int n, maxfd = 0;
struct server_ *stmp = slist;
struct timeval timeout;
char buf[10240];
FD_ZERO(&rfd); FD_ZERO(&wfd); FD_ZERO(&efd);
timeout.tv_usec = 0; timeout.tv_sec = 5;
FD_SET(0, &rfd);
while (stmp)
{
if (stmp->fd > maxfd)
maxfd = stmp->fd;
server = stmp;
if (option(stmp->sopt, S_NOTCONNECTED)
&& !option(stmp->sopt, S_CONNECTING)
&& !option(stmp->sopt, S_CONNECTED)
&& !option(stmp->sopt, S_QUIT)
&& !option(stmp->sopt, S_DCC)
&& (time(NULL) - stmp->ts) >= 1800
&& get_option(Z_RECONNECT, NULL))
{
select_active(NULL, 0);
vsic_slog(LOG_CLIENT, "--- Attempting to reconnect to %s:%d",
stmp->sname, stmp->port);
set_option(stmp->sopt, S_RECONNECT);
}
if (option(stmp->sopt, S_RECONNECT) && (time(NULL) - stmp->ts) >= 5)
{
select_active(NULL, 0);
sic_connect(stmp);
}
if (option(stmp->sopt, S_CONNECTED))
{
if (!option(stmp->sopt, S_DCC))
{
server = stmp;
select_active(NULL, 0);
if (stmp->lastr && time(NULL) - stmp->lastr > 60)
{
if ((time(NULL) - stmp->lastr) % 120 < 60)
{
if (!option(stmp->sopt, S_PING))
{
vsic_write("PING .");
set_option(stmp->sopt, S_PING);
}
}
else
unset_option(stmp->sopt, S_PING);
}
if (need_collect)
collect_channel();
}
FD_SET(stmp->fd, &rfd);
}
if (option(stmp->sopt, S_SEND) && stmp->sent < stmp->size)
FD_SET(stmp->fd, &wfd);
if (option(stmp->sopt, S_LISTEN))
FD_SET(stmp->fd, &rfd);
if (option(stmp->sopt, S_CONNECTING) && !option(stmp->sopt, S_DNS))
FD_SET(stmp->fd, &wfd);
stmp = stmp->nexts;
}
display_status();
term_input(NULL, 0);
term_flush();
#if defined(GNU_PTH)
n = pth_select(maxfd + 1, &rfd, &wfd, NULL, &timeout);
#else
n = select(maxfd + 1, &rfd, &wfd, &efd, &timeout);
#endif
if (n < 0)
return 0;
stmp = slist;
while (stmp)
{
/* first deal with pending DCCs */
if (stmp->fd >= 0 && option(stmp->sopt, S_LISTEN))
{
if (FD_ISSET(stmp->fd, &rfd))
{
struct sockaddr_in sock_sin;
int newfd, sz = sizeof(struct sockaddr_in);
server = stmp;
unset_option(stmp->sopt, S_LISTEN);
set_option(stmp->sopt, S_DCC);
select_active(NULL, 0);
if ((newfd=accept(stmp->fd,(struct sockaddr *)&sock_sin,&sz))<0)
{
vsic_slog(LOG_CLIENT, "DCC accept() failed for %s@%s: %s",
stmp->nick, inet_ntoa(sock_sin.sin_addr),
strerror(errno));
stmp = stmp->nexts;
sic_dserver(server);
}
else
{
close(stmp->fd);
stmp->fd = newfd;
free(stmp->sname);
stmp->sname = strdup((char *)inet_ntoa(sock_sin.sin_addr));
set_option(stmp->sopt, S_CONNECTED);
vsic_slog(LOG_CLIENT,
"--- DCC connection received from %s@%s",
stmp->nick, stmp->sname);
dns_lookup(stmp->sname, NULL);
sz = 1;
if (ioctl(stmp->fd, FIONBIO, &sz) < 0)
vsic_slog(LOG_CLIENT, "--- ioctl() failed: %s",
strerror(errno));
if (stmp->pass)
set_option(stmp->sopt, S_SEND);
stmp->ts = time(NULL);
}
}
}
/* DCC sends */
if (option(stmp->sopt, S_SEND))
{
server = stmp;
select_active(NULL, 0);
if (FD_ISSET(stmp->fd, &rfd))
{
u_32int_t total; /* DCC ack */
if (recv(stmp->fd, (char *) &total, sizeof(u_32int_t),0) > 0)
stmp->read = ntohl(total);
else
{
vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost: %s",
stmp->pass, stmp->nick, strerror(errno));
vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
stmp->read, stmp->size,
(int)(100*stmp->read/stmp->size));
close(stmp->fd);
stmp = stmp->nexts;
sic_dserver(server);
continue;
}
if (stmp->read == stmp->size)
{
vsic_slog(LOG_CLIENT,
"--- DCC \"%s\" to %s completed in %s (%ub/s)",
stmp->pass, stmp->nick,
sic_tdiff(time(NULL)-stmp->ts, 0),
stmp->size/(1+time(NULL)-stmp->ts));
shutdown(stmp->fd, 2);
close(stmp->fd);
stmp = stmp->nexts;
sic_dserver(server);
continue;
}
}
if (FD_ISSET(stmp->fd, &wfd) && stmp->sent - stmp->read < 10240)
{
char error = 0;
unsigned long rd, st = 0;
if (lseek(stmp->sendf, stmp->sent, SEEK_SET) < 0)
error = 1;
if ((rd = read(stmp->sendf, buf, 10240)) < 0)
error = 2;
if (rd > 0 && ((st = write(stmp->fd, buf, rd)) < 0))
error = 3;
if (error)
{
if (error != 3)
vsic_slog(LOG_CLIENT, "--- %s() failed: %s!",
(error == 1) ?"lseek":"read",strerror(errno));
vsic_slog(LOG_CLIENT, "--- DCC \"%s\" to %s lost.",
stmp->pass, stmp->nick);
vsic_slog(LOG_CLIENT, "--- Acked %lu out of %lu (%u%%).",
stmp->read, stmp->size,
(int)(100*stmp->read/stmp->size));
close(stmp->fd);
stmp = stmp->nexts;
sic_dserver(server);
continue;
}
stmp->sent += st;
}
stmp = stmp->nexts;
continue;
}
/* DCC chat, DCC get, and normal server traffic */
if (stmp->fd >= 0 && !option(stmp->sopt, S_LISTEN))
{
server = stmp;
select_active(NULL, 0); /* default window for this server */
if (FD_ISSET(stmp->fd, &rfd))
{
int sz;
if (option(stmp->sopt, S_DCC) && stmp->pass)
sz = read(stmp->fd, buf, 8192); /* big buffer for files */
else
{
/*read up to \r\n otherwise*/
unset_option(stmp->sopt, S_PING);
sz = readln(stmp->fd, stmp->readbuf, 520);
}
if (sz > 0)
{
stmp->lastr = time(NULL);
if (option(stmp->sopt, S_NOTCONNECTED))
{
assert(!option(stmp->sopt, S_DCC));
unset_option(stmp->sopt, S_NOTCONNECTED);
sic_slog(LOG_CLIENT, "--- Connection established.");
}
if (option(stmp->sopt, S_DCC) && stmp->pass)
{ /* file transfer */
if (stmp->readf == 0)
stmp->readf = open(stmp->pass,
O_WRONLY|O_TRUNC|O_CREAT, 0644);
if (stmp->readf == 0)
{
vsic_slog(LOG_CLIENT,
"--- Unable to create file \"%s\"!",
stmp->pass);
close(stmp->fd);
stmp = stmp->nexts;
sic_dserver(server); /* "continue;" below */
}
else
{
u_32int_t total; /* DCC ack */
write(stmp->readf, buf, sz); /* ugly! */
total = htonl(stmp->read += sz);
send(stmp->fd, (char *) &total, sizeof(u_32int_t),0);
if (stmp->read == stmp->size) /* got all of it? */
{
stmp->size /= 1024;
vsic_slog(LOG_CLIENT,
"--- DCC \"%s\" from %s completed in %s (%ub/s)",
stmp->pass, stmp->nick,
sic_tdiff(time(NULL)-stmp->ts, 0),
stmp->size/(1+time(NULL)-stmp->ts));
shutdown(stmp->fd, 2);
close(stmp->fd);
stmp = stmp->nexts;
sic_dserver(server); /* "continue;" below */
}
else
stmp = stmp->nexts;
}
continue;
}
else
{
/* readln() */
if (!option(stmp->sopt, S_DCC)
&& !strncmp("PING ", stmp->readbuf, 5))
vsic_write("PONG %s :%s", server->nick,
(stmp->readbuf[4] && stmp->readbuf[5]) ?
stmp->readbuf+6 : "");
else
{
/* current window for this server */
select_active(NULL, 1);
parse(stmp->readbuf);
}
stmp->readbuf[0] = '\0';
}
}
else if (sz == -2)
{
vsic_slog(LOG_DEBUG, "Incomplete line from %s", stmp->sname);
}
else
{
close(stmp->fd);
stmp->fd = -1;
if (option(stmp->sopt, S_NOTCONNECTED))
{
/* this can happen when connecting locally */
assert(!option(stmp->sopt, S_DCC));
vsic_slog(LOG_CLIENT,
"--- Connection to %s %d [%s] failed.",
stmp->sname, stmp->port, stmp->ip);
}
set_option(stmp->sopt, S_NOTCONNECTED);
unset_option(stmp->sopt, S_CONNECTED);
if ((time(NULL) - stmp->ts) > 60
&& !option(stmp->sopt, S_DCC)
&& !option(stmp->sopt, S_QUIT)
&& get_option(Z_RECONNECT, NULL))
set_option(stmp->sopt, S_RECONNECT);
stmp->ts = time(NULL);
if (option(stmp->sopt, S_DCC))
{
vsic_slog(LOG_CLIENT, "--- DCC to %s@%s lost.",
stmp->nick, stmp->sname);
if (stmp->pass)
{
vsic_slog(LOG_CLIENT,
"--- Got %lu out of %lu (%u%% of %s).",
stmp->read, stmp->size,
(int)(100*stmp->read/stmp->size),
stmp->pass);
stmp = stmp->nexts;
sic_dserver(server);
}
}
else
vsic_slog(LOG_CLIENT,
"--- Lost connection to %s:%d : %s",
stmp->sname, stmp->port,
option(stmp->sopt, S_RECONNECT) ?
"Reconnecting..." :
option(stmp->sopt, S_QUIT) || !get_option(Z_RECONNECT, NULL) ?
"No reconnect.":"Will attempt to reconnect.");
continue;
}
}
/* connection establishment */
if (FD_ISSET(stmp->fd, &wfd))
{
char *wp, buffer[512];
unset_option(stmp->sopt, S_CONNECTING);
if (!option(stmp->sopt, S_DCC))
{
server->umode[0] = '\0';
sprintf(buffer, "%s%s%sNICK %s\nUSER %s myhost 0 :%s\n",
(server->pass) ? "PASS " : "",
(server->pass) ? server->pass : "",
(server->pass) ? "\n" : "",
stmp->nick,
(stmp->uname) ? stmp->uname :
(wp = getenv("SICUSER")) ? wp :
(wp = getenv("USER")) ? wp : "sicuser",
(stmp->rname) ? stmp->rname :
(wp = getenv("SICNAME")) ? wp :"wasting time");
if (write(stmp->fd, buffer, strlen(buffer)) == -1)
{
close(stmp->fd);
stmp->fd = -1;
stmp->ts = time(NULL);
set_option(stmp->sopt, S_NOTCONNECTED);
sic_slog(LOG_CLIENT, "--- Connection failed.");
}
else
{
del_member(NULL, NULL);
set_option(stmp->sopt, S_CONNECTED);
}
}
else
{
vsic_slog(LOG_CLIENT, "--- DCC to %s@%s established.",
stmp->nick, stmp->sname);
set_option(stmp->sopt, S_CONNECTED);
unset_option(stmp->sopt, S_NOTCONNECTED);
}
}
}
stmp = stmp->nexts;
}
if (FD_ISSET(0, &rfd))
return 1;
else
return 0;
}
int
cmd_server(p)
char *p;
{
struct server_ *stmp = slist;
int n = 0;
if (p == NULL)
{
/* NULL argument, this is only used internally */
if (server == NULL)
return 0;
if (option(server->sopt, S_CONNECTING)
|| option(server->sopt, S_CONNECTED))
{
if (option(server->sopt, S_QUIT))
{
if (server->fd >= 0)
close(server->fd);
server->fd = -1;
set_option(server->sopt, S_NOTCONNECTED);
unset_option(server->sopt, S_CONNECTING);
unset_option(server->sopt, S_CONNECTED);
vsic_slog(LOG_CLIENT, "--- Closed connection to %s:%d",
server->sname, server->port);
}
}
unset_option(server->sopt, S_RECONNECT);
set_option(server->sopt, S_QUIT);
return 0;
}
/* real user input */
if (!*p || !strcasecmp(p, "-v"))
{
/* no argument or just `-v', user is simply requesting a list */
sic_slog(LOG_CLIENT, "--- Server list:");
while (stmp)
{
if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN))
vsic_slog(LOG_CLIENT, "--- %2d: %s %d %s(%s%s)%s",
n++, stmp->sname, stmp->port,
(stmp->pass) ? "*password* " : "",
(option(stmp->sopt, S_CONNECTING)) ? "will be " :
(option(stmp->sopt, S_CONNECTED)) ? "" : "was ",
stmp->nick,
(stmp->ip) ? "" : option(stmp->sopt, S_DNS) ?
" Looking up address" : " Unknown address");
if (*p && stmp->ip)
#if defined(HAVE_GETADDRINFO)
{
struct addrinfo *address = stmp->address;
vsic_slog(LOG_CLIENT, "--- => %s[%s]",
(stmp->address && stmp->address->ai_canonname) ?
stmp->address->ai_canonname : "???", stmp->ip);
if (address && address->ai_next)
while (address)
{
if (address->ai_family == PF_INET ||
address->ai_family == PF_INET6)
vsic_slog(LOG_CLIENT, "--- > %s[%s]",
(address->ai_canonname) ?
address->ai_canonname : "???",
aitoip(address));
address = address->ai_next;
}
}
#else
vsic_slog(LOG_CLIENT, "--- => [%s]", stmp->ip);
#endif
stmp = stmp->nexts;
}
}
else if (!strncasecmp(p, "-d", 2))
{
char *number = p;
while (*number && !isdigit(*number))
number += 1;
if (*number == '\0')
return -4;
n = 0;
while (stmp)
{
if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
&& n++ == atoi(number))
break;
stmp = stmp->nexts;
}
if (stmp == NULL)
return -4;
if (option(stmp->sopt, S_DNS))
{
sic_slog(LOG_CLIENT,
"--- DNS lookup in progress, cannot delete server. (try again)");
return 0;
}
if (option(stmp->sopt, S_CONNECTED))
{
sic_slog(LOG_CLIENT, "--- Close connection first.");
return 0;
}
if (n = sic_scnt(stmp))
{
vsic_slog(LOG_CLIENT,"--- %d window(s) are bound to this server.",n);
return 0;
}
assert(server != stmp); /* this shouldn't be possible? */
vsic_slog(LOG_CLIENT,"--- Deleted server %d (%s).", atoi(number),
stmp->sname);
sic_dserver(stmp);
return 0;
}
else
{
char *number = p;
if (*p == '+')
number = p+1;
while (*number)
if (!isdigit(*number++))
{
number = NULL;
break;
}
if (number)
{
/* argument is a number, find selected server and try to connect */
if (current->via && (option(current->via->sopt, S_CONNECTED)
|| option(current->via->sopt, S_CONNECTING)))
{
sic_slog(LOG_CLIENT, "--- This window is already active.");
return 0;
}
while (stmp)
{
if (!option(stmp->sopt, S_DCC) && !option(stmp->sopt, S_LISTEN)
&& n++ == atoi(p))
break;
stmp = stmp->nexts;
}
if (stmp)
{
server = stmp;
if (stmp->ip || option(stmp->sopt, S_DNS))
{
if (sic_swin(1))
{
if (!option(stmp->sopt, S_CONNECTED))
{
set_option(stmp->sopt, S_CONNECTING);
if (option(stmp->sopt, S_DNS))
{
vsic_slog(LOG_CLIENT,
"--- Connection to %s pending DNS lookup.",
stmp->sname);
}
else
{
if (*p == '+')
sic_nextaddr(stmp);
vsic_slog(LOG_CLIENT,
"--- Connecting to %s %d [%s]",
stmp->sname, stmp->port, stmp->ip);
sic_connect(stmp);
}
}
}
}
else
vsic_slog(LOG_CLIENT, "--- Unknown IP for %s", stmp->sname);
}
else
sic_slog(LOG_CLIENT, "--- No such server.");
}
else
{
/* format: /server name port pass */
char *port, *pass = NULL;
int portn = 6667;
if (port = index(p, ' '))
{
*port++ = '\0';
if (pass = index(port, ' '))
*pass++ = '\0';
portn = atoi(port);
}
sic_server((current->via) ? current->via->nick : "sic",
p, portn, pass);
cmd_server("");
}
}
return 0;
}
/*
* DCC code
*/
static void
sic_dwrite(nick, str)
char *nick, *str;
{
struct server_ *dtmp = slist;
while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, nick)))
dtmp = dtmp->nexts;
if (dtmp)
if (dtmp->fd >= 0)
{
if (write(dtmp->fd, str, strlen(str)) == -1
&& (errno == EAGAIN || errno == EWOULDBLOCK))
/* should be dealt with in a better way, not worth my time unless
it happens */
vsic_slog(LOG_CLIENT, "--- write() to DCC failed: %s",
strerror(errno));
str[strlen(str)-1] = '\0'; /* hmmpf */
sic_slog(LOG_OSNIF, str);
}
else
sic_slog(LOG_CLIENT, "--- DCC not established.");
else
sic_slog(LOG_CLIENT, "--- No such DCC.");
}
void
vsic_dwrite(char *nick, char *format, ...)
{
char buffer[1024];
va_list va;
va_start(va, format);
vsprintf(buffer, format, va);
va_end(va);
strcat(buffer, "\n");
sic_dwrite(nick, buffer);
}
/* sic_dcc: called to deal with DCC requests
* 0 -> chat , 1 -> send
*/
void
sic_dcc(type, nick, ip, port, file, size)
int type;
char *nick, *file;
unsigned long ip, size;
unsigned int port;
{
struct server_ *dtmp = slist;
unsigned long lip;
assert(type == 0 || type == 1);
while (dtmp)
{
if (option(dtmp->sopt, S_DCC)
&& !strcasecmp(dtmp->nick, nick)
&& (!dtmp->pass || !strcmp(dtmp->pass, file)))
break;
dtmp = dtmp->nexts;
}
if (dtmp)
{
/* should be dealt with more gracefully */
if ((option(dtmp->sopt, S_CONNECTING) || option(dtmp->sopt, S_CONNECTED))
&& (type == 0 || (dtmp->pass && !strcmp(dtmp->pass, file))))
{
vsic_slog(LOG_CLIENT,
"--- DCC chat request from %s ignored (duplicate).", nick);
return;
}
vsic_slog(LOG_CLIENT,
"--- old DCC chat request from %s deleted.", nick);
sic_dserver(dtmp);
}
if (type == 1 && size == 0)
{
vsic_slog(LOG_CLIENT, "--- Invalid DCC request (file size = 0) from %s.",
nick);
return;
}
dtmp = (struct server_ *) malloc(sizeof(struct server_));
bzero(dtmp, sizeof(struct server_));
dtmp->fd = -1;
dtmp->ts = time(NULL);
set_option(dtmp->sopt, S_DCC);
dtmp->nick = strdup(nick);
if (type == 1)
{
dtmp->pass = strdup(file);
dtmp->size = size;
}
lip = ntohl(ip);
dtmp->sname = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
dtmp->ip = strdup((char *)inet_ntoa(*(struct in_addr*)&lip));
set_option(dtmp->sopt, S_NOTCONNECTED);
dns_lookup(dtmp->ip, NULL);
switch (type)
{
case 0:
vsic_slog(LOG_CLIENT, "--- DCC CHAT request from %s@%s.",
dtmp->nick, dtmp->ip);
break;
case 1:
vsic_slog(LOG_CLIENT, "--- DCC SEND request from %s@%s: %s %lu",
dtmp->nick, dtmp->ip, file, size);
break;
default:
abort(); /* never */
}
if ((dtmp->port = port) < 1024)
vsic_slog(LOG_CLIENT, "--- DCC request from a privileged port %d!",port);
dtmp->nexts = slist;
slist = dtmp;
}
int
cmd_dcc(p)
char *p;
{
struct server_ *dtmp = slist;
if (!*p)
{
sic_slog(LOG_CLIENT, "--- DCC list:");
while (dtmp)
{
if (option(dtmp->sopt, S_DCC))
if (dtmp->pass)
vsic_slog(LOG_CLIENT,
"--- %s@%s:%d %s [%lu] %s %s: %d%% %s",
dtmp->nick, dtmp->sname, dtmp->port, dtmp->pass,
dtmp->size,
(option(dtmp->sopt, S_CONNECTED)) ? "active" :
(option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
"not connected",
(option(dtmp->sopt,S_SEND)) ? "Sending":"Reading",
(int)(100*dtmp->read/dtmp->size),
sic_tdiff(time(NULL)-dtmp->ts, 0));
else
vsic_slog(LOG_CLIENT, "--- %s@%s:%d %s",
dtmp->nick, dtmp->sname, dtmp->port,
(option(dtmp->sopt, S_CONNECTED)) ? "active" :
(option(dtmp->sopt, S_CONNECTING)) ? "connecting" :
"not connected");
if (option(dtmp->sopt, S_LISTEN))
vsic_slog(LOG_CLIENT, "--- %s@%s pending %s%s", dtmp->nick,
dtmp->sname, (dtmp->pass) ? "Send: " : "chat",
(dtmp->pass) ? dtmp->pass : "");
dtmp = dtmp->nexts;
}
return 0;
}
if (server == NULL)
return -5;
if (!strncasecmp("CHAT ", p, 5))
{
p += 5;
if (!*p)
return -1;
while (dtmp && (!option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)
|| dtmp->pass))
dtmp = dtmp->nexts;
if (dtmp)
{
if (option(dtmp->sopt, S_CONNECTING)
|| option(dtmp->sopt, S_CONNECTED))
{
sic_slog(LOG_CLIENT, "--- Connection already initiated.");
return 0;
}
sic_connect(dtmp);
}
else
{
dtmp = (struct server_ *) malloc(sizeof(struct server_));
bzero(dtmp, sizeof(struct server_));
sic_listen(dtmp);
if (dtmp->fd < 0)
free(dtmp);
else
{
dtmp->nick = strdup(p);
vsic_write("PRIVMSG %s :\001DCC CHAT chat %lu %u\001", p,
htonl(inet_addr(dtmp->ip)), htons(dtmp->port));
vsic_slog(LOG_CLIENT, "--- DCC chat offered to %s", p);
dtmp->nexts = slist;
slist = dtmp;
}
}
return 0;
}
if (!strncasecmp("SEND ", p, 5))
{
char *file = index(p+5, ' '), *basename;
if (file == NULL)
return -1;
*file++ = '\0';
if ((basename = rindex(file, '/')) == NULL)
basename = file;
else
basename++;
p += 5;
dtmp = (struct server_ *) malloc(sizeof(struct server_));
bzero(dtmp, sizeof(struct server_));
if ((dtmp->sendf = open(file, O_RDONLY)) < 0)
{
free(dtmp);
vsic_slog(LOG_CLIENT, "--- Unable to open %s: %s", file,
strerror(errno));
return 0;
}
dtmp->pass = strdup(file);
sic_listen(dtmp);
if (dtmp->fd < 0)
{
close(dtmp->sendf);
free(dtmp->pass);
free(dtmp);
}
else
{
struct stat st;
if (fstat(dtmp->sendf, &st) < 0)
{
vsic_slog(LOG_CLIENT, "--- Unable to find the file size: %s",
strerror(errno));
close(dtmp->fd);
close(dtmp->sendf);
free(dtmp->pass);
free(dtmp);
return 0;
}
dtmp->size = st.st_size;
dtmp->nick = strdup(p);
vsic_write("PRIVMSG %s :\001DCC SEND %s %lu %u %u\001", p, basename,
htonl(inet_addr(dtmp->ip)), htons(dtmp->port),st.st_size);
vsic_slog(LOG_CLIENT, "--- \"%s\" offered to %s", file, p);
dtmp->nexts = slist;
slist = dtmp;
}
return 0;
}
if (!strncasecmp("GET ", p, 4))
{
char *path = index(p+4, ' '), *file;
if (path == NULL)
return -1;
p += 4;
*path++ = '\0';
if (file = rindex(path, '/'))
file++;
else
file = path;
while (dtmp
&& (dtmp->pass == NULL || !rmatch(file, dtmp->pass)
|| !option(dtmp->sopt, S_DCC) || strcasecmp(dtmp->nick, p)))
dtmp = dtmp->nexts;
if (dtmp)
{
if (option(dtmp->sopt, S_CONNECTING)
|| option(dtmp->sopt, S_CONNECTED))
{
sic_slog(LOG_CLIENT, "--- Connection already initiated.");
return 0;
}
sic_connect(dtmp);
dtmp->ts = time(NULL);
}
else
return -4; /* invalid parameter */
return 0;
}
if (!strncasecmp("delete ", p, 7))
{
char *file = index(p+7, ' ');
p += 7;
if (file)
*file++ = '\0';
while (dtmp
&& ((!option(dtmp->sopt, S_DCC) && !option(dtmp->sopt, S_LISTEN))
|| strcasecmp(dtmp->nick, p)
|| (file && !rmatch(file, dtmp->pass))))
dtmp = dtmp->nexts;
if (dtmp)
{
if (option(dtmp->sopt, S_CONNECTING)
|| option(dtmp->sopt, S_CONNECTED))
{
sic_slog(LOG_CLIENT, "--- Closing DCC.");
close(dtmp->fd);
}
sic_dserver(dtmp);
}
else
sic_slog(LOG_CLIENT, "--- No such DCC.");
return 0;
}
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1