/* dircproxy
* Copyright (C) 2002 Scott James Remnant <scott@netsplit.com>.
* All Rights Reserved.
*
* irc_server.c
* - Handling of servers connected to the proxy
* - Reconnection to servers
* - Functions to send data to servers in the correct protocol format
* --
* @(#) $Id: irc_server.c,v 1.60 2002/01/31 14:56:37 scott Exp $
*
* This file is distributed according to the GNU General Public
* License. For full details, read the top of 'main.c' or the
* file called COPYING that was distributed with this code.
*/
#include <pwd.h>
#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <dircproxy.h>
#include "sprintf.h"
#include "net.h"
#include "dns.h"
#include "timers.h"
#include "dcc_net.h"
#include "irc_log.h"
#include "irc_net.h"
#include "irc_prot.h"
#include "irc_string.h"
#include "irc_client.h"
#include "irc_server.h"
/* forward declarations */
static void _ircserver_reconnect(struct ircproxy *, void *);
static void _ircserver_connect2(struct ircproxy *, void *, struct in_addr *,
const char *);
static void _ircserver_connect3(struct ircproxy *, void *, struct in_addr *,
const char *);
static void _ircserver_connected(struct ircproxy *, int);
static void _ircserver_connected2(struct ircproxy *, void *, struct in_addr *,
const char *);
static void _ircserver_connectfailed(struct ircproxy *, int, int);
static void _ircserver_data(struct ircproxy *, int);
static void _ircserver_error(struct ircproxy *, int, int);
static int _ircserver_gotmsg(struct ircproxy *, const char *);
static int _ircserver_close(struct ircproxy *);
static int _ircserver_lost(struct ircproxy *);
static void _ircserver_ping(struct ircproxy *, void *);
static void _ircserver_stoned(struct ircproxy *, void *);
static void _ircserver_antiidle(struct ircproxy *, void *);
static int _ircserver_forclient(struct ircproxy *, struct ircmessage *);
static int _ircserver_send_dccreject(struct ircproxy *, const char *);
/* Time/date format for strftime(3) */
#define CTCP_TIMEDATE_FORMAT "%a, %d %b %Y %H:%M:%S %z"
/* hook for timer code to reconnect to a server */
static void _ircserver_reconnect(struct ircproxy *p, void *data) {
debug("Reconnecting to server");
/* Choose the next server. If we have no more, choose the first again and
increment attempts */
p->conn_class->next_server = p->conn_class->next_server->next;
if (!p->conn_class->next_server) {
p->conn_class->next_server = p->conn_class->servers;
p->server_attempts++;
}
debug("%sAttempt %d", (p->server_status & IRC_SERVER_SEEN ? "" : "Initial "),
p->server_attempts + 1);
if (p->conn_class->server_maxattempts
&& (p->server_attempts >= p->conn_class->server_maxattempts)) {
/* If we go over maximum reattempts, then give up */
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Giving up on servers. Time to quit");
p->conn_class = 0;
if (p->client_status & IRC_CLIENT_CONNECTED) {
ircclient_send_error(p, "Maximum connection attempts exceeded");
ircclient_close(p);
}
debug("Giving up on servers, reattempted too much");
p->dead = 1;
} else if (!(p->server_status & IRC_SERVER_SEEN)
&& p->conn_class->server_maxinitattempts
&& (p->server_attempts >= p->conn_class->server_maxinitattempts)) {
/* Otherwise check initial attempts if its our first time */
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Giving up on servers. Time to quit");
p->conn_class = 0;
if (p->client_status & IRC_CLIENT_CONNECTED) {
ircclient_send_error(p, "Maximum initial connection attempts exceeded");
ircclient_close(p);
}
debug("Giving up on servers, can't get initial connection");
p->dead = 1;
} else {
/* Attempt a new connection */
ircserver_connect(p);
}
}
/* Called to initiate a connection to a server */
int ircserver_connect(struct ircproxy *p) {
char *server;
debug("Connecting to server (stage 1)");
if (timer_exists((void *)p, "server_recon")) {
debug("Connection already in progress");
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Connection already in progress...");
return 0;
}
server = x_strdup(p->conn_class->next_server->str);
if (strchr(server, ':') != strrchr(server, ':')) {
/* More than one :, second denotes password */
char *pass;
pass = strrchr(server, ':');
*(pass++) = 0;
if (strlen(pass))
p->serverpassword = x_strdup(pass);
}
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Looking up %s...", server);
/* DNS lookup the server */
dns_filladdr((void *)p, server, p->conn_class->server_port, 1,
&(p->server_addr), DNS_FUNCTION(_ircserver_connect2), 0);
free(server);
return 0;
}
/* Called to initiate a connection to a server once its been looked up */
static void _ircserver_connect2(struct ircproxy *p, void *data,
struct in_addr *addr, const char *host) {
if (!host || !addr) {
debug("DNS failure, retrying");
timer_new((void *)p, "server_recon", p->conn_class->server_retry,
TIMER_FUNCTION(_ircserver_reconnect), (void *)0);
free(p->serverpassword);
p->serverpassword = 0;
return;
}
debug("Resolved server");
/* Copy the found information into p */
free(p->servername);
p->servername = x_strdup(host);
p->server_addr.sin_addr.s_addr = addr->s_addr;
if (p->conn_class->local_address) {
dns_addrfromhost((void *)p, 0, p->conn_class->local_address,
DNS_FUNCTION(_ircserver_connect3));
} else {
_ircserver_connect3(p, 0, 0, 0);
}
}
/* Called to initiate a connection to a server once its been looked up
and the local_host has been looked up */
static void _ircserver_connect3(struct ircproxy *p, void *data,
struct in_addr *addr, const char *host) {
int ret;
#ifdef HAVE_SETEUID
int switched = 0;
pid_t old_euid;
#endif /* HAVE_SETEUID */
debug("Connecting to %s port %d", p->servername,
ntohs(p->server_addr.sin_port));
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Connecting to %s port %d",
p->servername, ntohs(p->server_addr.sin_port));
#ifdef HAVE_SETEUID
old_euid = geteuid();
/* Switch to a user */
if (p->conn_class->switch_user) {
struct passwd *pwd;
/* switch_user can be a username or a user id */
pwd = getpwnam(p->conn_class->switch_user);
if (pwd) {
debug("Switching to user '%s'", p->conn_class->switch_user);
} else {
uid_t uid;
/* Make sure that its "0" not an invalid user if atoi returns 0 */
uid = atoi(p->conn_class->switch_user);
if (uid || !strcmp(p->conn_class->switch_user, "0")) {
pwd = getpwuid(uid);
if (pwd)
debug("Switching to user #%d", uid);
}
}
/* Set the effective user id if we're root */
if (pwd && (getuid() == 0)) {
if (!seteuid(pwd->pw_uid)) {
switched = 1;
} else {
syscall_fail("seteuid", 0, 0);
}
}
/* Warn if we didn't */
if (!switched) {
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "(warning) Couldn't switch to username %s",
p->conn_class->switch_user);
}
}
#endif /* HAVE_SETEUID */
p->server_sock = net_socket();
#ifdef HAVE_SETEUID
/* Switch back to our original euid */
if (switched) {
if (seteuid(old_euid)) {
/* Oh, Fuck! */
syscall_fail("seteuid", 0, 0);
abort();
}
}
#endif /* HAVE_SETEUID */
if (p->server_sock == -1) {
ret = -1;
} else {
if (p->conn_class->server_keepalive)
net_keepalive(p->server_sock);
if (p->conn_class->local_address) {
if (addr) {
struct sockaddr_in local_addr;
memset(&local_addr, 0, sizeof(struct sockaddr_in));
local_addr.sin_family = AF_INET;
local_addr.sin_addr.s_addr = addr->s_addr;
if (bind(p->server_sock, (struct sockaddr *)&local_addr,
sizeof(struct sockaddr_in))) {
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "(warning) Couldn't use local address %s",
host);
} else {
free(p->hostname);
p->hostname = (host ? x_strdup(host) : p->conn_class->local_address);
}
} else {
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "(warning) Couldn't find address for %s",
p->conn_class->local_address);
}
}
if (connect(p->server_sock, (struct sockaddr *)&(p->server_addr),
sizeof(struct sockaddr_in))
&& (errno != EINPROGRESS)) {
syscall_fail("connect", p->servername, 0);
net_close(&(p->server_sock));
ret = -1;
} else {
ret = 0;
}
}
if (ret) {
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Connection failed: %s", strerror(errno));
debug("Connection failed: %s", strerror(errno));
net_close(&(p->server_sock));
timer_new((void *)p, "server_recon", p->conn_class->server_retry,
TIMER_FUNCTION(_ircserver_reconnect), (void *)0);
free(p->serverpassword);
p->serverpassword = 0;
} else {
p->server_status |= IRC_SERVER_CREATED;
net_hook(p->server_sock, SOCK_CONNECTING, (void *)p,
ACTIVITY_FUNCTION(_ircserver_connected),
ERROR_FUNCTION(_ircserver_connectfailed));
debug("Connection in progress");
}
}
/* Called when a new server has connected */
static void _ircserver_connected(struct ircproxy *p, int sock) {
if (sock != p->server_sock) {
error("Unexpected socket %d in _ircserver_connected, expected %d", sock,
p->server_sock);
net_close(&sock);
return;
}
debug("Connection succeeded");
p->server_status |= IRC_SERVER_CONNECTED;
net_hook(p->server_sock, SOCK_NORMAL, (void *)p,
ACTIVITY_FUNCTION(_ircserver_data),
ERROR_FUNCTION(_ircserver_error));
if (p->conn_class->server_throttle)
net_throttle(p->server_sock, p->conn_class->server_throttle[0],
p->conn_class->server_throttle[1]);
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Connected to server");
if (p->conn_class->log_events & IRC_LOG_SERVER)
irclog_notice(p, p->nickname, PACKAGE,
"Connected to server: %s", p->servername);
/* Need to try and look up our local hostname now we have a socket to
somewhere that will tell us */
if (!p->hostname) {
struct sockaddr_in sock_addr;
int len;
len = sizeof(struct sockaddr_in);
if (!getsockname(p->server_sock, (struct sockaddr *)&sock_addr, &len)) {
dns_hostfromaddr((void *)p, 0, sock_addr.sin_addr,
DNS_FUNCTION(_ircserver_connected2));
return;
} else {
syscall_fail("getsockname", "", 0);
_ircserver_connected2(p, 0, 0, 0);
}
} else {
_ircserver_connected2(p, 0, 0, 0);
}
}
/* Called when a new server has connected and we have a local hostname */
static void _ircserver_connected2(struct ircproxy *p, void *data,
struct in_addr *addr, const char *name) {
char *username;
if (!p->hostname && name)
p->hostname = x_strdup(name);
username = ircprot_sanitize_username(p->username);
if (p->serverpassword) {
ircserver_send_command(p, "PASS", ":%s", p->serverpassword);
free(p->serverpassword);
p->serverpassword = 0;
}
/* When connecting to a server, may as well try to start fresh, eh? */
if (p->setnickname && strcmp(p->nickname, p->setnickname)) {
free(p->nickname);
p->nickname = x_strdup(p->setnickname);
}
ircserver_send_command(p, "NICK", ":%s", p->nickname);
ircserver_send_command(p, "USER", "%s 0 * :%s", username, p->realname);
p->server_status |= IRC_SERVER_INTRODUCED;
free(username);
/* Begin stoned server checking */
if (p->conn_class->server_pingtimeout) {
timer_new((void *)p, "server_ping",
(int)(p->conn_class->server_pingtimeout / 2),
TIMER_FUNCTION(_ircserver_ping), (void *)0);
timer_new((void *)p, "server_stoned", p->conn_class->server_pingtimeout,
TIMER_FUNCTION(_ircserver_stoned), (void *)0);
}
/* Begin anti-idle */
if (p->conn_class->idle_maxtime)
timer_new((void *)p, "server_antiidle", p->conn_class->idle_maxtime,
TIMER_FUNCTION(_ircserver_antiidle), (void *)0);
}
/* Called when a connection fails */
static void _ircserver_connectfailed(struct ircproxy *p, int sock, int bad) {
if (sock != p->server_sock) {
error("Unexpected socket %d in _ircserver_connectfailed, expected %d", sock,
p->server_sock);
net_close(&sock);
return;
}
debug("Connection failed");
if (IS_CLIENT_READY(p))
ircclient_send_notice(p, "Connection failed: %s", strerror(errno));
net_close(&(p->server_sock));
p->server_status &= ~(IRC_SERVER_CREATED);
timer_new((void *)p, "server_recon", p->conn_class->server_retry,
TIMER_FUNCTION(_ircserver_reconnect), (void *)0);
}
/* Called when a server sends us stuff. */
static void _ircserver_data(struct ircproxy *p, int sock) {
char *str;
if (sock != p->server_sock) {
error("Unexpected socket %d in _ircserver_data, expected %d", sock,
p->server_sock);
net_close(&sock);
return;
}
str = 0;
while (!p->dead && (p->server_status & IRC_SERVER_CONNECTED)
&& net_gets(p->server_sock, &str, "\r\n") > 0) {
debug("<< '%s'", str);
_ircserver_gotmsg(p, str);
free(str);
}
}
/* Called on server disconnection or error */
static void _ircserver_error(struct ircproxy *p, int sock, int bad) {
if (sock != p->server_sock) {
error("Unexpected socket %d in _ircserver_error, expected %d", sock,
p->server_sock);
net_close(&sock);
return;
}
if (bad) {
debug("Socket error");
} else {
debug("Server disconnected");
}
_ircserver_close(p);
}
/* Called when we get an irc protocol data from a server */
static int _ircserver_gotmsg(struct ircproxy *p, const char *str) {
struct ircmessage msg;
int squelch = 1;
int important = 0;
if (ircprot_parsemsg(str, &msg) == -1)
return -1;
/* Check source */
if (!msg.src.orig) {
msg.src.orig = x_strdup(p->servername);
msg.src.fullname = x_strdup(p->servername);
msg.src.name = x_strdup(p->servername);
}
/* 437 is bizarre, it either means Nickname is juped or Channel is juped */
if (!irc_strcasecmp(msg.cmd, "437")) {
if (msg.numparams >= 2) {
if (!irc_strcasecmp(p->nickname, msg.params[1])) {
/* Our nickname is Juped - make it a 433 */
free(msg.cmd);
msg.cmd = x_strdup("433");
} else {
/* Channel is juped - make it a 471 */
free(msg.cmd);
msg.cmd = x_strdup("471");
}
}
}
if (!irc_strcasecmp(msg.cmd, "001")) {
/* Use 001 to get the servername */
if (msg.src.type & IRC_SERVER) {
free(p->servername);
p->servername = x_strdup(msg.src.name);
}
} else if (!irc_strcasecmp(msg.cmd, "002")) {
/* Ignore 002 */
} else if (!irc_strcasecmp(msg.cmd, "003")) {
/* Ignore 003 */
} else if (!irc_strcasecmp(msg.cmd, "004")) {
/* 004 contains all the juicy info, use it */
if (msg.numparams >= 5) {
free(p->servername);
free(p->serverver);
free(p->serverumodes);
free(p->servercmodes);
p->servername = x_strdup(msg.params[1]);
p->serverver = x_strdup(msg.params[2]);
p->serverumodes = x_strdup(msg.params[3]);
p->servercmodes = x_strdup(msg.params[4]);
p->server_status |= IRC_SERVER_GOTWELCOME | IRC_SERVER_SEEN;
p->server_attempts = 0;
if (IS_CLIENT_READY(p) && !(p->client_status & IRC_CLIENT_SENTWELCOME))
ircclient_welcome(p);
}
/* Also use this numeric to send everything state-related to the client.
From this moment on, we assume the server is happy. */
/* Restore the user mode */
if (p->modes)
ircserver_send_command(p, "MODE", "%s +%s", p->nickname, p->modes);
/* Restore the away message */
if (p->awaymessage) {
ircserver_send_command(p, "AWAY", ":%s", p->awaymessage);
} else if (!(p->client_status & IRC_CLIENT_AUTHED)
&& p->conn_class->away_message) {
ircserver_send_command(p, "AWAY", ":%s", p->conn_class->away_message);
}
/* Restore the channel list */
if (p->channels) {
struct ircchannel *c;
c = p->channels;
while (c) {
if (!c->unjoined) {
if (c->key) {
ircserver_send_command(p, "JOIN", "%s :%s", c->name, c->key);
} else {
ircserver_send_command(p, "JOIN", ":%s", c->name);
}
}
c = c->next;
}
}
} else if (!irc_strcasecmp(msg.cmd, "005")) {
/* Ignore 005 */
} else if (!irc_strcasecmp(msg.cmd, "375")) {
/* Ignore 375 unless allow_motd */
if (p->allow_motd)
squelch = 0;
} else if (!irc_strcasecmp(msg.cmd, "372")) {
/* Ignore 372 unless allow_motd */
if (p->allow_motd)
squelch = 0;
} else if (!irc_strcasecmp(msg.cmd, "376")) {
/* Ignore 376 unless allow_motd */
if (p->allow_motd) {
squelch = 0;
p->allow_motd = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "422")) {
/* Ignore 422 unless allow_motd */
if (p->allow_motd) {
squelch = 0;
p->allow_motd = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "431") || !irc_strcasecmp(msg.cmd, "432")
|| !irc_strcasecmp(msg.cmd, "433")
|| !irc_strcasecmp(msg.cmd, "436")
|| !irc_strcasecmp(msg.cmd, "438")) {
/* Our nickname got rejected. Don't update setnickname! */
if (msg.numparams >= 2) {
/* Fall back on our original if we can */
if (strlen(msg.params[0]) && strcmp(msg.params[0], "*")) {
if (p->client_status == IRC_CLIENT_ACTIVE)
ircclient_send_selfcmd(p, "NICK", ":%s", msg.params[0]);
ircclient_nick_changed(p, msg.params[0]);
ircclient_checknickname(p);
squelch = 0;
} else {
/* We don't have a nickname anymore. Don't free it, so we've
still really got the old one lying around. */
p->client_status &= ~(IRC_CLIENT_GOTNICK);
/* If we don't have a client connected, then we have to regenerate
a new nickname ourselves... Otherwise we can just let the client
do it */
if (!(p->client_status & IRC_CLIENT_CONNECTED)) {
ircclient_generate_nick(p, msg.params[1]);
} else {
/* Have to anti-squelch this manually */
net_send(p->client_sock, "%s\r\n", msg.orig);
}
}
} else {
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "471") || !irc_strcasecmp(msg.cmd, "473")
|| !irc_strcasecmp(msg.cmd, "474")) {
if (msg.numparams >= 2) {
/* Can't join a channel */
/* No client connected? Lets rejoin it for it */
if (p->client_status != IRC_CLIENT_ACTIVE) {
struct ircchannel *chan;
chan = ircnet_fetchchannel(p, msg.params[1]);
if (chan) {
chan->inactive = 1;
ircnet_rejoin(p, chan->name);
}
} else {
/* Let it handle it */
ircnet_delchannel(p, msg.params[1]);
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "403") || !irc_strcasecmp(msg.cmd, "475")
|| !irc_strcasecmp(msg.cmd, "476")
|| !irc_strcasecmp(msg.cmd, "405")) {
if (msg.numparams >= 2) {
struct ircchannel *c;
/* Can't join a channel, permanent error */
c = ircnet_fetchchannel(p, msg.params[1]);
if (c) {
/* No client connected? Better notify it */
if (p->client_status != IRC_CLIENT_ACTIVE) {
if (p->conn_class->log_events & IRC_LOG_ERROR) {
if (msg.numparams >= 3) {
irclog_notice(p, p->nickname, PACKAGE,
"Couldn't rejoin %s: %s (%s)",
msg.params[1], msg.params[2], msg.cmd);
} else {
irclog_notice(p, p->nickname, PACKAGE,
"Couldn't rejoin %s (%s)", msg.params[1], msg.cmd);
}
}
/* Set it to an unjoined channel until the client comes back */
c->unjoined = 1;
} else {
/* Client connected, so we really can't join it - delete it */
ircnet_delchannel(p, msg.params[1]);
}
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "411")) {
/* Ignore 411 if squelch_411 */
if (p->squelch_411) {
p->squelch_411 = 0;
} else {
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "324")) {
/* Here be channel modes */
if (msg.numparams >= 2) {
struct ircchannel *c;
/* Set this to 1 in a minute if we need to */
squelch = 0;
c = ircnet_fetchchannel(p, msg.params[1]);
if (c) {
if (msg.numparams >= 3) {
ircnet_channel_mode(p, c, &msg, 2);
} else {
free(c->key);
}
/* Look for the channel in the squelch_modes list */
if (p->squelch_modes) {
struct strlist *s, *l;
l = 0;
s = p->squelch_modes;
while (s) {
if (!irc_strcasecmp(msg.params[1], s->str)) {
struct strlist *n;
n = s->next;
free(s->str);
free(s);
/* Was in the squelch list, so remove it and stop looking */
s = *(l ? &(l->next) : &(p->squelch_modes)) = n;
squelch = 1;
break;
} else {
l = s;
s = s->next;
}
}
}
}
}
} else if (!irc_strcasecmp(msg.cmd, "477")) {
/* No channel modes for this channel */
if (msg.numparams >= 2) {
struct ircchannel *c;
/* Set this to 1 in a minute if we need to */
squelch = 0;
c = ircnet_fetchchannel(p, msg.params[1]);
if (c) {
debug("No channel modes for %s", c->name);
free(c->key);
/* Look for the channel in the squelch_modes list */
if (p->squelch_modes) {
struct strlist *s, *l;
l = 0;
s = p->squelch_modes;
while (s) {
if (!irc_strcasecmp(msg.params[1], s->str)) {
struct strlist *n;
n = s->next;
free(s->str);
free(s);
/* Was in the squelch list, so remove it and stop looking */
s = *(l ? &(l->next) : &(p->squelch_modes)) = n;
squelch = 1;
break;
} else {
l = s;
s = s->next;
}
}
}
}
}
} else if (!irc_strcasecmp(msg.cmd, "PING")) {
/* Reply to pings for the client */
if (msg.numparams == 1) {
net_sendurgent(p->server_sock, "PONG :%s\r\n", msg.params[0]);
debug("=> 'PONG :%s'", msg.params[0]);
} else if (msg.numparams >= 2) {
net_sendurgent(p->server_sock, "PONG %s :%s\r\n",
msg.params[0], msg.params[1]);
debug("=> 'PONG %s :%s'", msg.params[0], msg.params[1]);
}
/* but let it see them */
squelch = 0;
} else if (!irc_strcasecmp(msg.cmd, "PONG")) {
/* Use pongs to reset the server_stoned timer */
if (p->allow_pong)
squelch = 0;
if (p->conn_class->server_pingtimeout) {
timer_del((void *)p, "server_stoned");
timer_new((void *)p, "server_stoned", p->conn_class->server_pingtimeout,
TIMER_FUNCTION(_ircserver_stoned), (void *)0);
p->allow_pong = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "NICK")) {
if (_ircserver_forclient(p, &msg)) {
/* Server telling us our nickname */
if (msg.numparams >= 1) {
if (strcmp(p->nickname, msg.params[0])) {
if (IS_CLIENT_READY(p))
ircclient_send_selfcmd(p, "NICK", ":%s", msg.params[0]);
ircclient_nick_changed(p, msg.params[0]);
if (p->conn_class->log_events & IRC_LOG_NICK)
irclog_notice(p, p->nickname, p->servername,
"You changed your nickname to %s", msg.params[0]);
}
/* Is this as a result of a client NICK command? */
if (p->expecting_nick) {
ircclient_setnickname(p);
p->expecting_nick = 0;
}
ircclient_checknickname(p);
}
} else {
/* Someone changing their nickname */
if (msg.numparams >= 1) {
if (p->conn_class->log_events & IRC_LOG_NICK)
irclog_notice(p, p->nickname, p->servername,
"%s changed nickname to %s",
msg.src.fullname, msg.params[0]);
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "MODE")) {
if (msg.numparams >= 2) {
struct ircchannel *c;
if (!irc_strcasecmp(p->nickname, msg.params[0])) {
/* Personal mode change */
int param;
if (p->conn_class->log_events & IRC_LOG_MODE)
irclog_notice(p, p->nickname, p->servername,
"Your mode was changed: %s", msg.paramstarts[1]);
for (param = 1; param < msg.numparams; param++)
ircclient_change_mode(p, msg.params[param]);
/* Check for refuse modes */
if (p->modes && p->conn_class->refuse_modes &&
(strcspn(p->modes, p->conn_class->refuse_modes)
!= strlen(p->modes))) {
char *mode;
debug("Got refusal mode from server");
ircserver_send_command(p, "QUIT", ":Don't like this server - %s %s",
PACKAGE, VERSION);
mode = x_sprintf("-%s", p->conn_class->refuse_modes);
debug("Auto-mode-change '%s'", mode);
ircclient_change_mode(p, mode);
free(mode);
_ircserver_close(p);
}
} else if ((c = ircnet_fetchchannel(p, msg.params[0]))) {
/* Channel mode change */
ircnet_channel_mode(p, c, &msg, 1);
if (p->conn_class->log_events & IRC_LOG_MODE)
irclog_notice(p, c->name, p->servername, "%s changed mode: %s",
msg.src.fullname, msg.paramstarts[1]);
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "TOPIC")) {
if (msg.numparams >= 2) {
struct ircchannel *c;
/* Channel topic change */
c = ircnet_fetchchannel(p, msg.params[0]);
if (p->conn_class->log_events & IRC_LOG_TOPIC)
irclog_notice(p, c->name, p->servername, "%s changed topic: %s",
msg.src.fullname, msg.paramstarts[1]);
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "JOIN")) {
if (_ircserver_forclient(p, &msg)) {
/* Server telling us we joined a channel */
if (msg.numparams >= 1) {
struct ircchannel *c;
c = ircnet_fetchchannel(p, msg.params[0]);
if (c && c->inactive) {
/* Must have got KICK'd or something ... */
c->inactive = 0;
/* If a client is connected, tell it we just joined and give it
what they missed */
if (p->client_status == IRC_CLIENT_ACTIVE) {
net_send(p->client_sock, "%s\r\n", msg.orig);
if (p->conn_class->chan_log_enabled)
irclog_autorecall(p, msg.params[0]);
}
} else if (c && c->unjoined) {
/* Ah, rejoined a channel we left */
c->unjoined = 0;
squelch = 0;
} else if (!c) {
struct strlist *s;
/* Orginary join */
ircnet_addchannel(p, msg.params[0]);
/* Ask for the channel modes */
s = (struct strlist *)malloc(sizeof(struct strlist));
s->str = x_strdup(msg.params[0]);
s->next = p->squelch_modes;
p->squelch_modes = s;
ircserver_send_command(p, "MODE", ":%s", msg.params[0]);
squelch = 0;
} else {
/* Bizarre, joined a channel we thought we were already on */
squelch = 0;
}
if ((p->client_status != IRC_CLIENT_ACTIVE)
&& (p->conn_class->detach_message)) {
int slashme;
char *msg;
msg = p->conn_class->detach_message;
if ((strlen(msg) >= 5) && !strncasecmp(msg, "/me ", 4)) {
/* Starts with /me */
slashme = 1;
msg += 4;
} else {
slashme = 0;
}
if (slashme) {
ircserver_send_command(p, "PRIVMSG", "%s :\001ACTION %s\001",
c->name, msg);
} else {
ircserver_send_command(p, "PRIVMSG", "%s :%s", c->name, msg);
}
}
if (p->conn_class->log_events & IRC_LOG_JOIN)
irclog_notice(p, msg.params[0], p->servername,
"You joined the channel");
}
} else {
if (msg.numparams >= 1) {
if (p->conn_class->log_events & IRC_LOG_JOIN)
irclog_notice(p, msg.params[0], p->servername,
"%s joined the channel", msg.src.fullname);
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "PART")) {
if (_ircserver_forclient(p, &msg)) {
/* Server telling us we left a channel */
if (msg.numparams >= 1) {
struct ircchannel *c;
if (p->conn_class->log_events & IRC_LOG_PART)
irclog_notice(p, msg.params[0], p->servername,
"You left the channel");
c = ircnet_fetchchannel(p, msg.params[0]);
/* Ignore server PARTs for unjoined channels */
if (c && !c->unjoined)
ircnet_delchannel(p, msg.params[0]);
squelch = 0;
}
} else {
if (msg.numparams >= 1) {
if (p->conn_class->log_events & IRC_LOG_PART)
irclog_notice(p, msg.params[0], p->servername,
"%s left the channel", msg.src.fullname);
}
squelch = 0;
}
} else if (!irc_strcasecmp(msg.cmd, "KICK")) {
if (msg.numparams >= 2) {
if (!irc_strcasecmp(p->nickname, msg.params[1])) {
/* We got kicked off a channel */
if (p->conn_class->log_events & IRC_LOG_KICK) {
if (msg.numparams >= 3) {
irclog_notice(p, msg.params[0], p->servername,
"Kicked off by %s: %s", msg.src.fullname,
msg.params[2]);
} else {
irclog_notice(p, msg.params[0], p->servername,
"Kicked off by %s", msg.src.fullname);
}
}
/* No client connected? Lets rejoin it for it */
if (p->client_status != IRC_CLIENT_ACTIVE) {
struct ircchannel *chan;
chan = ircnet_fetchchannel(p, msg.params[0]);
if (chan) {
chan->inactive = 1;
ircnet_rejoin(p, chan->name);
}
} else {
/* Let it handle it */
ircnet_delchannel(p, msg.params[0]);
}
squelch = 0;
} else {
squelch = 0;
if (p->conn_class->log_events & IRC_LOG_KICK) {
if (msg.numparams >= 3) {
irclog_notice(p, msg.params[0], p->servername,
"%s kicked off by %s: %s", msg.params[1],
msg.src.fullname, msg.params[2]);
} else {
irclog_notice(p, msg.params[0], p->servername,
"%s kicked off by %s", msg.params[1],
msg.src.fullname);
}
}
}
}
} else if (!irc_strcasecmp(msg.cmd, "QUIT")) {
/* Somebody left IRC */
if (p->conn_class->log_events & IRC_LOG_QUIT) {
if (msg.numparams >= 1) {
irclog_notice(p, p->nickname, p->servername, "%s quit from IRC: %s",
msg.src.fullname, msg.params[0]);
} else {
irclog_notice(p, p->nickname, p->servername, "%s quit from IRC",
msg.src.fullname);
}
}
squelch = 0;
} else if (!irc_strcasecmp(msg.cmd, "ERROR")) {
/* Errors are important enough to always forward to the client */
important = 1;
squelch = 0;
} else if (!irc_strcasecmp(msg.cmd, "PRIVMSG")) {
/* All PRIVMSGs go to the client unless we fiddle */
squelch = 0;
if (msg.numparams >= 2) {
struct strlist *list, *s;
char *str;
ircprot_stripctcp(msg.params[1], &str, &list);
/* Privmsgs get logged */
if (str && strlen(str)) {
if (p->conn_class->log_events & IRC_LOG_TEXT)
irclog_msg(p, msg.params[0], msg.src.orig, "%s", str);
}
free(str);
/* Handle CTCP */
str = x_strdup(msg.params[1]);
s = list;
while (s) {
struct ctcpmessage cmsg;
struct strlist *n;
char *unquoted;
int r;
n = s->next;
r = ircprot_parsectcp(s->str, &cmsg);
unquoted = s->str;
free(s);
s = n;
if (r == -1) {
free(unquoted);
continue;
}
if (!strcmp(cmsg.cmd, "ACTION")) {
if (p->conn_class->log_events & IRC_LOG_ACTION)
irclog_ctcp(p, (msg.params != NULL ) ? msg.params[0]: "none", msg.src.orig, "%s", cmsg.orig);
} else if (!strcmp(cmsg.cmd, "DCC")
&& p->conn_class->dcc_proxy_incoming) {
struct sockaddr_in vis_addr;
int len;
/* We need our local address to do anything DCC related */
len = sizeof(struct sockaddr_in);
if ((p->client_status == IRC_CLIENT_ACTIVE) &&
getsockname(p->client_sock, (struct sockaddr *)&vis_addr, &len)) {
syscall_fail("getsockname", "", 0);
} else if ((cmsg.numparams >= 4)
&& (!irc_strcasecmp(cmsg.params[0], "CHAT")
|| !irc_strcasecmp(cmsg.params[0], "SEND"))) {
char *tmp, *ptr, *dccmsg, *rejmsg;
struct in_addr l_addr, r_addr;
int l_port, r_port, t_port;
char *capfile = 0;
char *rest = 0;
int type = 0;
/* Find out what type of DCC request this is */
if (!irc_strcasecmp(cmsg.params[0], "CHAT")) {
/* Can only proxy chats if we have a client */
if (p->client_status == IRC_CLIENT_ACTIVE)
type = DCC_CHAT;
} else if (!irc_strcasecmp(cmsg.params[0], "SEND")) {
/* Check if we're capturing it, instead of proxying */
if (p->conn_class->dcc_capture_directory
&& ((p->client_status != IRC_CLIENT_ACTIVE)
|| p->conn_class->dcc_capture_always))
{
char *file;
/* Filename is after / or \ characters, this fixes any
security issues we might have with it */
debug("Filename given '%s'", cmsg.params[1]);
file = strrchr(cmsg.params[1], '/');
if (file) {
char *ptr;
file++;
ptr = strrchr(file, '\\');
if (ptr)
file = ptr + 1;
} else {
file = strrchr(cmsg.params[1], '\\');
if (file) {
file++;
} else {
file = cmsg.params[1];
}
}
debug("Filtered to '%s'", file);
/* Assuming we got a filename ... */
if (file && strlen(file)) {
type = DCC_SEND_CAPTURE;
if (p->conn_class->dcc_capture_withnick) {
capfile = x_sprintf("%s/%s.%s",
p->conn_class->dcc_capture_directory,
msg.src.name, file);
} else {
capfile = x_sprintf("%s/%s",
p->conn_class->dcc_capture_directory,
file);
}
debug("Capture to '%s'", capfile);
}
} else if (p->client_status == IRC_CLIENT_ACTIVE) {
/* Proxying - so client must be active. See whether to
send it fast or normally */
if (p->conn_class->dcc_send_fast) {
type = DCC_SEND_FAST;
} else {
type = DCC_SEND_SIMPLE;
}
}
}
/* Check whether there's a tunnel port */
t_port = 0;
if (p->conn_class->dcc_tunnel_incoming)
t_port = dns_portfromserv(p->conn_class->dcc_tunnel_incoming);
/* Eww, host order, how the hell does this even work
between machines of a different byte order? */
if (!t_port) {
r_addr.s_addr = strtoul(cmsg.params[2], (char **)NULL, 10);
r_port = atoi(cmsg.params[3]);
} else {
r_addr.s_addr = INADDR_LOOPBACK;
r_port = ntohs(t_port);
}
l_addr.s_addr = ntohl(vis_addr.sin_addr.s_addr);
if (cmsg.numparams >= 5)
rest = cmsg.paramstarts[4];
/* Strip out this CTCP from the message, replacing it in
a moment with dccmsg */
tmp = x_sprintf("\001%s\001", unquoted);
ptr = strstr(str, tmp);
dccmsg = 0;
/* Save this in case we need it later */
rejmsg = x_sprintf(":%s NOTICE %s :\001DCC REJECT %s %s "
"(%s: unable to proxy)\001",
p->nickname, msg.src.name,
cmsg.params[0], cmsg.params[1], PACKAGE);
/* Set up a dcc proxy, note: type is 0 if there isn't a client
active and we're not capturing it. This will send a reject
back which is exactly what we want to do. */
if (ptr && type
&& !dccnet_new(type, p->conn_class->dcc_proxy_timeout,
p->conn_class->dcc_proxy_ports,
p->conn_class->dcc_proxy_ports_sz,
&l_port, r_addr, r_port,
capfile, p->conn_class->dcc_capture_maxsize,
DCCN_FUNCTION(_ircserver_send_dccreject),
p, rejmsg))
{
if (capfile) {
dccmsg = x_strdup("");
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername,
"Captured DCC %s from %s into %s",
cmsg.params[0], msg.src.fullname, capfile);
} else {
dccmsg = x_sprintf("\001DCC %s %s %lu %u%s%s\001",
cmsg.params[0], cmsg.params[1],
l_addr.s_addr, l_port,
(rest ? " " : ""), (rest ? rest : ""));
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername,
"DCC %s Request from %s", cmsg.params[0],
msg.src.fullname);
}
} else if (ptr) {
dccmsg = x_strdup("");
_ircserver_send_dccreject(p, rejmsg);
}
/* Don't need this anymore */
free(rejmsg);
/* Cut out the old CTCP and replace with dccmsg */
if (ptr) {
char *oldstr;
*ptr = 0;
ptr += strlen(tmp);
oldstr = str;
str = x_sprintf("%s%s%s", oldstr, dccmsg, ptr);
free(oldstr);
free(dccmsg);
}
free(tmp);
if (capfile)
free(capfile);
} else {
/* Unknown DCC */
debug("Unknown or Unimplemented DCC request - %s",
cmsg.params[0]);
}
} else if (!strcmp(cmsg.cmd, "PING")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
if (cmsg.numparams >= 1) {
ircserver_send_command(p, "NOTICE", "%s :\001PING %s\001",
msg.src.name, cmsg.paramstarts[0]);
} else {
ircserver_send_command(p, "NOTICE", "%s :\001PING\001",
msg.src.name);
}
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "ECHO")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
if (cmsg.numparams >= 1)
ircserver_send_command(p, "NOTICE", "%s :\001ECHO %s\001",
msg.src.name, cmsg.paramstarts[0]);
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "TIME")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
char tbuf[40];
time_t now;
time(&now);
strftime(tbuf, sizeof(tbuf), CTCP_TIMEDATE_FORMAT, localtime(&now));
ircserver_send_command(p, "NOTICE", "%s :\001TIME %s\001",
msg.src.name, tbuf);
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "CLIENTINFO")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
ircserver_send_command(p, "NOTICE", "%s :\001CLIENTINFO %s\001",
msg.src.name,
"ACTION DCC VERSION CLIENTINFO USERINFO "
"FINGER PING TIME ECHO");
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "VERSION")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
ircserver_send_command(p, "NOTICE", "%s :\001VERSION %s %s - %s\001",
msg.src.name, PACKAGE, VERSION,
"http://dircproxy.sourceforge.net/");
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "USERINFO")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
ircserver_send_command(p, "NOTICE", "%s :\001USERINFO %s -- %s\001",
msg.src.name, PACKAGE, "Saving the world from "
"mutant carrots since 1899!");
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else if (!strcmp(cmsg.cmd, "FINGER")
&& p->conn_class->ctcp_replies
&& (p->client_status != IRC_CLIENT_ACTIVE)) {
ircserver_send_command(p, "NOTICE", "%s :\001FINGER %s %s\001",
msg.src.name, PACKAGE,
"proxying for unconnected client");
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
} else {
/* Unknown CTCP */
if (p->conn_class->log_events & IRC_LOG_CTCP)
irclog_notice(p, msg.params[0], p->servername, "CTCP %s from %s",
cmsg.cmd, msg.src.fullname);
}
ircprot_freectcp(&cmsg);
free(unquoted);
}
/* Send str */
if (strlen(str) && (p->client_status == IRC_CLIENT_ACTIVE))
net_send(p->client_sock, ":%s PRIVMSG %s :%s\r\n",
msg.src.orig, msg.params[0], str);
squelch = 1;
free(str);
}
} else if (!irc_strcasecmp(msg.cmd, "NOTICE")) {
if (msg.numparams >= 1) {
struct strlist *list;
char *str;
ircprot_stripctcp(msg.params[1], &str, &list);
if (str && strlen(str)) {
if (p->conn_class->log_events & IRC_LOG_TEXT)
irclog_notice(p, msg.params[0], msg.src.orig, "%s", str);
}
free(str);
if (list) {
struct strlist *s;
s = list;
while (s) {
struct ctcpmessage cmsg;
struct strlist *n;
int r;
n = s->next;
r = ircprot_parsectcp(s->str, &cmsg);
free(s->str);
free(s);
s = n;
if (r == -1)
continue;
if (p->conn_class->log_events & IRC_LOG_CTCP) {
if (cmsg.numparams >= 1) {
irclog_notice(p, msg.params[0], p->servername,
"CTCP %s reply from %s: %s", cmsg.cmd,
msg.src.fullname, cmsg.paramstarts[0]);
} else {
irclog_notice(p, msg.params[0], p->servername,
"CTCP %s reply from %s",
cmsg.cmd, msg.src.fullname);
}
}
ircprot_freectcp(&cmsg);
}
}
}
/* All NOTICEs go to the client */
squelch = 0;
} else {
squelch = 0;
}
if (!squelch
&& ((p->client_status == IRC_CLIENT_ACTIVE)
|| (important && (p->client_status & IRC_CLIENT_CONNECTED)))) {
net_send(p->client_sock, "%s\r\n", msg.orig);
}
ircprot_freemsg(&msg);
return 0;
}
/* Close the server socket itself */
int ircserver_close_sock(struct ircproxy *p) {
net_close(&(p->server_sock));
p->server_status &= ~(IRC_SERVER_CREATED | IRC_SERVER_CONNECTED
| IRC_SERVER_INTRODUCED | IRC_SERVER_GOTWELCOME);
/* Make sure these don't get triggered */
timer_del((void *)p, "server_ping");
timer_del((void *)p, "server_stoned");
timer_del((void *)p, "server_antiidle");
timer_del((void *)p, "server_recon");
return 0;
}
/* Close the connection to the server */
static int _ircserver_close(struct ircproxy *p) {
ircserver_close_sock(p);
if (IS_CLIENT_READY(p)) {
ircclient_send_notice(p, "Lost connection to server");
_ircserver_lost(p);
}
if (p->conn_class->log_events & IRC_LOG_SERVER)
irclog_notice(p, p->nickname, PACKAGE, "Lost connection to server: %s",
p->servername);
timer_new((void *)p, "server_recon", p->conn_class->server_retry,
TIMER_FUNCTION(_ircserver_reconnect), (void *)0);
return 0;
}
/* Lost the connection to the server while client is active, so send it
PARTs so it doesn't get confused by later JOINs */
static int _ircserver_lost(struct ircproxy *p) {
struct ircchannel *c;
if (IS_CLIENT_READY(p)) {
c = p->channels;
while (c) {
ircclient_send_selfcmd(p, "PART", ":%s", c->name);
c = c->next;
}
}
return 0;
}
/* Drop the connection to the server and reconnect */
int ircserver_connectagain(struct ircproxy *p) {
if (IS_SERVER_READY(p)) {
if (IS_CLIENT_READY(p)) {
ircclient_send_notice(p, "Dropped connnection to server");
if (p->conn_class->log_events & IRC_LOG_SERVER)
irclog_notice(p, p->nickname, PACKAGE,
"Dropped connection to server: %s", p->servername);
_ircserver_lost(p);
}
ircserver_send_command(p, "QUIT", ":Reconnecting to server - %s %s",
PACKAGE, VERSION);
}
if (p->server_status & IRC_SERVER_CREATED)
ircserver_close_sock(p);
/* Reset seen so that we start with initattempts again */
p->server_status &= ~(IRC_SERVER_SEEN);
p->server_attempts = 0;
debug("Connecting again");
ircserver_connect(p);
return 0;
}
/* hook for timer code to ping server */
static void _ircserver_ping(struct ircproxy *p, void *data) {
/* Server might not be ready yet 8*/
if (IS_SERVER_READY(p)) {
debug("Pinging the server");
net_sendurgent(p->server_sock, "PING :%s\r\n", p->servername);
debug("=> 'PING :%s'", p->servername);
}
timer_new((void *)p, "server_ping",
(int)(p->conn_class->server_pingtimeout / 2),
TIMER_FUNCTION(_ircserver_ping), (void *)0);
}
/* hook for timer code to close a stoned server */
static void _ircserver_stoned(struct ircproxy *p, void *data) {
/* Server is, like, stoned. Yeah man! */
if (IS_SERVER_READY(p)) {
debug("Server is stoned, reconnecting");
ircserver_send_command(p, "QUIT", ":Getting off stoned server - %s %s",
PACKAGE, VERSION);
_ircserver_close(p);
}
}
/* hook for timer code to send empty privmsg to prevent idling */
static void _ircserver_antiidle(struct ircproxy *p, void *data) {
if (IS_SERVER_READY(p)) {
debug("Sending anti-idle");
p->squelch_411 = 1;
ircserver_send_command(p, "PRIVMSG", "");
}
timer_new((void *)p, "server_antiidle", p->conn_class->idle_maxtime,
TIMER_FUNCTION(_ircserver_antiidle), (void *)0);
}
/* Reset idle timer */
void ircserver_resetidle(struct ircproxy *p) {
timer_del((void *)p, "server_antiidle");
timer_new((void *)p, "server_antiidle", p->conn_class->idle_maxtime,
TIMER_FUNCTION(_ircserver_antiidle), (void *)0);
}
/* Check if a message is bound for us, and if so check our username and
hostname vars are the same */
static int _ircserver_forclient(struct ircproxy *p, struct ircmessage *msg) {
if (!(msg->src.type & IRC_USER))
return 0;
if (irc_strcasecmp(p->nickname, msg->src.name))
return 0;
if (msg->src.username) {
free(p->username);
p->username = x_strdup(msg->src.username);
}
if (msg->src.hostname) {
free(p->hostname);
p->hostname = x_strdup(msg->src.hostname);
}
return 1;
}
/* send a command to the server with no prefix */
int ircserver_send_command(struct ircproxy *p, const char *command,
const char *format, ...) {
va_list ap;
char *msg;
int ret;
va_start(ap, format);
msg = x_vsprintf(format, ap);
va_end(ap);
ret = net_send(p->server_sock, "%s %s\r\n", command, msg);
debug("-> '%s %s'", command, msg);
free(msg);
return ret;
}
/* Send a DCC reject message */
static int _ircserver_send_dccreject(struct ircproxy *p, const char *msg) {
int ret = 1;
if (p && p->conn_class && p->conn_class->dcc_proxy_sendreject &&
(p->server_status == IRC_SERVER_ACTIVE)) {
ret = net_send(p->server_sock, "%s\r\n", msg);
debug("-> '%s'", msg);
}
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1