/*
* dcc.c: Things dealing client to client connections.
*
* Written By Troy Rollo <troy@cbme.unsw.oz.au>
*
* Copyright (c) 1991, 1992 Troy Rollo.
* Copyright (c) 1992-2004 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: dcc.c,v 1.142 2005/09/21 22:19:19 mrg Exp $");
#if defined(ISC30) && defined(_POSIX_SOURCE)
# undef _POSIX_SOURCE
#include <sys/stat.h>
# define _POSIX_SOURCE
#else
# include <sys/stat.h>
#endif /* ICS30 || _POSIX_SOURCE */
#ifdef HAVE_WRITEV
#include <sys/uio.h>
#endif
#include "server.h"
#include "ircaux.h"
#include "whois.h"
#include "lastlog.h"
#include "ctcp.h"
#include "dcc.h"
#include "hook.h"
#include "vars.h"
#include "window.h"
#include "output.h"
#include "newio.h"
#include "crypt.h"
static void dcc_chat(u_char *);
static void dcc_chat_rename(u_char *);
static void dcc_filesend(u_char *);
static void dcc_getfile(u_char *);
static void dcc_close(u_char *);
static void dcc_rename(u_char *);
static void dcc_send_raw(u_char *);
static void process_incoming_chat(DCC_list *);
static void process_outgoing_file(DCC_list *);
static void process_incoming_file(DCC_list *);
static void process_incoming_raw(DCC_list *);
static void process_incoming_listen(DCC_list *);
#ifndef O_BINARY
#define O_BINARY 0
#endif /* O_BINARY */
struct
{
u_char *name; /* *MUST* be in ALL CAPITALS */
int uniq; /* minimum length to be a unique command */
void (*function)(u_char *);
} dcc_commands[] =
{
{ UP("CHAT"), 2, dcc_chat },
{ UP("LIST"), 1, dcc_list },
{ UP("SEND"), 2, dcc_filesend },
{ UP("GET"), 1, dcc_getfile },
{ UP("CLOSE"), 2, dcc_close },
{ UP("RENAME"), 2, dcc_rename },
{ UP("RAW"), 2, dcc_send_raw },
{ NULL, 0, (void (*)(u_char *)) NULL }
};
/*
* this list needs to be kept in sync with the DCC_TYPES defines
* in dcc.h
*/
u_char *dcc_types[] =
{
UP("<null>"),
UP("CHAT"),
UP("SEND"),
UP("GET"),
UP("RAW_LISTEN"),
UP("RAW"),
NULL
};
struct deadlist
{
DCC_list *it;
struct deadlist *next;
} *deadlist = NULL;
extern int in_ctcp_flag;
extern int dgets_errno;
static off_t filesize = 0;
DCC_list *ClientList = NULL;
static void add_to_dcc_buffer(DCC_list *, u_char *);
static void dcc_really_erase(void);
static void dcc_add_deadclient(DCC_list *);
static int dcc_open(DCC_list *);
static u_char *dcc_time(time_t);
static u_char *dcc_sockname(SOCKADDR_STORAGE *, int);
static u_char *
dcc_sockname(ss, salen)
SOCKADDR_STORAGE *ss;
int salen;
{
static u_char buf[NI_MAXHOST + NI_MAXSERV + 2];
u_char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (getnameinfo((struct sockaddr *)ss, salen,
CP(hbuf), sizeof hbuf, CP(sbuf), sizeof sbuf,
NI_NUMERICSERV | NI_NUMERICHOST))
{
my_strmcpy(hbuf, "[unknown]", sizeof(hbuf) - 1);
my_strmcpy(sbuf, "[unknown]", sizeof(sbuf) - 1);
}
snprintf(CP(buf), sizeof buf, "%s:%s", hbuf, sbuf);
return buf;
}
/*
* dcc_searchlist searches through the dcc_list and finds the client
* with the the flag described in type set.
*/
DCC_list *
dcc_searchlist(name, user, type, flag, othername)
u_char *name,
*user;
int type,
flag;
u_char *othername;
{
DCC_list **Client, *NewClient;
for (Client = (&ClientList); *Client ; Client = (&(**Client).next))
{
if ((((**Client).flags&DCC_TYPES) == type) &&
((!name || (!my_stricmp(name, (**Client).description))) ||
(othername && (**Client).othername && (!my_stricmp(othername, (**Client).othername)))) &&
(my_stricmp(user, (**Client).user)==0))
return *Client;
}
if (!flag)
return NULL;
*Client = NewClient = (DCC_list *) new_malloc(sizeof(DCC_list));
NewClient->flags = type;
NewClient->read = NewClient->write = NewClient->file = -1;
NewClient->filesize = filesize;
NewClient->next = (DCC_list *) 0;
NewClient->user = NewClient->description = NewClient->othername = NULL;
NewClient->bytes_read = NewClient->bytes_sent = 0L;
NewClient->starttime = 0;
NewClient->buffer = 0;
NewClient->remname = 0;
malloc_strcpy(&NewClient->description, name);
malloc_strcpy(&NewClient->user, user);
malloc_strcpy(&NewClient->othername, othername);
time(&NewClient->lasttime);
return NewClient;
}
static void
dcc_add_deadclient(client)
DCC_list *client;
{
struct deadlist *new;
new = (struct deadlist *) new_malloc(sizeof(struct deadlist));
new->next = deadlist;
new->it = client;
deadlist = new;
}
/*
* dcc_erase searches for the given entry in the dcc_list and
* removes it
*/
void
dcc_erase(Element)
DCC_list *Element;
{
DCC_list **Client;
for (Client = &ClientList; *Client; Client = &(**Client).next)
if (*Client == Element)
{
*Client = Element->next;
if ((Element->flags & DCC_TYPES) != DCC_RAW_LISTEN)
new_close(Element->write);
new_close(Element->read);
if (Element->file != -1)
new_close(Element->file);
new_free(&Element->description);
new_free(&Element->user);
new_free(&Element->othername);
new_free(&Element->buffer);
new_free(&Element->remname);
new_free(&Element);
return;
}
}
static void
dcc_really_erase()
{
struct deadlist *dies;
while ((dies = deadlist) != NULL)
{
deadlist = deadlist->next;
dcc_erase(dies->it);
new_free(&dies);
}
}
/*
* Set the descriptor set to show all fds in Client connections to
* be checked for data.
*/
void
set_dcc_bits(rd, wd)
fd_set *rd, *wd;
{
DCC_list *Client;
for (Client = ClientList; Client != NULL; Client = Client->next)
{
#ifdef DCC_CNCT_PEND
if (Client->write != -1 && (Client->flags & DCC_CNCT_PEND))
FD_SET(Client->write, wd);
#endif /* DCC_CNCT_PEND */
if (Client->read != -1)
FD_SET(Client->read, rd);
}
}
/*
* Check all DCCs for data, and if they have any, perform whatever
* actions are required.
*/
void
dcc_check(rd, wd)
fd_set *rd,
*wd;
{
DCC_list **Client;
struct timeval time_out;
int previous_server;
int lastlog_level;
previous_server = from_server;
from_server = (-1);
time_out.tv_sec = time_out.tv_usec = 0;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
for (Client = (&ClientList); *Client != NULL && !break_io_processing;)
{
#ifdef NON_BLOCKING_CONNECTS
/*
* run all connect-pending sockets.. suggested by deraadt@theos.com
*/
if ((*Client)->flags & DCC_CNCT_PEND)
{
SOCKADDR_STORAGE remaddr;
socklen_t rl = sizeof(remaddr);
if (getpeername((*Client)->read, (struct sockaddr *) &remaddr, &rl) != -1)
{
if ((*Client)->flags & DCC_OFFER)
{
(*Client)->flags &= ~DCC_OFFER;
save_message_from();
message_from((*Client)->user, LOG_DCC);
if (((*Client)->flags & DCC_TYPES) != DCC_RAW)
say("DCC %s connection with %s[%s] established",
dcc_types[(*Client)->flags&DCC_TYPES],
(*Client)->user,
dcc_sockname(&remaddr, rl));
restore_message_from();
}
(*Client)->starttime = time(NULL);
(*Client)->flags &= ~DCC_CNCT_PEND;
set_blocking((*Client)->read);
if ((*Client)->read != (*Client)->write)
set_blocking((*Client)->write);
} /* else we're not connected yet */
}
#endif /* NON_BLOCKING_CONNECTS */
if ((*Client)->read != -1 && FD_ISSET((*Client)->read, rd))
{
switch((*Client)->flags & DCC_TYPES)
{
case DCC_CHAT:
process_incoming_chat(*Client);
break;
case DCC_RAW_LISTEN:
process_incoming_listen(*Client);
break;
case DCC_RAW:
process_incoming_raw(*Client);
break;
case DCC_FILEOFFER:
process_outgoing_file(*Client);
break;
case DCC_FILEREAD:
process_incoming_file(*Client);
break;
}
}
if ((*Client)->flags & DCC_DELETE)
{
dcc_add_deadclient(*Client);
Client = (&(**Client).next);
}
else
Client = (&(**Client).next);
}
(void) set_lastlog_msg_level(lastlog_level);
dcc_really_erase();
from_server = previous_server;
}
/*
* Process a DCC command from the user.
*/
void
process_dcc(args)
u_char *args;
{
u_char *command;
int i;
size_t len;
if (!(command = next_arg(args, &args)))
return;
len = my_strlen(command);
upper(command);
for (i = 0; dcc_commands[i].name != NULL; i++)
{
if (!my_strncmp(dcc_commands[i].name, command, len))
{
if (len < dcc_commands[i].uniq)
{
say("DCC command not unique: %s", command );
return;
}
save_message_from();
message_from((u_char *) 0, LOG_DCC);
dcc_commands[i].function(args);
restore_message_from();
return;
}
}
say("Unknown DCC command: %s", command);
}
int listen_dcc(u_char *);
int
listen_dcc(src_host)
u_char *src_host;
{
SOCKADDR_STORAGE *ss;
struct addrinfo hints, *res, *res0;
int err, s;
if (!get_int_var(BIND_LOCAL_DCCHOST_VAR))
src_host = (u_char *) 0;
ss = get_server_localaddr(from_server);
memset(&hints, 0, sizeof hints);
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_UNSPEC;
errno = 0;
err = getaddrinfo(CP(src_host), CP(zero), &hints, &res0);
if (err != 0)
{
errno = err;
return -2;
}
for (res = res0; res; res = res->ai_next) {
if (ss && SS_FAMILY(ss) != res->ai_family)
continue;
if ((s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol)) < 0)
continue;
set_socket_options(s);
if (res->ai_family == AF_INET)
((struct sockaddr_in *)res->ai_addr)->sin_port = htons(get_int_var(DCCPORT_VAR));
#ifdef INET6
else if (res->ai_family == AF_INET6)
((struct sockaddr_in6 *)res->ai_addr)->sin6_port = htons(get_int_var(DCCPORT_VAR));
#endif
if (bind(s, res->ai_addr, res->ai_addrlen) == 0 &&
listen(s, 1) == 0)
{
freeaddrinfo(res0);
return s;
}
close(s);
}
freeaddrinfo(res0);
return -1;
}
static int
dcc_open(Client)
DCC_list *Client;
{
u_char *user, *Type;
int old_server, error;
struct addrinfo hints, *res0;
#ifndef NON_BLOCKING_CONNECTS
SOCKADDR_STORAGE remaddr;
int rl = sizeof(remaddr);
#endif /* NON_BLOCKING_CONNECTS */
user = Client->user;
old_server = from_server;
if (-1 == from_server)
from_server = get_window_server(0);
Type = dcc_types[Client->flags & DCC_TYPES];
if (Client->flags & DCC_OFFER)
{
#ifdef DCC_CNCT_PEND
Client->flags |= DCC_CNCT_PEND;
#endif /* DCC_CNCT_PEND */
if ((Client->write = connect_by_number(Client->remport,
Client->remname, 1, 0, 0)) < 0)
{
save_message_from();
message_from(user, LOG_DCC);
say("Unable to create connection: %s",
errno ? strerror(errno) : "Unknown Host");
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
Client->read = Client->write;
Client->bytes_read = Client->bytes_sent = 0L;
Client->flags |= DCC_ACTIVE;
#ifndef NON_BLOCKING_CONNECTS
Client->flags &= ~DCC_OFFER;
Client->starttime = time(NULL);
if (getpeername(Client->read, (struct sockaddr *) &remaddr, &rl) == -1)
{
save_message_from();
message_from(user, LOG_DCC);
say("DCC error: getpeername failed: %s", strerror(errno));
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
if ((Client->flags & DCC_TYPES) != DCC_RAW)
{
save_message_from();
message_from(user, LOG_DCC);
say("DCC %s connection with %s[%s] established",
Type, user, dcc_sockname(&remaddr, rl));
restore_message_from();
}
#endif /* NON_BLOCKING_CONNECTS */
from_server = old_server;
return 1;
}
else
{
#ifdef DCC_CNCT_PEND
Client->flags |= DCC_WAIT|DCC_CNCT_PEND;
#else
Client->flags |= DCC_WAIT;
#endif /* DCC_CNCT_PEND */
if ((Client->read = listen_dcc(dcc_source_host)) < 0)
{
save_message_from();
message_from(user, LOG_DCC);
say("Unable to initialise connection: %s",
Client->read ? gai_strerror(errno) : strerror(errno));
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
if (Client->flags & DCC_TWOCLIENTS)
{
SOCKADDR_STORAGE locaddr;
SOCKADDR_STORAGE *myip, me;
u_char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
u_char *printhbuf = 0;
u_char *nopath, *sh;
socklen_t sla;
int times = 0;
if ((Client->flags & DCC_FILEOFFER) &&
(nopath = my_rindex(Client->description, '/')))
nopath++;
else
nopath = Client->description;
sla = sizeof locaddr;
getsockname(Client->read,
(struct sockaddr *) &locaddr, &sla);
if ((error = getnameinfo((struct sockaddr *)&locaddr,
sla, 0, 0, CP(sbuf), sizeof sbuf, NI_NUMERICSERV)))
{
save_message_from();
message_from(user, LOG_DCC);
say("Unable to get socket port address: %s",
gai_strerror(error));
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
myip = 0;
sh = dcc_source_host;
if (sh && *sh)
{
do_it_again:
memset(&hints, 0, sizeof hints);
hints.ai_flags = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = PF_UNSPEC;
error = getaddrinfo(CP(sh), 0, &hints, &res0);
if (error == 0)
{
bcopy((char *) res0->ai_addr, &me, sizeof me);
myip = &me;
sla = res0->ai_addrlen;
freeaddrinfo(res0);
}
else
{
save_message_from();
message_from(user, LOG_DCC);
say("Unable to create address from %s: %s", sh,
gai_strerror(error));
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
}
else
{
myip = server_list[from_server].localaddr;
sla = server_list[from_server].localaddrlen;
}
if (!myip)
myip = &locaddr;
error = getnameinfo((struct sockaddr *) myip, sla,
CP(hbuf), sizeof hbuf, 0, 0, NI_NUMERICHOST);
if (error)
{
save_message_from();
message_from(user, LOG_DCC);
say("Unable to getnameinfo: %s", gai_strerror(error));
restore_message_from();
dcc_erase(Client);
from_server = old_server;
return 0;
}
#ifdef INET6
/* Make sure IPv4 goes as a single number */
if (SS_FAMILY(myip) == PF_INET)
#endif
{
struct sockaddr_in *in = (struct sockaddr_in *) myip;
u_32int l;
malloc_strcpy(&printhbuf, hbuf);
bcopy((char *) &in->sin_addr.s_addr, &l, sizeof l);
snprintf(CP(hbuf), sizeof hbuf, "%lu", (unsigned long)ntohl(l));
}
/* if the host is "0", force it to source_host or then hostname */
if (my_strcmp(hbuf, zero) == 0)
{
if (times == 0)
{
sh = source_host;
if (!sh || !*sh)
times++;
}
if (times == 1)
sh = hostname;
else
{
save_message_from();
message_from(user, LOG_DCC);
say("DCC: Unable to generate a source address");
restore_message_from();
dcc_erase(Client);
from_server = old_server;
if (printhbuf)
new_free(&printhbuf);
return 0;
}
times++;
goto do_it_again;
}
/*
* XXX
* should make the case below for the filesize into
* generic off_t2str() function, or something. this
* cast is merely a STOP-GAP measure.
*/
if (Client->filesize)
send_ctcp(ctcp_type[in_ctcp_flag], user, UP("DCC"),
"%s %s %s %s %lu", Type, nopath, hbuf, sbuf,
(u_long)Client->filesize);
else
send_ctcp(ctcp_type[in_ctcp_flag], user, UP("DCC"),
"%s %s %s %s", Type, nopath, hbuf, sbuf);
save_message_from();
message_from(user, LOG_DCC);
say("Sent DCC %s [%s:%s] request to %s",
Type, printhbuf ? printhbuf : hbuf, sbuf, user);
restore_message_from();
if (printhbuf)
new_free(&printhbuf);
}
Client->starttime = 0;
from_server = old_server;
return 2;
}
}
static void
dcc_chat(args)
u_char *args;
{
u_char *user;
DCC_list *Client;
if ((user = next_arg(args, &args)) == NULL)
{
say("You must supply a nickname for DCC CHAT");
return;
}
Client = dcc_searchlist(UP("chat"), user, DCC_CHAT, 1, (u_char *) 0);
if ((Client->flags&DCC_ACTIVE) || (Client->flags&DCC_WAIT))
{
say("A previous DCC CHAT to %s exists", user);
return;
}
Client->flags |= DCC_TWOCLIENTS;
dcc_open(Client);
}
u_char *
dcc_raw_listen(iport)
u_int iport;
{
DCC_list *Client;
u_char PortName[10];
struct sockaddr_in locaddr; /* XXX DCC IPv6: this one doesn't matter; for now only support DCC RAW for ipv4 */
u_char *RetName = NULL;
socklen_t size;
int lastlog_level;
u_short port = (u_short) iport;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
if (port && port < 1025)
{
say("Cannot bind to a privileged port");
(void) set_lastlog_msg_level(lastlog_level);
return NULL;
}
snprintf(CP(PortName), sizeof PortName, "%d", port);
Client = dcc_searchlist(UP("raw_listen"), PortName, DCC_RAW_LISTEN, 1, (u_char *) 0);
if (Client->flags & DCC_ACTIVE)
{
say("A previous DCC RAW_LISTEN on %s exists", PortName);
(void) set_lastlog_msg_level(lastlog_level);
return RetName;
}
if (0 > (Client->read = socket(AF_INET, SOCK_STREAM, 0)))
{
dcc_erase(Client);
say("socket() failed: %s", strerror(errno));
(void) set_lastlog_msg_level(lastlog_level);
return RetName;
}
set_socket_options(Client->read);
bzero((char *) &locaddr, sizeof(locaddr));
locaddr.sin_family = AF_INET;
locaddr.sin_addr.s_addr = htonl(INADDR_ANY);
locaddr.sin_port = htons(port);
if (bind(Client->read, (struct sockaddr *) &locaddr, sizeof(locaddr))
== -1)
{
dcc_erase(Client);
say("Could not bind port: %s", strerror(errno));
(void) set_lastlog_msg_level(lastlog_level);
return RetName;
}
listen(Client->read, 4);
size = sizeof(locaddr);
Client->starttime = time((time_t *) 0);
getsockname(Client->read, (struct sockaddr *) &locaddr, &size);
Client->write = ntohs(locaddr.sin_port);
Client->flags |= DCC_ACTIVE;
snprintf(CP(PortName), sizeof PortName, "%d", Client->write);
malloc_strcpy(&Client->user, PortName);
malloc_strcpy(&RetName, PortName);
(void) set_lastlog_msg_level(lastlog_level);
return RetName;
}
u_char *
dcc_raw_connect(host, iport)
u_char *host;
u_int iport;
{
DCC_list *Client;
struct addrinfo hints, *res = 0, *res0 = 0;
struct sockaddr_in address;
u_char addr[NI_MAXHOST];
u_char PortName[10], *RetName = (u_char *) 0;
u_short port = (u_short)iport;
int lastlog_level, err;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
memset(&hints, 0, sizeof hints);
hints.ai_flags = 0;
hints.ai_protocol = 0;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
err = getaddrinfo(CP(host), 0, &hints, &res0);
if (err == 0)
{
for (res = res0; res; res = res->ai_next)
if (res->ai_family == PF_INET)
{
bcopy(res->ai_addr, &address, sizeof address);
break;
}
freeaddrinfo(res0);
}
if (!res || err)
{
say("Unknown host: %s", host);
(void) set_lastlog_msg_level(lastlog_level);
goto out;
}
snprintf(CP(PortName), sizeof PortName, "%d", port);
Client = dcc_searchlist(host, PortName, DCC_RAW, 1, (u_char *) 0);
if (Client->flags & DCC_ACTIVE)
{
say("A previous DCC RAW to %s on %s exists", host, PortName);
(void) set_lastlog_msg_level(lastlog_level);
return RetName;
}
Client->remport = port;
err = getnameinfo((struct sockaddr *) &address, sizeof address, CP(addr), NI_MAXHOST, 0, 0, NI_NUMERICHOST);
if (err != 0)
{
my_strncpy(addr, "[unknown]", sizeof(addr) - 1);
yell("dcc_raw_connect: getnameinfo failed: %s", gai_strerror(err));
}
malloc_strcpy(&Client->remname, addr);
Client->flags = DCC_OFFER | DCC_RAW;
if (!dcc_open(Client))
return RetName;
snprintf(CP(PortName), sizeof PortName, "%d", Client->read);
malloc_strcpy(&Client->user, PortName);
if (do_hook(DCC_RAW_LIST, "%s %s E %d", PortName, host, port))
put_it("DCC RAW connection to %s on %s via %d established",
host, PortName, port);
malloc_strcpy(&RetName, PortName);
(void) set_lastlog_msg_level(lastlog_level);
out:
return RetName;
}
static void
dcc_filesend(args)
u_char *args;
{
u_char *user;
u_char *filename,
*fullname;
DCC_list *Client;
u_char FileBuf[BIG_BUFFER_SIZE];
struct stat stat_buf;
#ifdef DAEMON_UID
if (DAEMON_UID == getuid())
{
say("You are not permitted to use DCC to exchange files");
return;
}
#endif /* DAEMON_UID */
if (0 == (user = next_arg(args, &args)) ||
0 == (filename = next_arg(args, &args)))
{
say("You must supply a nickname and filename for DCC SEND");
return;
}
if (IS_ABSOLUTE_PATH(filename))
{
my_strmcpy(FileBuf, filename, sizeof FileBuf);
}
else if (*filename == '~')
{
if (0 == (fullname = expand_twiddle(filename)))
{
yell("Unable to expand %s", filename);
return;
}
my_strmcpy(FileBuf, fullname, sizeof FileBuf);
new_free(&fullname);
}
else
{
getcwd(CP(FileBuf), sizeof(FileBuf));
my_strmcat(FileBuf, "/", sizeof FileBuf);
my_strmcat(FileBuf, filename, sizeof FileBuf);
}
if (0 != access(CP(FileBuf), R_OK))
{
yell("Cannot access %s", FileBuf);
return;
}
stat(CP(FileBuf), &stat_buf);
/* some unix didn't have this ???? */
#ifdef S_IFDIR
if (stat_buf.st_mode & S_IFDIR)
{
yell("Cannot send a directory");
return;
}
#endif /* S_IFDER */
if (scanstr(FileBuf, UP("/etc/")))
{
yell("Send request rejected");
return;
}
if ((int) my_strlen(FileBuf) >= 7 && 0 == my_strcmp(FileBuf + my_strlen(FileBuf) - 7, "/passwd"))
{
yell("Send request rejected");
return;
}
filesize = stat_buf.st_size;
Client = dcc_searchlist(FileBuf, user, DCC_FILEOFFER, 1, filename);
if ((Client->file = open(CP(Client->description), O_RDONLY | O_BINARY)) == -1)
{
say("Unable to open %s: %s\n", Client->description,
errno ? strerror(errno) : "Unknown Host");
new_close(Client->read);
Client->read = Client->write = (-1);
Client->flags |= DCC_DELETE;
return;
}
filesize = 0;
if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
{
say("A previous DCC SEND:%s to %s exists", FileBuf, user);
return;
}
Client->flags |= DCC_TWOCLIENTS;
dcc_open(Client);
}
static void
dcc_getfile(args)
u_char *args;
{
u_char *user;
u_char *filename;
DCC_list *Client;
u_char *fullname = (u_char *) 0;
#ifdef DAEMON_UID
if (DAEMON_UID == getuid())
{
say("You are not permitted to use DCC to exchange files");
return;
}
#endif /* DAEMON_UID */
if (0 == (user = next_arg(args, &args)))
{
say("You must supply a nickname for DCC GET");
return;
}
filename = next_arg(args, &args);
if (0 == (Client = dcc_searchlist(filename, user, DCC_FILEREAD, 0, (u_char *) 0)))
{
if (filename)
say("No file (%s) offered in SEND mode by %s",
filename, user);
else
say("No file offered in SEND mode by %s", user);
return;
}
if ((Client->flags & DCC_ACTIVE) || (Client->flags & DCC_WAIT))
{
if (filename)
say("A previous DCC GET:%s to %s exists", filename, user);
else
say("A previous DCC GET to %s exists", user);
return;
}
if (0 == (Client->flags & DCC_OFFER))
{
say("I'm a teapot!");
dcc_erase(Client);
return;
}
if (0 == (fullname = expand_twiddle(Client->description)))
malloc_strcpy(&fullname, Client->description);
Client->file = open(CP(fullname), O_BINARY | O_WRONLY | O_TRUNC | O_CREAT, 0644);
new_free(&fullname);
if (-1 == Client->file)
{
say("Unable to open %s: %s", Client->description,
errno ? strerror(errno) : "<No Error>");
return;
}
Client->flags |= DCC_TWOCLIENTS;
Client->bytes_sent = Client->bytes_read = 0L;
if (!dcc_open(Client))
close(Client->file);
}
void
register_dcc_offer(user, type, description, address, port, size)
u_char *user;
u_char *type;
u_char *description;
u_char *address;
u_char *port;
u_char *size;
{
DCC_list *Client;
u_char *c, *s, *cmd = (u_char *) 0;
unsigned TempInt;
int CType;
int do_auto = 0; /* used in dcc chat collisions */
int lastlog_level;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
if (0 != (c = my_rindex((description), '/')))
description = c + 1;
if ('.' == *description)
*description = '_';
if (size && *size)
filesize = my_atoi(size);
else
filesize = 0;
malloc_strcpy(&cmd, type);
upper(cmd);
if (!my_strcmp(cmd, "CHAT"))
CType = DCC_CHAT;
#ifndef DAEMON_UID
else if (!my_strcmp(cmd, "SEND"))
#else
else if (!my_strcmp(cmd, "SEND") && DAEMON_UID != getuid())
#endif /* DAEMON_UID */
CType = DCC_FILEREAD;
else
{
say("Unknown DCC %s (%s) received from %s", type, description, user);
goto out;
}
Client = dcc_searchlist(description, user, CType, 1, (u_char *) 0);
filesize = 0;
if (Client->flags & DCC_WAIT)
{
new_close(Client->read);
dcc_erase(Client);
if (DCC_CHAT == CType)
{
Client = dcc_searchlist(description, user, CType, 1, (u_char *) 0);
do_auto = 1;
}
else
{
say("DCC %s collision for %s:%s", type, user,
description);
send_ctcp_reply(user, UP("DCC"), "DCC %s collision occured while connecting to %s (%s)", type, nickname, description);
goto out;
}
}
if (Client->flags & DCC_ACTIVE)
{
say("Received DCC %s request from %s while previous session still active", type, user);
goto out;
}
Client->flags |= DCC_OFFER;
sscanf(CP(port), "%u", &TempInt);
if (TempInt < 1024)
{
say("DCC %s (%s) request from %s rejected [addr = %s, port = %d]", type, description, user, address, TempInt);
dcc_erase(Client);
goto out;
}
for (s = address; *s; s++)
if (!isdigit(*s))
break;
if (*s)
malloc_strcpy(&Client->remname, address);
else
{
/* This is definately an IPv4 address, convert to a.b.c.d */
u_char buf[20];
u_long TempLong;
u_int dots[4], i;
sscanf(CP(address), "%lu", &TempLong);
if (0 == TempLong)
{
say("DCC %s (%s) request from %s rejected [addr = %s, port = %d]", type, description, user, address, (int)TempLong);
dcc_erase(Client);
goto out;
}
for (i = 0; i < 4; i++)
{
dots[i] = TempLong & 0xff;
TempLong >>= 8;
}
snprintf(CP(buf), sizeof buf, "%u.%u.%u.%u", dots[3], dots[2], dots[1], dots[0]);
malloc_strcpy(&Client->remname, buf);
}
Client->remport = TempInt;
if (do_auto)
{
say("DCC CHAT already requested by %s, connecting to [%s:%s] ...", user, Client->remname, port);
dcc_chat(user);
}
else if (Client->filesize)
say("DCC %s (%s %lu) request received from %s [%s:%s]", type, description, (u_long)Client->filesize, user, Client->remname, port);
else
say("DCC %s (%s) request received from %s [%s:%s]", type, description, user, Client->remname, port);
if (beep_on_level & LOG_CTCP)
beep_em(1);
out:
set_lastlog_msg_level(lastlog_level);
new_free(&cmd);
}
static void
process_incoming_chat(Client)
DCC_list *Client;
{
SOCKADDR_STORAGE remaddr;
socklen_t sra;
u_char tmp[BIG_BUFFER_SIZE];
u_char tmpuser[IRCD_BUFFER_SIZE];
u_char *s, *bufptr;
long bytesread;
int old_timeout;
size_t len;
save_message_from();
message_from(Client->user, LOG_DCC);
if (Client->flags & DCC_WAIT)
{
sra = sizeof remaddr;
Client->write = accept(Client->read, (struct sockaddr *)
&remaddr, &sra);
if (Client->write == -1)
{
say("DCC chat connect to %s failed in accept: %s",
Client->user, strerror(errno));
Client->read = -1;
Client->flags |= DCC_DELETE;
goto out;
}
new_close(Client->read);
Client->read = Client->write;
Client->flags &= ~DCC_WAIT;
Client->flags |= DCC_ACTIVE;
say("DCC chat connection to %s[%s] established", Client->user, dcc_sockname(&remaddr, sra));
Client->starttime = time(NULL);
goto out;
}
s = Client->buffer;
bufptr = tmp;
if (s && *s)
{
len = my_strlen(s);
my_strncpy(tmp, s, len);
bufptr += len;
}
else
len = 0;
old_timeout = dgets_timeout(1);
bytesread = dgets(bufptr, (int)((sizeof(tmp)/2) - len), Client->read, (u_char *) 0);
(void) dgets_timeout(old_timeout);
switch ((int)bytesread)
{
case -1:
add_to_dcc_buffer(Client, bufptr);
if (Client->buffer && (my_strlen(Client->buffer) > sizeof(tmp)/2))
{
new_free(&Client->buffer);
say("*** dropped long DCC CHAT message from %s", Client->user);
}
break;
case 0:
say("DCC CHAT connection to %s lost: %s", Client->user, dgets_errno == -1 ? "Remote end closed connection" : strerror(dgets_errno));
new_close(Client->read);
Client->read = Client->write = -1;
Client->flags |= DCC_DELETE;
break;
default:
new_free(&Client->buffer);
len = my_strlen(tmp);
if (len > sizeof(tmp)/2)
len = sizeof(tmp)/2;
Client->bytes_read += len;
*tmpuser = '=';
strmcpy(tmpuser+1, Client->user, sizeof(tmpuser)-2);
s = do_ctcp(tmpuser, nickname, tmp);
s[my_strlen(s) - 1] = '\0'; /* remove newline */
if (s && *s)
{
s[sizeof(tmp)/2-1] = '\0'; /* XXX XXX: stop dcc long messages, stupid but "safe"? */
if (do_hook(DCC_CHAT_LIST, "%s %s", Client->user, s))
{
if (away_set)
{
time_t t;
t = time(0);
snprintf(CP(tmp), sizeof tmp, "%s <%.16s>", s, ctime(&t));
s = tmp;
}
put_it("=%s= %s", Client->user, s);
if (beep_on_level & LOG_CTCP)
beep_em(1);
}
}
}
out:
restore_message_from();
}
static void
process_incoming_listen(Client)
DCC_list *Client;
{
SOCKADDR_STORAGE remaddr;
DCC_list *NewClient;
u_char host[NI_MAXHOST], FdName[10], *Name;
socklen_t sra;
int new_socket, err;
sra = sizeof remaddr;
new_socket = accept(Client->read, (struct sockaddr *) &remaddr, &sra);
err = getnameinfo((struct sockaddr *) &remaddr, sra, CP(host), NI_MAXHOST, 0, 0, 0);
if (err != 0)
{
my_strncpy(host, "[unknown]", sizeof(host) - 1);
yell("process_incoming_listen: getnameinfo failed?");
}
Name = host;
snprintf(CP(FdName), sizeof FdName, "%d", new_socket);
NewClient = dcc_searchlist(Name, FdName, DCC_RAW, 1, (u_char *) 0);
NewClient->starttime = time((time_t *) 0);
NewClient->read = NewClient->write = new_socket;
NewClient->flags |= DCC_ACTIVE;
NewClient->bytes_read = NewClient->bytes_sent = 0L;
malloc_strcpy(&NewClient->remname, Name);
if (SS_FAMILY(&remaddr) == PF_INET)
{
struct sockaddr_in *in = (struct sockaddr_in *) &remaddr;
NewClient->remport = in->sin_port;
}
#ifdef INET6
else
if (SS_FAMILY(&remaddr) == PF_INET6)
{
struct sockaddr_in6 *in = (struct sockaddr_in6 *) &remaddr;
NewClient->remport = in->sin6_port;
}
#endif
save_message_from();
message_from(NewClient->user, LOG_DCC);
if (do_hook(DCC_RAW_LIST, "%s %s N %d", NewClient->user,
NewClient->description,
Client->write))
say("DCC RAW connection to %s on %s via %d established",
NewClient->description,
NewClient->user,
Client->write);
restore_message_from();
}
static void
process_incoming_raw(Client)
DCC_list *Client;
{
u_char tmp[BIG_BUFFER_SIZE];
u_char *s, *bufptr;
long bytesread;
int old_timeout;
size_t len;
save_message_from();
message_from(Client->user, LOG_DCC);
s = Client->buffer;
bufptr = tmp;
if (s && *s)
{
len = my_strlen(s);
my_strncpy(tmp, s, len);
bufptr += len;
}
else
len = 0;
old_timeout = dgets_timeout(1);
switch((int)(bytesread = dgets(bufptr, (int)((sizeof(tmp)/2) - len), Client->read, (u_char *) 0)))
{
case -1:
add_to_dcc_buffer(Client, bufptr);
if (Client->buffer && (my_strlen(Client->buffer) > sizeof(tmp)/2))
{
new_free(&Client->buffer);
say("*** dropping long DCC message from %s", Client->user);
}
break;
case 0:
if (do_hook(DCC_RAW_LIST, "%s %s C",
Client->user, Client->description))
say("DCC RAW connection to %s on %s lost",
Client->user, Client->description);
new_close(Client->read);
Client->read = Client->write = -1;
Client->flags |= DCC_DELETE;
(void) dgets_timeout(old_timeout);
break;
default:
new_free(&Client->buffer);
len = my_strlen(tmp);
if (len > sizeof(tmp) / 2)
len = sizeof(tmp) / 2;
tmp[len - 1] = '\0';
Client->bytes_read += len;
if (do_hook(DCC_RAW_LIST, "%s %s D %s",
Client->user, Client->description, tmp))
say("Raw data on %s from %s: %s",
Client->user, Client->description, tmp);
(void) dgets_timeout(old_timeout);
}
restore_message_from();
}
static void
process_outgoing_file(Client)
DCC_list *Client;
{
SOCKADDR_STORAGE remaddr;
socklen_t sra;
u_char tmp[BIG_BUFFER_SIZE];
u_32int bytesrecvd;
int bytesread;
int BlockSize;
save_message_from();
message_from(Client->user, LOG_DCC);
if (Client->flags & DCC_WAIT)
{
sra = sizeof remaddr;
Client->write = accept(Client->read,
(struct sockaddr *) &remaddr, &sra);
new_close(Client->read);
Client->read = Client->write;
Client->flags &= ~DCC_WAIT;
Client->flags |= DCC_ACTIVE;
Client->bytes_sent = 0L;
Client->starttime = time(NULL);
say("DCC SEND connection to %s[%s] established", Client->user,
dcc_sockname(&remaddr, sra));
}
else
{
if ((bytesread = recv(Client->read, (char *) &bytesrecvd, sizeof(u_32int), 0)) < sizeof(u_32int))
{
#ifdef _Windows
int recv_error;
recv_error = WSAGetLastError();
if (bytesread == -1 &&
recv_error == WSAEWOULDBLOCK ||
recv_error == WSAEINTR)
goto out;
#endif /* _Windows */
say("DCC SEND:%s connection to %s lost: %s", Client->description, Client->user, strerror(errno));
new_close(Client->read);
Client->read = Client->write = (-1);
Client->flags |= DCC_DELETE;
new_close(Client->file);
goto out;
}
else
if (ntohl(bytesrecvd) != Client->bytes_sent)
goto out;
}
BlockSize = get_int_var(DCC_BLOCK_SIZE_VAR);
if (BlockSize > sizeof tmp)
BlockSize = sizeof tmp;
else if (BlockSize < 16)
BlockSize = 16;
if ((bytesread = read(Client->file, tmp, sizeof tmp)) != 0)
{
send(Client->write, CP(tmp), (size_t)bytesread, 0);
Client->bytes_sent += bytesread;
}
else
{
/*
* We do this here because lame Ultrix doesn't let us
* call put_it() with a float. Perhaps put_it() should
* be fixed properly, and this kludge removed ..
* sometime.... -phone jan, 1993.
*/
u_char lame_ultrix[10]; /* should be plenty */
time_t xtime = time(NULL) - Client->starttime;
double sent = (double)Client->bytes_sent;
if (sent <= 0)
sent = 1;
sent /= (double)1024.0;
if (xtime <= 0)
xtime = 1;
snprintf(CP(lame_ultrix), sizeof lame_ultrix, "%2.4g", (sent / (double)xtime));
say("DCC SEND:%s to %s completed %s kb/sec",
Client->description, Client->user, lame_ultrix);
new_close(Client->read);
Client->read = Client->write = -1;
Client->flags |= DCC_DELETE;
new_close(Client->file);
}
out:
restore_message_from();
}
static void
process_incoming_file(Client)
DCC_list *Client;
{
u_char tmp[BIG_BUFFER_SIZE];
u_32int bytestemp;
int bytesread;
if ((bytesread = recv(Client->read, CP(tmp), sizeof tmp, 0)) <= 0)
{
/*
* We do this here because lame Ultrix doesn't let us
* call put_it() with a float. Perhaps put_it() should
* be fixed properly, and this kludge removed ..
* sometime.... -phone jan, 1993.
*/
u_char lame_ultrix[10]; /* should be plenty */
time_t xtime = time(NULL) - Client->starttime;
double sent = (double)Client->bytes_read;
#ifdef _Windows
{
int recv_error;
recv_error = WSAGetLastError();
if (bytesread == -1 &&
recv_error == WSAEWOULDBLOCK ||
recv_error == WSAEINTR)
return;
}
#endif /* _Windows */
if (sent <= 0)
sent = 1;
sent /= (double)1024.0;
if (xtime <= 0)
xtime = 1;
snprintf(CP(lame_ultrix), sizeof lame_ultrix, "%2.4g", (sent / (double)xtime));
save_message_from();
message_from(Client->user, LOG_DCC);
say("DCC GET:%s from %s completed %s kb/sec",
Client->description, Client->user, lame_ultrix);
restore_message_from();
new_close(Client->read);
new_close(Client->file);
Client->read = Client->write = (-1);
Client->flags |= DCC_DELETE;
return;
}
write(Client->file, tmp, (size_t)bytesread);
Client->bytes_read += bytesread;
bytestemp = htonl(Client->bytes_read);
send(Client->write, (char *)&bytestemp, sizeof(u_32int), 0);
}
/* flag == 1 means show it. flag == 0 used by redirect */
void
dcc_message_transmit(user, text, type, flag)
u_char *user;
u_char *text;
int type,
flag;
{
DCC_list *Client;
u_char tmp[BIG_BUFFER_SIZE];
u_char nickbuf[128];
u_char thing = '\0';
u_char *host = (u_char *) 0;
crypt_key *key;
u_char *line;
int lastlog_level;
int list = 0;
size_t len;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
switch(type)
{
case DCC_CHAT:
host = UP("chat");
thing = '=';
list = SEND_DCC_CHAT_LIST;
break;
case DCC_RAW:
host = next_arg(text, &text);
if (!host)
{
say("No host specified for DCC RAW");
goto out1;
}
break;
}
save_message_from();
message_from(user, LOG_DCC);
if (!(Client = dcc_searchlist(host, user, type, 0, (u_char *) 0)) || !(Client->flags&DCC_ACTIVE))
{
say("No active DCC %s:%s connection for %s", dcc_types[type], host ? host : (u_char *) "<any>", user);
goto out;
}
#ifdef DCC_CNCT_PEND
/*
* XXX - should make this buffer
* XXX - just for dcc chat ? maybe raw dcc too. hmm.
*/
if (Client->flags & DCC_CNCT_PEND)
{
say("DCC %s:%s connection to %s is still connecting...", dcc_types[type], host ? host : (u_char *) "<any>", user);
goto out;
}
#endif /* DCC_DCNT_PEND */
strmcpy(tmp, text, sizeof tmp);
if (type == DCC_CHAT) {
nickbuf[0] = '=';
strmcpy(nickbuf+1, user, sizeof(nickbuf) - 2);
if ((key = is_crypted(nickbuf)) == 0 || (line = crypt_msg(tmp, key, 1)) == 0)
line = tmp;
}
else
line = tmp;
#ifdef HAVE_WRITEV
{
struct iovec iov[2];
iov[0].iov_base = CP(line);
iov[0].iov_len = len = my_strlen(line);
iov[1].iov_base = "\n";
iov[1].iov_len = 1;
len++;
(void)writev(Client->write, iov, 2);
}
#else
/* XXX XXX XXX THIS IS TERRIBLE! XXX XXX XXX */
#define CRYPT_BUFFER_SIZE (IRCD_BUFFER_SIZE - 50) /* XXX XXX FROM: crypt.c XXX XXX */
strmcat(line, "\n", (size_t)((line == tmp) ? sizeof tmp : CRYPT_BUFFER_SIZE));
len = my_strlen(line);
(void)send(Client->write, line, len, 0);
#endif
Client->bytes_sent += len;
if (flag && type != DCC_RAW) {
if (do_hook(list, "%s %s", Client->user, text))
put_it("=> %c%s%c %s", thing, Client->user, thing, text);
}
out:
restore_message_from();
out1:
set_lastlog_msg_level(lastlog_level);
return;
}
void
dcc_chat_transmit(user, text)
u_char *user;
u_char *text;
{
dcc_message_transmit(user, text, DCC_CHAT, 1);
}
static void
dcc_send_raw(args)
u_char *args;
{
u_char *name;
if (!(name = next_arg(args, &args)))
{
int lastlog_level;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
say("No name specified for DCC RAW");
(void) set_lastlog_msg_level(lastlog_level);
return;
}
dcc_message_transmit(name, args, DCC_RAW, 1);
}
/*
* dcc_time: Given a time value, it returns a string that is in the
* format of "hours:minutes:seconds month day year" . Used by
* dcc_list() to show the start time.
*/
static u_char *
dcc_time(the_time)
time_t the_time;
{
struct tm *btime;
u_char *buf;
static char *months[] =
{
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
btime = localtime(&the_time);
buf = (u_char *) malloc(22);
if (snprintf(CP(buf), 22, "%-2.2d:%-2.2d:%-2.2d %s %-2.2d %d",
btime->tm_hour, btime->tm_min, btime->tm_sec,
months[btime->tm_mon], btime->tm_mday,
btime->tm_year + 1900))
return buf;
else
return empty_string;
}
#define DCC_FORM "%-7.7s %-9.9s %-10.10s %-20.20s %-8.8s %-8.8s %s"
#define DCC_FORM_HOOK "%s %s %s %s %s %s %s"
#define DCC_FORM_HEADER \
"Type", "Nick", "Status", "Start time", "Sent", "Read", "Arguments"
void
dcc_list(args)
u_char *args;
{
DCC_list *Client;
unsigned flags;
int lastlog_level;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
if (do_hook(DCC_LIST_LIST, DCC_FORM_HOOK, DCC_FORM_HEADER))
put_it(DCC_FORM, DCC_FORM_HEADER);
for (Client = ClientList ; Client != NULL ; Client = Client->next)
{
u_char sent[9],
rd[9];
u_char *timestr;
snprintf(CP(sent), sizeof sent, "%ld", (long)Client->bytes_sent);
snprintf(CP(rd), sizeof rd, "%ld", (long)Client->bytes_read);
timestr = (Client->starttime) ? dcc_time(Client->starttime) : empty_string;
flags = Client->flags;
#ifdef DCC_DCNT_PEND
#define DCC_CONT_PEND_FORM flags & DCC_CNCT_PEND ? "Connecting" :
#else /* DCC_DCNT_PEND */
#define DCC_CONT_PEND_FORM /* nothing */
#endif /* DCC_DCNT_PEND */
#define DCC_FORM_BODY dcc_types[flags & DCC_TYPES], \
Client->user, \
flags & DCC_OFFER ? "Offered" : \
flags & DCC_DELETE ? "Closed" : \
flags & DCC_ACTIVE ? "Active" : \
flags & DCC_WAIT ? "Waiting" : \
DCC_CONT_PEND_FORM \
"Unknown", \
timestr, \
sent, \
rd, \
Client->description
if (do_hook(DCC_LIST_LIST, DCC_FORM_HOOK, DCC_FORM_BODY))
put_it(DCC_FORM, DCC_FORM_BODY);
if (*timestr)
new_free(×tr);
}
(void) set_lastlog_msg_level(lastlog_level);
}
#undef DCC_FORM
#undef DCC_FORM_HOOK
#undef DCC_FORM_HEADER
#undef DCC_CONT_PEND_FORM
#undef DCC_FORM_BODY
static void
dcc_close(args)
u_char *args;
{
DCC_list *Client;
unsigned flags;
u_char *Type;
u_char *user;
u_char *description;
int CType;
u_char *cmd = NULL;
int lastlog_level;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
if (!(Type = next_arg(args, &args)) || !(user=next_arg(args, &args)))
{
say("you must specify a type and nick for DCC CLOSE");
goto out;
}
description = next_arg(args, &args);
malloc_strcpy(&cmd, Type);
upper(cmd);
for (CType = 0; dcc_types[CType] != NULL; CType++)
if (!my_strcmp(cmd, dcc_types[CType]))
break;
if (!dcc_types[CType])
say("Unknown DCC type: %s", Type);
else if ((Client = dcc_searchlist(description, user, CType, 0, description)))
{
flags = Client->flags;
if (flags & DCC_DELETE)
goto out;
if ((flags & DCC_WAIT) || (flags & DCC_ACTIVE))
{
new_close(Client->read);
if (Client->file)
new_close(Client->file);
}
say("DCC %s:%s to %s closed", Type,
description ? description : (u_char *) "<any>", user);
dcc_erase(Client);
}
else
say("No DCC %s:%s to %s found", Type,
description ? description : (u_char *) "<any>", user);
new_free(&cmd);
out:
(void) set_lastlog_msg_level(lastlog_level);
}
/* this depends on dcc_rename() setting loglevel */
static void
dcc_chat_rename(args)
u_char *args;
{
DCC_list *Client;
u_char *user;
u_char *temp;
if (!(user = next_arg(args, &args)) || !(temp = next_arg(args, &args)))
{
say("you must specify a current DCC CHAT connection, and a new name for it");
return;
}
if (dcc_searchlist(UP("chat"), temp, DCC_CHAT, 0, (u_char *) 0))
{
say("You already have a DCC CHAT connection with %s, unable to rename.", temp);
return;
}
if ((Client = dcc_searchlist(UP("chat"), user, DCC_CHAT, 0, (u_char *) 0)))
{
new_free(&(Client->user));
malloc_strcpy(&(Client->user), temp);
say("DCC CHAT connection with %s renamed to %s", user, temp);
}
else
say("No DCC CHAT connection with %s", user);
}
static void
dcc_rename(args)
u_char *args;
{
DCC_list *Client;
u_char *user;
u_char *description;
u_char *newdesc;
u_char *temp;
int lastlog_level;
lastlog_level = set_lastlog_msg_level(LOG_DCC);
if ((user = next_arg(args, &args)) && my_strnicmp(user, UP("-chat"), my_strlen(user)) == 0)
{
dcc_chat_rename(args);
return;
}
if (!user || !(temp = next_arg(args, &args)))
{
say("you must specify a nick and new filename for DCC RENAME");
goto out;
}
if ((newdesc = next_arg(args, &args)) != NULL)
description = temp;
else
{
newdesc = temp;
description = NULL;
}
if ((Client = dcc_searchlist(description, user, DCC_FILEREAD, 0, (u_char *) 0)))
{
if (!(Client->flags & DCC_OFFER))
{
say("Too late to rename that file");
goto out;
}
new_free(&(Client->description));
malloc_strcpy(&(Client->description), newdesc);
say("File %s from %s renamed to %s",
description ? description : (u_char *) "<any>", user, newdesc);
}
else
say("No file %s from %s found",
description ? description : (u_char *) "<any>", user);
out:
(void) set_lastlog_msg_level(lastlog_level);
}
/*
* close_all_dcc: We call this when we create a new process so that
* we don't leave any fd's lying around, that won't close when we
* want them to..
*/
void
close_all_dcc()
{
DCC_list *Client;
while ((Client = ClientList))
dcc_erase(Client);
}
static void
add_to_dcc_buffer(Client, buf)
DCC_list *Client;
u_char *buf;
{
if (buf && *buf)
{
if (Client->buffer)
malloc_strcat(&Client->buffer, buf);
else
malloc_strcpy(&Client->buffer, buf);
}
}
syntax highlighted by Code2HTML, v. 0.9.1